diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e8629ce52e142ea9c72450458427e4e64dfbbc22..0000000000000000000000000000000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-language: cpp fortran
-compiler:
-  #- gcc # Avoid gcc because it exhausts travis VM memory
-  - gfortran
-  - clang
-
-before_install:
-   # Add PPA for recent boost libraries
- #- sudo add-apt-repository --yes ppa:boost-latest/ppa
- #- sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
- #- sudo add-apt-repository --yes ppa:kubuntu-ppa/backports
-   # Make sure package index is up to date:
- - sudo apt-get -qq update
- - sudo apt-get install gfortran
-install:
-   # Install build tools:
- - sudo apt-get install -yqq git ninja-build ccache
-   # Install library dependencies:
- - sudo apt-get install -yqq libatlas-dev libatlas-base-dev 
- - sudo apt-get install -yqq freeglut3-dev libglui-dev libglew-dev libxmu-dev
-   # Set user/email so any tests we might have for developers pass
- - git config --global user.email "travis@travis-ci.org"
- - git config --global user.name "Travis CI"
- - mkdir -p ~/VegaFEM-deps/cmake
-   # Download a new cmake since Ubuntu's is horribly old.
- - cd ~/VegaFEM-deps/cmake
- - wget -q "http://www.cmake.org/files/v3.1/cmake-3.1.0-Linux-x86_64.tar.gz"
- - tar xzf "cmake-3.1.0-Linux-x86_64.tar.gz"
- - mv cmake-3.1.0-Linux-x86_64 install
- - cd ~/VegaFEM-deps
- - wget -q "http://www.netlib.org/lapack/lapack-3.5.0.tgz"
- - tar xzf "lapack-3.5.0.tgz"
- - mkdir -p ~/VegaFEM-deps/lapack-build
- - cd ~/VegaFEM-deps/lapack-build
- - ~/VegaFEM-deps/cmake/install/bin/cmake -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo -DBUILD_SHARED_LIBS:BOOL=ON -DBUILD_STATIC_LIBS:BOOL=OFF -DBUILD_TESTING:BOOL=OFF -DLAPACKE:BOOL=ON -DUSE_OPTIMIZED_BLAS:BOOL=ON -DUSE_OPTIMIZED_LAPACK:BOOL=ON ../lapack-3.5.0
- - make
- - sudo make install
-
-script:
- - mkdir ~/VegaFEM-build && cd ~/VegaFEM-build
- - ~/VegaFEM-deps/cmake/install/bin/cmake -G Ninja "-DSITE:STRING=travis-ci.org" "-DBUILDNAME:STRING=${TRAVIS_OS_NAME}-${CC}-Job.${TRAVIS_JOB_NUMBER}-SMTK" -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo -DVegaFEM_ENABLE_OpenGL_SUPPORT:BOOL=ON "-DCMAKE_INSTALL_PREFIX=${HOME}/VegaFEM-install" ${TRAVIS_BUILD_DIR}
- - ninja
- - ninja install
diff --git a/CMake/FindAtlas.cmake b/CMake/FindAtlas.cmake
deleted file mode 100644
index 1e9dd2c566b99854e788c6169d44cff06d1a0d6d..0000000000000000000000000000000000000000
--- a/CMake/FindAtlas.cmake
+++ /dev/null
@@ -1,48 +0,0 @@
-
-find_path(ATLAS_INCLUDE_DIR
-  cblas.h
-  /usr/include
-  /usr/local/include
-  /usr/local/include/atlas
-  )
-
-find_library( ATLAS_LIBRARY
-  NAMES atlas
-  PATHS
-  /usr/local/lib/atlas
-  /usr/lib64/atlas
-  )
-
-find_library( CBLAS_LIBRARY
-  NAMES cblas
-  PATHS
-  /usr/local/lib/atlas
-  /usr/lib64/atlas
-  )
-
-find_library( LAPACK_LIBRARY
-  NAMES lapack
-  PATHS
-  /usr/local/lib/atlas
-  /usr/lib64/atlas
-  )
-
-set(ATLAS_LIBRARIES "${ATLAS_LIBRARY};${CBLAS_LIBRARY};${LAPACK_LIBRARY}")
-set(CBLAS_LIBRARIES "${CBLAS_LIBRARY}")
-set(LAPACK_LIBRARIES "${LAPACK_LIBRARY}")
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(ATLAS
-  REQUIRED_VARS
-    ATLAS_INCLUDE_DIR
-    ATLAS_LIBRARY
-    CBLAS_LIBRARY
-    LAPACK_LIBRARY
-    )
-
-mark_as_advanced(
-    ATLAS_INCLUDE_DIR
-    ATLAS_LIBRARY
-    CBLAS_LIBRARY
-    LAPACK_LIBRARY
-    )
diff --git a/CMake/FindCg.cmake b/CMake/FindCg.cmake
deleted file mode 100644
index d427c78fa27afd6cc569539f43b5068dc5815838..0000000000000000000000000000000000000000
--- a/CMake/FindCg.cmake
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# Try to find nVidia's Cg compiler, runtime libraries, and include path.
-# Once done this will define
-#
-# CG_FOUND        - system has NVidia Cg and it can be used.
-# CG_INCLUDE_PATH = directory where cg.h resides
-# CG_LIBRARY = full path to libCg.so (Cg.DLL on win32)
-# CG_GL_LIBRARY = full path to libCgGL.so (CgGL.dll on win32)
-# CG_COMPILER = full path to cgc (cgc.exe on win32)
-#
-
-# On OSX default to using the framework version of Cg.
-
-IF (APPLE)
-  INCLUDE(${CMAKE_ROOT}/Modules/CMakeFindFrameworks.cmake)
-  SET(CG_FRAMEWORK_INCLUDES)
-  CMAKE_FIND_FRAMEWORKS(Cg)
-  IF (Cg_FRAMEWORKS)
-    FOREACH(dir ${Cg_FRAMEWORKS})
-      SET(CG_FRAMEWORK_INCLUDES ${CG_FRAMEWORK_INCLUDES}
-        ${dir}/Headers ${dir}/PrivateHeaders)
-    ENDFOREACH(dir)
-
-    #Find the include  dir
-    FIND_PATH(CG_INCLUDE_PATH cg.h
-      ${CG_FRAMEWORK_INCLUDES}
-      )
-
-    #Since we are using Cg framework, we must link to it.
-    SET(CG_LIBRARY "-framework Cg" CACHE STRING "Cg library")
-    SET(CG_GL_LIBRARY "-framework Cg" CACHE STRING "Cg GL library")
-  ENDIF (Cg_FRAMEWORKS)
-  FIND_PROGRAM(CG_COMPILER cgc
-    /usr/bin
-    /usr/local/bin
-    DOC "The Cg compiler"
-    )
-ELSE (APPLE)
-  IF (WIN32)
-    FIND_PROGRAM( CG_COMPILER cgc
-      "C:/Program Files/NVIDIA Corporation/Cg/bin"
-      "C:/Program Files/Cg"
-      ${PROJECT_SOURCE_DIR}/../Cg
-      DOC "The Cg Compiler"
-      )
-    IF (CG_COMPILER)
-      GET_FILENAME_COMPONENT(CG_COMPILER_DIR ${CG_COMPILER} PATH)
-      GET_FILENAME_COMPONENT(CG_COMPILER_SUPER_DIR ${CG_COMPILER_DIR} PATH)
-    ELSE (CG_COMPILER)
-      SET (CG_COMPILER_DIR .)
-      SET (CG_COMPILER_SUPER_DIR ..)
-    ENDIF (CG_COMPILER)
-    FIND_PATH( CG_INCLUDE_PATH Cg/cg.h
-      "C:/Program Files/NVIDIA Corporation/Cg/include"
-      "C:/Program Files/Cg"
-      ${PROJECT_SOURCE_DIR}/../Cg
-      ${CG_COMPILER_SUPER_DIR}/include
-      ${CG_COMPILER_DIR}
-      DOC "The directory where Cg/cg.h resides"
-      )
-    FIND_LIBRARY( CG_LIBRARY
-      NAMES Cg
-      PATHS
-      "C:/Program Files/NVIDIA Corporation/Cg/lib"
-      "C:/Program Files/Cg"
-      ${PROJECT_SOURCE_DIR}/../Cg
-      ${CG_COMPILER_SUPER_DIR}/lib
-      ${CG_COMPILER_DIR}
-      DOC "The Cg runtime library"
-      )
-    FIND_LIBRARY( CG_GL_LIBRARY
-      NAMES CgGL
-      PATHS
-      "C:/Program Files/NVIDIA Corporation/Cg/lib"
-      "C:/Program Files/Cg"
-      ${PROJECT_SOURCE_DIR}/../Cg
-      ${CG_COMPILER_SUPER_DIR}/lib
-      ${CG_COMPILER_DIR}
-      DOC "The Cg runtime library"
-      )
-  ELSE (WIN32)
-    FIND_PROGRAM( CG_COMPILER cgc
-      /usr/bin
-      /usr/local/bin
-      DOC "The Cg Compiler"
-      )
-    GET_FILENAME_COMPONENT(CG_COMPILER_DIR "${CG_COMPILER}" PATH)
-    GET_FILENAME_COMPONENT(CG_COMPILER_SUPER_DIR "${CG_COMPILER_DIR}" PATH)
-    FIND_PATH( CG_INCLUDE_PATH Cg/cg.h
-      /usr/include
-      /usr/local/include
-      ${CG_COMPILER_SUPER_DIR}/include
-      DOC "The directory where Cg/cg.h resides"
-      )
-    FIND_LIBRARY( CG_LIBRARY Cg
-      PATHS
-      /usr/lib64
-      /usr/lib
-      /usr/local/lib64
-      /usr/local/lib
-      ${CG_COMPILER_SUPER_DIR}/lib64
-      ${CG_COMPILER_SUPER_DIR}/lib
-      DOC "The Cg runtime library"
-      )
-    FIND_LIBRARY( CG_GL_LIBRARY CgGL
-      PATHS
-      /usr/lib64
-      /usr/lib
-      /usr/local/lib64
-      /usr/local/lib
-      ${CG_COMPILER_SUPER_DIR}/lib64
-      ${CG_COMPILER_SUPER_DIR}/lib
-      DOC "The Cg runtime library"
-      )
-  ENDIF (WIN32)
-ENDIF (APPLE)
-
-IF (CG_INCLUDE_PATH)
-  SET( FOUND_CG 1 CACHE STRING "Set to 1 if CG is found, 0 otherwise")
-ELSE (CG_INCLUDE_PATH)
-  SET( FOUND_CG 0 CACHE STRING "Set to 1 if CG is found, 0 otherwise")
-ENDIF (CG_INCLUDE_PATH)
-
-MARK_AS_ADVANCED( FOUND_CG )
diff --git a/CMake/FindGLUI.cmake b/CMake/FindGLUI.cmake
deleted file mode 100644
index 51797cae4292d2957d0bec9ed33dc06b457202c3..0000000000000000000000000000000000000000
--- a/CMake/FindGLUI.cmake
+++ /dev/null
@@ -1,136 +0,0 @@
-# - Try to find GLUI (GL User Interface)
-# Requires OpenGL and GLUT - searches for them using find_package
-# Once done, this will define
-#
-#	GLUI_INCLUDE_DIR, where to find GL/glui.h (or GLUI/glui.h on mac)
-#	GLUI_LIBRARY, the libraries to link against
-#	GLUI_FOUND, If false, do not try to use GLUI.
-#
-# Plural versions refer to this library and its dependencies, and
-# are recommended to be used instead, unless you have a good reason.
-#
-# Useful configuration variables you might want to add to your cache:
-#   GLUI_ROOT_DIR - A directory prefix to search
-#                  (usually a path that contains include/ as a subdirectory)
-#
-# Original Author:
-# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
-# http://academic.cleardefinition.com
-# Iowa State University HCI Graduate Program/VRAC
-#
-# Copyright Iowa State University 2009-2010.
-# Distributed under the Boost Software License, Version 1.0.
-# (See accompanying file LICENSE_1_0.txt or copy at
-# http://www.boost.org/LICENSE_1_0.txt)
-
-if(GLUI_FIND_QUIETLY)
-	find_package(OpenGL QUIET)
-	find_package(GLUT QUIET)
-else()
-	find_package(OpenGL)
-	find_package(GLUT)
-endif()
-
-if(OPENGL_FOUND AND GLUT_FOUND)
-	if(WIN32)
-		find_path(GLUI_INCLUDE_DIR
-			NAMES
-			GL/glui.h
-			PATHS
-			${GLUI_ROOT_PATH}/include
-			DOC
-			"GLUI include directory")
-		find_library(GLUI_LIBRARY
-			NAMES
-			glui
-			${GLUI_ROOT_DIR}/lib
-			${GLUI_ROOT_DIR}/Release
-			HINTS
-			${OPENGL_LIBRARY_DIR}
-			${OPENGL_INCLUDE_DIR}/../lib
-			DOC
-			"GLUI library")
-		find_library(GLUI_DEBUG_LIBRARY
-			NAMES
-			glui32
-			${GLUI_ROOT_DIR}/lib
-			${GLUI_ROOT_DIR}/Debug
-			HINTS
-			${OPENGL_LIBRARY_DIR}
-			${OPENGL_INCLUDE_DIR}/../lib
-			DOC
-			"GLUI debug library")
-	else()
-		find_library(GLUI_LIBRARY
-			NAMES
-			GLUI
-			glui
-			PATHS
-			${GLUI_ROOT_DIR}/lib64
-			${GLUI_ROOT_DIR}/lib
-			${GLUI_ROOT_DIR}
-			/usr/openwin/lib
-			HINTS
-			${OPENGL_LIBRARY_DIR}
-			${OPENGL_INCLUDE_DIR}/../lib64
-			${OPENGL_INCLUDE_DIR}/../lib
-			DOC
-			"GLUI library")
-
-		if(APPLE)
-			find_path(GLUI_INCLUDE_DIR
-				GLUI/glui.h
-				HINTS
-				${OPENGL_INCLUDE_DIR}
-				DOC
-				"GLUI include directory")
-		else()
-			find_path(GLUI_INCLUDE_DIR
-				GL/glui.h
-				PATHS
-				${GLUI_ROOT_DIR}/include
-				/usr/include/GL
-				/usr/openwin/share/include
-				/usr/openwin/include
-				/opt/graphics/OpenGL/include
-				/opt/graphics/OpenGL/contrib/libglui
-				DOC
-				"GLUI include directory")
-		endif()
-	endif()
-endif()
-
-# handle the QUIETLY and REQUIRED arguments and set xxx_FOUND to TRUE if
-# all listed variables are TRUE
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(GLUI
-	DEFAULT_MSG
-	GLUI_INCLUDE_DIR
-	GLUI_LIBRARY
-	GLUT_FOUND
-	OPENGL_FOUND)
-
-if(GLUI_FOUND)
-	if(WIN32 AND GLUI_LIBRARY AND GLUI_DEBUG_LIBRARY)
-		set(GLUI_LIBRARIES
-			optimized
-			${GLUI_LIBRARY}
-			debug
-			${GLUI_DEBUG_LIBRARY}
-			${GLUT_LIBRARIES}
-			${OPENGL_LIBRARIES})
-	else()
-		set(GLUI_LIBRARIES
-			${GLUI_LIBRARY}
-			${GLUT_LIBRARIES}
-			${OPENGL_LIBRARIES})
-	endif()
-	set(GLUI_INCLUDE_DIRS
-		${GLUI_INCLUDE_DIR}
-		${GLUT_INCLUDE_DIR}
-		${OPENGL_INCLUDE_DIR})
-endif()
-
-if(GLUI_LIBRARY AND GLUI_INCLUDE_DIR)
-	mark_as_advanced(GLUI_INCLUDE_DIR GLUI_LIBRARY GLUI_DEBUG_LIBRARY)
-endif()
diff --git a/CMake/FindLAPACKE.cmake b/CMake/FindLAPACKE.cmake
deleted file mode 100644
index b6fb0f9a5015d8c597b6fb5ccb95fa0e0d4d4273..0000000000000000000000000000000000000000
--- a/CMake/FindLAPACKE.cmake
+++ /dev/null
@@ -1,64 +0,0 @@
-
-find_path(LAPACKE_INCLUDE_DIR
-  lapacke.h
-  /usr/include
-  /usr/local/include
-  )
-
-find_path(CBLAS_INCLUDE_DIR
-  cblas.h
-  /usr/include
-  /usr/local/include
-  PATH_SUFFIXES
-  atlas
-  atlas-base
-  libblas
-  )
-
-find_library(CBLAS_LIBRARY
-  NAMES cblas
-  PATHS
-  /usr/lib64
-  /usr/lib
-  /usr/local/lib64
-  /usr/local/lib
-  PATH_SUFFIXES
-  atlas
-  atlas-base
-  libblas
-  )
-
-# find_library( BLAS_LIBRARY
-#   NAMES blas
-#   PATHS
-#   /usr/lib64
-#   )
-
-
-find_library(LAPACKE_LIBRARY
-  NAMES lapacke
-  PATHS
-  /usr/lib64
-  )
-
-set(LAPACKE_LIBRARIES "${LAPACKE_LIBRARY}")
-set(LAPACK_LIBRARIES "${LAPACKE_LIBRARY}")
-set(CBLAS_LIBRARIES ${CBLAS_LIBRARY}
-#   ${BLAS_LIBRARY}
-  )
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(LAPACKE
-  REQUIRED_VARS
-    LAPACKE_INCLUDE_DIR
-    LAPACKE_LIBRARY
-    CBLAS_LIBRARY
-#     BLAS_LIBRARY
-    )
-
-mark_as_advanced(
-    LAPACKE_INCLUDE_DIR
-    LAPACKE_LIBRARY
-    CBLAS_LIBRARY
-#     BLAS_LIBRARY
-    )
diff --git a/CMake/FindMKL.cmake b/CMake/FindMKL.cmake
deleted file mode 100644
index 9b58459424e43392253232d09fe9595fba53664f..0000000000000000000000000000000000000000
--- a/CMake/FindMKL.cmake
+++ /dev/null
@@ -1,90 +0,0 @@
-#
-
-# INTEL_MKL_INCLUDE_DIR - where to find autopack.h
-# MKLSOLVER_LIBRARIES   - List of fully qualified libraries to link against.
-# MKLSOLVER_FOUND       - Do not attempt to use if "no" or undefined.
-
-set(INTEL_MKL_YEAR 2011)
-set(INTEL_MKL_ARCH intel64)
-find_path(INTEL_MKL_INCLUDE_DIR 
-  mkl_blas.h
-  "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/include"
-  /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/include
-)
-
-# set(LIB_MKL_LIST mkl_intel_ilp64 mkl_intel_thread mkl_core mkl_solver_ilp64 mkl_mc mkl_mc3 mkl_lapack PTHREAD mkl_p4n iomp5)
-
-find_library(MKL_INTEL_ILP64 mkl_intel_ilp64
-  "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-  /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-)
-
-if(CMAKE_COMPILER_IS_GNUCXX )
-    find_library(MKL_THREAD mkl_gnu_thread
-    "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-    /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-    )
-else(CMAKE_COMPILER_IS_GNUCXX )
-    find_library(MKL_THREAD mkl_intel_thread
-    "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-    /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-    )
-endif(CMAKE_COMPILER_IS_GNUCXX )
-
-find_library(MKL_CORE mkl_core
-  "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-  /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-)
-
-find_library(MKL_SOLVER_ILP64 mkl_solver_ilp64
-  "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-  /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-)
-
-find_library(MKL_MC mkl_mc
-  "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-  /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-)
-
-find_library(MKL_MC3 mkl_mc3
-  "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-  /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-)
-
-find_library(MKL_LAPACK mkl_lapack
-  "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-  /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-)
-
-find_library(MKL_P4N mkl_p4n
-  "C:/Program Files/Intel/ComposerXE-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}"
-  /opt/intel/composerxe-${INTEL_MKL_YEAR}/mkl/lib/${INTEL_MKL_ARCH}
-)
-
-set(INTEL_MKL_LIBRARIES ${MKL_P4N} ${MKL_MC3} ${MKL_MC} ${MKL_LAPACK} ${MKL_SOLVER_ILP64} ${MKL_CORE} ${MKL_THREAD} ${MKL_INTEL_ILP64})
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(MKL
-  REQUIRED_VARS
-    INTEL_MKL_INCLUDE_DIR
-    MKL_P4N
-    MKL_MC3 
-    MKL_MC 
-    MKL_LAPACK 
-    MKL_SOLVER_ILP64 
-    MKL_CORE 
-    MKL_THREAD 
-    MKL_INTEL_ILP64
-    )
-    
-mark_as_advanced(
-    INTEL_MKL_INCLUDE_DIR
-    MKL_P4N
-    MKL_MC3 
-    MKL_MC 
-    MKL_LAPACK 
-    MKL_SOLVER_ILP64 
-    MKL_CORE 
-    MKL_THREAD 
-    MKL_INTEL_ILP64
-)
diff --git a/CMake/FindPThreads.cmake b/CMake/FindPThreads.cmake
deleted file mode 100644
index d7f6de4ec3b4cc2f00270c749c033234a50c8545..0000000000000000000000000000000000000000
--- a/CMake/FindPThreads.cmake
+++ /dev/null
@@ -1,43 +0,0 @@
-find_path(PTHREAD_INCLUDE_DIR
-    pthread.h
-    )
-set(PTHREAD_INCLUDE_DIR ${PTHREAD_INCLUDE_DIR})
-
-find_library(PTHREAD_RELEASE_LIBRARY
-  NAMES
-    pthread
-    libpthread
-    )
-if (EXISTS ${PTHREAD_RELEASE_LIBRARY})
-  list(APPEND PTHREAD_LIBRARIES optimized ${PTHREAD_RELEASE_LIBRARY})
-endif()
-find_library(PTHREAD_DEBUG_LIBRARY
-  NAMES
-    pthreadd
-    libpthreadd
-    )
-if (EXISTS ${PTHREAD_DEBUG_LIBRARY})
-  list(APPEND PTHREAD_LIBRARIES debug ${PTHREAD_DEBUG_LIBRARY})
-endif()
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(PTHREAD
-  REQUIRED_VARS
-    PTHREAD_INCLUDE_DIR
-    PTHREAD_LIBRARIES)
-
-mark_as_advanced(
-  PTHREAD_INCLUDE_DIR
-  PTHREAD_DEBUG_LIBRARY
-  PTHREAD_RELEASE_LIBRARY)
-
-if(PTHREAD_FOUND AND NOT TARGET Threads::Threads)
-  add_library(Threads::Threads INTERFACE IMPORTED)
-  target_include_directories(Threads::Threads INTERFACE "${PTHREAD_INCLUDE_DIR}")
-  if (EXISTS ${PTHREAD_DEBUG_LIBRARY})
-    target_link_libraries(Threads::Threads INTERFACE debug ${PTHREAD_DEBUG_LIBRARY})
-  endif()
-  if (EXISTS ${PTHREAD_RELEASE_LIBRARY})
-    target_link_libraries(Threads::Threads INTERFACE optimized ${PTHREAD_RELEASE_LIBRARY})
-  endif()
-endif()
diff --git a/CMake/VegaFEMConfig.cmake.in b/CMake/VegaFEMConfig.cmake.in
deleted file mode 100644
index 2fbabdd72248e7f412a74fe87061d8d47b9bce6a..0000000000000000000000000000000000000000
--- a/CMake/VegaFEMConfig.cmake.in
+++ /dev/null
@@ -1,17 +0,0 @@
-# Add access to dependencies Find<package>.cmake files
-list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake)
-
-
-set(VegaFEM_ENABLE_PTHREADS_SUPPORT @VegaFEM_ENABLE_PTHREADS_SUPPORT@)
-if(VegaFEM_ENABLE_PTHREADS_SUPPORT)
-  if(WIN32)
-    find_package(PThreads REQUIRED)
-  else()
-    find_package(Threads REQUIRED)
-  endif()
-endif()
-
-# Remove access to those dependencies
-list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/CMake)
-
-include("${CMAKE_CURRENT_LIST_DIR}/VegaFEMTargets.cmake")
diff --git a/CMake/VegaFEMMacros.cmake b/CMake/VegaFEMMacros.cmake
deleted file mode 100644
index 85d9e105d03a5adfb28ffcf22cab6f6d4b97caa4..0000000000000000000000000000000000000000
--- a/CMake/VegaFEMMacros.cmake
+++ /dev/null
@@ -1,42 +0,0 @@
-function(vega_install_library target)
-  set(options)
-  set(oneValueArgs)
-  set(multiValueArgs DEPENDS)
-  cmake_parse_arguments(target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
-  install(TARGETS ${target}
-    EXPORT VegaFEMTargets
-    RUNTIME DESTINATION bin
-    LIBRARY DESTINATION lib
-    ARCHIVE DESTINATION lib
-    INCLUDES DESTINATION include
-  )
-endfunction()
-
-function(vega_add_library target)
-  set(options)
-  set(oneValueArgs)
-  set(multiValueArgs SOURCES PUBLIC_HEADERS)
-  if (NOT WIN32)
-    set(libtype SHARED)
-  else()
-    set(libtype STATIC)
-  endif()
-  cmake_parse_arguments(target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
-  add_library(${target} ${libtype}
-    ${target_SOURCES}
-  )
-  target_include_directories(${target}
-    PUBLIC
-      $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-      $<INSTALL_INTERFACE:include/vega>
-      $<INSTALL_INTERFACE:include/vega/${target}>
-    )
-  if (target_PUBLIC_HEADERS)
-    vega_install_library(${target})
-    install(FILES ${target_PUBLIC_HEADERS}
-      DESTINATION include/vega/${target}
-      COMPONENT Devel
-    )
-  endif()
-endfunction()
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index 62641a42f561a828986e9519ff863723506f029e..0000000000000000000000000000000000000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,232 +0,0 @@
-# *******************************************************************
-# ***                  VegaFEM CMakeLists.txt                     ***
-# *******************************************************************
-
-# This file contains the top level CMakeLists.txt logic for the
-# Vega FEM software package.
-
-project(VegaFEM)
-set(VegaFEM_VERSION 2.0)
-
-# Minimum required version of CMake
-cmake_minimum_required(VERSION 2.8)
-if(COMMAND CMAKE_POLICY)
-  cmake_policy(SET CMP0003 NEW)
-  if (POLICY CMP0022)
-    # Use INTERFACE_LINK_LIBRARIES when available.
-    cmake_policy(SET CMP0022 NEW)
-  endif()
-  if (POLICY CMP0042)
-    cmake_policy(SET CMP0042 NEW)
-  endif()
-endif(COMMAND CMAKE_POLICY)
-
-set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
-
-include(VegaFEMMacros)
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE OFF)
-option(VegaFEM_BUILD_UTILITIES "Build utility applications in addition to libraries." ON)
-option(VegaFEM_BUILD_MODEL_REDUCTION "Should model-reduction techniques be included?" ON)
-option(VegaFEM_ENABLE_ExpoKit_SUPPORT "Should matrix classes use ExpoKit for exponentiation?" OFF)
-# TODO: define USE_EXPOKIT if enabled
-option(VegaFEM_ENABLE_OpenGL_SUPPORT "Should modules that require OpenGL be enabled?" ON)
-option(VegaFEM_ENABLE_PTHREADS_SUPPORT "Use multithreading (pthread)" ON)
-# TODO: add option for arpack++ and define USEARPACKPLUSPLUS
-# TODO: add option for spooles and define SPOOLES_SOLVER_IS_AVAILABLE
-
-if (VegaFEM_ENABLE_OpenGL_SUPPORT)
-  # Only add Cg support if OpenGL is turned on. This may change in the future.
-  option(VegaFEM_ENABLE_Cg_SUPPORT "Should modules that require Cg be enabled?" OFF)
-endif()
-
-#---------------------------------------------------------------------
-# The following logic is what allows binaries to run successfully in
-# the build directory AND install directory.  Thanks to plplot for
-# identifying the necessity of setting CMAKE_INSTALL_NAME_DIR on OSX.
-# Documentation of these options is available at
-# http://www.cmake.org/Wiki/CMake_RPATH_handling
-
-# use, i.e. don't skip the full RPATH for the build tree
-set(CMAKE_SKIP_BUILD_RPATH  FALSE)
-
-# when building, don't use the install RPATH already
-# (but later on when installing)
-set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
-
-# the RPATH/INSTALL_NAME_DIR to be used when installing
-if (NOT APPLE)
-  set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib:\$ORIGIN/../lib")
-endif(NOT APPLE)
-# On OSX, we need to set INSTALL_NAME_DIR instead of RPATH
-# http://www.cmake.org/cmake/help/cmake-2-8-docs.html#variable:CMAKE_INSTALL_NAME_DIR
-set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib")
-
-# add the automatically determined parts of the RPATH which point to
-# directories outside the build tree to the install RPATH
-set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
-
-if (VegaFEM_ENABLE_OpenGL_SUPPORT)
-  find_package(OpenGL REQUIRED)
-  find_package(GLUT REQUIRED)
-  find_package(GLEW REQUIRED)
-
-  include_directories(${OPENGL_INCLUDE_DIR})
-  include_directories(${GLUT_INCLUDE_DIR})
-  include_directories(${GLEW_INCLUDE_DIR})
-
-  if (GLUT_FOUND AND APPLE)
-    mark_as_advanced(GLUT_cocoa_LIBRARY)
-  endif()
-
-  if (VegaFEM_ENABLE_Cg_SUPPORT)
-    find_package(Cg REQUIRED)
-    mark_as_advanced(
-      CG_COMPILER
-      CG_GL_LIBRARY
-      CG_INCLUDE_PATH
-      CG_LIBRARY
-    )
-  endif()
-endif()
-
-if(VegaFEM_ENABLE_PTHREADS_SUPPORT)
-  if(WIN32)
-    set(THREADS_USE_PTHREADS_WIN32 1)
-    find_package(PThreads REQUIRED)
-    configure_file(
-      CMake/FindPThreads.cmake
-      "${CMAKE_CURRENT_BINARY_DIR}/CMake/FindPThreads.cmake"
-      COPYONLY
-    )
-  else()
-    find_package(Threads REQUIRED)
-  endif()
-endif()
-
-if (VegaFEM_BUILD_MODEL_REDUCTION)
-  if (NOT APPLE)
-    find_package(MKL)
-    if(MKL_FOUND)
-      set(BLA_VENDOR "Intel mkl")
-      include_directories(${INTEL_MKL_INCLUDE_DIR})
-      # TODO: set option for the paradiso solvers and define PARDISO_SOLVER_IS_AVAILABLE
-    else()
-      find_package(LAPACKE REQUIRED) # Also searches for cblas
-      find_package(LAPACK REQUIRED)
-      include_directories(${LAPACKE_INCLUDE_DIR})
-      include_directories(${CBLAS_INCLUDE_DIR})
-    endif()
-  endif()
-endif()
-
-if (VegaFEM_ENABLE_OpenGL_SUPPORT AND VegaFEM_BUILD_UTILITIES)
-  find_package(GLUI)
-  if(NOT GLUI_FOUND)
-    add_subdirectory(thirdparty/glui/2.36/src)
-    set(GLUI_LIBRARY glui)
-    set(GLUI_INCLUDE_DIR) # Empty this as library will export its own includes.
-  endif()
-endif()
-
-set(VegaFEM_Modules
-  minivector
-  matrixIO
-  sparseMatrix
-  graph
-  imageIO
-  configFile
-  integrator
-  performanceCounter
-  insertRows
-  sparseSolver
-  forceModel
-  integratorSparse
-  objMesh
-  polarDecomposition
-  volumetricMesh
-  corotationalLinearFEM
-  massSpringSystem
-  stvk
-  quaternion
-  isotropicHyperelasticFEM
-  elasticForceModel
-  loadList
-  vega-getopts
-  clothBW
-  hashTable
-  rigidBodyDynamics
-)
-if (VegaFEM_BUILD_MODEL_REDUCTION)
-  set(VegaFEM_Modules
-    ${VegaFEM_Modules}
-    integratorDense
-    matrix
-    modalMatrix
-    reducedElasticForceModel
-    reducedForceModel
-    reducedStvk
-  )
-endif()
-if (VegaFEM_ENABLE_OpenGL_SUPPORT)
-  set(VegaFEM_Modules
-    ${VegaFEM_Modules}
-    camera
-    lighting
-    sceneObject
-    glslPhong
-  )
-  if (VegaFEM_ENABLE_Cg_SUPPORT)
-    set(VegaFEM_Modules
-      ${VegaFEM_Modules}
-      objMeshGPUDeformer
-    )
-  endif()
-  if (VegaFEM_BUILD_MODEL_REDUCTION)
-    set(VegaFEM_Modules
-      ${VegaFEM_Modules}
-      openGLHelper
-      sceneObjectReduced
-      renderVolumetricMesh
-    )
-  endif()
-endif()
-
-add_subdirectory(src)
-
-include(CMakePackageConfigHelpers)
-write_basic_package_version_file(
-  "${CMAKE_CURRENT_BINARY_DIR}/VegaFEMConfigVersion.cmake"
-  VERSION ${VegaFEM_VERSION}
-  COMPATIBILITY AnyNewerVersion
-)
-
-export(EXPORT VegaFEMTargets
-  FILE "${CMAKE_CURRENT_BINARY_DIR}/VegaFEMTargets.cmake"
-  NAMESPACE VegaFEM::
-)
-configure_file(
-  CMake/VegaFEMConfig.cmake.in
-  "${CMAKE_CURRENT_BINARY_DIR}/VegaFEMConfig.cmake"
-  @ONLY
-)
-
-
-set(ConfigPackageLocation lib/cmake/VegaFEM)
-install(EXPORT VegaFEMTargets
-  FILE VegaFEMTargets.cmake
-  NAMESPACE VegaFEM::
-  DESTINATION ${ConfigPackageLocation}
-)
-
-install(
-  FILES
-    "${CMAKE_CURRENT_BINARY_DIR}/VegaFEMConfig.cmake"
-    "${CMAKE_CURRENT_BINARY_DIR}/VegaFEMConfigVersion.cmake"
-  DESTINATION
-    ${ConfigPackageLocation}
-  COMPONENT
-    Devel
-)
-
-add_subdirectory(include)
diff --git a/LICENSE b/LICENSE.txt
similarity index 97%
rename from LICENSE
rename to LICENSE.txt
index ccfe089146b7729f452eb592ba565c05e0284c38..7f27c66b1ab7d18ccb4c529ff0ebc343595a8061 100644
--- a/LICENSE
+++ b/LICENSE.txt
@@ -1,75 +1,75 @@
-* Copyright (c) 2013, University of Southern California
-* All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are met:
-*     * Redistributions of source code must retain the above copyright
-*       notice, this list of conditions and the following disclaimer.
-*     * Redistributions in binary form must reproduce the above copyright
-*       notice, this list of conditions and the following disclaimer in the
-*       documentation and/or other materials provided with the distribution.
-*     * Neither the name of University of Southern California, nor the
-*       names of its contributors may be used to endorse or promote products
-*       derived from this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SOUTHERN CALIFORNIA ``AS IS'' AND ANY
-* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-* DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SOUTHERN CALIFORNIA BE LIABLE FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-* Copyright (c) 2009, Massachusetts Institute of Technology
-* All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are met:
-*     * Redistributions of source code must retain the above copyright
-*       notice, this list of conditions and the following disclaimer.
-*     * Redistributions in binary form must reproduce the above copyright
-*       notice, this list of conditions and the following disclaimer in the
-*       documentation and/or other materials provided with the distribution.
-*     * Neither the name of Massachusetts Institute of Technology, nor the
-*       names of its contributors may be used to endorse or promote products
-*       derived from this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY MASSACHUSETTS INSTITUTE OF TECHNOLOGY ``AS IS'' AND ANY
-* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-* DISCLAIMED. IN NO EVENT SHALL MASSACHUSETTS INSTITUTE OF TECHNOLOGY BE LIABLE FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-* Copyright (c) 2007, Carnegie Mellon University
-* All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are met:
-*     * Redistributions of source code must retain the above copyright
-*       notice, this list of conditions and the following disclaimer.
-*     * Redistributions in binary form must reproduce the above copyright
-*       notice, this list of conditions and the following disclaimer in the
-*       documentation and/or other materials provided with the distribution.
-*     * Neither the name of Carnegie Mellon University, nor the
-*       names of its contributors may be used to endorse or promote products
-*       derived from this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
-* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
+* Copyright (c) 2016, University of Southern California
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of University of Southern California, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SOUTHERN CALIFORNIA ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SOUTHERN CALIFORNIA BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+* Copyright (c) 2009, Massachusetts Institute of Technology
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Massachusetts Institute of Technology, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY MASSACHUSETTS INSTITUTE OF TECHNOLOGY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL MASSACHUSETTS INSTITUTE OF TECHNOLOGY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+* Copyright (c) 2007, Carnegie Mellon University
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/README b/README
deleted file mode 100644
index 8a38bd9da85e1ed8e0453eee15b838c66432eb58..0000000000000000000000000000000000000000
--- a/README
+++ /dev/null
@@ -1,42 +0,0 @@
-                           VegaFEM Library
-                             Version 2.0
-                       http://run.usc.edu/vega/
-
-Vega is a computationally efficient and stable C/C++ physics library for
-three-dimensional deformable object simulation. It is designed to model large
-deformations, including geometric and material nonlinearities, and can also
-efficiently simulate linear systems. Vega is open-source and free. It is
-released under the 3-clause BSD license, which means that it can be used both
-in academic research and in commercial applications.
-
-Vega implements several widely used methods for simulation of large
-deformations of 3D solid deformable objects:
-
-  * co-rotational linear FEM elasticity [MG04]; it can also compute the exact
-    tangent stiffness matrix [Bar12] (similar to [CPSS10]), 
-  * linear FEM elasticity [Sha90], 
-  * invertible isotropic nonlinear FEM models [ITF04, TSIF05],
-  * Saint-Venant Kirchhoff FEM deformable models (see [Bar07]), and 
-  * mass-spring systems.
-
-For any 3D tetrahedral or cubic mesh, Vega can compute both internal elastic
-forces and their gradients (tangent stiffness matrix), in any deformed
-configuration. Different parts of the mesh can be assigned arbitrary material
-properties. Vega can also timestep these models in time under any
-user-specified forces, using several provided integrators: implicit backward
-Euler [BW98], implicit Newmark [Wri02], explicit central differences [Wri02]
-and symplectic Euler. All models include support for multi-core computing. In
-addition to linear materials, Vega provides neo-Hookean and Mooney-Rivlin
-nonlinear material models; arbitrary nonlinear material models can be added to
-Vega. For isotropic hyperelastic materials, this is as easy as defining an
-energy function, and its first and second derivatives.
-
-Vega is a middleware physics library. It is aimed at researchers and engineers
-with some preexisting knowledge in numerical simulation, computer graphics
-and/or solid mechanics, who can integrate Vega into their projects. The
-strength of Vega lies in its many C/C++ libraries which depend minimally on
-each other, and are in most cases independently reusable. Vega contains about
-50,000 lines of C/C++ code.
-
-Most of Vega was written by Jernej Barbič. Other code contributors include Fun
-Shing Sin, Daniel Schroeder and Christopher Twigg.
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 5c78af4c423b1753ff189e6fc73a351eb492debf..0000000000000000000000000000000000000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-branches:
-  only:
-    - AddWindowsAndLapackFixes
-
-clone_depth: 16
-
-cache:
- - cmake-inst
-
-environment:
-  matrix:
-    #- GENERATOR: "Visual Studio 10 2010"
-    #  VS_VERSION: "10.0"
-    - GENERATOR: "Ninja"
-      VS_VERSION: "10.0"
-
-platform:
- - x86
-
-configuration:
- - Release
-
-install:
- - ps: cinst ninja
- - ps: cinst cmake
-
-build_script:
- - cmd: set PATH=C:/ProgramData/chocolatey/bin;C:/tools/ninja;%PATH%
- - cmd: call "C:/Program Files (x86)/Microsoft Visual Studio %VS_VERSION%/Common7/Tools/vsvars32.bat"
-   # Get a list of generators and the CMake version
- - cmd: cmake --version
- - cmd: cmake --help
-   # Create a build directory inside the source tree
- - cmd: mkdir build
- - cmd: cd build
-   # Run CMake on the source and build
- - cmd: cmake .. -Wdev --warn-uninitialized -G "%GENERATOR%" -D"VegaFEM_BUILD_MODEL_REDUCTION:BOOL=OFF" -D"VegaFEM_ENABLE_PTHREADS_SUPPORT:BOOL=OFF" -D"VegaFEM_ENABLE_OpenGL_SUPPORT:BOOL=OFF" -D"VegaFEM_BUILD_UTILITIES:BOOL=ON"
- - cmd: cmake --build .
-
-test_script:
-# - cmd: ctest -C Debug --output-on-failure
- - cmd: cd ..
diff --git a/doc/CHANGES.v1.1.txt b/doc/CHANGES.v1.1.txt
deleted file mode 100644
index 9a490a7d8aa63c9579ebc826f1f04f16b12df7b6..0000000000000000000000000000000000000000
--- a/doc/CHANGES.v1.1.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-Changes in version 1.1: 
-=======================
-
-integrator: improved comments in ImplicitNewmarkSparse and ImplicitBackwardEulerSparse
-integrator: both ImplicitNewmarkSparse and ImplicitBackwardEulerSparse now use a zero initial guess for each iteration (only relevant with PCG; before: ImplicitNewmarkSparse used previous timestep solution)
-isotropicHyperelasticFEM: improved the text description (in the comment) of the Mooney-Rivlin material model
-massSpringSystem: renamed ObjConfig => ObjMeshConfig
-massSpringSystem: added MassSpringSystemobjMesh::getRestPositions
-objMesh: added ObjMesh::setSingleMaterial
-objMesh: added ObjMesh::getTriangle
-objMesh: fixed the "iFace" bug with custom colors in ObjMeshRender
-objMesh: changed delete to free in ObjMeshRender
-objMesh: renamed findClosestVertex => getClosestVertex 
-objMesh: snychronized interface in ObjMeshRender with ObjMesh
-sceneObject: renamed FindClosestVertex => GetClosestVertex 
-sceneObject: added SceneObject::GetNumFaces
-sparseMatrix: improved headers in sparseMatrix.h: MultiplyMatrix*
-sparseMatrix: added SparseMatrix::AppendRowsColumns
-sparseMatrix: added SparseMatrix::MakeDenseMatrixTranspose
-sparseMatrix: fixed a rare bug in SparseMatrix::RemoveColumns
-sparseMatrix: changed the return type of Transpose to "SparseMatrix *"
-sparseSolver: added the ability to pass a diagonal preconditioner to the "black-box" mat-vec multiplication constructor in CGSolver
-sparseSolver: added PardisoSolver::SolveLinearSystemMultipleRHS
-volumetricMesh: added VolumetricMesh::getDefaultMaterial(double * E, double * nu, double * density)
-volumetricMesh: fixed a bug in the alternative constructor, VolumetricMesh::VolumetricMesh(int numVertices_, double * vertices_, int numElements_, int numElementVertices_, int * elements_, double E, double nu, double density): numElementVertices(numElementVertices_);
-volumetricMesh: added VolumetricMesh::getVolume and VolumetricMesh::getVertexVolumes
-volumetricMesh: added optional last parameter to VolumetricMesh::setToSubsetMesh
-volumetricMesh: added VolumetricMesh::computeGradient and VolumetricMesh::interpolateGradient
-volumetricMesh: added material information to the output mesh of GenerateSurfaceMesh::ComputeMesh
-volumetricMesh: added CubicMesh::subdivide
-volumetricMesh: changed "wa" to "w" and "ra" to "r" in fopen
-generateInterpolant: minor change (an extra printf)
-interactiveDeformableSimulator: minor changes to accommodate the above library changes
-cleaned up license headers (minor, stylistic change, no change in actual license)
-files converted to unix-style end-of-line characters
-small improvements to Makefile-header.linux
-
diff --git a/doc/Makefile-header.osx b/doc/Makefile-header.osx
deleted file mode 100644
index 63801c8f739d22c452dfe24893c7bb7712844ab9..0000000000000000000000000000000000000000
--- a/doc/Makefile-header.osx
+++ /dev/null
@@ -1,48 +0,0 @@
-# Vega Makefile header for OS X 
-
-CXX=g++
-CXXFLAGS=-Wall -Wno-write-strings -O2
-CXXLD=$(CXX)
-LDFLAGS=-O2
-
-RM=rm -f
-DYLIB_FLAG=-dynamiclib
-DYLIB_EXT=dylib
-
-L=$(R)/libraries
-
-LIBRARIES_DIR=$(L)
-
-# argument $(1) is a list of library names (e.g. "integrator minivector")
-GET_LIB_MAKEFILES=$(addsuffix /Makefile, $(addprefix $(R)/libraries/, $(1)))
-GET_LIB_FILENAMES=$(join $(addprefix $(R)/libraries/, $(1)), $(addsuffix .a, $(addprefix /lib, $(1))))
-
-INCLUDE=-I$(LIBRARIES_DIR)/include -I/usr/X11R6/include 
-
-LIBRARYPATH=-L$(LIBRARIES_DIR)/lib -L/usr/X11R6/lib/
-OPENGL_LIBS=-framework OpenGL -framework GLUT
-STANDARD_LIBS= $(OPENGL_LIBS) -framework Foundation -framework vecLib -lz -lm $(LIBRARYPATH)
-
-GLUI_DIR=../../libraries/glui/glui-2.35/src
-GLUI_INCLUDE=-I$(GLUI_DIR)/include
-GLUI_LIB=-L$(GLUI_DIR)/lib -lglui
-SET_GLUI_RPATH=-install_name @rpath/libglui.dylib
-
-# comment the next three lines if Pardiso is available
-PARDISO_DIR=
-PARDISO_INCLUDE=
-PARDISO_LIB=
-# set PATH to Pardiso here:
-#PARDISO_DIR=/opt/intel/Compiler/11.0/056/Frameworks/mkl
-#PARDISO_INCLUDE=-I$(PARDISO_DIR)/include
-#PARDISO_LIB=-L$(PARDISO_DIR)/../../lib -L$(PARDISO_DIR)/lib/em64t -lmkl_solver_lp64 -lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -liomp5
-
-# comment the next three lines if SPOOLES is available
-SPOOLES_DIR=
-SPOOLES_INCLUDE=
-SPOOLES_LIB=
-# set PATH to SPOOLES here:
-#SPOOLES_DIR=/Users/barbic/software/SPOOLES
-#SPOOLES_INCLUDE=-I$(SPOOLES_DIR)
-#SPOOLES_LIB=$(SPOOLES_DIR)/LinSol/srcST/Bridge.a $(SPOOLES_DIR)/LinSol/srcMT/BridgeMT.a $(SPOOLES_DIR)/MT/src/spoolesMT.a $(SPOOLES_DIR)/spooles.a
-
diff --git a/doc/VegaUsersManual.pdf b/documentation/VegaUsersManual.pdf
similarity index 88%
rename from doc/VegaUsersManual.pdf
rename to documentation/VegaUsersManual.pdf
index f429aa03f48d4cd27d2499544f51aff3d36cc170..8624bd9952aa63ab58d706fd26aeee41c0559436 100644
Binary files a/doc/VegaUsersManual.pdf and b/documentation/VegaUsersManual.pdf differ
diff --git a/examples/asianDragon/asianDragon.config b/examples/asianDragon/asianDragon.config
index 6b5468cafeb9fb2a6b6a995dff7161a424e6a1de..b341f10f432632fb4f71643890cb659b5ca81424 100644
--- a/examples/asianDragon/asianDragon.config
+++ b/examples/asianDragon/asianDragon.config
@@ -27,9 +27,6 @@ StVK
 ../../models/asianDragon/asianDragon.bou
 #../../models/asianDragon/asianDragon-empty.bou
 
-*massMatrixFilename
-../../models/asianDragon/asianDragon.mass
-
 *renderingMeshFilename
 ../../models/asianDragon/asianDragon.veg.obj
 
@@ -56,7 +53,7 @@ StVK
 *deformableObjectCompliance
 30.0
 
-*baseFrequency
+*frequencyScaling
 1.0
 
 *numInternalForceThreads
diff --git a/examples/asianDragon/asianDragon_MooneyRivlin.config b/examples/asianDragon/asianDragon_MooneyRivlin.config
index 59d4474bfa0395d67692d121dde3fa50ea65de36..38c937a873f3e45c19de440531bf3407cadbe990 100644
--- a/examples/asianDragon/asianDragon_MooneyRivlin.config
+++ b/examples/asianDragon/asianDragon_MooneyRivlin.config
@@ -27,9 +27,6 @@ MooneyRivlin
 ../../models/asianDragon/asianDragon.bou
 #../../models/asianDragon/asianDragon-empty.bou
 
-*massMatrixFilename
-../../models/asianDragon/asianDragon.mass
-
 *renderingMeshFilename
 ../../models/asianDragon/asianDragon.veg.obj
 
@@ -56,7 +53,7 @@ MooneyRivlin
 *deformableObjectCompliance
 30.0
 
-*baseFrequency
+*frequencyScaling
 1.0
 
 *numInternalForceThreads
diff --git a/examples/asianDragon/asianDragon_massspring.config b/examples/asianDragon/asianDragon_massspring.config
index 77f84eb560d42f1d9fcab555e7881c7a4ec6dd01..3cd438991a12bad987393352f85fe08f7a0163b3 100644
--- a/examples/asianDragon/asianDragon_massspring.config
+++ b/examples/asianDragon/asianDragon_massspring.config
@@ -7,9 +7,6 @@ implicitNewmark
 #symplecticEuler
 #Euler
 
-*massMatrixFilename
-../../models/asianDragon/asianDragon.mass
-
 *massSpringSystemTetMeshConfigFilename
 asianDragon.massspring
 
@@ -39,7 +36,7 @@ asianDragon.massspring
 *deformableObjectCompliance
 1.0
 
-*baseFrequency
+*frequencyScaling
 1.0
 
 *numInternalForceThreads
diff --git a/examples/asianDragon/asianDragon_unconstrained.config b/examples/asianDragon/asianDragon_unconstrained.config
index 91477bf8b779089497b52a0e678c88e74b47b72b..bde7dde9f93640aba55f0a5030ed4519658b014b 100644
--- a/examples/asianDragon/asianDragon_unconstrained.config
+++ b/examples/asianDragon/asianDragon_unconstrained.config
@@ -27,9 +27,6 @@ StVK
 #../../models/asianDragon/asianDragon.bou
 ../../models/asianDragon/asianDragon-empty.bou
 
-*massMatrixFilename
-../../models/asianDragon/asianDragon.mass
-
 *renderingMeshFilename
 ../../models/asianDragon/asianDragon.veg.obj
 
@@ -56,7 +53,7 @@ StVK
 *deformableObjectCompliance
 30.0
 
-*baseFrequency
+*frequencyScaling
 1.0
 
 *numInternalForceThreads
diff --git a/examples/asianDragon/run_asianDragon b/examples/asianDragon/run_asianDragon
old mode 100755
new mode 100644
diff --git a/examples/asianDragon/run_asianDragon_MooneyRivlin b/examples/asianDragon/run_asianDragon_MooneyRivlin
old mode 100755
new mode 100644
diff --git a/examples/asianDragon/run_asianDragon_massspring b/examples/asianDragon/run_asianDragon_massspring
old mode 100755
new mode 100644
diff --git a/examples/asianDragon/run_asianDragon_unconstrained b/examples/asianDragon/run_asianDragon_unconstrained
old mode 100755
new mode 100644
diff --git a/examples/beam3_tet/beam3_tet.config b/examples/beam3_tet/beam3_tet.config
index d6a09b35dce28d599f5c2f6603b787589d47d891..5c8f70cfd40eddc18b47ada8bdd41405c0dc5848 100644
--- a/examples/beam3_tet/beam3_tet.config
+++ b/examples/beam3_tet/beam3_tet.config
@@ -36,15 +36,12 @@ StVK
 *deformableObjectCompliance
 1.0
 
-*baseFrequency
+*frequencyScaling
 1.0
 
 *lightingConfigFilename
 beam3_tet.lighting
 
-*massMatrixFilename
-../../models/beam3/beam3_tet.mass
-
 *cameraRadius
 2
 
diff --git a/examples/beam3_tet/beam3_tet.massspring b/examples/beam3_tet/beam3_tet.massspring
index 874ca7a8f3f2b6d977de929328755168bc0fd1f7..d1f68e03d0c5069ea33e431b10b9eea4cd1531f6 100644
--- a/examples/beam3_tet/beam3_tet.massspring
+++ b/examples/beam3_tet/beam3_tet.massspring
@@ -8,7 +8,7 @@
 1.0
 
 *tensileStiffness
-20000.0
+5000.0
 
 *damping
 0.01
diff --git a/examples/beam3_tet/beam3_tet_massspring.config b/examples/beam3_tet/beam3_tet_massspring.config
index 452af1a6ce71d0ce695d8ad2062b8e5e60746bfc..1a6eae6bf23ea50b3f11cd6eb1a24561c3fdfecf 100644
--- a/examples/beam3_tet/beam3_tet_massspring.config
+++ b/examples/beam3_tet/beam3_tet_massspring.config
@@ -27,15 +27,12 @@ beam3_tet.massspring
 *deformableObjectCompliance
 0.1
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *lightingConfigFilename
 beam3_tet.lighting
 
-*massMatrixFilename
-../../models/beam3/beam3_tet.mass
-
 *cameraRadius
 2
 
diff --git a/examples/beam3_tet/run_beam3_tet b/examples/beam3_tet/run_beam3_tet
old mode 100755
new mode 100644
diff --git a/examples/beam3_tet/run_beam3_tet_massspring b/examples/beam3_tet/run_beam3_tet_massspring
old mode 100755
new mode 100644
diff --git a/examples/beam3_vox/beam3_vox.config b/examples/beam3_vox/beam3_vox.config
index 6b453bb8794e0391420843ebe791d30e45957424..40490479a217639b744b43abfb6dbb414b913eca 100644
--- a/examples/beam3_vox/beam3_vox.config
+++ b/examples/beam3_vox/beam3_vox.config
@@ -30,15 +30,12 @@ StVK
 *deformableObjectCompliance
 10.0
 
-*baseFrequency
+*frequencyScaling
 1.0
 
 *lightingConfigFilename
 beam3_vox.lighting
 
-*massMatrixFilename
-../../models/beam3/beam3.mass
-
 *cameraRadius
 2
 
diff --git a/examples/beam3_vox/beam3_vox.massspring b/examples/beam3_vox/beam3_vox.massspring
index 6347e3271f6f6427702140b4cc74370ea8e00f00..67780180c41e1d8b07da484cc57d40c75259ac78 100644
--- a/examples/beam3_vox/beam3_vox.massspring
+++ b/examples/beam3_vox/beam3_vox.massspring
@@ -8,7 +8,7 @@
 1.0
 
 *tensileStiffness
-20000.0
+5000.0
 
 *damping
 0.01
diff --git a/examples/beam3_vox/beam3_vox_massspring.config b/examples/beam3_vox/beam3_vox_massspring.config
index e7a761ab7bdd0e46c94323503fbda835961da7b7..a6233e6aba7b4fb00573c3622b23697bc98f65bc 100644
--- a/examples/beam3_vox/beam3_vox_massspring.config
+++ b/examples/beam3_vox/beam3_vox_massspring.config
@@ -27,15 +27,12 @@ beam3_vox.massspring
 *deformableObjectCompliance
 0.1
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *lightingConfigFilename
 beam3_vox.lighting
 
-*massMatrixFilename
-../../models/beam3/beam3.mass
-
 *cameraRadius
 2
 
diff --git a/examples/beam3_vox/run_beam3_vox b/examples/beam3_vox/run_beam3_vox
old mode 100755
new mode 100644
diff --git a/examples/beam3_vox/run_beam3_vox_massspring b/examples/beam3_vox/run_beam3_vox_massspring
old mode 100755
new mode 100644
diff --git a/examples/cloth/bend1.config b/examples/cloth/bend1.config
new file mode 100644
index 0000000000000000000000000000000000000000..b9abd17e49df2f36e522bfe6ef7d1315ee2025c3
--- /dev/null
+++ b/examples/cloth/bend1.config
@@ -0,0 +1,86 @@
+# === Camera Info ===
+# note: these are all optional
+
+*focusPositionX
+-0.9
+
+*focusPositionY
+-0.95
+
+*focusPositionZ
+-0.90
+
+*cameraRadius
+0.573
+
+*cameraLongitude
+-123.518
+
+*cameraLatitude
+15.1887
+
+*zBufferNear
+0.0001
+
+*zBufferFar
+0.1
+
+*lightingFilename
+../../models/cloth/cloth.lighting
+
+# === End Camera Info ===
+
+# === Force Parameters ===
+
+*timeStep
+0.01
+
+*tensileStiffness
+8500.0
+
+*shearStiffness
+100.0
+
+*bendStiffnessU
+0.01
+
+*bendStiffnessV
+0.01
+
+*useRestAngles
+0
+
+*dampingMass
+0.0
+
+*dampingStiffness
+0.001
+
+# === End Force Parameters ===
+
+# === Simulation Parameters ===
+
+*forceMagnitude
+0.25
+
+*numInternalThreads
+3
+
+*addGravity
+1
+
+*gravityForce
+0.0
+
+# === End Simulation Parameters ===
+
+# === object file info ===
+
+*objMeshname
+../../models/cloth/bend1.obj
+
+*fixedVerticesFilename
+../../models/cloth/bend1.bou
+
+# === end object file info ===
+
diff --git a/examples/cloth/bend2.config b/examples/cloth/bend2.config
new file mode 100644
index 0000000000000000000000000000000000000000..148496681cc7748802bf93e47182367fb02559c3
--- /dev/null
+++ b/examples/cloth/bend2.config
@@ -0,0 +1,80 @@
+# === Camera Info ===
+# note: these are all optional
+
+*focusPositionX
+-0.627282
+
+*focusPositionY
+-0.840574
+
+*focusPositionZ
+-0.141756
+
+*cameraRadius
+1.6575
+
+*cameraLongitude
+-123.518
+
+*cameraLatitude
+15.1887
+
+*lightingFilename
+../../models/cloth/cloth.lighting
+
+# === End Camera Info ===
+
+# === Force Parameters ===
+
+*timeStep
+0.01
+
+*tensileStiffness
+8500.0
+
+*shearStiffness
+1000.0
+
+*bendStiffnessU
+10
+
+*bendStiffnessV
+10
+
+*useRestAngles
+0
+
+*dampingMass
+0.0
+
+*dampingStiffness
+0.01
+
+# === End Force Parameters ===
+
+# === Simulation Parameters ===
+
+*forceMagnitude
+0.25
+
+*numInternalThreads
+3
+
+*addGravity
+1
+
+*gravityForce
+9.81
+
+# === End Simulation Parameters ===
+
+# === object file info ===
+
+*objMeshname
+../../models/cloth/bend2.obj
+
+*fixedVerticesFilename
+../../models/cloth/bend2.bou
+
+# === end object file info ===
+
diff --git a/examples/cloth/cloth100.config b/examples/cloth/cloth100.config
new file mode 100644
index 0000000000000000000000000000000000000000..50f882007587b1d953be3f383314d4ce73660743
--- /dev/null
+++ b/examples/cloth/cloth100.config
@@ -0,0 +1,104 @@
+# === Camera Info ===
+# note: these are all optional
+
+*focusPositionX
+-0.06865 
+
+*focusPositionY
+0.0949206 
+
+*focusPositionZ
+-0.0728166
+
+*cameraRadius
+0.95
+
+*cameraLongitude
+-118.361
+
+*cameraLatitude
+18.34
+
+*zBufferNear
+0.01
+
+*zBufferFar
+10
+
+*lightingFilename
+../../models/cloth/cloth.lighting
+
+# === End Camera Info ===
+
+# === Force Parameters ===
+
+*timeStep
+0.02
+
+*surfaceDensity
+100
+
+*tensileStiffness
+5e7
+
+*shearStiffness
+0.5e7
+
+*bendStiffnessU
+0.1
+
+*bendStiffnessV
+0.1
+
+*useRestAngles
+0
+
+*dampingMass
+0.0
+
+*dampingStiffness
+0.001
+
+# === End Force Parameters ===
+
+# === Simulation Parameters ===
+
+*forceMagnitude
+0.25
+
+*numInternalForceThreads
+8
+
+*numSolverThreads
+4
+
+*addGravity
+1
+
+*gravityForce
+9.81
+
+*computeStretchShearForce
+1
+
+*computeStretchShearStiffness
+1
+
+*computeBendForce
+1
+
+*computeBendStiffness
+1
+
+# === End Simulation Parameters ===
+
+# === object file info ===
+
+*objMeshname
+../../models/cloth/cloth100.obj
+
+*fixedVerticesFilename
+../../models/cloth/cloth100.bou
+
+# === end object file info ===
+
diff --git a/examples/cloth/run_bend1 b/examples/cloth/run_bend1
new file mode 100644
index 0000000000000000000000000000000000000000..9696c123aea06f762f0090cbe322bcd3097e54d2
--- /dev/null
+++ b/examples/cloth/run_bend1
@@ -0,0 +1 @@
+../../utilities/bin/clothBW-rt bend1.config
diff --git a/examples/cloth/run_bend2 b/examples/cloth/run_bend2
new file mode 100644
index 0000000000000000000000000000000000000000..b5a86e8bfb3c3b6bcf99b0b2a007706f3ec7d519
--- /dev/null
+++ b/examples/cloth/run_bend2
@@ -0,0 +1 @@
+../../utilities/bin/clothBW-rt bend2.config
diff --git a/examples/cloth/run_cloth100 b/examples/cloth/run_cloth100
new file mode 100644
index 0000000000000000000000000000000000000000..45639553e026b02c945392e820c0554a8011e57e
--- /dev/null
+++ b/examples/cloth/run_cloth100
@@ -0,0 +1 @@
+../../utilities/bin/clothBW-rt cloth100.config
diff --git a/examples/dragon/dragon.config b/examples/dragon/dragon.config
index 36ece9520644d6abd7b0726389323871631b533a..3c624fab63295bbfd0bf2e678b18cf483d538315 100644
--- a/examples/dragon/dragon.config
+++ b/examples/dragon/dragon.config
@@ -39,15 +39,12 @@ StVK
 *deformableObjectCompliance
 10.0
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *lightingConfigFilename
 dragon.lighting
 
-*massMatrixFilename
-../../models/dragon-77k/dragon-77k.mass
-
 *cameraRadius
 10.9
 
diff --git a/examples/dragon/run_dragon b/examples/dragon/run_dragon
old mode 100755
new mode 100644
diff --git a/examples/guideToExamples.txt b/examples/guideToExamples.txt
index bdf45efb9e22d9af8a93ce310ed7e8b2ffdbb981..b74aecb3216542be5f74d6e7d232909ca247c93b 100644
--- a/examples/guideToExamples.txt
+++ b/examples/guideToExamples.txt
@@ -1,9 +1,22 @@
+We have several folders for examples of unreduced simulations of 3D solid elasticity.
 == vv LEAST COMPLEX vv ==
   208 vertices: beam <== trivial
   347 vertices: turtle <== very good start (non-homogeneous material properties: backshell is made of harder material)
+  347 vertices: orthotropic turtle <== demonstrates real-time orthotropic materials
   959 vertices: asianDragon <== demonstrates unconstrained (free-fly) motion; also demonstrates Mooney-Rivlin material
  4000 vertices: simpleBridge
  9875 vertices: towerCrane <== runs at 2 fps on our machines
+25620 vertices: tube <== offline orthotropic
 47736 vertices: dragon <== offline
 == ^^ MOST  COMPLEX ^^ ==
 
+The cloth examples are in the "cloth" folder. They demonstrate bending, and a cloth sheet suspended under gravity.
+
+The mesher examples are in the "mesher" folder. They demonstrate how to use Vega's meshing capabilities.
+
+The virtual tets examples are in the "virtualTets" folder. They demonstrate how to use Vega's virtual tets algorithm to create tet meshes to embed nearly-self-intersecting surfaces.
+Run script runMesher_* in virtualTets/ to create the tet mesh and interpolation file, and then run script edit_* to deform the resulting tet mesh with ARAP. 
+
+The immersion mesher examples are in the "immersionMesher" folder. They demonstrate how to use Vega's immersion algorithm to create tet meshes to embed self-intersecting surfaces.
+Run script runMesher_* in immersionMesher/ to create the tet mesh and interpolation file, and then run script edit_* to deform the resulting tet mesh with ARAP. 
+
diff --git a/examples/immersionMesher/doubleLoop-arap.config b/examples/immersionMesher/doubleLoop-arap.config
new file mode 100644
index 0000000000000000000000000000000000000000..8eb6a4498936ccffade4b614f17e9f6b50106cf5
--- /dev/null
+++ b/examples/immersionMesher/doubleLoop-arap.config
@@ -0,0 +1,20 @@
+*volumetricMeshFilename
+doubleLoop-im.veg
+
+*embeddedMeshFilename
+../../models/selfIntersecting/doubleLoop/doubleLoopWithCube.obj
+
+*embeddedMeshInterpolationFilename
+doubleLoop-im.interp
+
+*numSolverThreads
+3
+
+*cameraLongitude
+64.2169
+
+*cameraLattitude
+16.6158
+
+*cameraRadius
+4.58
diff --git a/examples/immersionMesher/edit_doubleLoop b/examples/immersionMesher/edit_doubleLoop
new file mode 100644
index 0000000000000000000000000000000000000000..37bc684fc5f26b0a5b53f8b7a9b9e1becf1f6fde
--- /dev/null
+++ b/examples/immersionMesher/edit_doubleLoop
@@ -0,0 +1 @@
+../../utilities/bin/editShapeARAP doubleLoop-arap.config
diff --git a/examples/immersionMesher/edit_helix b/examples/immersionMesher/edit_helix
new file mode 100644
index 0000000000000000000000000000000000000000..1280ea69e71f0b871c1e6fae711b1d5f6482fb4b
--- /dev/null
+++ b/examples/immersionMesher/edit_helix
@@ -0,0 +1 @@
+../../utilities/bin/editShapeARAP helix-arap.config
diff --git a/examples/immersionMesher/edit_quintupleTorus b/examples/immersionMesher/edit_quintupleTorus
new file mode 100644
index 0000000000000000000000000000000000000000..17471def38be5e94810f804f16ec592e04c1d0bf
--- /dev/null
+++ b/examples/immersionMesher/edit_quintupleTorus
@@ -0,0 +1 @@
+../../utilities/bin/editShapeARAP quintupleTorus-arap.config
diff --git a/examples/immersionMesher/edit_torus_difficult b/examples/immersionMesher/edit_torus_difficult
new file mode 100644
index 0000000000000000000000000000000000000000..d5744c660ebeb3521c09dda868fed9d08aecdbb4
--- /dev/null
+++ b/examples/immersionMesher/edit_torus_difficult
@@ -0,0 +1 @@
+../../utilities/bin/editShapeARAP torus-difficult-arap.config
diff --git a/examples/immersionMesher/edit_torus_easy b/examples/immersionMesher/edit_torus_easy
new file mode 100644
index 0000000000000000000000000000000000000000..d8dcf227182a8ab96dc44e7b1145afe2e7c0648c
--- /dev/null
+++ b/examples/immersionMesher/edit_torus_easy
@@ -0,0 +1 @@
+../../utilities/bin/editShapeARAP torus-easy-arap.config
diff --git a/examples/immersionMesher/edit_yeahright b/examples/immersionMesher/edit_yeahright
new file mode 100644
index 0000000000000000000000000000000000000000..931f7a5502db6836a54070f10d41508307819f0d
--- /dev/null
+++ b/examples/immersionMesher/edit_yeahright
@@ -0,0 +1 @@
+../../utilities/bin/editShapeARAP yeahright-arap.config
diff --git a/examples/immersionMesher/helix-arap.config b/examples/immersionMesher/helix-arap.config
new file mode 100644
index 0000000000000000000000000000000000000000..2c3162da627fd22662d3cc56ad9795b98519809f
--- /dev/null
+++ b/examples/immersionMesher/helix-arap.config
@@ -0,0 +1,17 @@
+*volumetricMeshFilename
+helix-im.veg
+
+*embeddedMeshFilename
+../../models/selfIntersecting/helix/helix.obj
+
+*embeddedMeshInterpolationFilename
+helix-im.interp
+
+*numSolverThreads
+3
+
+*cameraLongitude
+-60
+
+*cameraLattitude
+20
diff --git a/examples/immersionMesher/quintupleTorus-arap.config b/examples/immersionMesher/quintupleTorus-arap.config
new file mode 100644
index 0000000000000000000000000000000000000000..9e1915b000ad898837d5e2889374f8e9af7a403c
--- /dev/null
+++ b/examples/immersionMesher/quintupleTorus-arap.config
@@ -0,0 +1,23 @@
+*volumetricMeshFilename
+quintupleTorus-im.veg
+
+*embeddedMeshFilename
+../../models/selfIntersecting/quintupleTorus/quintupleTorus.obj
+
+*embeddedMeshInterpolationFilename
+quintupleTorus-im.interp
+
+*numSolverThreads
+3
+
+*cameraLongitude
+-116.15
+
+*cameraLattitude
+-7.50197
+
+*cameraRadius
+8.69725
+
+*cameraFocusPosition
+-0.0292761 -0.935047 0.0748465
\ No newline at end of file
diff --git a/examples/immersionMesher/runMesher_doubleLoop b/examples/immersionMesher/runMesher_doubleLoop
new file mode 100644
index 0000000000000000000000000000000000000000..1e8ec299ea3aed03e5be559caebd73954318e107
--- /dev/null
+++ b/examples/immersionMesher/runMesher_doubleLoop
@@ -0,0 +1 @@
+../../utilities/bin/immersionMesher ../../models/selfIntersecting/doubleLoop/doubleLoopWithCube.obj ../../models/selfIntersecting/doubleLoop/doubleLoopWithCube.veg -o doubleLoop-im.veg -w doubleLoop-im.interp
diff --git a/examples/immersionMesher/runMesher_helix b/examples/immersionMesher/runMesher_helix
new file mode 100644
index 0000000000000000000000000000000000000000..e475af575a61fde61fdf057395a5b8ce32a6add8
--- /dev/null
+++ b/examples/immersionMesher/runMesher_helix
@@ -0,0 +1 @@
+../../utilities/bin/immersionMesher ../../models/selfIntersecting/helix/helix.obj ../../models/selfIntersecting/helix/helix.veg -o helix-im.veg -w helix-im.interp
diff --git a/examples/immersionMesher/runMesher_quintupleTorus b/examples/immersionMesher/runMesher_quintupleTorus
new file mode 100644
index 0000000000000000000000000000000000000000..f3c0c6c9cceee44b64cc1022d0c915324f316491
--- /dev/null
+++ b/examples/immersionMesher/runMesher_quintupleTorus
@@ -0,0 +1 @@
+../../utilities/bin/immersionMesher ../../models/selfIntersecting/quintupleTorus/quintupleTorus.obj ../../models/selfIntersecting/quintupleTorus/quintupleTorus.veg -o quintupleTorus-im.veg -w quintupleTorus-im.interp
diff --git a/examples/immersionMesher/runMesher_torus_difficult b/examples/immersionMesher/runMesher_torus_difficult
new file mode 100644
index 0000000000000000000000000000000000000000..8177daa7346a6fe00c508411b874529e6e642d0f
--- /dev/null
+++ b/examples/immersionMesher/runMesher_torus_difficult
@@ -0,0 +1 @@
+../../utilities/bin/immersionMesher ../../models/selfIntersecting/torus/torus-difficult.obj ../../models/selfIntersecting/torus/torus-difficult.veg -o torus-difficult-im.veg -w torus-difficult-im.interp
diff --git a/examples/immersionMesher/runMesher_torus_easy b/examples/immersionMesher/runMesher_torus_easy
new file mode 100644
index 0000000000000000000000000000000000000000..3107b6238b9b11428d69c7163fcbe8c66e22e790
--- /dev/null
+++ b/examples/immersionMesher/runMesher_torus_easy
@@ -0,0 +1 @@
+../../utilities/bin/immersionMesher ../../models/selfIntersecting/torus/torus-easy.obj ../../models/selfIntersecting/torus/torus-easy.veg -o torus-easy-im.veg -w torus-easy-im.interp
diff --git a/examples/immersionMesher/runMesher_yeahright b/examples/immersionMesher/runMesher_yeahright
new file mode 100644
index 0000000000000000000000000000000000000000..c62d21fc27db306ea8bef5a9f224e6e92b433c06
--- /dev/null
+++ b/examples/immersionMesher/runMesher_yeahright
@@ -0,0 +1 @@
+../../utilities/bin/immersionMesher ../../models/selfIntersecting/yeahright/yeahright-on-ground.obj ../../models/selfIntersecting/yeahright/yeahright-on-ground.veg -o yeahright-im.veg -w yeahright-im.interp
diff --git a/examples/immersionMesher/torus-difficult-arap.config b/examples/immersionMesher/torus-difficult-arap.config
new file mode 100644
index 0000000000000000000000000000000000000000..dbb4cf3983805a45a349e3fed16810a1d2e2a4fa
--- /dev/null
+++ b/examples/immersionMesher/torus-difficult-arap.config
@@ -0,0 +1,23 @@
+*volumetricMeshFilename
+torus-difficult-im.veg
+
+*embeddedMeshFilename
+../../models/selfIntersecting/torus/torus-difficult.obj
+
+*embeddedMeshInterpolationFilename
+torus-difficult-im.interp
+
+*numSolverThreads
+3
+
+*cameraLongitude
+-90
+
+*cameraLattitude
+0
+
+*cameraRadius
+48.4988
+
+*cameraFocusPosition
+1.70817 1.98932 -1.07034
\ No newline at end of file
diff --git a/examples/immersionMesher/torus-easy-arap.config b/examples/immersionMesher/torus-easy-arap.config
new file mode 100644
index 0000000000000000000000000000000000000000..c00b565d00850fc4bd1bd622480a6c909e1ea7ce
--- /dev/null
+++ b/examples/immersionMesher/torus-easy-arap.config
@@ -0,0 +1,23 @@
+*volumetricMeshFilename
+torus-easy-im.veg
+
+*embeddedMeshFilename
+../../models/selfIntersecting/torus/torus-easy.obj
+
+*embeddedMeshInterpolationFilename
+torus-easy-im.interp
+
+*numSolverThreads
+3
+
+*cameraLongitude
+-90
+
+*cameraLattitude
+0
+
+*cameraRadius
+47.9588
+
+*cameraFocusPosition
+1.70817 2.7799 -0.590127
\ No newline at end of file
diff --git a/examples/immersionMesher/yeahright-arap.config b/examples/immersionMesher/yeahright-arap.config
new file mode 100644
index 0000000000000000000000000000000000000000..1c18b6a6f5c9e3ecafb07989f3779667a71f8e82
--- /dev/null
+++ b/examples/immersionMesher/yeahright-arap.config
@@ -0,0 +1,23 @@
+*volumetricMeshFilename
+yeahright-im.veg
+
+*embeddedMeshFilename
+../../models/selfIntersecting/yeahright/yeahright-on-ground.obj
+
+*embeddedMeshInterpolationFilename
+yeahright-im.interp
+
+*numSolverThreads
+3
+
+*cameraLongitude
+-31.9251
+
+*cameraLattitude
+28.8394
+
+*cameraRadius
+22
+
+*cameraFocusPosition
+-0.989018 -6.32388 1.93762
diff --git a/examples/mesher/run_asianDragon b/examples/mesher/run_asianDragon
new file mode 100644
index 0000000000000000000000000000000000000000..5c04043763a3818c2a4b0c678e83942c9355e4e5
--- /dev/null
+++ b/examples/mesher/run_asianDragon
@@ -0,0 +1,3 @@
+../../utilities/computeDistanceField/computeDistanceField ../../models/asianDragon/asianDragon.obj 256 256 256 -s -m 1 -g 0.05 -o asianDragon_256.dist
+../../utilities/isosurfaceMesher/isosurfaceMesher asianDragon_256.dist 0.1 asianDragon.obj
+../../utilities/tetMesher/tetMesher asianDragon.obj asianDragon.veg -q 1.1 -a 1.5
diff --git a/examples/mesher/run_dragon b/examples/mesher/run_dragon
new file mode 100644
index 0000000000000000000000000000000000000000..6b29ab5a3536f6655de2ec14ffb4e14a14095108
--- /dev/null
+++ b/examples/mesher/run_dragon
@@ -0,0 +1,3 @@
+../../utilities/computeDistanceField/computeDistanceField ../../models/dragon-77k/dragon-77k.obj 256 256 256 -n -r -w 0.1 -s -m 0 -o dragon_256.dist
+../../utilities/isosurfaceMesher/isosurfaceMesher dragon_256.dist 0.15 dragon.obj -i0.05 -a30 -n
+../../utilities/tetMesher/tetMesher dragon.obj dragon.veg -q 1.1 -a 1.5 
diff --git a/examples/mesher/run_turtle b/examples/mesher/run_turtle
new file mode 100644
index 0000000000000000000000000000000000000000..f3293cb157ff433ceb6019a48d2a5aa600e7440a
--- /dev/null
+++ b/examples/mesher/run_turtle
@@ -0,0 +1,3 @@
+../../utilities/computeDistanceField/computeDistanceField ../../models/turtle/turtle.obj 64 64 64 -s -m 1 -g 0.1 -o turtle_64.dist
+../../utilities/isosurfaceMesher/isosurfaceMesher turtle_64.dist 0.2 turtle.obj
+../../utilities/tetMesher/tetMesher turtle.obj turtle.veg -q 1.1 -a 1.6
diff --git a/examples/simpleBridge_tet/run_simpleBridge_tet b/examples/simpleBridge_tet/run_simpleBridge_tet
old mode 100755
new mode 100644
diff --git a/examples/simpleBridge_tet/run_simpleBridge_tet_massspring b/examples/simpleBridge_tet/run_simpleBridge_tet_massspring
old mode 100755
new mode 100644
diff --git a/examples/simpleBridge_tet/simpleBridge_tet.config b/examples/simpleBridge_tet/simpleBridge_tet.config
index 596794a612a7ea25606f1ca84fb063f60f23d7cc..c8019f25bd755e4296dd2e2ed55a29de7068e86e 100644
--- a/examples/simpleBridge_tet/simpleBridge_tet.config
+++ b/examples/simpleBridge_tet/simpleBridge_tet.config
@@ -41,15 +41,12 @@ StVK
 *deformableObjectCompliance
 100.0
 
-*baseFrequency
+*frequencyScaling
 1.0
 
 *lightingConfigFilename
 simpleBridge_tet.lighting
 
-*massMatrixFilename
-../../models/simpleBridge/simpleBridge.mass
-
 *cameraRadius
 22
 
diff --git a/examples/simpleBridge_tet/simpleBridge_tet.massspring b/examples/simpleBridge_tet/simpleBridge_tet.massspring
index 10819b4d0af95818ed3ddcfb60a7aab6ce41e680..eb72bf173a614670820b4776905319603dda4565 100644
--- a/examples/simpleBridge_tet/simpleBridge_tet.massspring
+++ b/examples/simpleBridge_tet/simpleBridge_tet.massspring
@@ -8,7 +8,7 @@
 1.0
 
 *tensileStiffness
-100000.0
+25000.0
 
 *damping
 0.01
diff --git a/examples/simpleBridge_tet/simpleBridge_tet_massspring.config b/examples/simpleBridge_tet/simpleBridge_tet_massspring.config
index 711249488da427cf3b20dd162dbf1a03d4638cf4..b5a68ecd2cd2e27ab0535ff10d3862ec6c164a2a 100644
--- a/examples/simpleBridge_tet/simpleBridge_tet_massspring.config
+++ b/examples/simpleBridge_tet/simpleBridge_tet_massspring.config
@@ -40,15 +40,12 @@ simpleBridge_tet.massspring
 *deformableObjectCompliance
 0.5
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *lightingConfigFilename
 simpleBridge_tet.lighting
 
-*massMatrixFilename
-../../models/simpleBridge/simpleBridge.mass
-
 *cameraRadius
 22
 
diff --git a/examples/simpleBridge_vox/run_simpleBridge_vox b/examples/simpleBridge_vox/run_simpleBridge_vox
old mode 100755
new mode 100644
diff --git a/examples/simpleBridge_vox/run_simpleBridge_vox_massspring b/examples/simpleBridge_vox/run_simpleBridge_vox_massspring
old mode 100755
new mode 100644
diff --git a/examples/simpleBridge_vox/simpleBridge_vox.config b/examples/simpleBridge_vox/simpleBridge_vox.config
index 9c2318717c8f531600b0886f4e29948d0e2565ea..5847c2a4aba0c5aa744d711978e95e2887c02ea7 100644
--- a/examples/simpleBridge_vox/simpleBridge_vox.config
+++ b/examples/simpleBridge_vox/simpleBridge_vox.config
@@ -35,15 +35,12 @@ StVK
 *deformableObjectCompliance
 100.0
 
-*baseFrequency
+*frequencyScaling
 1.0
 
 *lightingConfigFilename
 simpleBridge_vox.lighting
 
-*massMatrixFilename
-../../models/simpleBridge/simpleBridge_resol92-b1.155_enlarged.mass
-
 *cameraRadius
 22
 
diff --git a/examples/simpleBridge_vox/simpleBridge_vox.massspring b/examples/simpleBridge_vox/simpleBridge_vox.massspring
index 1f6ae0008f9ca6d60cb08eb59804bbcebab0873c..75c37efa2664d3b350af680db8407ef815c215c6 100644
--- a/examples/simpleBridge_vox/simpleBridge_vox.massspring
+++ b/examples/simpleBridge_vox/simpleBridge_vox.massspring
@@ -8,7 +8,7 @@
 1.0
 
 *tensileStiffness
-100000.0
+25000.0
 
 *damping
 0.01
diff --git a/examples/simpleBridge_vox/simpleBridge_vox_massspring.config b/examples/simpleBridge_vox/simpleBridge_vox_massspring.config
index 69a4d8e917640924f52060bd13634db55c492acf..c23d8c4b3363922623c15cc47bd60b187af958d4 100644
--- a/examples/simpleBridge_vox/simpleBridge_vox_massspring.config
+++ b/examples/simpleBridge_vox/simpleBridge_vox_massspring.config
@@ -34,15 +34,12 @@ simpleBridge_vox.massspring
 *deformableObjectCompliance
 0.5
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *lightingConfigFilename
 simpleBridge_vox.lighting
 
-*massMatrixFilename
-../../models/simpleBridge/simpleBridge_resol92-b1.155_enlarged.mass
-
 *cameraRadius
 22
 
diff --git a/examples/towerCrane/run_towerCrane b/examples/towerCrane/run_towerCrane
old mode 100755
new mode 100644
diff --git a/examples/towerCrane/run_towerCrane_massspring b/examples/towerCrane/run_towerCrane_massspring
old mode 100755
new mode 100644
diff --git a/examples/towerCrane/towerCrane.config b/examples/towerCrane/towerCrane.config
index 6dd92416d7a2d8d6613b500f9d309e71e4711465..429631ea9005ab86e382ca528f655608cf12aa87 100644
--- a/examples/towerCrane/towerCrane.config
+++ b/examples/towerCrane/towerCrane.config
@@ -35,15 +35,12 @@ StVK
 *deformableObjectCompliance
 100.0
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *lightingConfigFilename
 towerCrane.lighting
 
-*massMatrixFilename
-../../models/towerCrane/towerCrane_resol166.mass
-
 *cameraRadius
 37
 
diff --git a/examples/towerCrane/towerCrane.massspring b/examples/towerCrane/towerCrane.massspring
index 18a5de15664036bfeec3339288a624628b8f5777..47741aba6755f2c73d0aa1a2153c7d8abc2faca6 100644
--- a/examples/towerCrane/towerCrane.massspring
+++ b/examples/towerCrane/towerCrane.massspring
@@ -8,7 +8,7 @@
 1.0
 
 *tensileStiffness
-100000.0
+25000.0
 
 *damping
 0.01
diff --git a/examples/towerCrane/towerCrane_massspring.config b/examples/towerCrane/towerCrane_massspring.config
index ca30f558f9e161a1f77cc97fda417df0f4647751..34b3e800211fa35d46c762ac981fee0ecfc23e4c 100644
--- a/examples/towerCrane/towerCrane_massspring.config
+++ b/examples/towerCrane/towerCrane_massspring.config
@@ -34,15 +34,12 @@ towerCrane.massspring
 *deformableObjectCompliance
 0.5
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *lightingConfigFilename
 towerCrane.lighting
 
-*massMatrixFilename
-../../models/towerCrane/towerCrane_resol166.mass
-
 *cameraRadius
 37
 
diff --git a/examples/tube/run_tube_ortho b/examples/tube/run_tube_ortho
new file mode 100644
index 0000000000000000000000000000000000000000..ab60058fdb30668f394100848d41d63fc02a3a6c
--- /dev/null
+++ b/examples/tube/run_tube_ortho
@@ -0,0 +1 @@
+../../utilities/bin/interactiveDeformableSimulator tube.config
diff --git a/examples/tube/tube.config b/examples/tube/tube.config
new file mode 100644
index 0000000000000000000000000000000000000000..8fedab3a76dcc11b24072d84137a1e6228ec2bcc
--- /dev/null
+++ b/examples/tube/tube.config
@@ -0,0 +1,82 @@
+*volumetricMeshFilename
+../../models/tube/tube-L9-R6-T6.veg
+#../../models/tube/tube-L6-R9-T6.veg
+#../../models/tube/tube-L6-R6-T9.veg
+
+*solver
+#implicitBackwardEuler
+implicitNewmark
+#centralDifferences
+#symplecticEuler
+#Euler
+
+# must use CLFEM for orthotropic materials
+*deformableObjectMethod
+#StVK
+#InvertibleFEM
+CLFEM
+#LinearFEM
+
+# irrelevant (only applies to InvertibleFEM)
+*invertibleMaterial
+StVK
+#neoHookean
+
+*renderingMeshFilename
+../../models/tube/tube.veg.obj
+
+*timestep
+0.0005
+
+*dampingMassCoef
+0.0
+#1.0
+#10.0
+
+*dampingStiffnessCoef
+0.01
+#0.001
+
+*deformableObjectCompliance
+10000.0
+
+*frequencyScaling
+1.0
+
+*lightingConfigFilename
+tube.lighting
+
+*cameraRadius
+18.26
+
+*cameraLongitude
+65.8
+
+*cameraLattitude
+29
+
+*focusPositionX
+-0.725779 
+
+*focusPositionY
+3.69411 
+
+*focusPositionZ
+1.61493
+
+*syncTimestepWithGraphics
+0
+
+*fixedVerticesFilename
+../../models/tube/tube.bou
+
+*numInternalForceThreads
+4
+
+*numSolverThreads
+4
+
+# does not have any effect for CLFEM
+*inversionThreshold
+0.1
+
diff --git a/examples/tube/tube.lighting b/examples/tube/tube.lighting
new file mode 100644
index 0000000000000000000000000000000000000000..4d9c560ef5ca93df3da7e0ef6464aee6dd46160d
--- /dev/null
+++ b/examples/tube/tube.lighting
@@ -0,0 +1,38 @@
+*globalAmbientIntensity
+0.2
+
+*enableSpecularTerm
+true
+
+*twoSidedLighting
+true
+
+#### light 3
+
+*lightEnabled_3
+true
+
+*position_3_X
+-10
+*position_3_Y
+10
+*position_3_Z
+-10
+
+*lightIntensity_3
+0.8
+
+#### light 6
+
+*lightEnabled_6
+true
+
+*position_6_X
+10
+*position_6_Y
+10
+*position_6_Z
+10
+
+*lightIntensity_6
+0.8
diff --git a/examples/turtle/model/CreativeCommonsLegalCode.pdf b/examples/turtle/model/CreativeCommonsLegalCode.pdf
deleted file mode 100644
index fbaa007b24067d449a597f7dda0f11c31c2b8e38..0000000000000000000000000000000000000000
Binary files a/examples/turtle/model/CreativeCommonsLegalCode.pdf and /dev/null differ
diff --git "a/examples/turtle/model/CreativeCommons\342\200\224Attribution-ShareAlike-3.0-Unported\342\200\224CC-BY-SA-3.0.pdf" "b/examples/turtle/model/CreativeCommons\342\200\224Attribution-ShareAlike-3.0-Unported\342\200\224CC-BY-SA-3.0.pdf"
deleted file mode 100644
index 0b8c90b2ec50714eff2ae498eb17611be01ca9cf..0000000000000000000000000000000000000000
Binary files "a/examples/turtle/model/CreativeCommons\342\200\224Attribution-ShareAlike-3.0-Unported\342\200\224CC-BY-SA-3.0.pdf" and /dev/null differ
diff --git a/examples/turtle/model/LICENSE.txt b/examples/turtle/model/LICENSE.txt
deleted file mode 100644
index c620ca70b1addbc1e14717cfe100f90aa19e7291..0000000000000000000000000000000000000000
--- a/examples/turtle/model/LICENSE.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-This model was created by Jeremy Quandt, who also owns copyright on it. It was
-downloaded from http://www.blendswap.com.
-
-The model uses the "Creative Commons Attribution-ShareAlike 3.0", which (in
-layman's terms, see the actual license for the exact legal version) permits one
-to copy, distribute and transmit the work, as well as adapt it and make
-commercial use of it, under the following conditions:
-
-Attribution — You must attribute the work in the manner specified by the author
-or licensor (but not in any way that suggests that they endorse you or your use
-of the work).
-
-Share Alike — If you alter, transform, or build upon this work, you may
-distribute the resulting work only under the same or similar license to this
-one.
-
-Therefore, the volumetric mesh and its surface mesh (both created by Jernej
-Barbic) in the folder "models/turtle" are also released under "Creative Commons
-Attribution-ShareAlike 3.0".
-
-See the attached PDF for the license specifics, and the following URL:
-http://www.blendswap.com/licenses/
diff --git a/examples/turtle/model/nonShellElementList.txt b/examples/turtle/model/nonShellElementList.txt
deleted file mode 100644
index d9a38ce6437d9a09a6ed28b9b8d790556266cc12..0000000000000000000000000000000000000000
--- a/examples/turtle/model/nonShellElementList.txt
+++ /dev/null
@@ -1,105 +0,0 @@
-1,2,3,4,5,6,7,8,9,
-10,11,12,13,14,15,16,17,18,19,
-20,21,22,23,24,25,26,27,28,29,
-30,31,33,34,35,36,37,39,40,42,
-43,44,45,46,47,48,49,50,51,52,
-53,55,56,57,58,59,60,61,62,63,
-64,65,66,67,68,69,70,71,72,73,
-74,75,76,77,78,79,80,81,82,83,
-84,85,86,87,88,89,90,91,93,94,
-95,96,97,98,99,100,101,103,104,107,
-108,109,110,111,112,113,114,116,119,120,
-121,122,123,124,125,126,127,128,129,130,
-131,132,133,135,137,138,140,141,142,143,
-144,145,146,147,148,149,151,153,155,156,
-157,158,159,160,161,162,163,164,166,168,
-169,170,171,172,173,174,175,176,177,178,
-179,180,181,183,184,185,186,187,188,189,
-190,191,192,193,194,196,197,198,199,200,
-201,202,203,204,205,206,207,208,209,210,
-211,212,213,214,215,216,217,218,220,221,
-222,223,224,225,226,227,228,229,230,231,
-233,234,235,236,237,238,240,241,242,243,
-244,245,247,249,250,251,252,253,254,255,
-256,257,258,259,260,261,262,263,264,265,
-266,267,268,269,272,273,274,276,277,278,
-279,280,281,282,284,285,286,288,289,290,
-291,292,293,294,295,296,298,299,301,302,
-304,305,306,307,308,309,310,311,313,315,
-317,318,319,320,321,322,323,324,325,326,
-327,328,329,330,331,332,334,335,336,337,
-338,339,340,341,342,343,344,345,346,347,
-348,349,350,351,352,353,354,355,356,357,
-358,359,360,361,363,364,365,366,367,368,
-370,371,372,373,374,375,376,377,378,379,
-380,381,382,383,384,385,386,387,388,389,
-390,391,392,394,395,396,397,398,399,400,
-401,402,403,404,405,406,407,408,409,410,
-411,412,413,414,415,416,417,418,419,421,
-422,423,424,425,426,427,428,429,430,431,
-432,433,434,435,436,438,439,440,441,442,
-443,444,445,446,447,448,449,450,451,452,
-453,454,455,456,457,458,460,461,462,463,
-464,465,467,469,470,471,472,473,474,475,
-476,477,478,480,481,483,484,485,486,487,
-488,489,490,491,492,493,494,495,496,497,
-498,500,501,502,503,506,507,508,509,510,
-512,513,515,516,517,518,519,520,521,522,
-523,524,525,526,527,528,529,530,532,533,
-535,536,537,538,539,540,542,544,545,547,
-548,549,550,551,552,553,554,555,556,557,
-558,559,560,561,563,564,565,566,567,569,
-570,571,572,573,575,576,577,578,579,580,
-582,583,584,585,586,588,589,590,591,592,
-593,594,597,598,601,602,603,605,606,607,
-608,609,610,613,614,615,617,618,619,620,
-621,622,624,625,627,628,629,630,631,632,
-634,635,637,638,640,642,643,644,645,646,
-647,648,649,650,651,652,654,655,657,658,
-661,662,663,664,665,666,667,668,669,670,
-671,672,673,674,675,676,677,678,679,681,
-682,683,684,685,686,687,688,689,691,692,
-693,694,695,696,697,698,699,700,701,702,
-703,704,705,706,707,708,709,711,712,713,
-714,716,717,718,719,720,722,723,724,725,
-726,727,728,729,730,731,732,733,736,737,
-738,739,740,741,742,745,746,747,748,749,
-750,751,752,753,754,755,756,757,758,759,
-760,761,764,765,767,769,770,771,772,773,
-774,775,776,777,778,779,780,781,782,783,
-784,785,786,787,788,789,790,791,792,793,
-794,795,796,797,798,799,800,802,803,805,
-806,807,808,809,810,811,812,813,814,815,
-816,817,818,819,820,821,822,824,825,826,
-827,828,829,830,831,832,834,835,836,837,
-838,839,840,841,842,843,844,845,846,847,
-848,849,850,851,852,853,854,855,858,859,
-860,861,862,863,864,865,866,867,868,869,
-870,872,874,875,876,877,878,879,880,882,
-883,884,885,887,888,889,890,891,892,894,
-896,897,898,899,900,901,902,903,904,905,
-906,907,908,909,910,911,912,913,914,915,
-916,917,918,919,920,921,922,923,924,925,
-926,927,928,929,931,932,935,936,937,938,
-939,940,941,942,943,945,948,949,950,953,
-954,955,956,957,958,959,960,961,963,964,
-965,966,967,968,971,973,974,975,976,979,
-980,983,984,985,987,990,991,992,993,994,
-995,996,997,998,999,1000,1001,1003,1005,1006,
-1007,1008,1009,1010,1011,1012,1013,1014,1015,1016,
-1017,1018,1020,1021,1022,1023,1024,1025,1026,1027,
-1028,1030,1031,1032,1034,1035,1036,1037,1039,1041,
-1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,
-1052,1053,1054,1055,1056,1057,1058,1059,1061,1062,
-1065,1068,1069,1070,1071,1072,1073,1074,1075,1076,
-1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,
-1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,
-1098,1099,1100,1102,1103,1105,1106,1107,1108,1109,
-1110,1111,1112,1113,1114,1115,1116,1117,1118,1119,
-1120,1121,1122,1123,1124,1125,1126,1127,1128,1130,
-1131,1132,1133,1134,1135,1136,1137,1139,1140,1142,
-1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,
-1153,1154,1155,1156,1158,1159,1160,1161,1162,1163,
-1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,
-1175,1176,1177,1178,1179,1180,1181,1182,1183,1184,
-1185
\ No newline at end of file
diff --git a/examples/turtle/model/shellElementList.txt b/examples/turtle/model/shellElementList.txt
deleted file mode 100644
index 739338777ae8adc5c554927bb68422188273652e..0000000000000000000000000000000000000000
--- a/examples/turtle/model/shellElementList.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-32,38,41,54,92,102,105,106,115,
-117,118,134,136,139,150,152,154,165,167,
-182,195,219,232,239,246,248,270,271,275,
-283,287,297,300,303,312,314,316,333,362,
-369,393,420,437,459,466,468,479,482,499,
-504,505,511,514,531,534,541,543,546,562,
-568,574,581,587,595,596,599,600,604,611,
-612,616,623,626,633,636,639,641,653,656,
-659,660,680,690,710,715,721,734,735,743,
-744,762,763,766,768,801,804,823,833,856,
-857,871,873,881,886,893,895,930,933,934,
-944,946,947,951,952,962,969,970,972,977,
-978,981,982,986,988,989,1002,1004,1019,1029,
-1033,1038,1040,1060,1063,1064,1066,1067,1097,1101,
-1104,1129,1138,1141,1157,1174
\ No newline at end of file
diff --git a/examples/turtle/model/turtle-volumetric-heterogeneous.veg b/examples/turtle/model/turtle-volumetric-heterogeneous.veg
deleted file mode 100644
index c96f5608f789c0a6048af68e2e37cde9c29db79c..0000000000000000000000000000000000000000
--- a/examples/turtle/model/turtle-volumetric-heterogeneous.veg
+++ /dev/null
@@ -1,1566 +0,0 @@
-# Vega mesh file.
-# 347 vertices, 1185 elements
-
-*VERTICES
-347 3 0 0
-1 -1.07586 -0.298468 -0.0952629
-2 1.14687 2.36868 0.807906
-3 -1.52689 0.224941 0.861942
-4 0.732394 -0.049599 1.14023
-5 -0.1488 0.686607 0.819301
-6 0.912779 -0.256313 0.99358
-7 -1.10349 3.06502 0.576252
-8 0.15638 -0.293391 0.183926
-9 1.36659 1.43824 -0.13223
-10 -0.799163 0.521933 1.1138
-11 -0.355696 -0.298592 0.0488136
-12 0.571505 -0.294818 0.450653
-13 1.31072 -0.296557 0.241448
-14 0.394326 0.322222 1.11038
-15 -0.746481 -0.297871 0.429331
-16 0.440164 1.02524 0.801095
-17 -0.14617 -0.280441 0.439071
-18 -1.81422 -0.0750205 0.280667
-19 -1.04928 0.89413 0.870746
-20 -0.12279 0.240891 0.844794
-21 0.125006 0.870122 -0.78
-22 1.45414 0.622585 0.750565
-23 0.756256 0.311578 -0.290053
-24 1.18055 0.216538 0.986384
-25 -0.515629 0.407269 0.232091
-26 0.125703 -0.202337 -0.741357
-27 -1.11196 1.57291 -0.426935
-28 -0.367027 1.758 1.00277
-29 0.503061 -0.294756 0.873759
-30 0.507497 3.56401 -1.4807
-31 -1.59062 0.784677 0.165546
-32 0.913687 0.721534 0.920112
-33 1.09097 1.41801 0.60306
-34 0.20791 2.02511 -0.130844
-35 1.20072 2.5498 -0.897716
-36 1.69334 4.41724 0.179419
-37 0.494875 1.85214 1.06304
-38 0.107596 3.20899 1.33689
-39 -1.06293 6.78103 1.16639
-40 -0.276029 4.89078 0.271272
-41 0.459803 3.4464 -0.0427515
-42 0.673682 -0.293966 -0.164778
-43 -0.434716 2.45908 -1.36411
-44 -1.15797 4.06886 -1.05429
-45 -0.591842 -0.299203 -0.64272
-46 -1.28957 0.458428 -0.772923
-47 1.67519 0.0415655 0.632792
-48 1.6179 0.42484 -0.156465
-49 1.32152 -0.159821 -0.642702
-50 -0.341747 0.328057 -0.914196
-51 -0.64823 1.05657 -0.817318
-52 -0.0744169 1.67541 -1.11973
-53 0.697426 1.54283 -0.704255
-54 0.441446 2.36633 -1.30899
-55 0.408346 1.12899 -0.0323087
-56 -0.435776 1.25449 -0.0146378
-57 -1.5454 2.82687 -0.686709
-58 -1.31269 -0.29861 0.419141
-59 -1.04037 -0.199519 1.01643
-60 -0.455365 -0.263005 0.944565
-61 1.28037 1.97772 0.269872
-62 1.80439 3.06274 0.0390899
-63 1.34987 2.07278 -0.414955
-64 1.3765 2.62142 0.253604
-65 0.568583 2.61017 0.296107
-66 1.14476 3.37464 0.353
-67 0.587054 4.29607 0.6882
-68 1.35338 6.50258 1.40841
-69 -0.126695 5.37232 2.17496
-70 1.40335 5.19735 1.20622
-71 -0.217956 3.04303 0.398081
-72 -0.579337 3.98678 0.776539
-73 0.595838 5.96376 0.379372
-74 0.203774 2.77773 -0.391999
-75 -1.35539 5.18403 1.30037
-76 -3.54422 3.99078 0.524449
-77 -1.85505 4.03707 0.976603
-78 -1.32323 4.49114 0.0799895
-79 -2.58433 4.09541 -0.614208
-80 -1.24307 5.9875 -0.10553
-81 1.76552 2.49515 -0.218198
-82 1.60719 3.03163 -0.620133
-83 3.14934 3.94535 -0.764735
-84 2.31237 3.63023 -0.499494
-85 1.42334 4.06088 -1.04605
-86 1.17093 3.25434 -1.22246
-87 0.238231 5.12013 -1.226
-88 0.269575 5.23574 1.15982
-89 -0.0309661 2.37536 1.49917
-90 -0.790926 2.48979 1.1255
-91 -1.28648 2.12657 0.362332
-92 0.00704813 2.20828 0.554514
-93 -0.572671 2.21946 -0.0149638
-94 0.226389 7.09199 0.995003
-95 0.82307 5.69728 1.90788
-96 -0.0084901 6.45645 1.89441
-97 -0.96959 5.97987 1.90767
-98 2.03767 3.67953 0.921421
-99 3.14679 3.55378 0.402342
-100 2.63428 4.48055 -0.113861
-101 1.12298 2.90838 0.661755
-102 0.671868 3.43441 0.917117
-103 0.670375 2.74933 1.21992
-104 0.0939719 3.86272 1.01997
-105 -0.34123 4.57417 1.50179
-106 0.360087 4.44592 1.36191
-107 1.00546 4.62175 1.14847
-108 0.73081 4.96677 1.75709
-109 1.30749 4.90086 0.553709
-110 0.977946 4.89264 -0.473883
-111 1.53141 5.82613 0.501039
-112 0.725754 5.91829 -0.689851
-113 0.220441 6.97838 -0.252861
-114 -0.393296 5.98479 -0.855123
-115 -0.732116 5.00047 -0.868277
-116 -0.24516 4.28284 -1.30236
-117 -0.604397 3.40365 -1.48687
-118 -0.108955 1.2919 0.681819
-119 -0.675808 1.32421 0.781786
-120 -1.25862 1.36284 0.389829
-121 -0.934451 1.88111 0.801962
-122 -0.394786 3.48502 1.09487
-123 -0.423778 2.9265 1.31788
-124 -0.820867 3.49748 0.753418
-125 -1.14599 3.97727 0.446719
-126 -0.414753 3.82266 -0.153308
-127 -2.02475 3.44735 0.0733384
-128 -1.03131 4.61188 0.883202
-129 -1.39118 5.25075 0.454226
-130 -1.65567 6.04088 0.954185
-131 -0.823861 3.04858 1.01213
-132 -1.07815 5.13436 -0.216906
-133 -1.19853 4.57797 -0.597847
-134 -1.81205 4.11002 -0.644368
-135 -2.18052 4.5108 0.0207978
-136 -3.04069 4.46798 0.0140465
-137 -2.69258 4.07617 0.717801
-138 -2.93907 3.4676 0.0811426
-139 -1.55032 3.50269 -0.894623
-140 -1.13201 3.03917 -1.2088
-141 -1.02803 2.36078 -0.934456
-142 -1.4533 2.18969 -0.398152
-143 -1.81449 2.71656 0.0180836
-144 -1.72303 4.16081 0.396577
-145 -1.82974 3.63029 0.577341
-146 -1.39293 3.47704 0.141023
-147 -2.44399 3.53783 0.525648
-148 0.592981 4.34654 -1.14796
-149 1.04753 4.39462 0.241342
-150 1.40834 3.92911 0.431606
-151 0.865492 3.82527 0.547644
-152 1.82372 4.25814 0.778357
-153 2.65369 4.19268 0.67403
-154 2.22445 4.52228 0.426497
-155 2.12825 4.49427 -0.0715017
-156 1.60993 4.38855 -0.488155
-157 2.33653 4.21203 -0.537879
-158 1.876 3.93252 -0.628192
-159 1.56727 3.53171 -0.890687
-160 1.81198 3.46349 -0.35362
-161 2.23225 3.41322 0.00410722
-162 2.87877 3.40225 -0.202694
-163 1.77663 3.55788 0.27041
-164 3.59717 3.66961 -0.207039
-165 3.42488 4.38624 0.283982
-166 2.84158 4.27903 -0.533384
-167 3.42268 4.28122 -0.32452
-168 -2.20092 4.49816 0.678009
-169 -0.837391 5.58386 -0.612758
-170 -0.0756911 5.43705 -0.319604
-171 -0.829069 6.16651 -0.493318
-172 -0.591949 5.76133 0.0289914
-173 -0.820351 6.76251 0.247334
-174 -0.312763 6.65181 -0.604176
-175 0.147715 6.22609 -0.175256
-176 0.174948 6.56239 0.482201
-177 1.01506 6.6707 0.635009
-178 -0.235345 7.17067 0.42761
-179 -0.437659 6.99736 1.04524
-180 -0.577442 6.67534 1.61608
-181 -0.121577 6.31682 1.13209
-182 0.197194 6.29489 -0.843984
-183 0.1451 5.68082 -0.960706
-184 0.605683 5.31345 -0.764086
-185 1.05498 5.49671 -0.385742
-186 1.21782 5.10422 0.0141696
-187 0.522703 5.27832 0.0423676
-188 1.32653 5.62349 0.0538311
-189 1.19699 6.27024 0.136566
-190 0.764346 6.75213 0.0347831
-191 -0.241136 5.26661 -0.962378
-192 -0.189962 4.80525 -1.22131
-193 -0.570957 4.54303 -1.01461
-194 0.374281 7.13454 0.326104
-195 0.640643 6.47454 -0.4823
-196 -0.0370509 6.90739 1.50068
-197 0.65681 6.66152 1.62488
-198 0.992671 6.14266 -0.3151
-199 -0.988197 6.42373 -0.088252
-200 -1.33786 6.40509 0.486226
-201 -0.625492 6.75098 -0.219249
-202 -1.48501 5.83471 0.425995
-203 -1.60474 5.53511 0.836151
-204 -1.51201 5.6961 1.33993
-205 -1.36648 6.30355 1.45497
-206 -1.15483 5.495 1.7131
-207 -0.888781 4.89781 1.66152
-208 -0.635916 5.51645 1.95523
-209 -0.235138 5.93637 2.04905
-210 0.342771 6.0083 2.02001
-211 -0.443039 5.01921 1.92928
-212 0.16744 4.77883 2.06898
-213 0.371534 5.48871 2.06837
-214 0.795684 6.21754 1.86358
-215 1.32877 5.96916 1.7027
-216 1.6274 5.98498 1.12475
-217 1.20944 5.454 1.63187
-218 0.636513 4.85218 -1.01313
-219 1.03038 4.50401 -0.857149
-220 1.49684 6.36273 0.709162
-221 0.0756571 2.9637 -1.5585
-222 0.678383 2.92988 -1.4151
-223 -2.65669 4.50277 0.390513
-224 -3.22174 4.48971 0.582401
-225 0.938403 2.04979 -0.810638
-226 0.460563 1.91114 -1.01969
-227 -1.80496 3.27365 -0.430695
-228 -2.39441 3.51338 -0.440479
-229 -0.352511 6.32985 0.207694
-230 -0.186527 5.75837 0.649305
-231 -0.733367 5.36887 0.562194
-232 -0.579507 1.98158 -1.03757
-233 -0.452448 1.54179 -0.781688
-234 -1.1503 2.33952 0.801462
-235 -1.29517 2.62788 0.414235
-236 -3.10918 3.57011 -0.634668
-237 -3.59172 3.68133 -0.118607
-238 -3.39111 4.20945 -0.540464
-239 0.426754 5.43655 0.614868
-240 0.00104384 5.09795 0.699452
-241 0.256901 4.8841 0.283504
-242 0.00166609 4.90778 -0.200775
-243 -1.43796 1.23698 -0.123313
-244 -1.20871 1.02465 -0.624764
-245 -1.36178 1.74352 0.00626368
-246 -1.48266 0.680417 -0.337806
-247 -1.548 0.0439621 -0.359122
-248 -1.18023 -0.162153 -0.831051
-249 -0.851352 0.227733 -0.930284
-250 1.19305 0.874774 -0.719222
-251 -0.0507249 3.36775 -0.426507
-252 -0.465963 2.84705 -0.267258
-253 -1.43749 6.49256 0.994053
-254 1.79785 0.489699 0.351739
-255 1.46809 0.993504 0.269343
-256 1.78714 0.0215539 0.125179
-257 1.45743 0.928724 -0.259584
-258 -1.04707 3.57109 -1.22888
-259 -0.681886 3.96498 -1.28781
-260 -0.223795 3.78481 -1.5075
-261 0.227559 4.05551 -1.40945
-262 0.351888 1.52142 0.695056
-263 -2.3412 3.76906 1.0218
-264 -0.18037 2.38824 -0.358475
-265 0.733015 -0.260462 -0.771812
-266 0.474904 0.328489 -0.91693
-267 1.00924 0.188998 -0.936223
-268 1.387 0.356678 -0.618714
-269 0.665413 0.838099 -0.859167
-270 0.185781 3.94858 -0.320652
-271 0.0224707 3.69608 0.11188
-272 -0.285072 6.49082 0.684253
-273 2.6677 3.50318 0.246386
-274 -0.801177 6.83292 0.742706
-275 -2.9566 3.64389 0.576316
-276 0.370738 5.73144 -0.218122
-277 0.956027 3.78022 -1.20675
-278 -0.680768 2.90478 -1.40722
-279 -0.602671 2.69869 0.235155
-280 0.637052 6.98713 0.726978
-281 0.942652 6.82606 1.17005
-282 -0.475259 3.31128 0.00722333
-283 -0.265889 4.22865 1.11732
-284 2.27494 4.14871 1.06259
-285 1.51453 5.3514 0.719228
-286 -3.57058 4.24278 0.00739403
-287 -0.462107 5.26681 -0.0260898
-288 -1.64335 4.49649 -0.33417
-289 -0.89404 4.44535 0.394617
-290 -1.77669 0.404991 0.453457
-291 0.773882 2.26545 1.12929
-292 0.935937 1.90161 0.806798
-293 -1.31372 5.50672 0.0273056
-294 0.17064 2.73888 0.683378
-295 0.343164 3.08665 0.3256
-296 0.196093 6.08997 0.663695
-297 3.59977 3.8609 0.313374
-298 3.24314 4.02703 0.798848
-299 3.07104 4.46528 0.629037
-300 1.25147 4.57896 -0.184531
-301 2.54816 3.66823 0.746262
-302 3.25834 3.47545 -0.557774
-303 -0.244043 7.04475 -0.0626802
-304 0.502134 2.43381 -0.172765
-305 -0.0278282 2.16841 -1.25178
-306 0.290654 1.33372 -0.961138
-307 0.03457 1.91341 1.28347
-308 -0.436272 2.16979 1.28337
-309 -2.60609 4.43979 -0.243887
-310 0.22940057491448 0.450106333873961 0.220919289175102
-311 1.04816057787935 3.84520987792536 -0.387523751716382
-312 0.791516162267201 5.89562424207913 1.13591677375976
-313 -0.836907493040771 6.00956918765895 0.978146412229845
-314 0.897235096056453 2.99636143289997 -0.410370825713378
-315 -0.451153521768497 5.3373767073504 1.26611855246413
-316 -0.601638207937747 3.24834428756796 -0.818228500086457
-317 -0.578150948677034 4.60793377466195 -0.348529829233355
-318 -0.002434094984176 4.46321724391838 -0.688750401140393
-319 -0.771645643454466 0.578006799520087 -0.363871817773244
-320 -0.0543601586649 4.28880656908635 0.479682985594208
-321 -1.23274599060468 3.95749634764298 -0.327673486839287
-322 -1.15641050567321 0.316701532655859 0.283456573141906
-323 0.642707908586347 4.39264064648036 -0.255037383091231
-324 0.973871575376334 0.351538589051357 0.352861283233105
-325 -0.020321478806152 2.73610983106167 -0.979132115832824
-326 -2.40524861426325 3.93502283313076 0.05260373980416
-327 -0.112996983771733 0.110945194308827 0.151259978153845
-328 -0.140534769323756 0.577138097361873 -0.28121993115995
-329 -1.03029782500383 3.18190347485469 -0.322734015974363
-330 -0.260994798085077 3.86857674472496 -0.794115722489941
-331 0.065159413450347 5.68657290403231 1.55244681150896
-332 0.523719426932323 3.42167358226641 -0.80544694113442
-333 0.632371068015134 0.163737689959675 0.685345188785802
-334 2.31078822450986 3.98133071806645 0.181165836561753
-335 0.43202322497883 6.50819566588173 1.01800701438157
-336 0.063722873059537 1.55165616521011 0.184639542086588
-337 -1.03566330067408 2.58450199111172 -0.183957249968463
-338 0.943759925112545 5.26525832956327 0.842224805338145
-339 0.462281715820711 2.40835401484519 -0.740861628646598
-340 0.937340482388049 0.854125848583737 0.02857211638466
-341 0.190752670540454 1.51421528893948 -0.414261182136289
-342 2.81600018453752 3.94755237848605 -0.069268031153165
-343 0.704933339335922 1.68718314317071 0.14524823220614
-344 -0.78984753898456 1.69652777556882 0.200366860857274
-345 0.046734407150132 5.50204997361444 0.216918257973715
-346 -0.548585865965186 5.88586716106866 1.49005572541013
-347 0.435146083625327 6.06982417481615 1.49594326442517
-
-*ELEMENTS
-TET
-1185 4 0
-1 55 340 9 343
-2 239 240 241 338
-3 181 313 230 346
-4 251 295 71 74
-5 114 183 170 191
-6 170 242 187 345
-7 8 310 23 327
-8 50 319 45 328
-9 229 174 171 201
-10 40 242 241 320
-11 217 88 108 95
-12 5 119 56 25
-13 229 199 201 171
-14 266 23 269 21
-15 66 64 65 314
-16 151 67 149 323
-17 269 21 55 341
-18 33 9 255 340
-19 118 56 55 310
-20 67 241 149 323
-21 119 121 28 344
-22 271 151 295 41
-23 152 150 36 334
-24 7 90 131 234
-25 136 138 79 326
-26 229 303 174 201
-27 127 134 228 326
-28 163 150 152 334
-29 198 175 195 276
-30 186 300 149 323
-31 25 319 1 322
-32 270 318 148 330
-33 231 293 202 172
-34 150 300 36 311
-35 247 319 246 322
-36 88 240 105 315
-37 105 240 128 315
-38 116 259 260 330
-39 41 311 66 314
-40 144 145 147 326
-41 141 93 27 232
-42 167 297 165 342
-43 175 176 229 296
-44 126 282 146 321
-45 25 11 15 1
-46 243 120 31 319
-47 47 22 254 324
-48 41 295 74 314
-49 10 15 25 60
-50 45 1 11 319
-51 132 231 40 287
-52 56 336 55 341
-53 185 73 188 198
-54 81 82 35 314
-55 240 230 231 315
-56 88 107 67 338
-57 168 147 144 145
-58 63 64 61 81
-59 128 240 40 231
-60 213 209 210 331
-61 205 204 130 313
-62 88 105 211 315
-63 66 102 151 295
-64 106 88 240 105
-65 34 336 56 341
-66 317 318 126 330
-67 71 90 123 131
-68 187 73 276 345
-69 180 97 205 346
-70 36 155 154 334
-71 276 183 184 170
-72 66 151 150 311
-73 167 166 83 342
-74 282 251 252 316
-75 13 324 12 333
-76 83 166 84 342
-77 66 160 62 314
-78 229 172 175 345
-79 240 105 283 106
-80 171 175 172 114
-81 75 128 231 315
-82 87 218 184 318
-83 243 56 120 319
-84 279 90 7 234
-85 262 336 92 343
-86 196 335 197 347
-87 92 336 34 343
-88 91 279 93 337
-89 289 78 132 317
-90 181 331 296 347
-91 285 188 186 338
-92 34 225 53 339
-93 242 320 270 323
-94 165 297 99 342
-95 219 110 218 323
-96 17 8 11 327
-97 325 332 74 339
-98 197 335 312 347
-99 25 5 10 20
-100 71 65 74 295
-101 268 48 257 340
-102 117 316 278 325
-103 41 151 295 66
-104 286 275 136 76
-105 44 259 193 330
-106 134 227 139 321
-107 162 83 84 342
-108 78 288 133 321
-109 153 301 99 298
-110 294 102 295 38
-111 250 257 9 340
-112 294 308 89 123
-113 214 197 68 312
-114 145 168 77 144
-115 260 330 221 332
-116 197 312 214 347
-117 141 264 43 325
-118 260 259 117 330
-119 230 315 88 331
-120 71 271 251 282
-121 137 223 168 326
-122 206 313 97 346
-123 88 315 211 331
-124 270 317 242 318
-125 24 324 6 333
-126 67 109 149 241
-127 247 246 31 322
-128 73 220 189 177
-129 187 184 170 276
-130 92 65 292 343
-131 205 39 180 313
-132 149 311 151 323
-133 127 227 134 321
-134 93 141 27 142
-135 213 331 210 347
-136 57 140 139 329
-137 56 27 243 344
-138 245 91 93 337
-139 86 311 277 332
-140 294 279 92 90
-141 25 120 19 56
-142 33 16 55 340
-143 242 287 40 345
-144 1 319 247 322
-145 175 113 182 174
-146 50 45 26 328
-147 170 169 115 287
-148 136 237 286 238
-149 93 91 245 344
-150 278 141 43 316
-151 287 293 172 132
-152 44 134 139 321
-153 120 19 31 322
-154 226 225 54 339
-155 273 162 161 342
-156 256 48 13 324
-157 126 317 270 318
-158 12 23 8 310
-159 74 252 251 325
-160 57 329 143 337
-161 152 153 284 334
-162 242 184 170 187
-163 18 3 58 322
-164 94 272 178 176
-165 226 54 305 339
-166 136 79 309 326
-167 193 317 44 330
-168 142 91 245 337
-169 3 59 58 322
-170 241 320 242 323
-171 25 19 120 322
-172 241 88 240 67
-173 130 202 200 313
-174 45 319 11 328
-175 230 181 272 313
-176 111 220 73 312
-177 264 325 74 339
-178 161 334 273 342
-179 71 122 38 104
-180 32 324 24 333
-181 210 209 96 331
-182 305 264 226 339
-183 105 128 207 315
-184 14 310 16 333
-185 211 315 208 331
-186 9 53 250 340
-187 8 42 26 327
-188 56 243 120 344
-189 15 59 10 322
-190 231 293 132 129
-191 282 126 251 316
-192 138 127 228 326
-193 296 335 181 347
-194 73 239 188 338
-195 30 277 261 332
-196 307 92 294 37
-197 279 234 7 235
-198 84 160 161 334
-199 70 285 109 338
-200 173 229 199 201
-201 233 264 93 34
-202 93 252 264 337
-203 65 64 304 314
-204 144 135 78 288
-205 18 322 1 247
-206 71 252 251 74
-207 96 196 197 347
-208 176 190 113 194
-209 243 245 120 344
-210 122 271 71 72
-211 160 159 82 311
-212 176 280 190 194
-213 172 80 199 171
-214 253 205 130 313
-215 68 197 281 312
-216 162 302 83 342
-217 289 125 78 321
-218 211 88 212 105
-219 159 311 86 314
-220 229 230 296 272
-221 99 162 273 342
-222 181 196 96 347
-223 25 319 56 328
-224 269 55 21 23
-225 136 237 138 286
-226 291 92 294 65
-227 271 295 251 41
-228 251 295 74 41
-229 294 102 38 103
-230 34 93 56 336
-231 69 208 209 331
-232 314 332 222 339
-233 61 292 65 343
-234 13 23 42 324
-235 2 65 103 101
-236 136 137 275 326
-237 31 290 247 322
-238 287 230 231 345
-239 54 325 305 339
-240 230 287 231 172
-241 158 155 36 334
-242 24 4 14 333
-243 279 235 7 337
-244 303 175 229 174
-245 67 240 241 320
-246 222 325 54 339
-247 72 104 271 320
-248 259 116 193 330
-249 126 321 316 329
-250 269 55 53 341
-251 23 269 55 340
-252 270 311 41 332
-253 128 240 231 315
-254 181 331 96 346
-255 311 314 41 332
-256 10 19 25 322
-257 38 294 71 295
-258 93 245 142 27
-259 119 118 56 344
-260 176 73 296 175
-261 233 27 56 344
-262 287 172 230 345
-263 283 289 40 320
-264 283 104 72 320
-265 315 331 230 346
-266 181 335 196 347
-267 237 286 275 138
-268 41 311 270 323
-269 25 310 20 327
-270 305 325 264 339
-271 222 54 35 339
-272 133 288 134 321
-273 21 328 55 341
-274 96 209 180 346
-275 232 305 43 325
-276 136 224 137 223
-277 251 271 270 126
-278 242 184 191 170
-279 150 149 300 311
-280 199 172 171 229
-281 209 208 97 346
-282 177 73 190 189
-283 258 44 139 321
-284 144 134 127 326
-285 66 65 101 295
-286 161 160 163 334
-287 277 85 219 311
-288 235 91 142 337
-289 92 262 307 28
-290 301 153 273 334
-291 176 113 303 194
-292 26 11 328 45
-293 317 170 287 242
-294 175 113 190 195
-295 84 334 161 342
-296 286 237 275 76
-297 148 218 192 318
-298 28 118 119 344
-299 181 39 179 274
-300 140 258 329 316
-301 247 248 46 319
-302 14 5 16 310
-303 225 226 53 339
-304 150 300 149 36
-305 27 245 243 344
-306 220 73 189 111
-307 11 1 25 319
-308 26 42 23 327
-309 230 296 239 345
-310 10 25 15 322
-311 289 317 126 321
-312 148 116 261 318
-313 16 324 32 333
-314 221 316 117 325
-315 136 223 137 326
-316 252 316 141 329
-317 304 92 34 65
-318 184 242 191 87
-319 111 312 73 338
-320 236 79 138 228
-321 238 136 236 79
-322 66 62 160 163
-323 68 215 214 312
-324 172 229 200 313
-325 70 312 285 338
-326 154 152 36 334
-327 216 215 68 312
-328 316 321 126 330
-329 251 270 41 332
-330 170 183 184 191
-331 13 48 23 324
-332 184 185 187 110
-333 53 226 34 339
-334 171 175 174 229
-335 115 132 133 317
-336 11 26 328 327
-337 264 279 71 252
-338 46 249 51 319
-339 230 88 240 239
-340 255 9 257 340
-341 175 170 276 345
-342 251 41 74 332
-343 146 227 127 321
-344 19 5 25 119
-345 23 310 55 328
-346 294 89 291 103
-347 304 264 34 92
-348 294 102 101 295
-349 89 294 291 307
-350 296 229 272 176
-351 294 90 308 123
-352 9 340 33 343
-353 36 156 150 311
-354 176 175 303 113
-355 118 336 56 344
-356 146 143 127 227
-357 272 274 173 313
-358 273 334 153 342
-359 38 123 71 294
-360 66 102 295 101
-361 22 255 254 324
-362 221 222 30 332
-363 74 332 314 339
-364 303 176 194 178
-365 240 40 231 345
-366 39 181 179 180
-367 181 274 179 272
-368 300 219 156 311
-369 139 227 57 329
-370 23 265 26 266
-371 124 71 131 7
-372 190 73 198 189
-373 172 202 231 313
-374 270 126 271 320
-375 176 73 177 335
-376 56 328 21 341
-377 120 56 25 319
-378 122 71 131 124
-379 282 125 124 126
-380 71 122 72 124
-381 238 136 79 309
-382 95 312 88 331
-383 23 12 8 42
-384 282 125 126 146
-385 138 237 238 236
-386 23 268 267 250
-387 51 56 244 319
-388 50 249 45 319
-389 302 164 83 342
-390 294 89 38 123
-391 71 104 38 295
-392 151 271 104 67
-393 86 35 82 314
-394 161 162 84 342
-395 271 251 270 41
-396 78 125 144 321
-397 192 218 87 318
-398 244 246 46 319
-399 12 310 8 333
-400 27 51 233 56
-401 65 294 279 92
-402 82 64 62 314
-403 56 319 51 328
-404 242 184 110 218
-405 251 330 270 332
-406 100 334 157 342
-407 273 153 99 342
-408 127 145 144 326
-409 71 122 131 123
-410 287 293 132 231
-411 97 208 206 346
-412 74 295 65 314
-413 79 134 135 326
-414 56 93 233 344
-415 23 12 42 324
-416 98 150 152 163
-417 21 306 233 341
-418 254 255 48 324
-419 95 210 214 347
-420 52 233 306 341
-421 231 313 75 315
-422 172 200 229 199
-423 282 125 146 124
-424 29 20 14 333
-425 294 37 291 307
-426 17 29 12 333
-427 144 125 146 321
-428 169 293 172 80
-429 287 169 132 172
-430 296 88 230 239
-431 170 317 287 115
-432 248 1 249 319
-433 172 170 175 345
-434 73 177 190 176
-435 319 327 11 328
-436 13 47 256 324
-437 141 329 57 337
-438 281 197 94 335
-439 284 301 98 334
-440 61 304 63 343
-441 170 172 287 345
-442 282 7 146 329
-443 179 181 196 180
-444 208 207 206 315
-445 82 62 64 81
-446 241 239 187 345
-447 158 36 163 334
-448 93 27 233 344
-449 92 336 28 344
-450 231 202 203 313
-451 304 64 63 314
-452 81 63 64 314
-453 202 80 172 293
-454 172 169 170 287
-455 13 24 6 47
-456 94 280 281 335
-457 287 169 115 132
-458 10 15 60 59
-459 148 311 219 323
-460 313 315 230 346
-461 252 316 251 325
-462 84 157 158 334
-463 89 92 294 307
-464 84 158 160 334
-465 264 252 74 325
-466 278 43 221 325
-467 200 202 172 313
-468 258 140 117 316
-469 25 56 5 310
-470 88 296 230 331
-471 125 124 126 72
-472 296 312 73 335
-473 16 310 55 340
-474 312 335 296 347
-475 239 296 73 345
-476 303 176 178 229
-477 124 282 7 146
-478 78 317 289 321
-479 86 314 311 332
-480 282 124 7 71
-481 178 272 274 173
-482 258 259 44 330
-483 55 328 56 341
-484 135 144 168 326
-485 253 39 205 313
-486 115 287 132 317
-487 240 239 241 345
-488 157 334 84 342
-489 198 175 190 195
-490 272 178 274 179
-491 229 178 173 272
-492 99 298 165 342
-493 211 105 207 315
-494 112 175 195 182
-495 40 241 242 345
-496 181 94 179 196
-497 229 303 173 178
-498 303 229 173 201
-499 141 57 142 337
-500 175 183 114 182
-501 294 65 279 71
-502 276 183 112 184
-503 251 316 126 330
-504 225 34 53 343
-505 117 259 258 316
-506 80 200 172 199
-507 31 19 290 322
-508 121 92 28 344
-509 143 142 57 337
-510 160 163 150 311
-511 304 225 34 339
-512 284 153 301 334
-513 256 47 254 324
-514 278 140 141 316
-515 276 170 187 345
-516 300 156 36 311
-517 121 93 92 344
-518 118 92 28 262
-519 295 65 101 294
-520 164 162 99 342
-521 235 142 143 337
-522 103 65 291 294
-523 32 324 16 340
-524 120 245 91 344
-525 234 93 121 91
-526 92 307 308 28
-527 164 297 167 342
-528 191 169 115 170
-529 294 279 90 71
-530 69 213 212 331
-531 44 133 134 321
-532 192 87 191 318
-533 206 315 313 346
-534 222 332 325 339
-535 25 327 319 328
-536 32 16 33 340
-537 132 78 133 317
-538 92 308 121 28
-539 251 126 270 330
-540 55 336 262 343
-541 44 316 258 330
-542 92 279 264 93
-543 233 93 232 27
-544 191 169 170 114
-545 180 181 96 346
-546 258 140 329 139
-547 271 102 295 151
-548 48 324 255 340
-549 129 289 132 231
-550 128 240 283 40
-551 53 341 55 343
-552 55 56 118 336
-553 317 191 170 242
-554 33 255 32 340
-555 187 109 186 338
-556 312 331 95 347
-557 4 24 6 333
-558 31 246 243 319
-559 13 24 47 324
-560 186 323 241 187
-561 157 155 158 334
-562 226 305 52 264
-563 298 299 165 342
-564 322 18 1 58
-565 153 299 298 342
-566 100 153 154 334
-567 42 12 13 324
-568 30 86 277 332
-569 283 289 72 128
-570 124 271 71 282
-571 312 220 281 68
-572 65 304 74 314
-573 122 271 104 71
-574 258 316 44 321
-575 130 204 203 313
-576 253 200 274 313
-577 205 97 204 313
-578 156 158 160 311
-579 62 64 66 314
-580 47 24 22 324
-581 277 148 261 332
-582 180 39 181 313
-583 145 125 146 144
-584 156 160 150 311
-585 100 155 157 334
-586 150 158 156 36
-587 115 317 193 318
-588 56 336 93 344
-589 119 5 56 118
-590 75 206 207 315
-591 6 13 12 333
-592 241 40 240 345
-593 181 176 272 335
-594 203 202 130 313
-595 270 330 261 332
-596 316 321 258 329
-597 229 296 230 345
-598 71 264 252 74
-599 221 43 305 325
-600 193 192 115 318
-601 246 319 31 322
-602 142 235 143 91
-603 243 244 56 319
-604 141 316 140 329
-605 214 347 96 197
-606 17 60 25 15
-607 192 191 115 318
-608 7 235 146 329
-609 21 23 55 328
-610 60 17 25 20
-611 260 117 221 316
-612 193 116 192 318
-613 185 184 187 276
-614 73 187 185 188
-615 293 169 172 132
-616 264 252 141 337
-617 31 319 120 322
-618 72 289 126 125
-619 56 310 25 328
-620 188 187 186 338
-621 118 16 5 310
-622 25 5 20 310
-623 258 321 139 329
-624 336 341 34 343
-625 137 136 76 224
-626 225 35 54 339
-627 96 331 181 347
-628 177 312 281 335
-629 55 341 336 343
-630 154 155 100 334
-631 61 63 9 343
-632 119 120 56 19
-633 316 221 332 325
-634 279 252 93 337
-635 172 230 229 313
-636 226 52 306 341
-637 112 175 182 276
-638 205 313 180 346
-639 304 225 35 63
-640 164 302 162 342
-641 258 316 259 330
-642 23 266 26 328
-643 100 157 166 342
-644 310 324 23 340
-645 157 84 166 342
-646 185 276 198 112
-647 187 185 186 110
-648 185 73 198 276
-649 15 17 11 327
-650 169 172 170 114
-651 174 175 114 182
-652 20 29 17 333
-653 139 321 227 329
-654 34 341 53 343
-655 9 63 53 343
-656 317 321 44 330
-657 181 296 176 335
-658 34 65 92 343
-659 141 316 252 325
-660 306 53 226 341
-661 26 266 50 328
-662 126 317 289 320
-663 323 186 241 149
-664 143 329 235 337
-665 55 310 23 340
-666 158 150 156 160
-667 104 67 283 106
-668 74 314 304 339
-669 67 88 106 107
-670 185 276 112 184
-671 239 241 187 338
-672 128 105 283 240
-673 203 231 75 129
-674 70 217 216 312
-675 230 331 181 346
-676 247 290 18 322
-677 287 293 231 172
-678 79 135 134 309
-679 114 169 172 171
-680 260 316 221 330
-681 293 231 202 129
-682 187 73 239 188
-683 97 313 205 346
-684 190 176 113 175
-685 274 39 253 313
-686 276 112 195 198
-687 209 331 208 346
-688 73 190 198 175
-689 94 196 181 335
-690 221 30 260 332
-691 34 304 65 343
-692 163 150 158 160
-693 158 36 155 156
-694 66 65 64 101
-695 272 94 181 335
-696 16 324 310 340
-697 121 119 120 344
-698 136 237 238 138
-699 55 53 9 340
-700 121 91 93 344
-701 207 128 75 315
-702 107 88 106 108
-703 61 304 64 63
-704 120 119 56 344
-705 95 331 213 347
-706 61 65 64 304
-707 32 24 14 333
-708 310 324 16 333
-709 308 92 121 90
-710 141 252 264 325
-711 19 5 10 25
-712 308 92 90 294
-713 121 120 91 344
-714 89 294 38 103
-715 218 148 219 323
-716 231 40 287 345
-717 279 329 252 337
-718 25 119 56 19
-719 187 242 241 345
-720 21 56 51 328
-721 35 304 63 314
-722 279 65 264 71
-723 279 7 282 329
-724 160 311 82 314
-725 75 129 231 128
-726 112 175 276 195
-727 208 315 206 346
-728 175 183 182 276
-729 175 113 174 303
-730 175 171 172 229
-731 171 175 114 174
-732 118 28 92 336
-733 172 175 170 114
-734 304 225 63 343
-735 63 225 53 343
-736 153 273 99 301
-737 33 340 55 343
-738 239 187 188 338
-739 23 48 268 340
-740 231 132 40 289
-741 60 20 25 10
-742 176 73 175 190
-743 93 141 142 337
-744 221 117 278 325
-745 251 325 316 332
-746 46 248 249 319
-747 290 19 3 322
-748 153 334 100 342
-749 151 271 270 41
-750 251 271 126 282
-751 88 213 95 331
-752 120 319 25 322
-753 29 14 4 333
-754 126 146 125 321
-755 253 130 200 313
-756 184 218 242 318
-757 32 22 24 324
-758 23 327 310 328
-759 25 17 15 327
-760 126 318 270 330
-761 138 275 147 326
-762 35 63 81 314
-763 221 325 222 332
-764 88 312 296 331
-765 217 312 70 338
-766 193 115 133 317
-767 91 279 235 234
-768 260 261 116 330
-769 144 127 146 145
-770 107 108 70 338
-771 290 3 18 322
-772 220 312 281 177
-773 91 235 279 337
-774 154 284 152 153
-775 100 167 165 342
-776 155 158 156 157
-777 177 280 176 335
-778 73 312 177 335
-779 123 38 71 122
-780 9 53 55 343
-781 83 164 167 342
-782 245 27 93 344
-783 65 2 291 292
-784 65 2 61 64
-785 1 15 25 322
-786 89 92 307 308
-787 103 65 294 101
-788 71 65 295 294
-789 214 312 95 347
-790 292 92 262 37
-791 163 98 301 334
-792 11 8 26 327
-793 254 48 256 324
-794 20 310 14 333
-795 134 135 144 288
-796 67 109 107 149
-797 187 109 241 186
-798 241 109 149 186
-799 270 318 242 323
-800 270 151 41 323
-801 219 148 277 311
-802 230 313 231 315
-803 24 13 6 324
-804 218 318 148 323
-805 11 25 15 327
-806 177 176 280 190
-807 175 176 303 229
-808 113 175 182 195
-809 181 176 296 272
-810 230 239 240 345
-811 230 172 229 345
-812 272 94 178 179
-813 88 230 240 315
-814 73 220 177 312
-815 92 93 34 336
-816 17 327 20 333
-817 216 68 220 312
-818 39 274 181 313
-819 263 145 168 77
-820 108 217 70 338
-821 191 317 170 115
-822 296 331 312 347
-823 117 140 278 316
-824 131 279 71 90
-825 121 279 93 234
-826 121 279 234 90
-827 294 90 123 71
-828 178 176 272 229
-829 118 55 262 16
-830 282 321 126 329
-831 28 336 118 344
-832 296 181 230 331
-833 264 226 34 341
-834 187 239 73 345
-835 289 72 126 320
-836 12 324 310 333
-837 124 271 282 72
-838 33 292 61 343
-839 216 285 70 312
-840 239 312 88 338
-841 65 264 74 304
-842 89 92 308 294
-843 134 127 228 227
-844 245 93 142 337
-845 291 92 37 294
-846 262 92 307 37
-847 7 329 279 337
-848 6 29 4 333
-849 92 279 121 90
-850 235 329 7 337
-851 292 92 291 65
-852 92 292 291 37
-853 241 88 67 338
-854 154 153 152 334
-855 151 66 41 311
-856 232 264 305 325
-857 85 277 159 311
-858 168 147 263 137
-859 282 252 279 329
-860 23 42 8 327
-861 211 208 69 331
-862 273 161 301 334
-863 235 143 146 329
-864 242 318 218 323
-865 241 242 187 323
-866 165 299 100 342
-867 270 317 126 320
-868 300 311 149 323
-869 94 176 194 280
-870 49 48 13 256
-871 222 221 54 325
-872 146 321 282 329
-873 141 140 57 329
-874 233 34 93 341
-875 76 224 136 286
-876 88 240 239 338
-877 73 312 239 338
-878 241 240 88 338
-879 161 163 301 334
-880 176 296 73 335
-881 304 34 225 343
-882 122 271 72 104
-883 150 151 149 311
-884 152 284 98 334
-885 187 73 185 276
-886 261 330 260 332
-887 187 188 186 185
-888 246 244 243 319
-889 269 306 21 341
-890 262 292 33 343
-891 50 266 21 328
-892 271 126 72 320
-893 148 318 261 330
-894 93 56 233 341
-895 221 316 332 330
-896 23 265 266 267
-897 231 203 202 129
-898 175 276 73 345
-899 199 200 229 173
-900 8 327 17 333
-901 23 324 48 340
-902 20 327 310 333
-903 200 80 172 202
-904 66 295 41 314
-905 67 104 283 320
-906 292 262 92 343
-907 136 236 79 138
-908 126 125 289 321
-909 211 69 212 331
-910 283 106 67 320
-911 168 147 137 326
-912 282 279 71 7
-913 231 203 75 313
-914 49 23 13 48
-915 138 238 136 236
-916 53 306 269 341
-917 55 16 118 310
-918 242 317 270 320
-919 53 269 250 340
-920 56 243 27 244
-921 88 312 217 338
-922 88 211 212 331
-923 283 40 240 320
-924 271 104 67 320
-925 240 40 241 320
-926 124 271 72 71
-927 124 126 72 282
-928 242 317 191 318
-929 212 213 88 331
-930 52 226 264 341
-931 132 40 289 317
-932 57 227 143 329
-933 44 321 316 330
-934 192 116 148 318
-935 249 50 51 319
-936 191 87 242 318
-937 46 246 247 319
-938 150 163 66 311
-939 298 99 165 297
-940 177 281 280 335
-941 187 241 109 338
-942 184 242 87 318
-943 252 71 251 282
-944 44 317 133 321
-945 58 59 15 322
-946 148 318 270 323
-947 35 314 222 339
-948 151 311 41 323
-949 191 317 115 318
-950 184 242 110 187
-951 264 141 93 337
-952 264 233 52 341
-953 10 59 3 322
-954 163 160 158 334
-955 65 279 264 92
-956 217 95 215 312
-957 127 144 146 321
-958 73 296 239 312
-959 215 95 214 312
-960 51 244 46 319
-961 110 242 218 323
-962 305 54 221 325
-963 64 82 81 314
-964 310 327 8 333
-965 265 23 26 42
-966 82 311 159 314
-967 180 313 181 346
-968 160 158 159 311
-969 222 314 86 332
-970 43 264 232 325
-971 167 100 166 342
-972 278 316 43 325
-973 347 214 96 210
-974 310 327 25 328
-975 23 49 13 42
-976 51 50 21 328
-977 233 264 232 93
-978 264 141 232 93
-979 65 264 304 92
-980 92 264 34 93
-981 233 264 52 232
-982 264 141 43 232
-983 147 145 127 326
-984 264 71 65 74
-985 133 317 78 321
-986 30 261 260 332
-987 227 321 146 329
-988 277 311 148 332
-989 261 148 270 332
-990 271 102 151 104
-991 55 262 33 343
-992 295 271 71 104
-993 271 71 251 295
-994 271 282 72 126
-995 41 314 74 332
-996 73 188 111 338
-997 11 319 25 327
-998 156 219 85 311
-999 152 98 163 334
-1000 5 56 118 310
-1001 316 330 251 332
-1002 117 316 260 330
-1003 88 108 107 338
-1004 116 318 193 330
-1005 272 176 94 335
-1006 20 17 25 327
-1007 36 150 163 334
-1008 136 275 138 326
-1009 175 296 229 345
-1010 175 73 296 345
-1011 134 144 127 321
-1012 94 176 280 335
-1013 216 220 111 312
-1014 51 319 50 328
-1015 32 255 33 22
-1016 12 29 6 333
-1017 153 298 99 342
-1018 100 299 153 342
-1019 43 316 141 325
-1020 159 158 85 311
-1021 110 186 187 323
-1022 250 269 23 340
-1023 111 188 285 338
-1024 138 228 79 326
-1025 88 106 212 105
-1026 96 331 209 346
-1027 79 135 309 326
-1028 73 111 188 189
-1029 264 34 226 339
-1030 73 188 198 189
-1031 97 180 209 346
-1032 181 274 272 313
-1033 193 318 317 330
-1034 198 73 175 276
-1035 264 74 304 339
-1036 131 279 90 7
-1037 264 279 252 93
-1038 261 318 116 330
-1039 282 279 252 71
-1040 259 316 117 330
-1041 131 279 7 71
-1042 91 279 234 93
-1043 279 92 121 93
-1044 65 2 103 291
-1045 242 170 287 345
-1046 126 321 317 330
-1047 65 2 64 101
-1048 2 65 61 292
-1049 93 34 56 341
-1050 23 268 49 267
-1051 285 312 111 338
-1052 65 304 61 343
-1053 62 160 82 314
-1054 208 331 315 346
-1055 272 230 296 181
-1056 168 223 135 326
-1057 118 92 262 336
-1058 6 324 13 333
-1059 14 16 32 333
-1060 86 82 159 314
-1061 66 311 160 314
-1062 65 295 66 314
-1063 225 304 35 339
-1064 35 86 222 314
-1065 55 33 262 16
-1066 226 53 34 341
-1067 252 329 141 337
-1068 99 297 164 342
-1069 23 267 269 250
-1070 67 106 240 320
-1071 267 23 269 266
-1072 264 34 233 341
-1073 23 265 49 42
-1074 135 134 144 326
-1075 255 257 48 340
-1076 17 12 8 333
-1077 88 239 296 312
-1078 281 312 197 335
-1079 197 196 94 335
-1080 219 311 300 323
-1081 230 240 231 345
-1082 184 87 191 183
-1083 183 276 112 182
-1084 183 170 175 114
-1085 170 183 175 276
-1086 93 336 92 344
-1087 268 23 49 48
-1088 231 289 40 128
-1089 283 289 128 40
-1090 58 15 1 322
-1091 20 5 14 310
-1092 23 310 12 324
-1093 213 69 209 331
-1094 129 289 231 128
-1095 289 129 132 78
-1096 75 204 206 313
-1097 261 270 148 330
-1098 95 88 108 213
-1099 212 88 213 108
-1100 200 229 173 313
-1101 304 314 35 339
-1102 106 88 212 108
-1103 147 275 137 326
-1104 222 86 30 332
-1105 173 229 272 313
-1106 67 88 240 106
-1107 210 331 96 347
-1108 40 317 242 320
-1109 27 51 56 244
-1110 21 51 56 233
-1111 240 106 283 320
-1112 150 163 158 36
-1113 67 320 241 323
-1114 147 168 263 145
-1115 55 269 53 340
-1116 289 317 40 320
-1117 207 208 211 315
-1118 247 1 248 319
-1119 309 135 223 326
-1120 228 134 79 326
-1121 110 300 186 323
-1122 271 151 270 323
-1123 45 1 249 248
-1124 176 94 194 178
-1125 265 23 49 267
-1126 126 316 282 329
-1127 272 94 179 181
-1128 109 285 186 338
-1129 270 311 148 323
-1130 67 107 109 338
-1131 282 316 252 329
-1132 230 272 229 313
-1133 203 204 75 313
-1134 118 262 55 336
-1135 74 325 251 332
-1136 181 180 96 196
-1137 136 137 76 275
-1138 193 133 44 317
-1139 45 249 1 319
-1140 168 144 147 326
-1141 232 305 264 52
-1142 147 127 138 326
-1143 102 104 295 38
-1144 169 80 172 171
-1145 102 294 101 103
-1146 67 109 241 338
-1147 271 102 104 295
-1148 271 320 67 323
-1149 257 250 268 340
-1150 300 110 219 323
-1151 268 250 23 340
-1152 187 242 110 323
-1153 271 67 151 323
-1154 270 320 271 323
-1155 283 72 289 320
-1156 85 158 156 311
-1157 159 277 86 311
-1158 173 274 200 313
-1159 172 231 230 313
-1160 204 97 206 313
-1161 217 215 216 312
-1162 275 286 136 138
-1163 255 324 32 340
-1164 255 22 32 324
-1165 233 56 21 341
-1166 143 227 146 329
-1167 26 327 23 328
-1168 223 136 309 326
-1169 216 111 285 312
-1170 304 34 264 339
-1171 217 88 95 312
-1172 55 310 56 328
-1173 3 19 10 322
-1174 148 311 270 332
-1175 242 40 287 317
-1176 88 217 108 338
-1177 287 40 132 317
-1178 21 266 23 328
-1179 109 107 70 338
-1180 95 213 210 347
-1181 66 163 160 311
-1182 288 144 134 321
-1183 9 33 61 343
-1184 288 78 144 321
-1185 75 313 206 315
-
-# backshell elements
-*SET backshell
-*INCLUDE shellElementList.txt
-
-# all other elements
-*SET theRest
-*INCLUDE nonShellElementList.txt
-
-# material for the backshell
-*MATERIAL hardMaterial
-ENU, 1000, 100000000, 0.45
-
-# material for the rest of the body
-*MATERIAL softMaterial
-ENU, 1000, 1000000, 0.35
-
-# the backshell
-*REGION
-backshell, hardMaterial
-
-# the rest
-*REGION
-theRest, softMaterial
-
diff --git a/examples/turtle/model/turtle-volumetric-homogeneous.veg b/examples/turtle/model/turtle-volumetric-homogeneous.veg
deleted file mode 100644
index 3fbbbba83951f08b5115c204c11f0bf9dd59d2dd..0000000000000000000000000000000000000000
--- a/examples/turtle/model/turtle-volumetric-homogeneous.veg
+++ /dev/null
@@ -1,1548 +0,0 @@
-# Vega mesh file.
-# 347 vertices, 1185 elements
-
-*VERTICES
-347 3 0 0
-1 -1.07586 -0.298468 -0.0952629
-2 1.14687 2.36868 0.807906
-3 -1.52689 0.224941 0.861942
-4 0.732394 -0.049599 1.14023
-5 -0.1488 0.686607 0.819301
-6 0.912779 -0.256313 0.99358
-7 -1.10349 3.06502 0.576252
-8 0.15638 -0.293391 0.183926
-9 1.36659 1.43824 -0.13223
-10 -0.799163 0.521933 1.1138
-11 -0.355696 -0.298592 0.0488136
-12 0.571505 -0.294818 0.450653
-13 1.31072 -0.296557 0.241448
-14 0.394326 0.322222 1.11038
-15 -0.746481 -0.297871 0.429331
-16 0.440164 1.02524 0.801095
-17 -0.14617 -0.280441 0.439071
-18 -1.81422 -0.0750205 0.280667
-19 -1.04928 0.89413 0.870746
-20 -0.12279 0.240891 0.844794
-21 0.125006 0.870122 -0.78
-22 1.45414 0.622585 0.750565
-23 0.756256 0.311578 -0.290053
-24 1.18055 0.216538 0.986384
-25 -0.515629 0.407269 0.232091
-26 0.125703 -0.202337 -0.741357
-27 -1.11196 1.57291 -0.426935
-28 -0.367027 1.758 1.00277
-29 0.503061 -0.294756 0.873759
-30 0.507497 3.56401 -1.4807
-31 -1.59062 0.784677 0.165546
-32 0.913687 0.721534 0.920112
-33 1.09097 1.41801 0.60306
-34 0.20791 2.02511 -0.130844
-35 1.20072 2.5498 -0.897716
-36 1.69334 4.41724 0.179419
-37 0.494875 1.85214 1.06304
-38 0.107596 3.20899 1.33689
-39 -1.06293 6.78103 1.16639
-40 -0.276029 4.89078 0.271272
-41 0.459803 3.4464 -0.0427515
-42 0.673682 -0.293966 -0.164778
-43 -0.434716 2.45908 -1.36411
-44 -1.15797 4.06886 -1.05429
-45 -0.591842 -0.299203 -0.64272
-46 -1.28957 0.458428 -0.772923
-47 1.67519 0.0415655 0.632792
-48 1.6179 0.42484 -0.156465
-49 1.32152 -0.159821 -0.642702
-50 -0.341747 0.328057 -0.914196
-51 -0.64823 1.05657 -0.817318
-52 -0.0744169 1.67541 -1.11973
-53 0.697426 1.54283 -0.704255
-54 0.441446 2.36633 -1.30899
-55 0.408346 1.12899 -0.0323087
-56 -0.435776 1.25449 -0.0146378
-57 -1.5454 2.82687 -0.686709
-58 -1.31269 -0.29861 0.419141
-59 -1.04037 -0.199519 1.01643
-60 -0.455365 -0.263005 0.944565
-61 1.28037 1.97772 0.269872
-62 1.80439 3.06274 0.0390899
-63 1.34987 2.07278 -0.414955
-64 1.3765 2.62142 0.253604
-65 0.568583 2.61017 0.296107
-66 1.14476 3.37464 0.353
-67 0.587054 4.29607 0.6882
-68 1.35338 6.50258 1.40841
-69 -0.126695 5.37232 2.17496
-70 1.40335 5.19735 1.20622
-71 -0.217956 3.04303 0.398081
-72 -0.579337 3.98678 0.776539
-73 0.595838 5.96376 0.379372
-74 0.203774 2.77773 -0.391999
-75 -1.35539 5.18403 1.30037
-76 -3.54422 3.99078 0.524449
-77 -1.85505 4.03707 0.976603
-78 -1.32323 4.49114 0.0799895
-79 -2.58433 4.09541 -0.614208
-80 -1.24307 5.9875 -0.10553
-81 1.76552 2.49515 -0.218198
-82 1.60719 3.03163 -0.620133
-83 3.14934 3.94535 -0.764735
-84 2.31237 3.63023 -0.499494
-85 1.42334 4.06088 -1.04605
-86 1.17093 3.25434 -1.22246
-87 0.238231 5.12013 -1.226
-88 0.269575 5.23574 1.15982
-89 -0.0309661 2.37536 1.49917
-90 -0.790926 2.48979 1.1255
-91 -1.28648 2.12657 0.362332
-92 0.00704813 2.20828 0.554514
-93 -0.572671 2.21946 -0.0149638
-94 0.226389 7.09199 0.995003
-95 0.82307 5.69728 1.90788
-96 -0.0084901 6.45645 1.89441
-97 -0.96959 5.97987 1.90767
-98 2.03767 3.67953 0.921421
-99 3.14679 3.55378 0.402342
-100 2.63428 4.48055 -0.113861
-101 1.12298 2.90838 0.661755
-102 0.671868 3.43441 0.917117
-103 0.670375 2.74933 1.21992
-104 0.0939719 3.86272 1.01997
-105 -0.34123 4.57417 1.50179
-106 0.360087 4.44592 1.36191
-107 1.00546 4.62175 1.14847
-108 0.73081 4.96677 1.75709
-109 1.30749 4.90086 0.553709
-110 0.977946 4.89264 -0.473883
-111 1.53141 5.82613 0.501039
-112 0.725754 5.91829 -0.689851
-113 0.220441 6.97838 -0.252861
-114 -0.393296 5.98479 -0.855123
-115 -0.732116 5.00047 -0.868277
-116 -0.24516 4.28284 -1.30236
-117 -0.604397 3.40365 -1.48687
-118 -0.108955 1.2919 0.681819
-119 -0.675808 1.32421 0.781786
-120 -1.25862 1.36284 0.389829
-121 -0.934451 1.88111 0.801962
-122 -0.394786 3.48502 1.09487
-123 -0.423778 2.9265 1.31788
-124 -0.820867 3.49748 0.753418
-125 -1.14599 3.97727 0.446719
-126 -0.414753 3.82266 -0.153308
-127 -2.02475 3.44735 0.0733384
-128 -1.03131 4.61188 0.883202
-129 -1.39118 5.25075 0.454226
-130 -1.65567 6.04088 0.954185
-131 -0.823861 3.04858 1.01213
-132 -1.07815 5.13436 -0.216906
-133 -1.19853 4.57797 -0.597847
-134 -1.81205 4.11002 -0.644368
-135 -2.18052 4.5108 0.0207978
-136 -3.04069 4.46798 0.0140465
-137 -2.69258 4.07617 0.717801
-138 -2.93907 3.4676 0.0811426
-139 -1.55032 3.50269 -0.894623
-140 -1.13201 3.03917 -1.2088
-141 -1.02803 2.36078 -0.934456
-142 -1.4533 2.18969 -0.398152
-143 -1.81449 2.71656 0.0180836
-144 -1.72303 4.16081 0.396577
-145 -1.82974 3.63029 0.577341
-146 -1.39293 3.47704 0.141023
-147 -2.44399 3.53783 0.525648
-148 0.592981 4.34654 -1.14796
-149 1.04753 4.39462 0.241342
-150 1.40834 3.92911 0.431606
-151 0.865492 3.82527 0.547644
-152 1.82372 4.25814 0.778357
-153 2.65369 4.19268 0.67403
-154 2.22445 4.52228 0.426497
-155 2.12825 4.49427 -0.0715017
-156 1.60993 4.38855 -0.488155
-157 2.33653 4.21203 -0.537879
-158 1.876 3.93252 -0.628192
-159 1.56727 3.53171 -0.890687
-160 1.81198 3.46349 -0.35362
-161 2.23225 3.41322 0.00410722
-162 2.87877 3.40225 -0.202694
-163 1.77663 3.55788 0.27041
-164 3.59717 3.66961 -0.207039
-165 3.42488 4.38624 0.283982
-166 2.84158 4.27903 -0.533384
-167 3.42268 4.28122 -0.32452
-168 -2.20092 4.49816 0.678009
-169 -0.837391 5.58386 -0.612758
-170 -0.0756911 5.43705 -0.319604
-171 -0.829069 6.16651 -0.493318
-172 -0.591949 5.76133 0.0289914
-173 -0.820351 6.76251 0.247334
-174 -0.312763 6.65181 -0.604176
-175 0.147715 6.22609 -0.175256
-176 0.174948 6.56239 0.482201
-177 1.01506 6.6707 0.635009
-178 -0.235345 7.17067 0.42761
-179 -0.437659 6.99736 1.04524
-180 -0.577442 6.67534 1.61608
-181 -0.121577 6.31682 1.13209
-182 0.197194 6.29489 -0.843984
-183 0.1451 5.68082 -0.960706
-184 0.605683 5.31345 -0.764086
-185 1.05498 5.49671 -0.385742
-186 1.21782 5.10422 0.0141696
-187 0.522703 5.27832 0.0423676
-188 1.32653 5.62349 0.0538311
-189 1.19699 6.27024 0.136566
-190 0.764346 6.75213 0.0347831
-191 -0.241136 5.26661 -0.962378
-192 -0.189962 4.80525 -1.22131
-193 -0.570957 4.54303 -1.01461
-194 0.374281 7.13454 0.326104
-195 0.640643 6.47454 -0.4823
-196 -0.0370509 6.90739 1.50068
-197 0.65681 6.66152 1.62488
-198 0.992671 6.14266 -0.3151
-199 -0.988197 6.42373 -0.088252
-200 -1.33786 6.40509 0.486226
-201 -0.625492 6.75098 -0.219249
-202 -1.48501 5.83471 0.425995
-203 -1.60474 5.53511 0.836151
-204 -1.51201 5.6961 1.33993
-205 -1.36648 6.30355 1.45497
-206 -1.15483 5.495 1.7131
-207 -0.888781 4.89781 1.66152
-208 -0.635916 5.51645 1.95523
-209 -0.235138 5.93637 2.04905
-210 0.342771 6.0083 2.02001
-211 -0.443039 5.01921 1.92928
-212 0.16744 4.77883 2.06898
-213 0.371534 5.48871 2.06837
-214 0.795684 6.21754 1.86358
-215 1.32877 5.96916 1.7027
-216 1.6274 5.98498 1.12475
-217 1.20944 5.454 1.63187
-218 0.636513 4.85218 -1.01313
-219 1.03038 4.50401 -0.857149
-220 1.49684 6.36273 0.709162
-221 0.0756571 2.9637 -1.5585
-222 0.678383 2.92988 -1.4151
-223 -2.65669 4.50277 0.390513
-224 -3.22174 4.48971 0.582401
-225 0.938403 2.04979 -0.810638
-226 0.460563 1.91114 -1.01969
-227 -1.80496 3.27365 -0.430695
-228 -2.39441 3.51338 -0.440479
-229 -0.352511 6.32985 0.207694
-230 -0.186527 5.75837 0.649305
-231 -0.733367 5.36887 0.562194
-232 -0.579507 1.98158 -1.03757
-233 -0.452448 1.54179 -0.781688
-234 -1.1503 2.33952 0.801462
-235 -1.29517 2.62788 0.414235
-236 -3.10918 3.57011 -0.634668
-237 -3.59172 3.68133 -0.118607
-238 -3.39111 4.20945 -0.540464
-239 0.426754 5.43655 0.614868
-240 0.00104384 5.09795 0.699452
-241 0.256901 4.8841 0.283504
-242 0.00166609 4.90778 -0.200775
-243 -1.43796 1.23698 -0.123313
-244 -1.20871 1.02465 -0.624764
-245 -1.36178 1.74352 0.00626368
-246 -1.48266 0.680417 -0.337806
-247 -1.548 0.0439621 -0.359122
-248 -1.18023 -0.162153 -0.831051
-249 -0.851352 0.227733 -0.930284
-250 1.19305 0.874774 -0.719222
-251 -0.0507249 3.36775 -0.426507
-252 -0.465963 2.84705 -0.267258
-253 -1.43749 6.49256 0.994053
-254 1.79785 0.489699 0.351739
-255 1.46809 0.993504 0.269343
-256 1.78714 0.0215539 0.125179
-257 1.45743 0.928724 -0.259584
-258 -1.04707 3.57109 -1.22888
-259 -0.681886 3.96498 -1.28781
-260 -0.223795 3.78481 -1.5075
-261 0.227559 4.05551 -1.40945
-262 0.351888 1.52142 0.695056
-263 -2.3412 3.76906 1.0218
-264 -0.18037 2.38824 -0.358475
-265 0.733015 -0.260462 -0.771812
-266 0.474904 0.328489 -0.91693
-267 1.00924 0.188998 -0.936223
-268 1.387 0.356678 -0.618714
-269 0.665413 0.838099 -0.859167
-270 0.185781 3.94858 -0.320652
-271 0.0224707 3.69608 0.11188
-272 -0.285072 6.49082 0.684253
-273 2.6677 3.50318 0.246386
-274 -0.801177 6.83292 0.742706
-275 -2.9566 3.64389 0.576316
-276 0.370738 5.73144 -0.218122
-277 0.956027 3.78022 -1.20675
-278 -0.680768 2.90478 -1.40722
-279 -0.602671 2.69869 0.235155
-280 0.637052 6.98713 0.726978
-281 0.942652 6.82606 1.17005
-282 -0.475259 3.31128 0.00722333
-283 -0.265889 4.22865 1.11732
-284 2.27494 4.14871 1.06259
-285 1.51453 5.3514 0.719228
-286 -3.57058 4.24278 0.00739403
-287 -0.462107 5.26681 -0.0260898
-288 -1.64335 4.49649 -0.33417
-289 -0.89404 4.44535 0.394617
-290 -1.77669 0.404991 0.453457
-291 0.773882 2.26545 1.12929
-292 0.935937 1.90161 0.806798
-293 -1.31372 5.50672 0.0273056
-294 0.17064 2.73888 0.683378
-295 0.343164 3.08665 0.3256
-296 0.196093 6.08997 0.663695
-297 3.59977 3.8609 0.313374
-298 3.24314 4.02703 0.798848
-299 3.07104 4.46528 0.629037
-300 1.25147 4.57896 -0.184531
-301 2.54816 3.66823 0.746262
-302 3.25834 3.47545 -0.557774
-303 -0.244043 7.04475 -0.0626802
-304 0.502134 2.43381 -0.172765
-305 -0.0278282 2.16841 -1.25178
-306 0.290654 1.33372 -0.961138
-307 0.03457 1.91341 1.28347
-308 -0.436272 2.16979 1.28337
-309 -2.60609 4.43979 -0.243887
-310 0.22940057491448 0.450106333873961 0.220919289175102
-311 1.04816057787935 3.84520987792536 -0.387523751716382
-312 0.791516162267201 5.89562424207913 1.13591677375976
-313 -0.836907493040771 6.00956918765895 0.978146412229845
-314 0.897235096056453 2.99636143289997 -0.410370825713378
-315 -0.451153521768497 5.3373767073504 1.26611855246413
-316 -0.601638207937747 3.24834428756796 -0.818228500086457
-317 -0.578150948677034 4.60793377466195 -0.348529829233355
-318 -0.002434094984176 4.46321724391838 -0.688750401140393
-319 -0.771645643454466 0.578006799520087 -0.363871817773244
-320 -0.0543601586649 4.28880656908635 0.479682985594208
-321 -1.23274599060468 3.95749634764298 -0.327673486839287
-322 -1.15641050567321 0.316701532655859 0.283456573141906
-323 0.642707908586347 4.39264064648036 -0.255037383091231
-324 0.973871575376334 0.351538589051357 0.352861283233105
-325 -0.020321478806152 2.73610983106167 -0.979132115832824
-326 -2.40524861426325 3.93502283313076 0.05260373980416
-327 -0.112996983771733 0.110945194308827 0.151259978153845
-328 -0.140534769323756 0.577138097361873 -0.28121993115995
-329 -1.03029782500383 3.18190347485469 -0.322734015974363
-330 -0.260994798085077 3.86857674472496 -0.794115722489941
-331 0.065159413450347 5.68657290403231 1.55244681150896
-332 0.523719426932323 3.42167358226641 -0.80544694113442
-333 0.632371068015134 0.163737689959675 0.685345188785802
-334 2.31078822450986 3.98133071806645 0.181165836561753
-335 0.43202322497883 6.50819566588173 1.01800701438157
-336 0.063722873059537 1.55165616521011 0.184639542086588
-337 -1.03566330067408 2.58450199111172 -0.183957249968463
-338 0.943759925112545 5.26525832956327 0.842224805338145
-339 0.462281715820711 2.40835401484519 -0.740861628646598
-340 0.937340482388049 0.854125848583737 0.02857211638466
-341 0.190752670540454 1.51421528893948 -0.414261182136289
-342 2.81600018453752 3.94755237848605 -0.069268031153165
-343 0.704933339335922 1.68718314317071 0.14524823220614
-344 -0.78984753898456 1.69652777556882 0.200366860857274
-345 0.046734407150132 5.50204997361444 0.216918257973715
-346 -0.548585865965186 5.88586716106866 1.49005572541013
-347 0.435146083625327 6.06982417481615 1.49594326442517
-
-*ELEMENTS
-TET
-1185 4 0
-1 55 340 9 343
-2 239 240 241 338
-3 181 313 230 346
-4 251 295 71 74
-5 114 183 170 191
-6 170 242 187 345
-7 8 310 23 327
-8 50 319 45 328
-9 229 174 171 201
-10 40 242 241 320
-11 217 88 108 95
-12 5 119 56 25
-13 229 199 201 171
-14 266 23 269 21
-15 66 64 65 314
-16 151 67 149 323
-17 269 21 55 341
-18 33 9 255 340
-19 118 56 55 310
-20 67 241 149 323
-21 119 121 28 344
-22 271 151 295 41
-23 152 150 36 334
-24 7 90 131 234
-25 136 138 79 326
-26 229 303 174 201
-27 127 134 228 326
-28 163 150 152 334
-29 198 175 195 276
-30 186 300 149 323
-31 25 319 1 322
-32 270 318 148 330
-33 231 293 202 172
-34 150 300 36 311
-35 247 319 246 322
-36 88 240 105 315
-37 105 240 128 315
-38 116 259 260 330
-39 41 311 66 314
-40 144 145 147 326
-41 141 93 27 232
-42 167 297 165 342
-43 175 176 229 296
-44 126 282 146 321
-45 25 11 15 1
-46 243 120 31 319
-47 47 22 254 324
-48 41 295 74 314
-49 10 15 25 60
-50 45 1 11 319
-51 132 231 40 287
-52 56 336 55 341
-53 185 73 188 198
-54 81 82 35 314
-55 240 230 231 315
-56 88 107 67 338
-57 168 147 144 145
-58 63 64 61 81
-59 128 240 40 231
-60 213 209 210 331
-61 205 204 130 313
-62 88 105 211 315
-63 66 102 151 295
-64 106 88 240 105
-65 34 336 56 341
-66 317 318 126 330
-67 71 90 123 131
-68 187 73 276 345
-69 180 97 205 346
-70 36 155 154 334
-71 276 183 184 170
-72 66 151 150 311
-73 167 166 83 342
-74 282 251 252 316
-75 13 324 12 333
-76 83 166 84 342
-77 66 160 62 314
-78 229 172 175 345
-79 240 105 283 106
-80 171 175 172 114
-81 75 128 231 315
-82 87 218 184 318
-83 243 56 120 319
-84 279 90 7 234
-85 262 336 92 343
-86 196 335 197 347
-87 92 336 34 343
-88 91 279 93 337
-89 289 78 132 317
-90 181 331 296 347
-91 285 188 186 338
-92 34 225 53 339
-93 242 320 270 323
-94 165 297 99 342
-95 219 110 218 323
-96 17 8 11 327
-97 325 332 74 339
-98 197 335 312 347
-99 25 5 10 20
-100 71 65 74 295
-101 268 48 257 340
-102 117 316 278 325
-103 41 151 295 66
-104 286 275 136 76
-105 44 259 193 330
-106 134 227 139 321
-107 162 83 84 342
-108 78 288 133 321
-109 153 301 99 298
-110 294 102 295 38
-111 250 257 9 340
-112 294 308 89 123
-113 214 197 68 312
-114 145 168 77 144
-115 260 330 221 332
-116 197 312 214 347
-117 141 264 43 325
-118 260 259 117 330
-119 230 315 88 331
-120 71 271 251 282
-121 137 223 168 326
-122 206 313 97 346
-123 88 315 211 331
-124 270 317 242 318
-125 24 324 6 333
-126 67 109 149 241
-127 247 246 31 322
-128 73 220 189 177
-129 187 184 170 276
-130 92 65 292 343
-131 205 39 180 313
-132 149 311 151 323
-133 127 227 134 321
-134 93 141 27 142
-135 213 331 210 347
-136 57 140 139 329
-137 56 27 243 344
-138 245 91 93 337
-139 86 311 277 332
-140 294 279 92 90
-141 25 120 19 56
-142 33 16 55 340
-143 242 287 40 345
-144 1 319 247 322
-145 175 113 182 174
-146 50 45 26 328
-147 170 169 115 287
-148 136 237 286 238
-149 93 91 245 344
-150 278 141 43 316
-151 287 293 172 132
-152 44 134 139 321
-153 120 19 31 322
-154 226 225 54 339
-155 273 162 161 342
-156 256 48 13 324
-157 126 317 270 318
-158 12 23 8 310
-159 74 252 251 325
-160 57 329 143 337
-161 152 153 284 334
-162 242 184 170 187
-163 18 3 58 322
-164 94 272 178 176
-165 226 54 305 339
-166 136 79 309 326
-167 193 317 44 330
-168 142 91 245 337
-169 3 59 58 322
-170 241 320 242 323
-171 25 19 120 322
-172 241 88 240 67
-173 130 202 200 313
-174 45 319 11 328
-175 230 181 272 313
-176 111 220 73 312
-177 264 325 74 339
-178 161 334 273 342
-179 71 122 38 104
-180 32 324 24 333
-181 210 209 96 331
-182 305 264 226 339
-183 105 128 207 315
-184 14 310 16 333
-185 211 315 208 331
-186 9 53 250 340
-187 8 42 26 327
-188 56 243 120 344
-189 15 59 10 322
-190 231 293 132 129
-191 282 126 251 316
-192 138 127 228 326
-193 296 335 181 347
-194 73 239 188 338
-195 30 277 261 332
-196 307 92 294 37
-197 279 234 7 235
-198 84 160 161 334
-199 70 285 109 338
-200 173 229 199 201
-201 233 264 93 34
-202 93 252 264 337
-203 65 64 304 314
-204 144 135 78 288
-205 18 322 1 247
-206 71 252 251 74
-207 96 196 197 347
-208 176 190 113 194
-209 243 245 120 344
-210 122 271 71 72
-211 160 159 82 311
-212 176 280 190 194
-213 172 80 199 171
-214 253 205 130 313
-215 68 197 281 312
-216 162 302 83 342
-217 289 125 78 321
-218 211 88 212 105
-219 159 311 86 314
-220 229 230 296 272
-221 99 162 273 342
-222 181 196 96 347
-223 25 319 56 328
-224 269 55 21 23
-225 136 237 138 286
-226 291 92 294 65
-227 271 295 251 41
-228 251 295 74 41
-229 294 102 38 103
-230 34 93 56 336
-231 69 208 209 331
-232 314 332 222 339
-233 61 292 65 343
-234 13 23 42 324
-235 2 65 103 101
-236 136 137 275 326
-237 31 290 247 322
-238 287 230 231 345
-239 54 325 305 339
-240 230 287 231 172
-241 158 155 36 334
-242 24 4 14 333
-243 279 235 7 337
-244 303 175 229 174
-245 67 240 241 320
-246 222 325 54 339
-247 72 104 271 320
-248 259 116 193 330
-249 126 321 316 329
-250 269 55 53 341
-251 23 269 55 340
-252 270 311 41 332
-253 128 240 231 315
-254 181 331 96 346
-255 311 314 41 332
-256 10 19 25 322
-257 38 294 71 295
-258 93 245 142 27
-259 119 118 56 344
-260 176 73 296 175
-261 233 27 56 344
-262 287 172 230 345
-263 283 289 40 320
-264 283 104 72 320
-265 315 331 230 346
-266 181 335 196 347
-267 237 286 275 138
-268 41 311 270 323
-269 25 310 20 327
-270 305 325 264 339
-271 222 54 35 339
-272 133 288 134 321
-273 21 328 55 341
-274 96 209 180 346
-275 232 305 43 325
-276 136 224 137 223
-277 251 271 270 126
-278 242 184 191 170
-279 150 149 300 311
-280 199 172 171 229
-281 209 208 97 346
-282 177 73 190 189
-283 258 44 139 321
-284 144 134 127 326
-285 66 65 101 295
-286 161 160 163 334
-287 277 85 219 311
-288 235 91 142 337
-289 92 262 307 28
-290 301 153 273 334
-291 176 113 303 194
-292 26 11 328 45
-293 317 170 287 242
-294 175 113 190 195
-295 84 334 161 342
-296 286 237 275 76
-297 148 218 192 318
-298 28 118 119 344
-299 181 39 179 274
-300 140 258 329 316
-301 247 248 46 319
-302 14 5 16 310
-303 225 226 53 339
-304 150 300 149 36
-305 27 245 243 344
-306 220 73 189 111
-307 11 1 25 319
-308 26 42 23 327
-309 230 296 239 345
-310 10 25 15 322
-311 289 317 126 321
-312 148 116 261 318
-313 16 324 32 333
-314 221 316 117 325
-315 136 223 137 326
-316 252 316 141 329
-317 304 92 34 65
-318 184 242 191 87
-319 111 312 73 338
-320 236 79 138 228
-321 238 136 236 79
-322 66 62 160 163
-323 68 215 214 312
-324 172 229 200 313
-325 70 312 285 338
-326 154 152 36 334
-327 216 215 68 312
-328 316 321 126 330
-329 251 270 41 332
-330 170 183 184 191
-331 13 48 23 324
-332 184 185 187 110
-333 53 226 34 339
-334 171 175 174 229
-335 115 132 133 317
-336 11 26 328 327
-337 264 279 71 252
-338 46 249 51 319
-339 230 88 240 239
-340 255 9 257 340
-341 175 170 276 345
-342 251 41 74 332
-343 146 227 127 321
-344 19 5 25 119
-345 23 310 55 328
-346 294 89 291 103
-347 304 264 34 92
-348 294 102 101 295
-349 89 294 291 307
-350 296 229 272 176
-351 294 90 308 123
-352 9 340 33 343
-353 36 156 150 311
-354 176 175 303 113
-355 118 336 56 344
-356 146 143 127 227
-357 272 274 173 313
-358 273 334 153 342
-359 38 123 71 294
-360 66 102 295 101
-361 22 255 254 324
-362 221 222 30 332
-363 74 332 314 339
-364 303 176 194 178
-365 240 40 231 345
-366 39 181 179 180
-367 181 274 179 272
-368 300 219 156 311
-369 139 227 57 329
-370 23 265 26 266
-371 124 71 131 7
-372 190 73 198 189
-373 172 202 231 313
-374 270 126 271 320
-375 176 73 177 335
-376 56 328 21 341
-377 120 56 25 319
-378 122 71 131 124
-379 282 125 124 126
-380 71 122 72 124
-381 238 136 79 309
-382 95 312 88 331
-383 23 12 8 42
-384 282 125 126 146
-385 138 237 238 236
-386 23 268 267 250
-387 51 56 244 319
-388 50 249 45 319
-389 302 164 83 342
-390 294 89 38 123
-391 71 104 38 295
-392 151 271 104 67
-393 86 35 82 314
-394 161 162 84 342
-395 271 251 270 41
-396 78 125 144 321
-397 192 218 87 318
-398 244 246 46 319
-399 12 310 8 333
-400 27 51 233 56
-401 65 294 279 92
-402 82 64 62 314
-403 56 319 51 328
-404 242 184 110 218
-405 251 330 270 332
-406 100 334 157 342
-407 273 153 99 342
-408 127 145 144 326
-409 71 122 131 123
-410 287 293 132 231
-411 97 208 206 346
-412 74 295 65 314
-413 79 134 135 326
-414 56 93 233 344
-415 23 12 42 324
-416 98 150 152 163
-417 21 306 233 341
-418 254 255 48 324
-419 95 210 214 347
-420 52 233 306 341
-421 231 313 75 315
-422 172 200 229 199
-423 282 125 146 124
-424 29 20 14 333
-425 294 37 291 307
-426 17 29 12 333
-427 144 125 146 321
-428 169 293 172 80
-429 287 169 132 172
-430 296 88 230 239
-431 170 317 287 115
-432 248 1 249 319
-433 172 170 175 345
-434 73 177 190 176
-435 319 327 11 328
-436 13 47 256 324
-437 141 329 57 337
-438 281 197 94 335
-439 284 301 98 334
-440 61 304 63 343
-441 170 172 287 345
-442 282 7 146 329
-443 179 181 196 180
-444 208 207 206 315
-445 82 62 64 81
-446 241 239 187 345
-447 158 36 163 334
-448 93 27 233 344
-449 92 336 28 344
-450 231 202 203 313
-451 304 64 63 314
-452 81 63 64 314
-453 202 80 172 293
-454 172 169 170 287
-455 13 24 6 47
-456 94 280 281 335
-457 287 169 115 132
-458 10 15 60 59
-459 148 311 219 323
-460 313 315 230 346
-461 252 316 251 325
-462 84 157 158 334
-463 89 92 294 307
-464 84 158 160 334
-465 264 252 74 325
-466 278 43 221 325
-467 200 202 172 313
-468 258 140 117 316
-469 25 56 5 310
-470 88 296 230 331
-471 125 124 126 72
-472 296 312 73 335
-473 16 310 55 340
-474 312 335 296 347
-475 239 296 73 345
-476 303 176 178 229
-477 124 282 7 146
-478 78 317 289 321
-479 86 314 311 332
-480 282 124 7 71
-481 178 272 274 173
-482 258 259 44 330
-483 55 328 56 341
-484 135 144 168 326
-485 253 39 205 313
-486 115 287 132 317
-487 240 239 241 345
-488 157 334 84 342
-489 198 175 190 195
-490 272 178 274 179
-491 229 178 173 272
-492 99 298 165 342
-493 211 105 207 315
-494 112 175 195 182
-495 40 241 242 345
-496 181 94 179 196
-497 229 303 173 178
-498 303 229 173 201
-499 141 57 142 337
-500 175 183 114 182
-501 294 65 279 71
-502 276 183 112 184
-503 251 316 126 330
-504 225 34 53 343
-505 117 259 258 316
-506 80 200 172 199
-507 31 19 290 322
-508 121 92 28 344
-509 143 142 57 337
-510 160 163 150 311
-511 304 225 34 339
-512 284 153 301 334
-513 256 47 254 324
-514 278 140 141 316
-515 276 170 187 345
-516 300 156 36 311
-517 121 93 92 344
-518 118 92 28 262
-519 295 65 101 294
-520 164 162 99 342
-521 235 142 143 337
-522 103 65 291 294
-523 32 324 16 340
-524 120 245 91 344
-525 234 93 121 91
-526 92 307 308 28
-527 164 297 167 342
-528 191 169 115 170
-529 294 279 90 71
-530 69 213 212 331
-531 44 133 134 321
-532 192 87 191 318
-533 206 315 313 346
-534 222 332 325 339
-535 25 327 319 328
-536 32 16 33 340
-537 132 78 133 317
-538 92 308 121 28
-539 251 126 270 330
-540 55 336 262 343
-541 44 316 258 330
-542 92 279 264 93
-543 233 93 232 27
-544 191 169 170 114
-545 180 181 96 346
-546 258 140 329 139
-547 271 102 295 151
-548 48 324 255 340
-549 129 289 132 231
-550 128 240 283 40
-551 53 341 55 343
-552 55 56 118 336
-553 317 191 170 242
-554 33 255 32 340
-555 187 109 186 338
-556 312 331 95 347
-557 4 24 6 333
-558 31 246 243 319
-559 13 24 47 324
-560 186 323 241 187
-561 157 155 158 334
-562 226 305 52 264
-563 298 299 165 342
-564 322 18 1 58
-565 153 299 298 342
-566 100 153 154 334
-567 42 12 13 324
-568 30 86 277 332
-569 283 289 72 128
-570 124 271 71 282
-571 312 220 281 68
-572 65 304 74 314
-573 122 271 104 71
-574 258 316 44 321
-575 130 204 203 313
-576 253 200 274 313
-577 205 97 204 313
-578 156 158 160 311
-579 62 64 66 314
-580 47 24 22 324
-581 277 148 261 332
-582 180 39 181 313
-583 145 125 146 144
-584 156 160 150 311
-585 100 155 157 334
-586 150 158 156 36
-587 115 317 193 318
-588 56 336 93 344
-589 119 5 56 118
-590 75 206 207 315
-591 6 13 12 333
-592 241 40 240 345
-593 181 176 272 335
-594 203 202 130 313
-595 270 330 261 332
-596 316 321 258 329
-597 229 296 230 345
-598 71 264 252 74
-599 221 43 305 325
-600 193 192 115 318
-601 246 319 31 322
-602 142 235 143 91
-603 243 244 56 319
-604 141 316 140 329
-605 214 347 96 197
-606 17 60 25 15
-607 192 191 115 318
-608 7 235 146 329
-609 21 23 55 328
-610 60 17 25 20
-611 260 117 221 316
-612 193 116 192 318
-613 185 184 187 276
-614 73 187 185 188
-615 293 169 172 132
-616 264 252 141 337
-617 31 319 120 322
-618 72 289 126 125
-619 56 310 25 328
-620 188 187 186 338
-621 118 16 5 310
-622 25 5 20 310
-623 258 321 139 329
-624 336 341 34 343
-625 137 136 76 224
-626 225 35 54 339
-627 96 331 181 347
-628 177 312 281 335
-629 55 341 336 343
-630 154 155 100 334
-631 61 63 9 343
-632 119 120 56 19
-633 316 221 332 325
-634 279 252 93 337
-635 172 230 229 313
-636 226 52 306 341
-637 112 175 182 276
-638 205 313 180 346
-639 304 225 35 63
-640 164 302 162 342
-641 258 316 259 330
-642 23 266 26 328
-643 100 157 166 342
-644 310 324 23 340
-645 157 84 166 342
-646 185 276 198 112
-647 187 185 186 110
-648 185 73 198 276
-649 15 17 11 327
-650 169 172 170 114
-651 174 175 114 182
-652 20 29 17 333
-653 139 321 227 329
-654 34 341 53 343
-655 9 63 53 343
-656 317 321 44 330
-657 181 296 176 335
-658 34 65 92 343
-659 141 316 252 325
-660 306 53 226 341
-661 26 266 50 328
-662 126 317 289 320
-663 323 186 241 149
-664 143 329 235 337
-665 55 310 23 340
-666 158 150 156 160
-667 104 67 283 106
-668 74 314 304 339
-669 67 88 106 107
-670 185 276 112 184
-671 239 241 187 338
-672 128 105 283 240
-673 203 231 75 129
-674 70 217 216 312
-675 230 331 181 346
-676 247 290 18 322
-677 287 293 231 172
-678 79 135 134 309
-679 114 169 172 171
-680 260 316 221 330
-681 293 231 202 129
-682 187 73 239 188
-683 97 313 205 346
-684 190 176 113 175
-685 274 39 253 313
-686 276 112 195 198
-687 209 331 208 346
-688 73 190 198 175
-689 94 196 181 335
-690 221 30 260 332
-691 34 304 65 343
-692 163 150 158 160
-693 158 36 155 156
-694 66 65 64 101
-695 272 94 181 335
-696 16 324 310 340
-697 121 119 120 344
-698 136 237 238 138
-699 55 53 9 340
-700 121 91 93 344
-701 207 128 75 315
-702 107 88 106 108
-703 61 304 64 63
-704 120 119 56 344
-705 95 331 213 347
-706 61 65 64 304
-707 32 24 14 333
-708 310 324 16 333
-709 308 92 121 90
-710 141 252 264 325
-711 19 5 10 25
-712 308 92 90 294
-713 121 120 91 344
-714 89 294 38 103
-715 218 148 219 323
-716 231 40 287 345
-717 279 329 252 337
-718 25 119 56 19
-719 187 242 241 345
-720 21 56 51 328
-721 35 304 63 314
-722 279 65 264 71
-723 279 7 282 329
-724 160 311 82 314
-725 75 129 231 128
-726 112 175 276 195
-727 208 315 206 346
-728 175 183 182 276
-729 175 113 174 303
-730 175 171 172 229
-731 171 175 114 174
-732 118 28 92 336
-733 172 175 170 114
-734 304 225 63 343
-735 63 225 53 343
-736 153 273 99 301
-737 33 340 55 343
-738 239 187 188 338
-739 23 48 268 340
-740 231 132 40 289
-741 60 20 25 10
-742 176 73 175 190
-743 93 141 142 337
-744 221 117 278 325
-745 251 325 316 332
-746 46 248 249 319
-747 290 19 3 322
-748 153 334 100 342
-749 151 271 270 41
-750 251 271 126 282
-751 88 213 95 331
-752 120 319 25 322
-753 29 14 4 333
-754 126 146 125 321
-755 253 130 200 313
-756 184 218 242 318
-757 32 22 24 324
-758 23 327 310 328
-759 25 17 15 327
-760 126 318 270 330
-761 138 275 147 326
-762 35 63 81 314
-763 221 325 222 332
-764 88 312 296 331
-765 217 312 70 338
-766 193 115 133 317
-767 91 279 235 234
-768 260 261 116 330
-769 144 127 146 145
-770 107 108 70 338
-771 290 3 18 322
-772 220 312 281 177
-773 91 235 279 337
-774 154 284 152 153
-775 100 167 165 342
-776 155 158 156 157
-777 177 280 176 335
-778 73 312 177 335
-779 123 38 71 122
-780 9 53 55 343
-781 83 164 167 342
-782 245 27 93 344
-783 65 2 291 292
-784 65 2 61 64
-785 1 15 25 322
-786 89 92 307 308
-787 103 65 294 101
-788 71 65 295 294
-789 214 312 95 347
-790 292 92 262 37
-791 163 98 301 334
-792 11 8 26 327
-793 254 48 256 324
-794 20 310 14 333
-795 134 135 144 288
-796 67 109 107 149
-797 187 109 241 186
-798 241 109 149 186
-799 270 318 242 323
-800 270 151 41 323
-801 219 148 277 311
-802 230 313 231 315
-803 24 13 6 324
-804 218 318 148 323
-805 11 25 15 327
-806 177 176 280 190
-807 175 176 303 229
-808 113 175 182 195
-809 181 176 296 272
-810 230 239 240 345
-811 230 172 229 345
-812 272 94 178 179
-813 88 230 240 315
-814 73 220 177 312
-815 92 93 34 336
-816 17 327 20 333
-817 216 68 220 312
-818 39 274 181 313
-819 263 145 168 77
-820 108 217 70 338
-821 191 317 170 115
-822 296 331 312 347
-823 117 140 278 316
-824 131 279 71 90
-825 121 279 93 234
-826 121 279 234 90
-827 294 90 123 71
-828 178 176 272 229
-829 118 55 262 16
-830 282 321 126 329
-831 28 336 118 344
-832 296 181 230 331
-833 264 226 34 341
-834 187 239 73 345
-835 289 72 126 320
-836 12 324 310 333
-837 124 271 282 72
-838 33 292 61 343
-839 216 285 70 312
-840 239 312 88 338
-841 65 264 74 304
-842 89 92 308 294
-843 134 127 228 227
-844 245 93 142 337
-845 291 92 37 294
-846 262 92 307 37
-847 7 329 279 337
-848 6 29 4 333
-849 92 279 121 90
-850 235 329 7 337
-851 292 92 291 65
-852 92 292 291 37
-853 241 88 67 338
-854 154 153 152 334
-855 151 66 41 311
-856 232 264 305 325
-857 85 277 159 311
-858 168 147 263 137
-859 282 252 279 329
-860 23 42 8 327
-861 211 208 69 331
-862 273 161 301 334
-863 235 143 146 329
-864 242 318 218 323
-865 241 242 187 323
-866 165 299 100 342
-867 270 317 126 320
-868 300 311 149 323
-869 94 176 194 280
-870 49 48 13 256
-871 222 221 54 325
-872 146 321 282 329
-873 141 140 57 329
-874 233 34 93 341
-875 76 224 136 286
-876 88 240 239 338
-877 73 312 239 338
-878 241 240 88 338
-879 161 163 301 334
-880 176 296 73 335
-881 304 34 225 343
-882 122 271 72 104
-883 150 151 149 311
-884 152 284 98 334
-885 187 73 185 276
-886 261 330 260 332
-887 187 188 186 185
-888 246 244 243 319
-889 269 306 21 341
-890 262 292 33 343
-891 50 266 21 328
-892 271 126 72 320
-893 148 318 261 330
-894 93 56 233 341
-895 221 316 332 330
-896 23 265 266 267
-897 231 203 202 129
-898 175 276 73 345
-899 199 200 229 173
-900 8 327 17 333
-901 23 324 48 340
-902 20 327 310 333
-903 200 80 172 202
-904 66 295 41 314
-905 67 104 283 320
-906 292 262 92 343
-907 136 236 79 138
-908 126 125 289 321
-909 211 69 212 331
-910 283 106 67 320
-911 168 147 137 326
-912 282 279 71 7
-913 231 203 75 313
-914 49 23 13 48
-915 138 238 136 236
-916 53 306 269 341
-917 55 16 118 310
-918 242 317 270 320
-919 53 269 250 340
-920 56 243 27 244
-921 88 312 217 338
-922 88 211 212 331
-923 283 40 240 320
-924 271 104 67 320
-925 240 40 241 320
-926 124 271 72 71
-927 124 126 72 282
-928 242 317 191 318
-929 212 213 88 331
-930 52 226 264 341
-931 132 40 289 317
-932 57 227 143 329
-933 44 321 316 330
-934 192 116 148 318
-935 249 50 51 319
-936 191 87 242 318
-937 46 246 247 319
-938 150 163 66 311
-939 298 99 165 297
-940 177 281 280 335
-941 187 241 109 338
-942 184 242 87 318
-943 252 71 251 282
-944 44 317 133 321
-945 58 59 15 322
-946 148 318 270 323
-947 35 314 222 339
-948 151 311 41 323
-949 191 317 115 318
-950 184 242 110 187
-951 264 141 93 337
-952 264 233 52 341
-953 10 59 3 322
-954 163 160 158 334
-955 65 279 264 92
-956 217 95 215 312
-957 127 144 146 321
-958 73 296 239 312
-959 215 95 214 312
-960 51 244 46 319
-961 110 242 218 323
-962 305 54 221 325
-963 64 82 81 314
-964 310 327 8 333
-965 265 23 26 42
-966 82 311 159 314
-967 180 313 181 346
-968 160 158 159 311
-969 222 314 86 332
-970 43 264 232 325
-971 167 100 166 342
-972 278 316 43 325
-973 347 214 96 210
-974 310 327 25 328
-975 23 49 13 42
-976 51 50 21 328
-977 233 264 232 93
-978 264 141 232 93
-979 65 264 304 92
-980 92 264 34 93
-981 233 264 52 232
-982 264 141 43 232
-983 147 145 127 326
-984 264 71 65 74
-985 133 317 78 321
-986 30 261 260 332
-987 227 321 146 329
-988 277 311 148 332
-989 261 148 270 332
-990 271 102 151 104
-991 55 262 33 343
-992 295 271 71 104
-993 271 71 251 295
-994 271 282 72 126
-995 41 314 74 332
-996 73 188 111 338
-997 11 319 25 327
-998 156 219 85 311
-999 152 98 163 334
-1000 5 56 118 310
-1001 316 330 251 332
-1002 117 316 260 330
-1003 88 108 107 338
-1004 116 318 193 330
-1005 272 176 94 335
-1006 20 17 25 327
-1007 36 150 163 334
-1008 136 275 138 326
-1009 175 296 229 345
-1010 175 73 296 345
-1011 134 144 127 321
-1012 94 176 280 335
-1013 216 220 111 312
-1014 51 319 50 328
-1015 32 255 33 22
-1016 12 29 6 333
-1017 153 298 99 342
-1018 100 299 153 342
-1019 43 316 141 325
-1020 159 158 85 311
-1021 110 186 187 323
-1022 250 269 23 340
-1023 111 188 285 338
-1024 138 228 79 326
-1025 88 106 212 105
-1026 96 331 209 346
-1027 79 135 309 326
-1028 73 111 188 189
-1029 264 34 226 339
-1030 73 188 198 189
-1031 97 180 209 346
-1032 181 274 272 313
-1033 193 318 317 330
-1034 198 73 175 276
-1035 264 74 304 339
-1036 131 279 90 7
-1037 264 279 252 93
-1038 261 318 116 330
-1039 282 279 252 71
-1040 259 316 117 330
-1041 131 279 7 71
-1042 91 279 234 93
-1043 279 92 121 93
-1044 65 2 103 291
-1045 242 170 287 345
-1046 126 321 317 330
-1047 65 2 64 101
-1048 2 65 61 292
-1049 93 34 56 341
-1050 23 268 49 267
-1051 285 312 111 338
-1052 65 304 61 343
-1053 62 160 82 314
-1054 208 331 315 346
-1055 272 230 296 181
-1056 168 223 135 326
-1057 118 92 262 336
-1058 6 324 13 333
-1059 14 16 32 333
-1060 86 82 159 314
-1061 66 311 160 314
-1062 65 295 66 314
-1063 225 304 35 339
-1064 35 86 222 314
-1065 55 33 262 16
-1066 226 53 34 341
-1067 252 329 141 337
-1068 99 297 164 342
-1069 23 267 269 250
-1070 67 106 240 320
-1071 267 23 269 266
-1072 264 34 233 341
-1073 23 265 49 42
-1074 135 134 144 326
-1075 255 257 48 340
-1076 17 12 8 333
-1077 88 239 296 312
-1078 281 312 197 335
-1079 197 196 94 335
-1080 219 311 300 323
-1081 230 240 231 345
-1082 184 87 191 183
-1083 183 276 112 182
-1084 183 170 175 114
-1085 170 183 175 276
-1086 93 336 92 344
-1087 268 23 49 48
-1088 231 289 40 128
-1089 283 289 128 40
-1090 58 15 1 322
-1091 20 5 14 310
-1092 23 310 12 324
-1093 213 69 209 331
-1094 129 289 231 128
-1095 289 129 132 78
-1096 75 204 206 313
-1097 261 270 148 330
-1098 95 88 108 213
-1099 212 88 213 108
-1100 200 229 173 313
-1101 304 314 35 339
-1102 106 88 212 108
-1103 147 275 137 326
-1104 222 86 30 332
-1105 173 229 272 313
-1106 67 88 240 106
-1107 210 331 96 347
-1108 40 317 242 320
-1109 27 51 56 244
-1110 21 51 56 233
-1111 240 106 283 320
-1112 150 163 158 36
-1113 67 320 241 323
-1114 147 168 263 145
-1115 55 269 53 340
-1116 289 317 40 320
-1117 207 208 211 315
-1118 247 1 248 319
-1119 309 135 223 326
-1120 228 134 79 326
-1121 110 300 186 323
-1122 271 151 270 323
-1123 45 1 249 248
-1124 176 94 194 178
-1125 265 23 49 267
-1126 126 316 282 329
-1127 272 94 179 181
-1128 109 285 186 338
-1129 270 311 148 323
-1130 67 107 109 338
-1131 282 316 252 329
-1132 230 272 229 313
-1133 203 204 75 313
-1134 118 262 55 336
-1135 74 325 251 332
-1136 181 180 96 196
-1137 136 137 76 275
-1138 193 133 44 317
-1139 45 249 1 319
-1140 168 144 147 326
-1141 232 305 264 52
-1142 147 127 138 326
-1143 102 104 295 38
-1144 169 80 172 171
-1145 102 294 101 103
-1146 67 109 241 338
-1147 271 102 104 295
-1148 271 320 67 323
-1149 257 250 268 340
-1150 300 110 219 323
-1151 268 250 23 340
-1152 187 242 110 323
-1153 271 67 151 323
-1154 270 320 271 323
-1155 283 72 289 320
-1156 85 158 156 311
-1157 159 277 86 311
-1158 173 274 200 313
-1159 172 231 230 313
-1160 204 97 206 313
-1161 217 215 216 312
-1162 275 286 136 138
-1163 255 324 32 340
-1164 255 22 32 324
-1165 233 56 21 341
-1166 143 227 146 329
-1167 26 327 23 328
-1168 223 136 309 326
-1169 216 111 285 312
-1170 304 34 264 339
-1171 217 88 95 312
-1172 55 310 56 328
-1173 3 19 10 322
-1174 148 311 270 332
-1175 242 40 287 317
-1176 88 217 108 338
-1177 287 40 132 317
-1178 21 266 23 328
-1179 109 107 70 338
-1180 95 213 210 347
-1181 66 163 160 311
-1182 288 144 134 321
-1183 9 33 61 343
-1184 288 78 144 321
-1185 75 313 206 315
-
-*MATERIAL defaultMaterial
-ENU, 1000, 1000000, 0.45
-
-*REGION
-allElements, defaultMaterial
-
diff --git a/examples/turtle/model/turtle-volumetric.bou b/examples/turtle/model/turtle-volumetric.bou
deleted file mode 100644
index 44cee8753e973977d5dc4638f6944162386230f3..0000000000000000000000000000000000000000
--- a/examples/turtle/model/turtle-volumetric.bou
+++ /dev/null
@@ -1,4 +0,0 @@
-1,3,6,8,11,12,13,15,
-17,18,26,29,42,45,47,
-49,58,59,60,247,248,
-256,265
diff --git a/examples/turtle/model/turtle-volumetric.mass b/examples/turtle/model/turtle-volumetric.mass
deleted file mode 100644
index eabe29c550247a4ed811b1aa0ffa57ccefa09259..0000000000000000000000000000000000000000
--- a/examples/turtle/model/turtle-volumetric.mass
+++ /dev/null
@@ -1,3941 +0,0 @@
-347
-347
-0 0 55.7631995436483
-0 10 7.86071363596517
-0 14 5.38246182642466
-0 17 3.828479766089
-0 24 8.87895918104205
-0 44 7.10041117066521
-0 57 3.08627833880136
-0 246 7.85726235253867
-0 247 5.27264901900405
-0 248 4.6686130297093
-0 318 17.1764039366704
-0 321 12.5325670585626
-1 1 22.9304122767608
-1 60 4.38144080083423
-1 63 4.04993663953328
-1 64 11.4652061383804
-1 100 3.9615238203097
-1 102 3.84865697827165
-1 290 3.12224151723649
-1 291 3.56661252057549
-2 2 23.1512910259816
-2 9 4.75406803872556
-2 17 3.09021380514338
-2 18 4.01072904139602
-2 57 3.56655714522442
-2 58 4.47470266645142
-2 289 3.25502032904085
-2 321 11.5756455129908
-3 3 5.46114587154299
-3 5 1.13633371291269
-3 13 1.5942392228588
-3 23 1.55722676453486
-3 28 1.17334617123663
-3 332 2.73057293577149
-4 4 51.4088242240287
-4 9 4.02251697039814
-4 13 4.18624150041861
-4 15 4.61492314455308
-4 18 4.67840952319579
-4 19 5.12334748962595
-4 24 14.8717668138987
-4 55 11.2877319546395
-4 117 6.64640379769704
-4 118 7.86638218018645
-4 309 13.8155129614298
-5 3 1.13633371291269
-5 5 17.1944803040681
-5 11 2.42636353743031
-5 12 5.82771247763366
-5 23 4.66968405082642
-5 28 1.1569717782112
-5 46 1.17044819042869
-5 323 3.86409471126234
-5 332 5.54011199739678
-6 6 48.5481026285959
-6 70 6.42262368487737
-6 89 3.92281769793109
-6 123 5.86624855617804
-6 130 4.70048874766894
-6 145 5.99551221229033
-6 233 3.8120159671127
-6 234 5.15807321069384
-6 278 13.5871392150643
-6 281 10.2437021270169
-6 328 8.9747465534896
-6 336 4.13878597057071
-7 7 24.3104754153738
-7 10 2.18747908500053
-7 11 4.84135566237218
-7 16 2.49158118605633
-7 22 5.19720784901753
-7 25 3.17177927927662
-7 41 3.42832753514684
-7 309 4.62710580923352
-7 326 7.31388204531474
-7 332 3.20699467164245
-8 8 47.2695605021934
-8 32 6.41912689115822
-8 52 11.4101107282512
-8 54 6.06082442299641
-8 60 4.06024758641904
-8 62 5.26641670039231
-8 249 4.69826184710261
-8 254 3.06585816297328
-8 256 2.55750732489276
-8 339 13.4819280789172
-8 342 13.884059010187
-9 2 4.75406803872556
-9 4 4.02251697039814
-9 9 47.8223759244248
-9 14 10.2644155423446
-9 18 6.31397420484639
-9 19 4.66773813594734
-9 24 14.6174309391333
-9 58 7.20474906342764
-9 59 7.2428663923819
-9 321 12.6458045994324
-10 0 7.86071363596517
-10 7 2.18747908500053
-10 10 44.4463006251924
-10 14 3.45678654396981
-10 16 1.35224558712164
-10 24 6.4803825137331
-10 25 6.85696613422458
-10 44 10.0450871502645
-10 318 11.3301417266515
-10 326 7.70783639609937
-10 327 9.39181216475845
-11 5 2.42636353743031
-11 7 4.84135566237218
-11 11 33.7692975411062
-11 12 5.04391597447046
-11 16 1.87367909315134
-11 22 7.30637554517329
-11 28 1.84863362950566
-11 41 4.75955721232791
-11 309 6.86597612629242
-11 323 8.44863021324848
-11 332 7.23945931768732
-12 5 5.82771247763366
-12 11 5.04391597447046
-12 12 53.1609721622628
-12 22 11.3107806805944
-12 23 4.72750789328211
-12 41 6.73566698723515
-12 46 4.49453508794626
-12 47 9.73394691739388
-12 48 8.27630961241781
-12 255 4.47372647521484
-12 323 15.3876990128581
-12 332 3.72965712434755
-13 3 1.5942392228588
-13 4 4.18624150041861
-13 13 24.950686158591
-13 15 5.75768172394597
-13 19 3.84260987942512
-13 23 2.17834281089548
-13 28 1.94522500881094
-13 31 2.68949380055781
-13 309 6.94309371209691
-13 332 8.2891015788769
-14 0 5.38246182642466
-14 9 10.2644155423446
-14 10 3.45678654396981
-14 14 42.7410538044823
-14 16 3.19821029734258
-14 24 12.7685723951098
-14 57 3.28927584340651
-14 58 6.32351614145936
-14 59 6.26970830896965
-14 321 11.0290911529705
-14 326 2.129542654726
-15 4 4.61492314455308
-15 13 5.75768172394597
-15 15 61.3597852745445
-15 31 7.06225212617119
-15 32 7.65070987874968
-15 54 12.1678177195433
-15 117 6.43805966244927
-15 261 4.10303582555378
-15 309 16.9460009518378
-15 323 8.11544510952301
-15 332 6.86029064962345
-15 339 12.3234611198662
-16 7 2.49158118605633
-16 10 1.35224558712164
-16 11 1.87367909315134
-16 14 3.19821029734258
-16 16 26.9787344269767
-16 19 6.6312763725873
-16 24 5.36080342005508
-16 28 2.91830420071589
-16 59 3.66525737431586
-16 326 6.20042590280744
-16 332 6.77631820631166
-17 0 3.828479766089
-17 2 3.09021380514338
-17 17 16.9783904567051
-17 57 3.33774421034779
-17 246 3.81815177070849
-17 289 2.90380090441645
-17 321 8.48919522835256
-18 2 4.01072904139602
-18 4 4.67840952319579
-18 9 6.31397420484639
-18 18 48.8486698022172
-18 24 14.6341366940516
-18 30 3.57465956011931
-18 55 6.99390701170956
-18 118 6.72797056848651
-18 119 9.64502414612545
-18 289 3.94217558719193
-18 321 12.7520183662033
-19 4 5.12334748962595
-19 9 4.66773813594734
-19 13 3.84260987942512
-19 16 6.6312763725873
-19 19 39.3127765080042
-19 24 10.6052266366076
-19 28 2.99852118699573
-19 59 4.63841545772811
-19 309 7.10885740168547
-19 326 5.85386877256004
-19 332 7.49930342884365
-20 20 60.9438691072489
-20 22 9.49257842389642
-20 49 4.52127302931613
-20 50 7.735718251297
-20 54 9.44436659281241
-20 55 9.90187213192848
-20 232 7.59159260218997
-20 265 5.66861890467766
-20 268 7.23756290757175
-20 305 3.04223547380461
-20 327 15.6427790438627
-20 340 11.1372062995162
-21 21 17.9435276053948
-21 23 2.97805858022863
-21 31 4.18807188898001
-21 32 1.12571714577343
-21 46 3.14600521801675
-21 253 3.17466851794404
-21 254 4.45672340022537
-21 323 7.84604665692398
-22 7 5.19720784901753
-22 11 7.30637554517329
-22 12 11.3107806805944
-22 20 9.49257842389642
-22 22 148.938234378348
-22 25 14.0913208255565
-22 41 14.3888066459066
-22 47 12.220274842055
-22 48 12.8767977061911
-22 54 12.6427003238097
-22 249 7.21403509433489
-22 264 8.55725417487786
-22 265 11.5591443370708
-22 266 9.59076702330409
-22 267 7.7047959406338
-22 268 11.8209053642106
-22 309 14.2195406224501
-22 323 12.6118364412957
-22 326 10.5866983962057
-22 327 16.6601648933597
-22 339 13.3553664375777
-23 3 1.55722676453486
-23 5 4.66968405082642
-23 12 4.72750789328211
-23 13 2.17834281089548
-23 21 2.97805858022863
-23 23 25.6279535286871
-23 31 3.96735902126907
-23 46 4.4498513248469
-23 323 8.8054895563144
-23 332 5.10841029083282
-24 0 8.87895918104205
-24 4 14.8717668138987
-24 9 14.6174309391333
-24 10 6.4803825137331
-24 14 12.7685723951098
-24 16 5.36080342005508
-24 18 14.6341366940516
-24 19 10.6052266366076
-24 24 130.653573592595
-24 55 20.1671811793065
-24 59 8.94104431716504
-24 118 7.65057272154526
-24 119 13.5362499712341
-24 309 10.0021203364781
-24 318 17.1912246740264
-24 321 16.4075989144768
-24 326 7.09083679484579
-24 327 6.77625288618304
-25 7 3.17177927927662
-25 10 6.85696613422458
-25 22 14.0913208255565
-25 25 55.5094490329845
-25 41 6.914646423453
-25 44 6.26836988941326
-25 49 5.24288145468479
-25 264 3.68977301844515
-25 265 7.49200800988663
-25 326 12.1429984356847
-25 327 17.3934300788516
-26 26 55.5767701996833
-26 50 4.35005159480284
-26 55 10.234458819287
-26 92 16.3621421599692
-26 140 7.26233007840434
-26 141 5.79575456662355
-26 231 6.60351303184619
-26 232 9.47574187102034
-26 242 5.22352511762527
-26 243 4.08325775125859
-26 244 4.99929198518843
-26 343 8.97508832349916
-27 27 36.3372666650688
-27 91 12.8111906209826
-27 117 5.97839102611516
-27 118 3.22340615904667
-27 120 5.38188537591172
-27 261 3.43216949600334
-27 306 3.42922767491242
-27 307 3.23417118707057
-27 335 6.21787488553831
-27 343 10.7975835720223
-28 3 1.17334617123663
-28 5 1.1569717782112
-28 11 1.84863362950566
-28 13 1.94522500881094
-28 16 2.91830420071589
-28 19 2.99852118699573
-28 28 12.041001975476
-28 332 6.02050098773802
-29 29 24.557346249739
-29 85 3.69880772257428
-29 220 5.14448923368199
-29 221 4.13926496569429
-29 259 4.79765059772958
-29 260 3.43537616861325
-29 276 3.34175756144565
-29 331 12.2786731248695
-30 18 3.57465956011931
-30 30 29.8797867351115
-30 119 6.75785312061506
-30 242 3.8816607448399
-30 245 4.18491310550933
-30 246 3.33927269121903
-30 289 3.99712714143134
-30 318 8.0259611162174
-30 321 11.0582326227158
-31 13 2.68949380055781
-31 15 7.06225212617119
-31 21 4.18807188898001
-31 23 3.96735902126907
-31 31 36.184038293393
-31 32 6.15556156428595
-31 254 7.06240799925622
-31 323 9.24696378185273
-31 332 5.48896473779918
-31 339 8.41498251991728
-32 8 6.41912689115822
-32 15 7.65070987874968
-32 21 1.12571714577343
-32 31 6.15556156428595
-32 32 48.5618611669246
-32 54 8.48632349267742
-32 60 3.51895023279781
-32 254 5.37286108623742
-32 261 6.25986736939913
-32 291 3.2199186353407
-32 339 13.3763958354919
-32 342 11.2573596184752
-33 33 78.9994827303527
-33 52 8.36464804928586
-33 55 4.7367801757614
-33 64 4.38110704882969
-33 91 10.650141431402
-33 92 11.1277248259578
-33 224 7.62837823597931
-33 225 6.46471932594645
-33 232 4.32874804883405
-33 263 11.0900000363404
-33 303 8.43565453970796
-33 335 8.82988648948717
-33 338 7.23113387502795
-33 340 11.3376251945185
-33 342 13.8926768184505
-34 34 49.4402450308041
-34 53 3.43069841919005
-34 62 7.03462677202164
-34 80 4.95788364555116
-34 81 5.07832972378576
-34 85 5.20425797722815
-34 221 7.67173736572783
-34 224 5.42611987522028
-34 303 7.92642684069481
-34 313 17.4552959893807
-34 338 9.97499093740575
-35 35 40.9545720425812
-35 148 0.831134293055666
-35 149 9.17240597593465
-35 151 3.59884010300682
-35 153 2.67111710418377
-35 154 4.47245138674477
-35 155 5.28965625626309
-35 157 10.2881188785621
-35 162 6.06445421278592
-35 299 4.13208498223611
-35 310 3.93090466711676
-35 333 10.9806902039821
-36 36 16.8020066782432
-36 91 6.82999331943623
-36 261 3.08030169727487
-36 290 3.51677891586161
-36 291 3.05389706430654
-36 293 3.86391472846527
-36 306 4.85812429202025
-37 37 48.8726085527594
-37 70 11.8417077465097
-37 88 5.68545014353657
-37 101 6.90914638633339
-37 102 5.7607900866365
-37 103 8.70076591406752
-37 121 4.78640814266697
-37 122 6.89080340386339
-37 293 13.5128210501658
-37 294 9.22101995535921
-38 38 27.4344827000853
-38 178 3.59175451113516
-38 179 6.79705563191574
-38 180 8.22254060644798
-38 204 4.02538447330858
-38 252 2.95550160596442
-38 273 5.43400038244862
-38 312 10.1254868389075
-39 39 80.376854144729
-39 127 10.4043252397606
-39 131 9.66230370353798
-39 230 13.0375948093247
-39 239 12.1114019634348
-39 240 5.07954110312761
-39 241 7.20349482973682
-39 282 10.1839367037965
-39 286 7.30757083491639
-39 288 16.2406581291583
-39 316 10.6041784899271
-39 319 11.4278353518325
-39 344 7.30244005853995
-40 40 75.0434871911906
-40 65 9.91911580889884
-40 73 7.99489068038659
-40 150 12.9481925474443
-40 250 8.01885814114102
-40 269 10.4121544836561
-40 270 6.16100250864211
-40 294 10.6666716337746
-40 310 15.8079985925687
-40 313 12.5490336588627
-40 322 6.34851440320446
-40 331 11.7387983282065
-41 7 3.42832753514684
-41 11 4.75955721232791
-41 12 6.73566698723515
-41 22 14.3888066459066
-41 25 6.914646423453
-41 41 36.5823533112511
-41 48 4.51874444864908
-41 264 3.63335629198808
-41 323 4.99301435040304
-41 326 5.50141007176695
-42 42 28.2665237988626
-42 140 8.39780472905653
-42 220 2.96234575996194
-42 231 4.41717179681239
-42 263 4.7805247339541
-42 277 4.32944950633231
-42 304 2.69411244518052
-42 315 5.14524229149521
-42 324 9.67313443550096
-43 43 53.327755284484
-43 132 6.02014520525775
-43 133 4.3932899699797
-43 138 3.74252791164124
-43 192 6.37148386079764
-43 257 5.55934820161733
-43 258 2.99645707612704
-43 315 6.56611619123775
-43 316 11.2092366907608
-43 320 17.6345773034624
-43 329 15.4984505158443
-44 0 7.10041117066521
-44 10 10.0450871502645
-44 25 6.26836988941326
-44 44 41.1931152390403
-44 49 6.8415461683233
-44 247 1.4942542426401
-44 248 5.49107235272103
-44 318 12.8339334874668
-44 327 11.7149983970663
-45 45 18.5505394540188
-45 50 3.2328081203047
-45 243 2.75741688528121
-45 245 2.9024575206597
-45 246 3.55435844110573
-45 247 3.14000408604497
-45 248 2.96349440062244
-45 318 9.27526972700938
-46 5 1.17044819042869
-46 12 4.49453508794626
-46 21 3.14600521801675
-46 23 4.4498513248469
-46 46 18.4103675371283
-46 253 3.10162528484453
-46 255 3.21835062147384
-46 323 8.03473557813545
-47 12 9.73394691739388
-47 22 12.220274842055
-47 47 46.366254620145
-47 48 7.09101780801227
-47 253 3.54586811865254
-47 254 5.19173400922677
-47 255 4.39420217632546
-47 256 2.65091861943826
-47 267 4.43791140594868
-47 323 11.9316402929018
-47 339 8.35186774026289
-48 12 8.27630961241781
-48 22 12.8767977061911
-48 41 4.51874444864908
-48 47 7.09101780801227
-48 48 29.035232155368
-48 255 1.6408183714929
-48 264 3.34912055462914
-48 266 2.90785382102262
-48 267 2.89218591063703
-49 20 4.52127302931613
-49 25 5.24288145468479
-49 44 6.8415461683233
-49 49 35.6549129450922
-49 50 6.29571173131609
-49 248 3.51880708951375
-49 265 4.69019857290673
-49 318 8.0633019885452
-49 327 14.3086493830324
-50 20 7.735718251297
-50 26 4.35005159480284
-50 45 3.2328081203047
-50 49 6.29571173131609
-50 50 47.850435288506
-50 55 14.3966977926322
-50 232 5.00737570018183
-50 243 5.77029689061641
-50 248 3.5047568383438
-50 318 11.8394477981532
-50 327 9.64278821511096
-51 51 28.6059394781674
-51 225 6.66836828689805
-51 231 3.86321025812458
-51 232 5.6624498840886
-51 263 11.9371942998336
-51 304 4.09398606209803
-51 305 2.36577543925016
-51 340 8.31792498695818
-52 8 11.4101107282512
-52 33 8.36464804928586
-52 52 66.8105807981572
-52 54 11.0438823318482
-52 62 4.78088361353662
-52 224 6.39472743178123
-52 225 4.85138138231069
-52 249 5.85211178343262
-52 268 8.25104482116554
-52 305 2.46089092517163
-52 338 3.6664824689898
-52 339 10.4573116225187
-52 340 9.0993946440491
-52 342 13.5830013948946
-53 34 3.43069841919005
-53 53 22.9526443543163
-53 220 3.0080441547276
-53 221 5.0690651434233
-53 224 2.65783591157811
-53 225 2.13757447761929
-53 304 3.74942112215672
-53 324 5.90804928034879
-53 338 8.46827802243053
-54 8 6.06082442299641
-54 15 12.1678177195433
-54 20 9.44436659281241
-54 22 12.6427003238097
-54 32 8.48632349267742
-54 52 11.0438823318482
-54 54 119.953030813113
-54 55 11.6548920323249
-54 117 11.1067670337801
-54 261 9.04672863840493
-54 268 11.0218711364747
-54 309 16.0565206493721
-54 327 12.5382832224245
-54 335 7.52999653609288
-54 339 16.3823640879194
-54 340 12.4456956308042
-54 342 12.3005123683838
-55 4 11.2877319546395
-55 18 6.99390701170956
-55 20 9.90187213192848
-55 24 20.1671811793065
-55 26 10.234458819287
-55 33 4.7367801757614
-55 50 14.3966977926322
-55 54 11.6548920323249
-55 55 173.884463973891
-55 92 10.6865472893209
-55 117 13.2507802186471
-55 118 12.8050914577344
-55 119 14.922859321633
-55 232 15.7600239514844
-55 242 10.4470105017171
-55 243 8.75108273590699
-55 309 14.4365635748742
-55 318 15.8789955921498
-55 327 16.013365254427
-55 335 9.3521656411034
-55 340 15.5184775180814
-55 343 13.6302118061672
-56 56 35.8954435620806
-56 138 4.26775419885961
-56 139 5.12158088102786
-56 140 6.84608183347436
-56 141 4.34090216502694
-56 142 6.83388574870635
-56 226 4.09200937036907
-56 328 13.6068196160134
-56 336 8.73413152964337
-57 0 3.08627833880136
-57 2 3.56655714522442
-57 14 3.28927584340651
-57 17 3.33774421034779
-57 57 16.8733252822632
-57 58 3.59346974448314
-57 321 8.43666264113161
-58 2 4.47470266645142
-58 9 7.20474906342764
-58 14 6.32351614145936
-58 57 3.59346974448314
-58 58 21.5964376158216
-58 59 1.96707944953272
-58 321 8.83113935837805
-59 9 7.2428663923819
-59 14 6.26970830896965
-59 16 3.66525737431586
-59 19 4.63841545772811
-59 24 8.94104431716504
-59 58 1.96707944953272
-59 59 21.8162475333955
-60 1 4.38144080083423
-60 8 4.06024758641904
-60 32 3.51895023279781
-60 60 48.4899146780719
-60 62 9.4265204956345
-60 63 8.96054043795483
-60 64 11.2994866106037
-60 80 1.35218020163783
-60 291 6.41553144284117
-60 303 10.0908389865638
-60 342 13.229135221821
-61 61 25.6395854816418
-61 63 6.53740460450125
-61 65 7.6808746486415
-61 80 1.59593166086126
-61 81 5.13891809217941
-61 159 6.28238813631966
-61 162 1.51572387092385
-61 313 9.7081372090358
-62 8 5.26641670039231
-62 34 7.03462677202164
-62 52 4.78088361353662
-62 60 9.4265204956345
-62 62 61.789725673666
-62 63 11.3106949408362
-62 80 6.5519098025421
-62 224 6.74746710412636
-62 303 17.4843995119015
-62 313 11.5321284455326
-62 342 12.5495411239752
-63 1 4.04993663953328
-63 60 8.96054043795483
-63 61 6.53740460450125
-63 62 11.3106949408362
-63 63 66.1836659467598
-63 64 13.7079386535553
-63 65 8.72145091466888
-63 80 7.23199942108588
-63 81 4.92594723113181
-63 100 3.56396731426306
-63 303 11.2942252023085
-63 313 18.9713935603007
-64 1 11.4652061383804
-64 33 4.38110704882969
-64 60 11.2994866106037
-64 63 13.7079386535553
-64 64 134.402087097671
-64 65 9.57992245736699
-64 70 8.57174407817237
-64 73 9.30091200998139
-64 91 18.5027027340745
-64 100 9.68349407684935
-64 102 6.48543974247064
-64 263 10.6040386717988
-64 278 8.70259068009494
-64 290 9.57376776821079
-64 291 12.6905547997722
-64 293 10.584813015958
-64 294 10.2383769333753
-64 303 11.6054717866947
-64 313 12.1424390416595
-64 342 12.4831243986576
-65 40 9.91911580889884
-65 61 7.6808746486415
-65 63 8.72145091466888
-65 64 9.57992245736699
-65 65 83.7936957374622
-65 100 5.35847111165744
-65 101 4.07425057977149
-65 149 4.2267516242498
-65 150 8.24297767784241
-65 159 10.9495552505874
-65 162 5.74944789039574
-65 294 12.2063627791225
-65 310 16.4354116741548
-65 313 22.5459511888355
-66 66 99.9810685728003
-66 87 13.2843334922982
-66 103 8.42203292133984
-66 105 10.4438698620678
-66 106 9.03699004504711
-66 108 8.8868138213058
-66 148 7.44696799258099
-66 150 8.28110685780453
-66 239 8.33007533430367
-66 240 17.9408707747115
-66 270 12.2243009649984
-66 282 3.93778286828627
-66 319 15.857981513763
-66 322 13.6017745037195
-66 337 12.2767019069738
-67 67 27.8654880079627
-67 196 4.43100716958009
-67 213 4.44996088115761
-67 214 4.33222067172021
-67 215 4.50889249577145
-67 219 5.16951616268104
-67 280 4.97389062705228
-67 311 13.9327440039813
-68 68 15.6683075902336
-68 207 2.96813697813348
-68 208 2.72865422059991
-68 210 3.45864680200291
-68 211 3.60993079771867
-68 212 2.90293879177865
-68 330 7.83415379511681
-69 69 28.5126578582308
-69 106 4.01449441990402
-69 107 3.98546046693161
-69 108 2.76778903500454
-69 215 4.90708551607554
-69 216 5.06697843361728
-69 284 5.1748560755941
-69 311 7.50307942717926
-69 337 9.34924341303987
-70 6 6.42262368487737
-70 37 11.8417077465097
-70 64 8.57174407817237
-70 70 127.343388240814
-70 71 4.89460179125002
-70 73 10.7804127512299
-70 89 8.18808231803009
-70 103 10.5518729646661
-70 121 13.9947688337924
-70 122 10.5613654478756
-70 123 10.385063701505
-70 130 10.2815846774429
-70 250 9.29426564745738
-70 251 7.53053475643435
-70 263 7.70671632021801
-70 270 13.5508349836576
-70 278 12.1287340817913
-70 281 9.35321308436415
-70 293 10.4210850585324
-70 294 14.5558704334137
-71 70 4.89460179125002
-71 71 55.785763236227
-71 103 6.3612824750882
-71 121 6.28291916356688
-71 123 7.35209063738048
-71 124 4.84585449863444
-71 125 12.7601419477523
-71 127 1.19688328440509
-71 270 13.2987028187757
-71 281 4.05417691925428
-71 282 4.12422228694374
-71 288 8.17572298941397
-71 319 10.3320460418754
-72 72 134.044373476588
-72 110 13.3989874579397
-72 174 12.9552944299155
-72 175 10.2252827183587
-72 176 14.6980026638632
-72 184 8.16979711719743
-72 186 8.79345923773742
-72 187 14.4091878934909
-72 188 11.699635113636
-72 189 12.6405622908362
-72 197 13.2619620659598
-72 219 9.72179197563599
-72 238 9.91638647983625
-72 275 9.43535213772136
-72 295 9.00335864243183
-72 311 17.2017453703996
-72 334 7.81302354329331
-72 337 9.87885898036393
-72 344 7.84387209626571
-73 40 7.99489068038659
-73 64 9.30091200998139
-73 70 10.7804127512299
-73 73 74.8769347171083
-73 250 12.3151744179468
-73 251 7.4982419544144
-73 263 9.70249579684884
-73 294 9.49075616417553
-73 303 5.09155032848032
-73 313 11.6263285269973
-73 324 9.19679188251412
-73 331 11.0447317776898
-73 338 8.27311578499729
-74 74 53.7675221523095
-74 127 9.6158605495177
-74 128 4.74571367737195
-74 202 6.79559078946361
-74 203 3.61618949608371
-74 205 6.68132263904293
-74 206 4.85256602159696
-74 230 15.4472945829296
-74 312 13.6664925135715
-74 314 15.2302529588863
-75 75 15.7139471912253
-75 135 6.2666381163977
-75 136 4.06865801424508
-75 223 2.91085863145515
-75 236 1.59033547921496
-75 274 4.9461149641575
-75 285 3.78831558136758
-76 76 5.44355224040698
-76 143 1.10999228627763
-76 144 2.72177612020349
-76 167 2.72177612020349
-76 262 1.61178383392586
-77 77 35.7080703094124
-77 124 3.04993940171718
-77 128 2.29755089727815
-77 131 6.50746678106389
-77 132 5.73690519972655
-77 134 0.955792990859513
-77 143 4.37826814504605
-77 287 3.92691339803705
-77 288 7.73886180993359
-77 316 8.57963145767381
-77 320 10.3907753827828
-78 78 41.7741175062917
-78 133 5.27714462265576
-78 134 3.87310786558283
-78 135 10.4767174720106
-78 137 10.7072225425921
-78 227 6.53723341555245
-78 235 7.76188693054932
-78 237 2.80532214306199
-78 308 3.15490041877203
-78 325 12.0676408486605
-79 79 20.0906347282279
-79 168 3.54074072540923
-79 170 2.99575317179875
-79 171 10.0453173641139
-79 198 3.10028999058146
-79 199 3.81386126406498
-79 201 3.40428664812324
-79 292 3.2357029282502
-80 34 4.95788364555116
-80 60 1.35218020163783
-80 61 1.59593166086126
-80 62 6.5519098025421
-80 63 7.23199942108588
-80 80 24.3797661332741
-80 81 5.63797326409494
-80 313 9.24177120413795
-81 34 5.07832972378576
-81 61 5.13891809217941
-81 63 4.92594723113181
-81 80 5.63797326409494
-81 81 36.5655269160004
-81 85 4.3358443325409
-81 158 5.07017030290232
-81 159 4.90896617663477
-81 310 4.76595943004621
-81 313 14.9861818206845
-82 82 19.4544113116886
-82 83 3.96964730405301
-82 161 2.9001094888918
-82 163 3.78065716805905
-82 165 3.04643899889343
-82 166 3.48144863170352
-82 301 2.27610972008774
-82 341 9.72720565584428
-83 82 3.96964730405301
-83 83 28.0846739352277
-83 156 4.32682746098229
-83 157 3.18547932378588
-83 159 2.88902402245754
-83 160 4.10079771851523
-83 161 3.77427739319715
-83 165 2.80767459178687
-83 333 7.46038498262984
-83 341 9.61289810543377
-84 84 19.7893495896312
-84 155 4.10822525892703
-84 157 3.73488618497967
-84 158 3.71578860448806
-84 218 4.26778146050592
-84 276 3.96266808073053
-84 310 9.8946747948156
-85 29 3.69880772257428
-85 34 5.20425797722815
-85 81 4.3358443325409
-85 85 45.7107580025243
-85 158 5.95417936777617
-85 221 7.13647943392787
-85 276 5.5926110144294
-85 310 9.73514089744575
-85 313 15.2118746016061
-85 331 11.6969416562578
-86 86 21.7175681806329
-86 182 1.1503767238575
-86 183 6.61499635879291
-86 190 6.54406676993046
-86 191 2.09222795953165
-86 217 2.39305604155476
-86 241 5.92097892235994
-86 317 7.86064949492207
-87 66 13.2843334922982
-87 87 161.480773262217
-87 94 11.8443877858731
-87 104 14.0438313938043
-87 105 16.4448022244854
-87 106 11.0565918795575
-87 107 17.7164556494629
-87 210 10.8073488085903
-87 211 16.7716584336737
-87 212 8.64960270974841
-87 216 10.4173215415914
-87 229 9.5701877555419
-87 238 8.07346250954404
-87 239 13.3583067626322
-87 240 5.94822619346144
-87 295 9.00072944416245
-87 311 15.823359417921
-87 314 11.8369942149032
-87 330 19.32116616588
-87 337 18.2523935101938
-88 37 5.68545014353657
-88 88 33.6075115918618
-88 91 3.67896335186618
-88 102 5.49696750060549
-88 122 4.94395728016222
-88 290 5.0751469116107
-88 293 15.3476746603182
-88 306 4.93030922087058
-88 307 5.25279831882279
-89 6 3.92281769793109
-89 70 8.18808231803009
-89 89 55.569850230096
-89 91 11.785916679725
-89 120 8.26796936069725
-89 122 6.43002239377162
-89 130 5.52040194150147
-89 233 4.23607544777072
-89 278 16.7870275078791
-89 293 11.8596786239604
-89 307 6.35678337387733
-90 90 37.8732127520999
-90 92 9.56562972889973
-90 119 2.93901761858691
-90 120 4.9851877472927
-90 141 3.59518782551748
-90 142 1.37849869088869
-90 233 4.12666481759435
-90 234 5.45543339599991
-90 244 5.51755700438717
-90 278 5.81519943141598
-90 336 7.43742516097345
-90 343 5.99401770659346
-91 27 12.8111906209826
-91 33 10.650141431402
-91 36 6.82999331943623
-91 64 18.5027027340745
-91 88 3.67896335186618
-91 89 11.785916679725
-91 91 154.72583501324
-91 92 13.6222684174061
-91 117 3.65432085837117
-91 120 15.8149103846929
-91 261 12.9077821287667
-91 263 9.86889433591251
-91 278 18.0985628243192
-91 290 6.86374510386666
-91 291 12.1155032173734
-91 293 14.1622658002414
-91 303 3.51691552037983
-91 306 8.96278350482087
-91 307 10.4090030974069
-91 335 14.5482321950768
-91 342 13.1331319889259
-91 343 10.1515250048132
-92 26 16.3621421599692
-92 33 11.1277248259578
-92 55 10.6865472893209
-92 90 9.56562972889973
-92 91 13.6222684174061
-92 92 133.854931255088
-92 120 9.83576732760929
-92 140 13.2597045079622
-92 141 8.97399773246037
-92 231 10.6946237750171
-92 232 16.8107624530181
-92 233 3.91823089230897
-92 244 8.24122958110862
-92 251 2.70935879873139
-92 263 13.1768533518013
-92 278 11.0986254958369
-92 335 7.98449218297208
-92 336 10.70628623574
-92 340 7.11934411027876
-92 343 14.8888080162329
-93 93 40.9553685147395
-93 175 7.71740305090141
-93 177 5.7684813486277
-93 178 5.62731741744446
-93 180 6.30788451265802
-93 193 3.09257011403693
-93 195 4.85114793008625
-93 196 3.28864332923669
-93 271 8.49177982903803
-93 279 3.62221344306604
-93 280 2.75678405542892
-93 334 9.90882774158485
-94 87 11.8443877858731
-94 94 40.8298977427651
-94 107 4.64146084786266
-94 209 1.96690742362201
-94 212 5.56967322400087
-94 213 3.87598966815988
-94 214 3.31871444572034
-94 216 6.66718778366297
-94 311 11.5210124880593
-94 330 6.58766630739739
-94 346 5.25184663978908
-95 95 41.2702474203472
-95 179 5.67533790793055
-95 180 10.5823163251865
-95 195 5.26807934893554
-95 196 3.17979713887867
-95 208 4.46106361875804
-95 209 3.75067209945928
-95 213 2.56255021343815
-95 330 8.93364932947612
-95 345 8.1592007628085
-95 346 9.33270438564935
-96 96 27.5919599202998
-96 179 4.75413009479584
-96 203 4.44607021574858
-96 204 5.45995751966327
-96 205 4.64040281764532
-96 207 2.56207098860688
-96 208 3.69561962284133
-96 312 6.4797788767472
-96 345 9.34990974440134
-97 97 16.1794276180481
-97 149 1.1319800877749
-97 151 5.17297022006941
-97 162 4.69792970839541
-97 283 3.39178410062863
-97 300 2.91674358895462
-97 333 6.95773372124914
-98 98 28.6795798190035
-98 152 5.72224513495199
-98 161 3.0893314480768
-98 163 3.62326959808172
-98 164 3.9289978838915
-98 272 3.98875795193454
-98 296 3.86267910407658
-98 297 5.57222454859408
-98 300 2.43588580639597
-98 341 10.795978252502
-99 99 29.5537018281023
-99 152 4.91661034960112
-99 153 2.72549350174577
-99 154 2.39286392906204
-99 156 3.72061793478158
-99 164 3.76453133504487
-99 165 2.28586733950765
-99 166 3.27011917025761
-99 298 3.4742989202096
-99 333 7.00302153763641
-99 341 10.7771287243069
-100 1 3.9615238203097
-100 63 3.56396731426306
-100 64 9.68349407684935
-100 65 5.35847111165744
-100 100 34.0878036423772
-100 101 7.36040774433927
-100 102 6.13003598763042
-100 293 7.72390688922149
-100 294 7.34989851929515
-101 37 6.90914638633339
-101 65 4.07425057977149
-101 100 7.36040774433927
-101 101 45.6033052636332
-101 102 5.44415879870944
-101 103 7.58317987779558
-101 150 6.59717569804197
-101 270 6.7096840376604
-101 293 9.09343054295831
-101 294 14.63352422984
-102 1 3.84865697827165
-102 37 5.7607900866365
-102 64 6.48543974247064
-102 88 5.49696750060549
-102 100 6.13003598763042
-102 101 5.44415879870944
-102 102 34.8531320835711
-102 290 5.53573996751866
-102 293 13.5779090635139
-103 37 8.70076591406752
-103 66 8.42203292133984
-103 70 10.5518729646661
-103 71 6.3612824750882
-103 101 7.58317987779558
-103 103 65.8367364777795
-103 105 1.66824980699059
-103 121 6.99519151711083
-103 150 5.55518653530202
-103 270 19.7590530754645
-103 282 4.45854924935774
-103 294 10.985210804778
-103 319 7.71452957470835
-104 87 14.0438313938043
-104 104 59.049251616802
-104 105 8.65703087719652
-104 127 11.053984097666
-104 206 4.80194572146659
-104 210 7.30260500930091
-104 211 6.46593896723233
-104 239 15.7528617375516
-104 282 6.27696642372157
-104 314 14.2187131972632
-105 66 10.4438698620678
-105 87 16.4448022244854
-105 103 1.66824980699059
-105 104 8.65703087719652
-105 105 55.2578236729289
-105 106 5.67844228966805
-105 107 5.57372907122288
-105 211 6.18647522399033
-105 239 13.2894535739348
-105 282 8.11809967099856
-105 319 6.82658290883839
-106 66 9.03699004504711
-106 69 4.01449441990402
-106 87 11.0565918795575
-106 105 5.67844228966805
-106 106 36.5119914991408
-106 107 7.51185997998037
-106 108 4.89205517465178
-106 148 0.986296927341089
-106 337 11.5912565325613
-107 69 3.98546046693161
-107 87 17.7164556494629
-107 94 4.64146084786266
-107 105 5.57372907122288
-107 106 7.51185997998037
-107 107 43.403832232789
-107 211 5.57380309500578
-107 212 5.09536048121545
-107 216 6.31296719207092
-107 337 8.694651565431
-108 66 8.8868138213058
-108 69 2.76778903500454
-108 106 4.89205517465178
-108 108 40.7185123907895
-108 148 6.37770872020036
-108 185 6.39708382372096
-108 186 4.66282124322803
-108 240 11.9284318978296
-108 284 2.39198368272734
-108 337 12.7730811875158
-109 109 37.7406090375556
-109 183 6.69701681482615
-109 184 3.53287170025802
-109 185 4.76917480834749
-109 186 10.0512230859689
-109 217 6.39668318658156
-109 218 2.76494798522307
-109 241 9.45714960472595
-109 299 2.42239824622738
-109 322 10.5194481241749
-110 72 13.3989874579397
-110 110 40.7043119143022
-110 187 6.03658927590785
-110 188 3.86218442848385
-110 215 4.4217365835139
-110 219 7.21685200124191
-110 284 4.81591794942673
-110 311 12.3308026981058
-110 337 8.97339747683355
-111 111 22.3690834800243
-111 174 3.27932227068342
-111 181 4.50422673898645
-111 182 3.56417398264737
-111 183 3.31724914339484
-111 184 3.06745752564673
-111 194 3.49317191937137
-111 197 2.74130570952541
-111 275 9.5867179297808
-112 112 30.7349554209315
-112 173 3.94023673466286
-112 174 12.0852545579824
-112 175 7.78273840584395
-112 181 4.05344936794433
-112 189 5.97724755902435
-112 193 3.28222315248329
-112 194 3.64450256995891
-112 302 5.33678078349704
-113 113 46.3567602546052
-113 168 6.27024052399184
-113 169 12.6292690634512
-113 170 6.48019469099129
-113 171 9.86721921281054
-113 173 4.44483509561475
-113 174 15.0843381816147
-113 181 4.06891637286016
-113 182 6.54717197225376
-113 190 4.14295526831967
-114 114 40.8560031005166
-114 131 6.20510131468709
-114 132 3.52869570366641
-114 168 5.78205275056841
-114 169 7.0799507578158
-114 190 6.24886089825792
-114 191 2.82259604635156
-114 192 4.07882126469947
-114 286 8.1093721603589
-114 316 11.8233527533383
-114 317 5.60520100103102
-115 115 21.9730548821115
-115 147 2.80536746739225
-115 191 3.12051485811777
-115 192 3.92820515757374
-115 258 2.57338943369797
-115 259 2.55115862699197
-115 260 3.83077140020114
-115 317 7.11059433228005
-115 329 7.03958104691228
-116 116 27.8992096855334
-116 139 2.69848714340859
-116 220 4.6494032976539
-116 257 2.81436287016156
-116 258 4.19573488702134
-116 259 4.3754261190164
-116 277 4.73097769698642
-116 315 10.9315996950367
-116 324 4.04674291048473
-116 329 3.40607990853046
-117 4 6.64640379769704
-117 15 6.43805966244927
-117 27 5.97839102611516
-117 54 11.1067670337801
-117 55 13.2507802186471
-117 91 3.65432085837117
-117 117 57.0664250268627
-117 118 5.41970602267376
-117 261 5.92637263027851
-117 309 10.866959528998
-117 335 9.1861559377009
-117 343 7.12572082358306
-118 4 7.86638218018645
-118 18 6.72797056848651
-118 24 7.65057272154526
-118 27 3.22340615904667
-118 55 12.8050914577344
-118 117 5.41970602267376
-118 118 40.47141083572
-118 119 5.70061281879575
-118 120 3.13182877223154
-118 343 8.18154555287971
-119 18 9.64502414612545
-119 24 13.5362499712341
-119 30 6.75785312061506
-119 55 14.922859321633
-119 90 2.93901761858691
-119 118 5.70061281879575
-119 119 68.0047425760896
-119 120 3.37309580548074
-119 242 7.39799704143347
-119 244 2.26882356653491
-119 318 15.2982867944114
-119 321 11.1082333117757
-119 343 9.05906034750793
-120 27 5.38188537591172
-120 89 8.26796936069725
-120 90 4.9851877472927
-120 91 15.8149103846929
-120 92 9.83576732760929
-120 118 3.13182877223154
-120 119 3.37309580548074
-120 120 53.717435739398
-120 233 4.53896429281058
-120 278 10.1973222710413
-120 307 4.21318950917793
-120 343 10.8360327621511
-121 37 4.78640814266697
-121 70 13.9947688337924
-121 71 6.28291916356688
-121 103 6.99519151711083
-121 121 33.1281758329734
-121 122 4.11357324707795
-121 123 3.48385625612005
-121 130 3.63257900635688
-121 270 6.40296758276811
-122 37 6.89080340386339
-122 70 10.5613654478756
-122 88 4.94395728016222
-122 89 6.43002239377162
-122 121 4.11357324707795
-122 122 35.1517542429681
-122 130 3.80867455547174
-122 293 11.5444852538659
-122 307 4.43474978236376
-123 6 5.86624855617804
-123 70 10.385063701505
-123 71 7.35209063738048
-123 121 3.48385625612005
-123 123 42.6195156703403
-123 124 6.57238590715421
-123 125 4.90330679459536
-123 130 3.35418549288719
-123 145 4.94169689903844
-123 270 4.48070996243808
-123 281 12.5897292982135
-124 71 4.84585449863444
-124 77 3.04993940171718
-124 123 6.57238590715421
-124 124 45.5424717533449
-124 125 13.7757397961855
-124 143 5.13697841778227
-124 144 1.30040670224748
-124 145 10.6709673394591
-124 281 6.12660651452348
-124 288 6.33646089104657
-124 320 10.498368161267
-125 71 12.7601419477523
-125 123 4.90330679459536
-125 124 13.7757397961855
-125 125 120.168945108822
-125 145 5.91323232269262
-125 250 7.60611983505073
-125 269 10.5406854658889
-125 270 8.00954115102144
-125 281 13.4017651234903
-125 288 15.8011208192031
-125 315 11.4282401774041
-125 316 17.5570745806316
-125 317 4.53168274411372
-125 319 13.7931265821377
-125 320 21.0260155767998
-125 328 5.77441731750494
-125 329 13.4312074287609
-126 126 56.7507717713558
-126 133 14.4012518346228
-126 137 3.53056616408524
-126 142 1.9820633386985
-126 143 12.5212147889455
-126 144 4.16380166835547
-126 145 7.79232743194498
-126 146 3.00463128964592
-126 226 8.56541169949543
-126 227 6.45109779713573
-126 320 10.6710908875934
-126 325 12.0427007565108
-127 39 10.4043252397606
-127 71 1.19688328440509
-127 74 9.6158605495177
-127 104 11.053984097666
-127 127 76.5040230916999
-127 128 5.45078719456879
-127 206 5.96921398033506
-127 230 17.0477361326729
-127 239 17.6211439814161
-127 282 10.8331791634335
-127 288 7.9505947142302
-127 314 17.6123262995439
-128 74 4.74571367737195
-128 77 2.29755089727815
-128 127 5.45078719456879
-128 128 34.0268665557751
-128 131 7.08208407932594
-128 201 2.82691833287508
-128 202 3.04965356623316
-128 230 14.7158823806094
-128 288 8.06612596322177
-128 292 2.80558374217838
-129 129 20.3932785206759
-129 199 3.60327562841654
-129 201 3.68935093211986
-129 202 3.25833149204096
-129 203 3.44056652355893
-129 204 3.33503213988044
-129 252 3.06672180465915
-129 312 10.1966392603379
-130 6 4.70048874766894
-130 70 10.2815846774429
-130 89 5.52040194150147
-130 121 3.63257900635688
-130 122 3.80867455547174
-130 123 3.35418549288719
-130 130 24.1350543729702
-130 233 0.28164623296626
-130 278 4.62302090515992
-131 39 9.66230370353798
-131 77 6.50746678106389
-131 114 6.20510131468709
-131 128 7.08208407932594
-131 131 64.1618457098833
-131 132 4.20190312616001
-131 168 5.13084019998772
-131 171 3.7226933622857
-131 230 11.1581896764297
-131 286 10.5383374932361
-131 288 14.3433248322448
-131 292 4.83991148146619
-131 316 12.8506125143998
-132 43 6.02014520525775
-132 77 5.73690519972655
-132 114 3.52869570366641
-132 131 4.20190312616001
-132 132 33.0980695265834
-132 133 3.41282842711611
-132 192 3.14578858790402
-132 287 2.57899191248317
-132 316 11.8205030783334
-132 320 9.20134304922767
-133 43 4.3932899699797
-133 78 5.27714462265576
-133 126 14.4012518346228
-133 132 3.41282842711611
-133 133 68.2642568197184
-133 134 7.60987580145014
-133 138 4.37366066092068
-133 143 13.8626540904988
-133 226 6.92879585033756
-133 227 6.92248446204656
-133 287 5.0736213627081
-133 308 0.474501326201459
-133 320 16.1458292307082
-133 325 13.5204475903318
-134 77 0.955792990859513
-134 78 3.87310786558283
-134 133 7.60987580145014
-134 134 28.3706464720971
-134 143 7.53495372412097
-134 167 3.36054943315711
-134 222 2.77726164634473
-134 287 2.78017356393267
-134 308 2.73360633678323
-134 325 10.9306483459144
-135 75 6.2666381163977
-135 78 10.4767174720106
-135 135 67.150121263957
-135 136 9.02936635873247
-135 137 15.9421695378525
-135 222 3.69753648007885
-135 223 3.965507243976
-135 235 7.03028843858184
-135 236 4.44673942086305
-135 237 7.97697516475199
-135 274 10.5783163274109
-135 285 6.55537206139508
-135 308 2.90208218542663
-135 325 11.8574730884579
-136 75 4.06865801424508
-136 135 9.02936635873247
-136 136 30.0168300598056
-136 146 4.42269162227157
-136 167 4.7130336937432
-136 222 4.08768764243894
-136 223 3.07715289050411
-136 262 1.45290110280411
-136 274 5.74154646463621
-136 325 8.43220730033278
-137 78 10.7072225425921
-137 126 3.53056616408524
-137 135 15.9421695378525
-137 137 56.6373149983862
-137 146 2.85359450022582
-137 227 6.06584675064162
-137 235 9.02512319020422
-137 236 6.62261617884938
-137 237 5.91111952402551
-137 274 7.42977056915726
-137 285 4.51263226240642
-137 325 12.3553112775393
-138 43 3.74252791164124
-138 56 4.26775419885961
-138 133 4.37366066092068
-138 138 35.0488007703899
-138 139 4.49901276695232
-138 226 6.4222680574751
-138 257 6.42083900055934
-138 320 11.1951765490389
-138 328 11.6519620101376
-139 56 5.12158088102786
-139 116 2.69848714340859
-139 138 4.49901276695232
-139 139 32.6705577297101
-139 140 7.3133876795842
-139 257 5.29722009011325
-139 277 3.24762202685984
-139 315 9.15222834653078
-139 328 11.6762976600883
-140 26 7.26233007840434
-140 42 8.39780472905653
-140 56 6.84608183347436
-140 92 13.2597045079622
-140 139 7.3133876795842
-140 140 94.930040291322
-140 141 7.67819509015524
-140 231 8.42282169023471
-140 251 14.3320791469557
-140 263 13.2467614221054
-140 277 3.87653463750117
-140 315 15.7917860237936
-140 324 10.9612187417026
-140 328 12.6807411784207
-140 336 12.3256136776323
-141 26 5.79575456662355
-141 56 4.34090216502694
-141 90 3.59518782551748
-141 92 8.97399773246037
-141 140 7.67819509015524
-141 141 37.0313983102074
-141 142 5.22947910951373
-141 234 4.224273625053
-141 244 4.36786145336933
-141 336 11.3414458975915
-142 56 6.83388574870635
-142 90 1.37849869088869
-142 126 1.9820633386985
-142 141 5.22947910951373
-142 142 38.629731448516
-142 145 7.43595906951637
-142 226 6.82994860400593
-142 234 7.91282967271111
-142 328 12.1033232760458
-142 336 8.23860966268758
-143 76 1.10999228627763
-143 77 4.37826814504605
-143 124 5.13697841778227
-143 126 12.5212147889455
-143 133 13.8626540904988
-143 134 7.53495372412097
-143 143 69.184743236895
-143 144 7.34168611187254
-143 145 7.39572153230213
-143 146 4.64786311699183
-143 167 6.78154591129827
-143 287 6.42154284826198
-143 320 13.7326532162052
-143 325 12.9120406657393
-144 76 2.72177612020349
-144 124 1.30040670224748
-144 126 4.16380166835547
-144 143 7.34168611187254
-144 144 24.9282700761209
-144 145 2.81296791557803
-144 146 5.62938855865944
-144 167 6.12574717177482
-144 262 3.77121091478251
-144 325 3.52541995070762
-145 6 5.99551221229033
-145 123 4.94169689903844
-145 124 10.6709673394591
-145 125 5.91323232269262
-145 126 7.79232743194498
-145 142 7.43595906951637
-145 143 7.39572153230213
-145 144 2.81296791557803
-145 145 71.8813094201562
-145 226 8.81011939494645
-145 234 4.52851350157075
-145 281 12.4778183769359
-145 320 15.1236504904882
-145 328 13.923477643471
-146 126 3.00463128964592
-146 136 4.42269162227157
-146 137 2.85359450022582
-146 143 4.64786311699183
-146 144 5.62938855865944
-146 146 30.8696286635026
-146 167 9.08978734701019
-146 262 3.61232818366076
-146 274 2.46621619941246
-146 325 10.5779421773758
-147 115 2.80536746739225
-147 147 68.9272882334402
-147 191 3.9616771423798
-147 217 5.83671840767789
-147 218 5.54759901603714
-147 260 7.66271700426281
-147 269 15.926533165622
-147 276 6.5718595109712
-147 310 14.2695739258681
-147 317 13.0242641724277
-147 322 11.5127267733532
-147 329 4.46224562268925
-147 331 11.809650141479
-148 35 0.831134293055666
-148 66 7.44696799258099
-148 106 0.986296927341089
-148 108 6.37770872020036
-148 148 46.4441545882135
-148 149 4.74254642484878
-148 150 6.14161607713341
-148 185 7.20141575650698
-148 240 10.5478476106826
-148 299 5.54631667894965
-148 310 7.74255925196311
-148 322 12.1018221490576
-149 35 9.17240597593465
-149 65 4.2267516242498
-149 97 1.1319800877749
-149 148 4.74254642484878
-149 149 52.8175593684455
-149 150 4.21919193259826
-149 151 4.39638317724495
-149 155 7.24742516887087
-149 157 5.08397538041381
-149 159 6.73761013340095
-149 162 8.82231012475618
-149 299 4.0840690995967
-149 310 15.3770591135367
-149 333 3.98463080944166
-150 40 12.9481925474443
-150 65 8.24297767784241
-150 66 8.28110685780453
-150 101 6.59717569804197
-150 103 5.55518653530202
-150 148 6.14161607713341
-150 149 4.21919193259826
-150 150 70.6406980898171
-150 269 4.38744749534349
-150 270 16.0004595338307
-150 294 7.54509697726686
-150 310 12.3827281112265
-150 322 13.6598676908913
-151 35 3.59884010300682
-151 97 5.17297022006941
-151 149 4.39638317724495
-151 151 25.9463905007873
-151 152 2.97276519624809
-151 153 3.69991986874541
-151 162 4.60619952396761
-151 283 3.17473139019345
-151 333 11.2977762717052
-152 98 5.72224513495199
-152 99 4.91661034960112
-152 151 2.97276519624809
-152 152 38.2374940266011
-152 153 3.74372138482159
-152 272 5.46457901358106
-152 283 2.72531280794014
-152 297 4.41882060895245
-152 298 3.34348683522071
-152 300 4.81637915905769
-152 333 9.50957615221429
-152 341 9.72274439731248
-153 35 2.67111710418377
-153 99 2.72549350174577
-153 151 3.69991986874541
-153 152 3.74372138482159
-153 153 15.0669474601377
-153 154 2.22669560064117
-153 283 0.543438890913548
-153 333 6.99003483915531
-154 35 4.47245138674477
-154 99 2.39286392906204
-154 153 2.22669560064117
-154 154 18.6403566530039
-154 155 2.20639850851116
-154 156 3.72909169869368
-154 157 5.81925403786224
-154 333 7.11377981799079
-155 35 5.28965625626309
-155 84 4.10822525892703
-155 149 7.24742516887087
-155 154 2.20639850851116
-155 155 36.6753268139121
-155 156 0.900601028522006
-155 157 7.36593093678825
-155 159 6.09268108222828
-155 218 4.14362031012104
-155 299 3.72090322077262
-155 310 13.9375484498638
-156 83 4.32682746098229
-156 99 3.72061793478158
-156 154 3.72909169869368
-156 155 0.900601028522006
-156 156 21.004616812918
-156 157 3.99527785041769
-156 165 2.10686312496166
-156 333 7.49484425297535
-156 341 5.23280186804278
-157 35 10.2881188785621
-157 83 3.18547932378588
-157 84 3.73488618497967
-157 149 5.08397538041381
-157 154 5.81925403786224
-157 155 7.36593093678825
-157 156 3.99527785041769
-157 157 50.8830875428284
-157 158 3.59738485979418
-157 159 7.42326085745477
-157 162 7.67989312169479
-157 310 6.56320083949018
-157 333 11.587969042999
-158 81 5.07017030290232
-158 84 3.71578860448806
-158 85 5.95417936777617
-158 157 3.59738485979418
-158 158 29.4999604846021
-158 159 3.4742533810906
-158 276 3.54925331814863
-158 310 12.9862188047994
-158 313 5.90269208790375
-159 61 6.28238813631966
-159 65 10.9495552505874
-159 81 4.90896617663477
-159 83 2.88902402245754
-159 149 6.73761013340095
-159 155 6.09268108222828
-159 157 7.42326085745477
-159 158 3.4742533810906
-159 159 61.7926714994431
-159 160 2.53215933491061
-159 162 7.83936943724609
-159 310 17.486842075547
-159 313 10.601007826699
-159 333 5.47188953458793
-160 83 4.10079771851523
-160 159 2.53215933491061
-160 160 20.5490153802733
-160 161 2.59421827814797
-160 162 3.07428279537611
-160 272 3.0994271762453
-160 300 2.82853498439357
-160 333 7.68028941198866
-160 341 4.91381337083245
-161 82 2.9001094888918
-161 83 3.77427739319715
-161 98 3.0893314480768
-161 160 2.59421827814797
-161 161 19.4609308705468
-161 163 3.17086037565709
-161 272 2.12464272880759
-161 301 1.80749115776836
-161 341 9.73046543527338
-162 35 6.06445421278592
-162 61 1.51572387092385
-162 65 5.74944789039574
-162 97 4.69792970839541
-162 149 8.82231012475618
-162 151 4.60619952396761
-162 157 7.67989312169479
-162 159 7.83936943724609
-162 160 3.07428279537611
-162 162 47.6180807093671
-162 300 3.10643291435683
-162 310 4.89236340725244
-162 333 13.3787140568996
-163 82 3.78065716805905
-163 98 3.62326959808172
-163 161 3.17086037565709
-163 163 21.1990828438416
-163 166 4.21404082120608
-163 296 3.64802387820468
-163 301 2.76223100263301
-163 341 10.5995414219208
-164 98 3.9289978838915
-164 99 3.76453133504487
-164 164 22.7022556369458
-164 166 3.98267241565936
-164 296 4.23247296045699
-164 297 4.46204937357477
-164 298 3.43945751892202
-164 341 10.2432019678692
-165 82 3.04643899889343
-165 83 2.80767459178687
-165 99 2.28586733950765
-165 156 2.10686312496166
-165 165 12.8195163548304
-165 166 2.57267229968078
-165 341 6.40975817741519
-166 82 3.48144863170352
-166 99 3.27011917025761
-166 163 4.21404082120608
-166 164 3.98267241565936
-166 165 2.57267229968078
-166 166 21.5387710730924
-166 296 4.01781773458509
-166 341 10.7693855365462
-167 76 2.72177612020349
-167 134 3.36054943315711
-167 136 4.7130336937432
-167 143 6.78154591129827
-167 144 6.12574717177482
-167 146 9.08978734701019
-167 167 33.4569398985392
-167 222 3.01903647834437
-167 262 5.22411201758663
-167 325 9.14982167469065
-168 79 3.54074072540923
-168 113 6.27024052399184
-168 114 5.78205275056841
-168 131 5.13084019998772
-168 168 40.3061756550344
-168 169 9.81021867468597
-168 170 3.38129706916205
-168 171 12.0518812303252
-168 190 4.00882143939187
-168 286 7.02975688646584
-168 292 3.45341398256348
-169 113 12.6292690634512
-169 114 7.0799507578158
-169 168 9.81021867468597
-169 169 86.8214499160044
-169 171 10.6172144223367
-169 174 11.4064251114468
-169 182 9.74185330388717
-169 183 9.72495287924938
-169 186 6.11034126948676
-169 190 13.1848819553317
-169 241 9.68331655819117
-169 275 7.34609281350233
-169 286 8.73694899646379
-169 316 6.13486236528808
-169 344 8.02584670286984
-170 79 2.99575317179875
-170 113 6.48019469099129
-170 168 3.38129706916205
-170 170 37.3667691457774
-170 171 10.9427958733264
-170 173 6.40273837179551
-170 174 9.48104774069225
-170 198 4.07314648813133
-170 200 3.08574360266994
-170 228 9.20743671009869
-171 79 10.0453173641139
-171 113 9.86721921281054
-171 131 3.7226933622857
-171 168 12.0518812303252
-171 169 10.6172144223367
-171 170 10.9427958733264
-171 171 121.113168506371
-171 174 12.4981753424693
-171 198 6.93337394678439
-171 199 13.4018687390584
-171 201 12.2274837691452
-171 228 16.2841784029647
-171 229 9.45855230742168
-171 230 11.2136536821911
-171 286 9.23429297719949
-171 292 9.41893927550589
-171 312 15.6210667776571
-171 344 8.13104607396117
-172 172 32.6533895308267
-172 177 4.89660313278759
-172 198 2.61264700593178
-172 199 5.09968900861755
-172 200 2.35553961728895
-172 228 11.0256749476344
-172 271 7.17445358059264
-172 273 5.3010198177789
-172 302 2.95569289546737
-172 312 7.55876429014079
-173 112 3.94023673466286
-173 113 4.44483509561475
-173 170 6.40273837179551
-173 173 31.937670235587
-173 174 12.8504530482226
-173 181 4.29523054925352
-173 200 3.11838206957095
-173 228 7.58376328751591
-173 302 5.27086619674449
-174 72 12.9552944299155
-174 111 3.27932227068342
-174 112 12.0852545579824
-174 113 15.0843381816147
-174 169 11.4064251114468
-174 170 9.48104774069225
-174 171 12.4981753424693
-174 173 12.8504530482226
-174 174 134.30327824245
-174 175 12.5273862105465
-174 181 11.7158810273201
-174 182 7.34385128351062
-174 189 11.6951684598536
-174 194 8.21157539409118
-174 197 7.52708932218429
-174 228 15.2640778850468
-174 275 10.0221664675491
-174 295 7.50623699232551
-174 302 8.28922742452711
-174 344 11.7119462136938
-175 72 10.2252827183587
-175 93 7.71740305090141
-175 112 7.78273840584395
-175 174 12.5273862105465
-175 175 83.9405707437682
-175 176 7.91741083857606
-175 177 8.30539298379523
-175 180 2.29053101919074
-175 189 12.7121565125441
-175 193 9.40498879122763
-175 228 8.02973187319526
-175 271 6.92233987492352
-175 279 6.88550056693011
-175 295 7.02903773752608
-175 302 9.07014426984109
-175 334 9.09081126225188
-176 72 14.6980026638632
-176 175 7.91741083857606
-176 176 45.9245769984909
-176 188 4.02124952522667
-176 189 6.4025528629266
-176 219 6.88958380411938
-176 279 4.02674242471294
-176 280 5.36988305569567
-176 311 9.89128849041644
-176 334 9.67015183219949
-177 93 5.7684813486277
-177 172 4.89660313278759
-177 175 8.30539298379523
-177 177 33.4671231580969
-177 178 3.53156546246565
-177 193 3.41707018028051
-177 228 6.32089786786639
-177 271 10.0197681236525
-177 273 3.10641506289099
-177 302 4.83449057477886
-178 38 3.59175451113516
-178 93 5.62731741744446
-178 177 3.53156546246565
-178 178 26.8541217829073
-178 179 3.83334267627951
-178 180 9.89549542898803
-178 195 3.66672882157689
-178 271 6.16857755874162
-178 273 3.9664007977297
-179 38 6.79705563191574
-179 95 5.67533790793055
-179 96 4.75413009479584
-179 178 3.83334267627951
-179 179 47.9917315820272
-179 180 12.7017807748007
-179 195 3.6109946859842
-179 204 7.56207916973814
-179 208 3.73200584647473
-179 312 9.73305511200773
-179 345 13.5878154731137
-180 38 8.22254060644798
-180 93 6.30788451265802
-180 95 10.5823163251865
-180 175 2.29053101919074
-180 178 9.89549542898803
-180 179 12.7017807748007
-180 180 98.5231062948412
-180 195 10.5586861724536
-180 229 9.74475354005693
-180 271 9.94431359566422
-180 273 6.50842706943993
-180 295 8.44371674931923
-180 312 12.7119971006365
-180 330 10.8479492038439
-180 334 7.79164994524195
-180 345 12.0738366822136
-180 346 9.15878071611981
-181 111 4.50422673898645
-181 112 4.05344936794433
-181 113 4.06891637286016
-181 173 4.29523054925352
-181 174 11.7158810273201
-181 181 27.1250912874572
-181 182 4.74035854433146
-181 194 3.46721823779695
-181 275 3.84235609269278
-182 86 1.1503767238575
-182 111 3.56417398264737
-182 113 6.54717197225376
-182 169 9.74185330388717
-182 174 7.34385128351062
-182 181 4.74035854433146
-182 182 34.7001958766301
-182 183 6.33578061669985
-182 190 4.74974349681785
-182 275 7.87698389093954
-183 86 6.61499635879291
-183 109 6.69701681482615
-183 111 3.31724914339484
-183 169 9.72495287924938
-183 182 6.33578061669985
-183 183 57.4938108423811
-183 184 5.16824239106719
-183 186 9.96450433581757
-183 190 7.16261472888652
-183 217 4.68854675892123
-183 241 13.8322810799323
-183 275 8.41444966108786
-183 317 4.32008149489586
-184 72 8.16979711719743
-184 109 3.53287170025802
-184 111 3.06745752564673
-184 183 5.16824239106719
-184 184 35.672562604427
-184 185 3.03052404545808
-184 186 10.5026633269212
-184 187 5.30227516353074
-184 197 5.73387819813631
-184 275 9.00113443842472
-185 108 6.39708382372096
-185 109 4.76917480834749
-185 148 7.20141575650698
-185 184 3.03052404545808
-185 185 46.5951078422402
-185 186 11.8777162841117
-185 187 5.71782217403642
-185 240 8.98589411695334
-185 284 3.10991477723958
-185 299 2.45504411259295
-185 322 9.52880834358599
-185 337 6.81926352080687
-186 72 8.79345923773742
-186 108 4.66282124322803
-186 109 10.0512230859689
-186 169 6.11034126948676
-186 183 9.96450433581757
-186 184 10.5026633269212
-186 185 11.8777162841117
-186 186 89.4347644770791
-186 187 9.6963085280256
-186 238 7.88512157060213
-186 240 11.181102830712
-186 241 10.9718624251669
-186 275 7.91013247926325
-186 322 7.76753171303047
-186 337 9.29495815005535
-186 344 7.4824002354914
-187 72 14.4091878934909
-187 110 6.03658927590785
-187 184 5.30227516353074
-187 185 5.71782217403642
-187 186 9.6963085280256
-187 187 46.4230807400099
-187 188 3.78134332959349
-187 197 3.94356573854557
-187 238 5.62761533796422
-187 284 3.05735259549558
-187 337 12.0625610734245
-188 72 11.699635113636
-188 110 3.86218442848385
-188 176 4.02124952522667
-188 187 3.78134332959349
-188 188 23.3992702272721
-188 189 4.090982149748
-188 197 3.81620115992551
-188 219 3.82730963429453
-189 72 12.6405622908362
-189 112 5.97724755902435
-189 174 11.6951684598536
-189 175 12.7121565125441
-189 176 6.4025528629266
-189 188 4.090982149748
-189 189 46.48166948892
-189 193 3.23083514600134
-189 194 3.35981835973362
-189 197 6.57486621545303
-189 279 3.03831467725912
-190 86 6.54406676993046
-190 113 4.14295526831967
-190 114 6.24886089825792
-190 168 4.00882143939187
-190 169 13.1848819553317
-190 182 4.74974349681785
-190 183 7.16261472888652
-190 190 50.8572281259928
-190 191 2.98427127108606
-190 241 10.7164834187528
-190 316 7.297545429944
-190 317 9.24559751227036
-191 86 2.09222795953165
-191 114 2.82259604635156
-191 115 3.12051485811777
-191 147 3.9616771423798
-191 190 2.98427127108606
-191 191 20.0361601500284
-191 192 2.37427610456093
-191 217 2.68059676800065
-191 317 10.0180800750142
-192 43 6.37148386079764
-192 114 4.07882126469947
-192 115 3.92820515757374
-192 132 3.14578858790402
-192 191 2.37427610456093
-192 192 32.7896884511706
-192 258 3.00360359983226
-192 316 9.55511277215327
-192 317 7.16083740627956
-192 329 9.566403922955
-193 93 3.09257011403693
-193 112 3.28222315248329
-193 175 9.40498879122763
-193 177 3.41707018028051
-193 189 3.23083514600134
-193 193 18.8099775824553
-193 279 2.70569545846383
-193 302 3.08158353118935
-194 111 3.49317191937137
-194 112 3.64450256995891
-194 174 8.21157539409118
-194 181 3.46721823779695
-194 189 3.35981835973362
-194 194 18.9703267102516
-194 197 3.62107682683011
-194 275 2.65812675759521
-195 93 4.85114793008625
-195 95 5.26807934893554
-195 178 3.66672882157689
-195 179 3.6109946859842
-195 180 10.5586861724536
-195 195 32.0468153210067
-195 196 5.46472148804973
-195 334 7.08859948999091
-195 346 7.5612650444329
-196 67 4.43100716958009
-196 93 3.28864332923669
-196 95 3.17979713887867
-196 195 5.46472148804973
-196 196 35.0748320482831
-196 213 4.90109924522922
-196 280 5.71362873532922
-196 311 8.99324155898426
-196 334 8.74455403753489
-196 346 7.89555536960192
-197 72 13.2619620659598
-197 111 2.74130570952541
-197 174 7.52708932218429
-197 184 5.73387819813631
-197 187 3.94356573854557
-197 188 3.81620115992551
-197 189 6.57486621545303
-197 194 3.62107682683011
-197 197 36.7015132825614
-197 275 7.83232468728212
-198 79 3.10028999058146
-198 170 4.07314648813133
-198 171 6.93337394678439
-198 172 2.61264700593178
-198 198 21.767742560966
-198 199 5.71386551161592
-198 200 2.43470960850262
-198 228 7.78358128990156
-199 79 3.81386126406498
-199 129 3.60327562841654
-199 171 13.4018687390584
-199 172 5.09968900861755
-199 198 5.71386551161592
-199 199 48.5357268662177
-199 201 7.4477116313142
-199 228 9.46790076598938
-199 252 3.74701143487298
-199 273 3.95372399743819
-199 312 16.5546823179384
-200 170 3.08574360266994
-200 172 2.35553961728895
-200 173 3.11838206957095
-200 198 2.43470960850262
-200 200 13.6235440292535
-200 228 6.81177201462674
-200 302 2.62916913122103
-201 79 3.40428664812324
-201 128 2.82691833287508
-201 129 3.68935093211986
-201 171 12.2274837691452
-201 199 7.4477116313142
-201 201 42.6421810034517
-201 202 5.64340404426111
-201 230 10.798351144283
-201 292 4.90465035259356
-201 312 13.0211146504623
-202 74 6.79559078946361
-202 128 3.04965356623316
-202 129 3.25833149204096
-202 201 5.64340404426111
-202 202 28.0545392884115
-202 203 3.43861698501414
-202 230 8.91859597763168
-202 312 10.9776160779726
-203 74 3.61618949608371
-203 96 4.44607021574858
-203 129 3.44056652355893
-203 202 3.43861698501414
-203 203 23.0056524707824
-203 204 4.42488874881406
-203 205 3.63932050156301
-203 312 11.5028262353912
-204 38 4.02538447330858
-204 96 5.45995751966327
-204 129 3.33503213988044
-204 179 7.56207916973814
-204 203 4.42488874881406
-204 204 30.8755043986406
-204 252 2.96892576248086
-204 312 13.0322502336805
-204 345 5.50473855039512
-205 74 6.68132263904293
-205 96 4.64040281764532
-205 203 3.63932050156301
-205 205 34.1132076223412
-205 206 3.84496998571448
-205 207 4.74721096632666
-205 312 10.3616285028961
-205 314 10.6503536719747
-205 345 6.60460234834862
-206 74 4.85256602159696
-206 104 4.80194572146659
-206 127 5.96921398033506
-206 205 3.84496998571448
-206 206 26.1295808968168
-206 207 3.41027870534484
-206 210 3.25060648235885
-206 314 13.0647904484084
-207 68 2.96813697813348
-207 96 2.56207098860688
-207 205 4.74721096632666
-207 206 3.41027870534484
-207 207 31.4197156974633
-207 208 4.26119727969406
-207 210 4.97257385232032
-207 314 8.73801229731783
-207 330 8.10258232714143
-207 345 7.36751015130946
-208 68 2.72865422059991
-208 95 4.46106361875804
-208 96 3.69561962284133
-208 179 3.73200584647473
-208 207 4.26119727969406
-208 208 27.2180944341575
-208 209 2.62079764231495
-208 212 2.53815818947067
-208 330 8.53004987691867
-208 345 8.25959535416389
-209 94 1.96690742362201
-209 95 3.75067209945928
-209 208 2.62079764231495
-209 209 15.7380299518317
-209 212 3.13225471565941
-209 213 2.10242557169007
-209 330 4.78577014140093
-209 346 5.24821733360089
-210 68 3.45864680200291
-210 87 10.8073488085903
-210 104 7.30260500930091
-210 206 3.25060648235885
-210 207 4.97257385232032
-210 210 38.9610682137919
-210 211 7.89248543598248
-210 314 10.0924798941152
-210 330 10.6648560360168
-211 68 3.60993079771867
-211 87 16.7716584336737
-211 104 6.46593896723233
-211 105 6.18647522399033
-211 107 5.57380309500578
-211 210 7.89248543598248
-211 211 40.7631784627847
-211 212 6.30262857141951
-211 330 8.34184716915421
-212 68 2.90293879177865
-212 87 8.64960270974841
-212 94 5.56967322400087
-212 107 5.09536048121545
-212 208 2.53815818947067
-212 209 3.13225471565941
-212 211 6.30262857141951
-212 212 30.5596465350392
-212 330 9.20364352347931
-212 346 2.44520959578653
-213 67 4.44996088115761
-213 94 3.87598966815988
-213 95 2.56255021343815
-213 196 4.90109924522922
-213 209 2.10242557169007
-213 213 24.1411168818071
-213 214 3.89611681301152
-213 311 8.52192006666824
-213 346 5.90161286335596
-214 67 4.33222067172021
-214 94 3.31871444572034
-214 213 3.89611681301152
-214 214 19.3028782098784
-214 215 4.15559254259733
-214 216 3.60023373682903
-214 311 9.65143910493921
-215 67 4.50889249577145
-215 69 4.90708551607554
-215 110 4.4217365835139
-215 214 4.15559254259733
-215 215 31.6764371657191
-215 216 4.40262195972049
-215 219 4.49105449045749
-215 284 4.78945357758291
-215 311 15.8382185828596
-216 69 5.06697843361728
-216 87 10.4173215415914
-216 94 6.66718778366297
-216 107 6.31296719207092
-216 214 3.60023373682903
-216 215 4.40262195972049
-216 216 38.1690674240755
-216 311 12.7715665199668
-216 337 8.01472396865429
-217 86 2.39305604155476
-217 109 6.39668318658156
-217 147 5.83671840767789
-217 183 4.68854675892123
-217 191 2.68059676800065
-217 217 36.6608400421548
-217 218 2.97582391414952
-217 241 8.64958872958719
-217 317 10.4089697626039
-217 322 10.9612764941555
-218 84 4.26778146050592
-218 109 2.76494798522307
-218 147 5.54759901603714
-218 155 4.14362031012104
-218 217 2.97582391414952
-218 218 33.4486892970181
-218 276 3.96157383219373
-218 299 5.45790732970841
-218 310 12.434629591394
-218 322 8.61915050619428
-219 67 5.16951616268104
-219 72 9.72179197563599
-219 110 7.21685200124191
-219 176 6.88958380411938
-219 188 3.82730963429453
-219 215 4.49105449045749
-219 219 38.5519039360847
-219 280 5.06310550194885
-219 311 15.4486423337478
-220 29 5.14448923368199
-220 42 2.96234575996194
-220 53 3.0080441547276
-220 116 4.6494032976539
-220 220 62.5207934368984
-220 221 5.86075310875086
-220 259 11.2110589417387
-220 277 3.16460135936716
-220 304 2.90235914588669
-220 315 13.2630334516975
-220 324 14.0522754825337
-220 329 9.66834081414525
-220 331 17.8944854052022
-221 29 4.13926496569429
-221 34 7.67173736572783
-221 53 5.0690651434233
-221 85 7.13647943392787
-221 220 5.86075310875086
-221 221 51.0110275943252
-221 313 11.0210138000398
-221 324 8.50652838062748
-221 331 14.6034179388125
-221 338 12.5082812544839
-222 134 2.77726164634473
-222 135 3.69753648007885
-222 136 4.08768764243894
-222 167 3.01903647834437
-222 222 16.0623103506447
-222 223 1.05464861252085
-222 308 2.48078810343783
-222 325 6.97650656280151
-223 75 2.91085863145515
-223 135 3.965507243976
-223 136 3.07715289050411
-223 222 1.05464861252085
-223 223 7.93101448795201
-223 285 0.888354353471895
-224 33 7.62837823597931
-224 34 5.42611987522028
-224 52 6.39472743178123
-224 53 2.65783591157811
-224 62 6.74746710412636
-224 224 39.0714772221286
-224 225 1.96871783899275
-224 303 10.4831752677049
-224 338 7.99725411450205
-224 342 9.30354005330781
-225 33 6.46471932594645
-225 51 6.66836828689805
-225 52 4.85138138231069
-225 53 2.13757447761929
-225 224 1.96871783899275
-225 225 38.2346648176031
-225 263 10.7708568061472
-225 304 4.99371515545876
-225 305 2.5011660382308
-225 338 7.98548861998393
-225 340 9.01000929481664
-226 56 4.09200937036907
-226 126 8.56541169949543
-226 133 6.92879585033756
-226 138 6.4222680574751
-226 142 6.82994860400593
-226 145 8.81011939494645
-226 226 44.5861422810168
-226 227 2.16696968954465
-226 320 11.4659417777541
-226 328 11.5977489775969
-227 78 6.53723341555245
-227 126 6.45109779713573
-227 133 6.92248446204656
-227 137 6.06584675064162
-227 226 2.16696968954465
-227 227 25.9766624253764
-227 235 2.19649079212744
-227 325 8.62487073101609
-228 170 9.20743671009869
-228 171 16.2841784029647
-228 172 11.0256749476344
-228 173 7.58376328751591
-228 174 15.2640778850468
-228 175 8.02973187319526
-228 177 6.32089786786639
-228 198 7.78358128990156
-228 199 9.46790076598938
-228 200 6.81177201462674
-228 228 103.238325210788
-228 229 9.19222605364934
-228 271 9.29732212073614
-228 295 8.05935022666051
-228 302 10.4221552788925
-228 312 12.2921510107006
-228 344 7.81526808070313
-229 87 9.5701877555419
-229 171 9.45855230742168
-229 180 9.74475354005693
-229 228 9.19222605364934
-229 229 94.7353437404858
-229 230 11.2599780268876
-229 238 5.32468054944859
-229 239 8.7845086972978
-229 271 6.33871605180258
-229 286 3.26912053940876
-229 295 10.7182043269548
-229 312 15.5666238306152
-229 314 13.5551641704489
-229 330 11.3815534238817
-229 344 9.10664797643295
-229 345 8.83209836087983
-230 39 13.0375948093247
-230 74 15.4472945829296
-230 127 17.0477361326729
-230 128 14.7158823806094
-230 131 11.1581896764297
-230 171 11.2136536821911
-230 201 10.798351144283
-230 202 8.91859597763168
-230 229 11.2599780268876
-230 230 128.711176885109
-230 239 12.3328845157365
-230 286 8.45892374384325
-230 288 10.9556204552563
-230 292 7.92123293502502
-230 312 17.9730926791734
-230 314 15.9987475371751
-230 344 5.82898704849495
-231 26 6.60351303184619
-231 42 4.41717179681239
-231 51 3.86321025812458
-231 92 10.6946237750171
-231 140 8.42282169023471
-231 231 41.5963461962135
-231 232 6.70694766259722
-231 263 12.9495109522405
-231 304 5.04046795026984
-231 324 3.69625217717775
-232 20 7.59159260218997
-232 26 9.47574187102034
-232 33 4.32874804883405
-232 50 5.00737570018183
-232 51 5.6624498840886
-232 55 15.7600239514844
-232 92 16.8107624530181
-232 231 6.70694766259722
-232 232 69.5447401028142
-232 263 9.6229480977299
-232 305 2.79243944842805
-232 340 14.5290670085825
-232 343 6.02901342606624
-233 6 3.8120159671127
-233 89 4.23607544777072
-233 90 4.12666481759435
-233 92 3.91823089230897
-233 120 4.53896429281058
-233 130 0.28164623296626
-233 233 21.7309435003779
-233 234 2.71116541010925
-233 278 8.97165218989401
-234 6 5.15807321069384
-234 90 5.45543339599991
-234 141 4.224273625053
-234 142 7.91282967271111
-234 145 4.52851350157075
-234 233 2.71116541010925
-234 234 34.2956751770324
-234 278 5.42457394784589
-234 328 7.49899001561729
-234 336 8.52965998594749
-235 78 7.76188693054932
-235 135 7.03028843858184
-235 137 9.02512319020422
-235 227 2.19649079212744
-235 235 22.4948313710633
-235 236 2.02063645482238
-235 237 5.70782125030979
-236 75 1.59033547921496
-236 135 4.44673942086305
-236 137 6.62261617884938
-236 235 2.02063645482238
-236 236 18.9882432211024
-236 237 5.72739717635242
-236 274 3.02674573486579
-236 285 5.04789438668567
-237 78 2.80532214306199
-237 135 7.97697516475199
-237 137 5.91111952402551
-237 235 5.70782125030979
-237 236 5.72739717635242
-237 237 19.9952232391487
-237 285 1.28116995248687
-237 308 0.583029647734538
-238 72 9.91638647983625
-238 87 8.07346250954404
-238 186 7.88512157060213
-238 187 5.62761533796422
-238 229 5.32468054944859
-238 238 54.4427870622441
-238 239 6.09118430062895
-238 240 5.06374276187285
-238 295 7.88097947000713
-238 311 7.54833481003205
-238 337 10.8582120869256
-238 344 7.39446071650433
-239 39 12.1114019634348
-239 66 8.33007533430367
-239 87 13.3583067626322
-239 104 15.7528617375516
-239 105 13.2894535739348
-239 127 17.6211439814161
-239 229 8.7845086972978
-239 230 12.3328845157365
-239 238 6.09118430062895
-239 239 110.963253352688
-239 240 8.72376888866916
-239 282 14.2097419716884
-239 314 14.8838588868432
-239 319 10.9356494780895
-239 337 2.74891479266176
-239 344 7.27112514414379
-240 39 5.07954110312761
-240 66 17.9408707747115
-240 87 5.94822619346144
-240 108 11.9284318978296
-240 148 10.5478476106826
-240 185 8.98589411695334
-240 186 11.181102830712
-240 238 5.06374276187285
-240 239 8.72376888866916
-240 240 90.9181996911773
-240 241 8.1049979488142
-240 319 9.82816249629526
-240 322 14.2710371793164
-240 337 12.6683738713022
-240 344 6.10530186301774
-241 39 7.20349482973682
-241 86 5.92097892235994
-241 109 9.45714960472595
-241 169 9.68331655819117
-241 183 13.8322810799323
-241 186 10.9718624251669
-241 190 10.7164834187528
-241 217 8.64958872958719
-241 240 8.1049979488142
-241 241 110.914485116429
-241 269 11.3041723464439
-241 286 4.91537886957645
-241 316 14.9237552199522
-241 317 14.4036443078769
-241 319 12.9895966350314
-241 322 17.108690212069
-241 344 6.18633656642656
-242 26 5.22352511762527
-242 30 3.8816607448399
-242 55 10.4470105017171
-242 119 7.39799704143347
-242 242 35.942892574968
-242 243 5.86160024927577
-242 244 2.22767095662787
-242 245 2.96740554651854
-242 318 10.1236816541918
-242 343 5.78378705022229
-243 26 4.08325775125859
-243 45 2.75741688528121
-243 50 5.77029689061641
-243 55 8.75108273590699
-243 242 5.86160024927577
-243 243 25.8472074109747
-243 245 2.70681064989433
-243 318 8.84034595422876
-244 26 4.99929198518843
-244 90 5.51755700438717
-244 92 8.24122958110862
-244 119 2.26882356653491
-244 141 4.36786145336933
-244 242 2.22767095662787
-244 244 25.3567258015847
-244 336 4.02218079293206
-244 343 6.39047336222869
-245 30 4.18491310550933
-245 45 2.9024575206597
-245 242 2.96740554651854
-245 243 2.70681064989433
-245 245 21.9404218876226
-245 246 5.44108724364268
-245 318 9.60761088857624
-245 321 5.10034787663305
-246 0 7.85726235253867
-246 17 3.81815177070849
-246 30 3.33927269121903
-246 45 3.55435844110573
-246 245 5.44108724364268
-246 246 37.5782627506534
-246 247 4.76331353362827
-246 289 3.54717429310416
-246 318 11.6317069133992
-246 321 12.4150668866339
-247 0 5.27264901900405
-247 44 1.4942542426401
-247 45 3.14000408604497
-247 246 4.76331353362827
-247 247 16.825306210098
-247 248 3.64933957142075
-247 318 6.91839886240892
-248 0 4.6686130297093
-248 44 5.49107235272103
-248 45 2.96349440062244
-248 49 3.51880708951375
-248 50 3.5047568383438
-248 247 3.64933957142075
-248 248 22.301829039691
-248 318 9.65666027720538
-249 8 4.69826184710261
-249 22 7.21403509433489
-249 52 5.85211178343262
-249 249 31.7641000006238
-249 256 2.81590312254441
-249 266 3.5941972068682
-249 267 4.75160562737538
-249 268 6.43218252583392
-249 339 12.2878527934437
-250 40 8.01885814114102
-250 70 9.29426564745738
-250 73 12.3151744179468
-250 125 7.60611983505073
-250 250 78.5528526563097
-250 251 9.70375003509297
-250 269 7.92939448783433
-250 270 8.35710692251493
-250 281 7.20073069413069
-250 294 6.96816866211944
-250 315 11.747736100595
-250 324 8.77612343471651
-250 329 7.52297209975208
-250 331 12.3888785061126
-251 70 7.53053475643435
-251 73 7.4982419544144
-251 92 2.70935879873139
-251 140 14.3320791469557
-251 250 9.70375003509297
-251 251 72.7212686507948
-251 263 11.0813973170864
-251 278 6.79985059031426
-251 281 6.74608455970271
-251 315 12.4883370584201
-251 324 12.9916041393077
-251 328 9.55326858964423
-251 336 7.64739603008807
-252 38 2.95550160596442
-252 129 3.06672180465915
-252 199 3.74701143487298
-252 204 2.96892576248086
-252 252 16.3705069352799
-252 273 3.6323463273025
-252 312 8.18525346763996
-253 21 3.17466851794404
-253 46 3.10162528484453
-253 47 3.54586811865254
-253 253 16.5703601983954
-253 254 3.60937195436984
-253 255 3.13882632258446
-253 323 8.2851800991977
-254 8 3.06585816297328
-254 21 4.45672340022537
-254 31 7.06240799925622
-254 32 5.37286108623742
-254 47 5.19173400922677
-254 253 3.60937195436984
-254 254 33.9153737343138
-254 256 2.46415272642008
-254 323 9.1206730544994
-254 339 10.5292782082623
-255 12 4.47372647521484
-255 46 3.21835062147384
-255 47 4.39420217632546
-255 48 1.6408183714929
-255 253 3.13882632258446
-255 255 15.2251055955986
-255 323 5.9717344263064
-256 8 2.55750732489276
-256 47 2.65091861943826
-256 249 2.81590312254441
-256 254 2.46415272642008
-256 256 13.3139442628596
-256 267 2.82546246956411
-256 339 6.6569721314298
-257 43 5.55934820161733
-257 116 2.81436287016156
-257 138 6.42083900055934
-257 139 5.29722009011325
-257 257 36.0365131293227
-257 258 3.28517028670869
-257 315 10.2797896060235
-257 320 8.09567766793589
-257 328 9.08000685650691
-257 329 3.22235511435754
-258 43 2.99645707612704
-258 115 2.57338943369797
-258 116 4.19573488702134
-258 192 3.00360359983226
-258 257 3.28517028670869
-258 258 20.6602400664438
-258 259 2.61540116907864
-258 315 3.39348730623252
-258 329 8.92711634096724
-259 29 4.79765059772958
-259 115 2.55115862699197
-259 116 4.3754261190164
-259 220 11.2110589417387
-259 258 2.61540116907864
-259 259 39.8024186315967
-259 260 5.46140049121722
-259 315 5.75475800275554
-259 329 12.7082675299805
-259 331 10.2285064688865
-260 29 3.43537616861325
-260 115 3.83077140020114
-260 147 7.66271700426281
-260 259 5.46140049121722
-260 260 38.6198686494557
-260 269 6.57011942537647
-260 276 3.55894016130593
-260 317 3.71947424682822
-260 329 10.3082112011937
-260 331 13.3827928751847
-261 15 4.10303582555378
-261 27 3.43216949600334
-261 32 6.25986736939913
-261 36 3.08030169727487
-261 54 9.04672863840493
-261 91 12.9077821287667
-261 117 5.92637263027851
-261 261 47.2761136945615
-261 291 6.0421973132538
-261 306 3.53430795038263
-261 335 6.42483927029561
-261 342 10.1565682222289
-262 76 1.61178383392586
-262 136 1.45290110280411
-262 144 3.77121091478251
-262 146 3.61232818366076
-262 167 5.22411201758663
-262 262 10.4482240351733
-263 33 11.0900000363404
-263 42 4.7805247339541
-263 51 11.9371942998336
-263 64 10.6040386717988
-263 70 7.70671632021801
-263 73 9.70249579684884
-263 91 9.86889433591251
-263 92 13.1768533518013
-263 140 13.2467614221054
-263 225 10.7708568061472
-263 231 12.9495109522405
-263 232 9.6229480977299
-263 251 11.0813973170864
-263 263 131.252931921982
-263 278 9.21286786382995
-263 303 6.59798119783833
-263 304 9.82291158740529
-263 324 12.18054930019
-263 336 4.64477969225771
-263 338 9.52961916329388
-263 340 8.35249693614158
-264 22 8.55725417487786
-264 25 3.68977301844515
-264 41 3.63335629198808
-264 48 3.34912055462914
-264 264 17.1145083497557
-264 265 3.356785438741
-264 266 3.08547304595236
-265 20 5.66861890467766
-265 22 11.5591443370708
-265 25 7.49200800988663
-265 49 4.69019857290673
-265 264 3.356785438741
-265 265 32.4986858199551
-265 266 3.08871599541327
-265 268 3.13767000249417
-265 327 9.75488746874239
-266 22 9.59076702330409
-266 48 2.90785382102262
-266 249 3.5941972068682
-266 264 3.08547304595236
-266 265 3.08871599541327
-266 266 19.1815340466082
-266 267 2.92795158347879
-266 268 3.57734239387294
-267 22 7.7047959406338
-267 47 4.43791140594868
-267 48 2.89218591063703
-267 249 4.75160562737538
-267 256 2.82546246956411
-267 266 2.92795158347879
-267 267 21.0605168203958
-267 339 6.05086229295593
-268 20 7.23756290757175
-268 22 11.8209053642106
-268 52 8.25104482116554
-268 54 11.0218711364747
-268 249 6.43218252583392
-268 265 3.13767000249417
-268 266 3.57734239387294
-268 268 46.3340400327406
-268 305 2.57529635156756
-268 339 9.24087537378802
-268 340 6.20630917213176
-269 40 10.4121544836561
-269 125 10.5406854658889
-269 147 15.926533165622
-269 150 4.38744749534349
-269 241 11.3041723464439
-269 250 7.92939448783433
-269 260 6.57011942537647
-269 269 110.72024326813
-269 270 8.12898712745273
-269 310 11.4365515569001
-269 316 10.6749364890014
-269 317 11.3931622217141
-269 319 13.6434464468966
-269 322 16.0280449064912
-269 329 11.2985474934128
-269 331 16.4061817901611
-270 40 6.16100250864211
-270 66 12.2243009649984
-270 70 13.5508349836576
-270 71 13.2987028187757
-270 101 6.7096840376604
-270 103 19.7590530754645
-270 121 6.40296758276811
-270 123 4.48070996243808
-270 125 8.00954115102144
-270 150 16.0004595338307
-270 250 8.35710692251493
-270 269 8.12898712745273
-270 270 110.29826081441
-270 281 7.96373757662169
-270 294 12.0948704781677
-270 319 12.9918213584788
-270 322 9.31361113912166
-271 93 8.49177982903803
-271 172 7.17445358059264
-271 175 6.92233987492352
-271 177 10.0197681236525
-271 178 6.16857755874162
-271 180 9.94431359566422
-271 228 9.29732212073614
-271 229 6.33871605180258
-271 271 59.2459153286159
-271 273 7.54542481339311
-271 295 4.24912634071688
-271 312 9.53299160283064
-271 334 3.18405950083196
-272 98 3.98875795193454
-272 152 5.46457901358106
-272 160 3.0994271762453
-272 161 2.12464272880759
-272 272 19.2585669648058
-272 300 3.25673393262681
-272 333 4.58116009423728
-272 341 6.37254954977607
-273 38 5.43400038244862
-273 172 5.3010198177789
-273 177 3.10641506289099
-273 178 3.9664007977297
-273 180 6.50842706943993
-273 199 3.95372399743819
-273 252 3.6323463273025
-273 271 7.54542481339311
-273 273 33.8662983865598
-273 312 11.3516893114178
-274 75 4.9461149641575
-274 135 10.5783163274109
-274 136 5.74154646463621
-274 137 7.42977056915726
-274 146 2.46621619941246
-274 236 3.02674573486579
-274 274 32.1425565233783
-274 285 6.6726147909691
-274 325 7.35250973445824
-275 72 9.43535213772136
-275 111 9.5867179297808
-275 169 7.34609281350233
-275 174 10.0221664675491
-275 181 3.84235609269278
-275 182 7.87698389093954
-275 183 8.41444966108786
-275 184 9.00113443842472
-275 186 7.91013247926325
-275 194 2.65812675759521
-275 197 7.83232468728212
-275 275 59.5127303736546
-275 344 5.34325820464276
-276 29 3.34175756144565
-276 84 3.96266808073053
-276 85 5.5926110144294
-276 147 6.5718595109712
-276 158 3.54925331814863
-276 218 3.96157383219373
-276 260 3.55894016130593
-276 276 35.6419636604583
-276 310 12.6141273315756
-276 331 10.3101546798868
-277 42 4.32944950633231
-277 116 4.73097769698642
-277 139 3.24762202685984
-277 140 3.87653463750117
-277 220 3.16460135936716
-277 277 22.0418425293538
-277 315 7.85631990530973
-277 324 5.85725866167403
-278 6 13.5871392150643
-278 64 8.70259068009494
-278 70 12.1287340817913
-278 89 16.7870275078791
-278 90 5.81519943141598
-278 91 18.0985628243192
-278 92 11.0986254958369
-278 120 10.1973222710413
-278 130 4.62302090515992
-278 233 8.97165218989401
-278 234 5.42457394784589
-278 251 6.79985059031426
-278 263 9.21286786382995
-278 278 108.5214906263
-278 281 6.30324639396351
-278 293 8.81401903955116
-278 328 7.15239588625657
-278 336 9.06540761519221
-279 93 3.62221344306604
-279 175 6.88550056693011
-279 176 4.02674242471294
-279 189 3.03831467725912
-279 193 2.70569545846383
-279 279 18.282768185646
-279 280 2.25588352589289
-279 334 4.88980218214406
-280 67 4.97389062705228
-280 93 2.75678405542892
-280 176 5.36988305569567
-280 196 5.71362873532922
-280 219 5.06310550194885
-280 279 2.25588352589289
-280 280 30.0455357877995
-280 311 11.1336441934445
-280 334 7.80148398690687
-281 6 10.2437021270169
-281 70 9.35321308436415
-281 71 4.05417691925428
-281 123 12.5897292982135
-281 124 6.12660651452348
-281 125 13.4017651234903
-281 145 12.4778183769359
-281 250 7.20073069413069
-281 251 6.74608455970271
-281 270 7.96373757662169
-281 278 6.30324639396351
-281 281 80.0850040783575
-281 315 6.57603757931861
-281 320 4.58370884640402
-281 328 12.5069490235964
-282 39 10.1839367037965
-282 66 3.93778286828627
-282 71 4.12422228694374
-282 103 4.45854924935774
-282 104 6.27696642372157
-282 105 8.11809967099856
-282 127 10.8331791634335
-282 239 14.2097419716884
-282 282 54.9543806174508
-282 288 8.00260814579864
-282 319 12.2864844421513
-283 97 3.39178410062863
-283 151 3.17473139019345
-283 152 2.72531280794014
-283 153 0.543438890913548
-283 283 12.2341938171375
-283 300 2.94236551837532
-283 333 5.57365801765522
-284 69 5.1748560755941
-284 108 2.39198368272734
-284 110 4.81591794942673
-284 185 3.10991477723958
-284 187 3.05735259549558
-284 215 4.78945357758291
-284 284 26.2013776045208
-284 311 7.65135252403749
-284 337 8.3112352246775
-285 75 3.78831558136758
-285 135 6.55537206139508
-285 137 4.51263226240642
-285 223 0.888354353471895
-285 236 5.04789438668567
-285 237 1.28116995248687
-285 274 6.6726147909691
-285 285 19.1642355925217
-286 39 7.30757083491639
-286 114 8.1093721603589
-286 131 10.5383374932361
-286 168 7.02975688646584
-286 169 8.73694899646379
-286 171 9.23429297719949
-286 229 3.26912053940876
-286 230 8.45892374384325
-286 241 4.91537886957645
-286 286 57.3493680890383
-286 292 3.6906078894277
-286 316 7.89294103676575
-286 344 6.84080070589507
-287 77 3.92691339803705
-287 132 2.57899191248317
-287 133 5.0736213627081
-287 134 2.78017356393267
-287 143 6.42154284826198
-287 287 18.0010695214903
-287 320 6.22036119681247
-288 39 16.2406581291583
-288 71 8.17572298941397
-288 77 7.73886180993359
-288 124 6.33646089104657
-288 125 15.8011208192031
-288 127 7.9505947142302
-288 128 8.06612596322177
-288 131 14.3433248322448
-288 230 10.9556204552563
-288 282 8.00260814579864
-288 288 96.6927615764606
-288 316 17.7787287230655
-288 319 13.60158739569
-288 320 10.0477274964281
-289 2 3.25502032904085
-289 17 2.90380090441645
-289 18 3.94217558719193
-289 30 3.99712714143134
-289 246 3.54717429310416
-289 289 17.6452982551847
-289 321 8.82264912759236
-290 1 3.12224151723649
-290 36 3.51677891586161
-290 64 9.57376776821079
-290 88 5.0751469116107
-290 91 6.86374510386666
-290 102 5.53573996751866
-290 290 36.3313871913662
-290 291 5.62137127385674
-290 293 10.9334116459052
-290 306 4.25487768298239
-291 1 3.56661252057549
-291 32 3.2199186353407
-291 36 3.05389706430654
-291 60 6.41553144284117
-291 64 12.6905547997722
-291 91 12.1155032173734
-291 261 6.0421973132538
-291 290 5.62137127385674
-291 291 43.4518231632781
-291 342 12.4521484775971
-292 79 3.2357029282502
-292 128 2.80558374217838
-292 131 4.83991148146619
-292 168 3.45341398256348
-292 171 9.41893927550589
-292 201 4.90465035259356
-292 230 7.92123293502502
-292 286 3.6906078894277
-292 292 26.8466950580069
-293 36 3.86391472846527
-293 37 13.5128210501658
-293 64 10.584813015958
-293 70 10.4210850585324
-293 88 15.3476746603182
-293 89 11.8596786239604
-293 91 14.1622658002414
-293 100 7.72390688922149
-293 101 9.09343054295831
-293 102 13.5779090635139
-293 122 11.5444852538659
-293 278 8.81401903955116
-293 290 10.9334116459052
-293 293 109.184047126765
-293 294 7.69112352246102
-293 306 6.84916083092837
-293 307 7.79637096410051
-294 37 9.22101995535921
-294 40 10.6666716337746
-294 64 10.2383769333753
-294 65 12.2063627791225
-294 70 14.5558704334137
-294 73 9.49075616417553
-294 100 7.34989851929515
-294 101 14.63352422984
-294 103 10.985210804778
-294 150 7.54509697726686
-294 250 6.96816866211944
-294 270 12.0948704781677
-294 293 7.69112352246102
-294 294 95.2403760549474
-294 313 9.21361298927207
-295 72 9.00335864243183
-295 87 9.00072944416245
-295 174 7.50623699232551
-295 175 7.02903773752608
-295 180 8.44371674931923
-295 228 8.05935022666051
-295 229 10.7182043269548
-295 238 7.88097947000713
-295 271 4.24912634071688
-295 295 77.7245539079735
-295 311 11.7806371653146
-295 330 10.6976382591721
-295 334 6.88563892065171
-295 344 8.69743670280273
-295 346 6.63473988391483
-296 98 3.86267910407658
-296 163 3.64802387820468
-296 164 4.23247296045699
-296 166 4.01781773458509
-296 296 15.7609936773233
-296 297 1.10792585060373
-296 341 6.77257098805793
-297 98 5.57222454859408
-297 152 4.41882060895245
-297 164 4.46204937357477
-297 296 1.10792585060373
-297 297 17.7617399650544
-297 298 3.30864543393313
-297 300 1.25612536704693
-297 341 6.51681876487655
-298 99 3.4742989202096
-298 152 3.34348683522071
-298 164 3.43945751892202
-298 297 3.30864543393313
-298 298 13.5658887082855
-298 341 6.78294435414273
-299 35 4.13208498223611
-299 109 2.42239824622738
-299 148 5.54631667894965
-299 149 4.0840690995967
-299 155 3.72090322077262
-299 185 2.45504411259295
-299 218 5.45790732970841
-299 299 30.8273636022008
-299 310 10.8136122524863
-299 322 7.60870948073107
-300 97 2.91674358895462
-300 98 2.43588580639597
-300 152 4.81637915905769
-300 160 2.82853498439357
-300 162 3.10643291435683
-300 272 3.25673393262681
-300 283 2.94236551837532
-300 297 1.25612536704693
-300 300 21.1233154648118
-300 333 8.12577192600992
-301 82 2.27610972008774
-301 161 1.80749115776836
-301 163 2.76223100263301
-301 301 6.84583188048911
-301 341 3.42291594024455
-302 112 5.33678078349704
-302 172 2.95569289546737
-302 173 5.27086619674449
-302 174 8.28922742452711
-302 175 9.07014426984109
-302 177 4.83449057477886
-302 193 3.08158353118935
-302 200 2.62916913122103
-302 228 10.4221552788925
-302 302 34.5934067241059
-303 33 8.43565453970796
-303 34 7.92642684069481
-303 60 10.0908389865638
-303 62 17.4843995119015
-303 63 11.2942252023085
-303 64 11.6054717866947
-303 73 5.09155032848032
-303 91 3.51691552037983
-303 224 10.4831752677049
-303 263 6.59798119783833
-303 303 83.6264695217223
-303 313 12.9569828650832
-303 338 8.65585246350543
-303 342 11.3002297717202
-304 42 2.69411244518052
-304 51 4.09398606209803
-304 53 3.74942112215672
-304 220 2.90235914588669
-304 225 4.99371515545876
-304 231 5.04046795026984
-304 263 9.82291158740529
-304 304 32.5328903094851
-304 324 9.30057843118671
-304 338 6.20178356458504
-305 20 3.04223547380461
-305 51 2.36577543925016
-305 52 2.46089092517163
-305 225 2.5011660382308
-305 232 2.79243944842805
-305 268 2.57529635156756
-305 305 15.7378036764528
-305 340 7.8689018382264
-306 27 3.42922767491242
-306 36 4.85812429202025
-306 88 4.93030922087058
-306 91 8.96278350482087
-306 261 3.53430795038263
-306 290 4.25487768298239
-306 293 6.84916083092837
-306 306 26.4353223756065
-306 307 2.83419240649225
-307 27 3.23417118707057
-307 88 5.25279831882279
-307 89 6.35678337387733
-307 91 10.4090030974069
-307 120 4.21318950917793
-307 122 4.43474978236376
-307 293 7.79637096410051
-307 306 2.83419240649225
-307 307 29.6875057595414
-308 78 3.15490041877203
-308 133 0.474501326201459
-308 134 2.73360633678323
-308 135 2.90208218542663
-308 222 2.48078810343783
-308 237 0.583029647734538
-308 308 11.2713770444197
-308 325 4.57815754827386
-309 4 13.8155129614298
-309 7 4.62710580923352
-309 11 6.86597612629242
-309 13 6.94309371209691
-309 15 16.9460009518378
-309 19 7.10885740168547
-309 22 14.2195406224501
-309 24 10.0021203364781
-309 54 16.0565206493721
-309 55 14.4365635748742
-309 117 10.866959528998
-309 309 113.085716031561
-309 323 11.2787680432581
-309 326 7.49598141327053
-309 327 10.3179954175851
-309 332 9.28078673460223
-309 339 9.36679076387693
-310 35 3.93090466711676
-310 40 15.8079985925687
-310 65 16.4354116741548
-310 81 4.76595943004621
-310 84 9.8946747948156
-310 85 9.73514089744575
-310 147 14.2695739258681
-310 148 7.74255925196311
-310 149 15.3770591135367
-310 150 12.3827281112265
-310 155 13.9375484498638
-310 157 6.56320083949018
-310 158 12.9862188047994
-310 159 17.486842075547
-310 162 4.89236340725244
-310 218 12.434629591394
-310 269 11.4365515569001
-310 276 12.6141273315756
-310 299 10.8136122524863
-310 310 177.688441810528
-310 313 19.0582391371312
-310 322 16.1429535808097
-310 331 17.8243652297998
-311 67 13.9327440039813
-311 69 7.50307942717926
-311 72 17.2017453703996
-311 87 15.823359417921
-311 94 11.5210124880593
-311 110 12.3308026981058
-311 176 9.89128849041644
-311 196 8.99324155898426
-311 213 8.52192006666824
-311 214 9.65143910493921
-311 215 15.8382185828596
-311 216 12.7715665199668
-311 219 15.4486423337478
-311 238 7.54833481003205
-311 280 11.1336441934445
-311 284 7.65135252403749
-311 295 11.7806371653146
-311 311 160.36618300515
-311 330 9.12959890927822
-311 334 11.2368344576054
-311 337 14.0710114099913
-311 346 8.56880097479281
-312 38 10.1254868389075
-312 74 13.6664925135715
-312 96 6.4797788767472
-312 129 10.1966392603379
-312 171 15.6210667776571
-312 172 7.55876429014079
-312 179 9.73305511200773
-312 180 12.7119971006365
-312 199 16.5546823179384
-312 201 13.0211146504623
-312 202 10.9776160779726
-312 203 11.5028262353912
-312 204 13.0322502336805
-312 205 10.3616285028961
-312 228 12.2921510107006
-312 229 15.5666238306152
-312 230 17.9730926791734
-312 252 8.18525346763996
-312 271 9.53299160283064
-312 273 11.3516893114178
-312 312 175.326303940348
-312 314 13.3977405612161
-312 345 13.1465146585805
-313 34 17.4552959893807
-313 40 12.5490336588627
-313 61 9.7081372090358
-313 62 11.5321284455326
-313 63 18.9713935603007
-313 64 12.1424390416595
-313 65 22.5459511888355
-313 73 11.6263285269973
-313 80 9.24177120413795
-313 81 14.9861818206845
-313 85 15.2118746016061
-313 158 5.90269208790375
-313 159 10.601007826699
-313 221 11.0210138000398
-313 294 9.21361298927207
-313 303 12.9569828650832
-313 310 19.0582391371312
-313 313 167.782877836509
-313 331 15.684550893526
-313 338 11.2656819080746
-314 74 15.2302529588863
-314 87 11.8369942149032
-314 104 14.2187131972632
-314 127 17.6123262995439
-314 205 10.6503536719747
-314 206 13.0647904484084
-314 207 8.73801229731783
-314 210 10.0924798941152
-314 229 13.5551641704489
-314 230 15.9987475371751
-314 239 14.8838588868432
-314 312 13.3977405612161
-314 314 119.882385417683
-314 330 10.8370087389704
-314 345 9.70713524945801
-315 42 5.14524229149521
-315 43 6.56611619123775
-315 116 10.9315996950367
-315 125 11.4282401774041
-315 139 9.15222834653078
-315 140 15.7917860237936
-315 220 13.2630334516975
-315 250 11.747736100595
-315 251 12.4883370584201
-315 257 10.2797896060235
-315 258 3.39348730623252
-315 259 5.75475800275554
-315 277 7.85631990530973
-315 281 6.57603757931861
-315 315 140.686216781156
-315 320 13.6465106740049
-315 324 17.7443238493793
-315 328 16.1591130349418
-315 329 21.4190451361487
-315 331 11.6856207414086
-316 39 10.6041784899271
-316 43 11.2092366907608
-316 77 8.57963145767381
-316 114 11.8233527533383
-316 125 17.5570745806316
-316 131 12.8506125143998
-316 132 11.8205030783334
-316 169 6.13486236528808
-316 190 7.297545429944
-316 192 9.55511277215327
-316 241 14.9237552199522
-316 269 10.6749364890014
-316 286 7.89294103676575
-316 288 17.7787287230655
-316 316 146.699222866498
-316 317 12.4610110271973
-316 319 16.0359148769318
-316 320 18.2452847112651
-316 329 14.6041520831175
-317 86 7.86064949492207
-317 114 5.60520100103102
-317 115 7.11059433228005
-317 125 4.53168274411372
-317 147 13.0242641724277
-317 183 4.32008149489586
-317 190 9.24559751227036
-317 191 10.0180800750142
-317 192 7.16083740627956
-317 217 10.4089697626039
-317 241 14.4036443078769
-317 260 3.71947424682822
-317 269 11.3931622217141
-317 316 12.4610110271973
-317 317 95.3475629539011
-317 322 9.54649779878429
-317 329 12.2115968326124
-318 0 17.1764039366704
-318 10 11.3301417266515
-318 24 17.1912246740264
-318 30 8.0259611162174
-318 44 12.8339334874668
-318 45 9.27526972700938
-318 49 8.0633019885452
-318 50 11.8394477981532
-318 55 15.8789955921498
-318 119 15.2982867944114
-318 242 10.1236816541918
-318 243 8.84034595422876
-318 245 9.60761088857624
-318 246 11.6317069133992
-318 247 6.91839886240892
-318 248 9.65666027720538
-318 318 144.617622297927
-318 321 15.4757061711296
-318 326 3.3608343041327
-318 327 14.3985215803163
-319 39 11.4278353518325
-319 66 15.857981513763
-319 71 10.3320460418754
-319 103 7.71452957470835
-319 105 6.82658290883839
-319 125 13.7931265821377
-319 239 10.9356494780895
-319 240 9.82816249629526
-319 241 12.9895966350314
-319 269 13.6434464468966
-319 270 12.9918213584788
-319 282 12.2864844421513
-319 288 13.60158739569
-319 316 16.0359148769318
-319 319 120.964153901573
-319 322 13.1814657496391
-320 43 17.6345773034624
-320 77 10.3907753827828
-320 124 10.498368161267
-320 125 21.0260155767998
-320 126 10.6710908875934
-320 132 9.20134304922767
-320 133 16.1458292307082
-320 138 11.1951765490389
-320 143 13.7326532162052
-320 145 15.1236504904882
-320 226 11.4659417777541
-320 257 8.09567766793589
-320 281 4.58370884640402
-320 287 6.22036119681247
-320 288 10.0477274964281
-320 315 13.6465106740049
-320 316 18.2452847112651
-320 320 159.335093834183
-320 328 16.5146235304566
-320 329 14.5633250026395
-321 0 12.5325670585626
-321 2 11.5756455129908
-321 9 12.6458045994324
-321 14 11.0290911529705
-321 17 8.48919522835256
-321 18 12.7520183662033
-321 24 16.4075989144768
-321 30 11.0582326227158
-321 57 8.43666264113161
-321 58 8.83113935837805
-321 119 11.1082333117757
-321 245 5.10034787663305
-321 246 12.4150668866339
-321 289 8.82264912759236
-321 318 15.4757061711296
-321 321 111.119972552653
-322 40 6.34851440320446
-322 66 13.6017745037195
-322 109 10.5194481241749
-322 147 11.5127267733532
-322 148 12.1018221490576
-322 150 13.6598676908913
-322 185 9.52880834358599
-322 186 7.76753171303047
-322 217 10.9612764941555
-322 218 8.61915050619428
-322 240 14.2710371793164
-322 241 17.108690212069
-322 269 16.0280449064912
-322 270 9.31361113912166
-322 299 7.60870948073107
-322 310 16.1429535808097
-322 317 9.54649779878429
-322 319 13.1814657496391
-322 322 138.54795383222
-323 5 3.86409471126234
-323 11 8.44863021324848
-323 12 15.3876990128581
-323 15 8.11544510952301
-323 21 7.84604665692398
-323 22 12.6118364412957
-323 23 8.8054895563144
-323 31 9.24696378185273
-323 41 4.99301435040304
-323 46 8.03473557813545
-323 47 11.9316402929018
-323 253 8.2851800991977
-323 254 9.1206730544994
-323 255 5.9717344263064
-323 309 11.2787680432581
-323 323 103.89195250973
-323 332 9.4523654477679
-323 339 12.443611988847
-324 42 9.67313443550096
-324 53 5.90804928034879
-324 73 9.19679188251412
-324 116 4.04674291048473
-324 140 10.9612187417026
-324 220 14.0522754825337
-324 221 8.50652838062748
-324 231 3.69625217717775
-324 250 8.77612343471651
-324 251 12.9916041393077
-324 263 12.18054930019
-324 277 5.85725866167403
-324 304 9.30057843118671
-324 315 17.7443238493793
-324 324 106.502949759432
-324 331 15.282882688393
-324 338 11.5801108434112
-325 78 12.0676408486605
-325 126 12.0427007565108
-325 133 13.5204475903318
-325 134 10.9306483459144
-325 135 11.8574730884579
-325 136 8.43220730033278
-325 137 12.3553112775393
-325 143 12.9120406657393
-325 144 3.52541995070762
-325 146 10.5779421773758
-325 167 9.14982167469065
-325 222 6.97650656280151
-325 227 8.62487073101609
-325 274 7.35250973445824
-325 308 4.57815754827386
-325 325 96.6024655018738
-326 7 7.31388204531474
-326 10 7.70783639609937
-326 14 2.129542654726
-326 16 6.20042590280744
-326 19 5.85386877256004
-326 22 10.5866983962057
-326 24 7.09083679484579
-326 25 12.1429984356847
-326 41 5.50141007176695
-326 309 7.49598141327053
-326 318 3.3608343041327
-326 326 60.759927929481
-326 327 10.3143846837436
-326 332 5.44119202306396
-327 10 9.39181216475845
-327 20 15.6427790438627
-327 22 16.6601648933597
-327 24 6.77625288618304
-327 25 17.3934300788516
-327 44 11.7149983970663
-327 49 14.3086493830324
-327 50 9.64278821511096
-327 54 12.5382832224245
-327 55 16.013365254427
-327 265 9.75488746874239
-327 309 10.3179954175851
-327 318 14.3985215803163
-327 326 10.3143846837436
-327 327 120.242734708748
-327 340 5.49578937365807
-328 6 8.9747465534896
-328 56 13.6068196160134
-328 125 5.77441731750494
-328 138 11.6519620101376
-328 139 11.6762976600883
-328 140 12.6807411784207
-328 142 12.1033232760458
-328 145 13.923477643471
-328 226 11.5977489775969
-328 234 7.49899001561729
-328 251 9.55326858964423
-328 257 9.08000685650691
-328 278 7.15239588625657
-328 281 12.5069490235964
-328 315 16.1591130349418
-328 320 16.5146235304566
-328 328 128.950396344593
-328 336 12.9707133471019
-329 43 15.4984505158443
-329 115 7.03958104691228
-329 116 3.40607990853046
-329 125 13.4312074287609
-329 147 4.46224562268925
-329 192 9.566403922955
-329 220 9.66834081414525
-329 250 7.52297209975208
-329 257 3.22235511435754
-329 258 8.92711634096724
-329 259 12.7082675299805
-329 260 10.3082112011937
-329 269 11.2985474934128
-329 315 21.4190451361487
-329 316 14.6041520831175
-329 317 12.2115968326124
-329 320 14.5633250026395
-329 329 130.693342891489
-329 331 16.1821162432137
-330 68 7.83415379511681
-330 87 19.32116616588
-330 94 6.58766630739739
-330 95 8.93364932947612
-330 180 10.8479492038439
-330 207 8.10258232714143
-330 208 8.53004987691867
-330 209 4.78577014140093
-330 210 10.6648560360168
-330 211 8.34184716915421
-330 212 9.20364352347931
-330 229 11.3815534238817
-330 295 10.6976382591721
-330 311 9.12959890927822
-330 314 10.8370087389704
-330 330 111.584146334083
-330 345 12.3437490859181
-330 346 9.83333720807825
-331 29 12.2786731248695
-331 40 11.7387983282065
-331 73 11.0447317776898
-331 85 11.6969416562578
-331 147 11.809650141479
-331 220 17.8944854052022
-331 221 14.6034179388125
-331 250 12.3888785061126
-331 259 10.2285064688865
-331 260 13.3827928751847
-331 269 16.4061817901611
-331 276 10.3101546798868
-331 310 17.8243652297998
-331 313 15.684550893526
-331 315 11.6856207414086
-331 324 15.282882688393
-331 329 16.1821162432137
-331 331 160.306705589507
-331 338 10.0173098951699
-332 3 2.73057293577149
-332 5 5.54011199739678
-332 7 3.20699467164245
-332 11 7.23945931768732
-332 12 3.72965712434755
-332 13 8.2891015788769
-332 15 6.86029064962345
-332 16 6.77631820631166
-332 19 7.49930342884365
-332 23 5.10841029083282
-332 28 6.02050098773802
-332 31 5.48896473779918
-332 309 9.28078673460223
-332 323 9.4523654477679
-332 326 5.44119202306396
-332 332 61.7760200882036
-333 35 10.9806902039821
-333 83 7.46038498262984
-333 97 6.95773372124914
-333 99 7.00302153763641
-333 149 3.98463080944166
-333 151 11.2977762717052
-333 152 9.50957615221429
-333 153 6.99003483915531
-333 154 7.11377981799079
-333 156 7.49484425297535
-333 157 11.587969042999
-333 159 5.47188953458793
-333 160 7.68028941198866
-333 162 13.3787140568996
-333 272 4.58116009423728
-333 283 5.57365801765522
-333 300 8.12577192600992
-333 333 95.8202378284394
-333 341 8.53843206930136
-334 72 7.81302354329331
-334 93 9.90882774158485
-334 175 9.09081126225188
-334 176 9.67015183219949
-334 180 7.79164994524195
-334 195 7.08859948999091
-334 196 8.74455403753489
-334 271 3.18405950083196
-334 279 4.88980218214406
-334 280 7.80148398690687
-334 295 6.88563892065171
-334 311 11.2368344576054
-334 334 68.5016256782778
-334 346 8.64700161717944
-335 27 6.21787488553831
-335 33 8.82988648948717
-335 54 7.52999653609288
-335 55 9.3521656411034
-335 91 14.5482321950768
-335 92 7.98449218297208
-335 117 9.1861559377009
-335 261 6.42483927029561
-335 335 63.6555343924756
-335 340 5.75641457034014
-335 342 8.52383864015534
-335 343 11.1294052399507
-336 6 4.13878597057071
-336 56 8.73413152964337
-336 90 7.43742516097345
-336 92 10.70628623574
-336 140 12.3256136776323
-336 141 11.3414458975915
-336 142 8.23860966268758
-336 234 8.52965998594749
-336 244 4.02218079293206
-336 251 7.64739603008807
-336 263 4.64477969225771
-336 278 9.06540761519221
-336 328 12.9707133471019
-336 336 73.2016237322389
-337 66 12.2767019069738
-337 69 9.34924341303987
-337 72 9.87885898036393
-337 87 18.2523935101938
-337 106 11.5912565325613
-337 107 8.694651565431
-337 108 12.7730811875158
-337 110 8.97339747683355
-337 185 6.81926352080687
-337 186 9.29495815005535
-337 187 12.0625610734245
-337 216 8.01472396865429
-337 238 10.8582120869256
-337 239 2.74891479266176
-337 240 12.6683738713022
-337 284 8.3112352246775
-337 311 14.0710114099913
-337 337 117.759225780942
-338 33 7.23113387502795
-338 34 9.97499093740575
-338 52 3.6664824689898
-338 53 8.46827802243053
-338 73 8.27311578499729
-338 221 12.5082812544839
-338 224 7.99725411450205
-338 225 7.98548861998393
-338 263 9.52961916329388
-338 303 8.65585246350543
-338 304 6.20178356458504
-338 313 11.2656819080746
-338 324 11.5801108434112
-338 331 10.0173098951699
-338 338 82.2369219439075
-339 8 13.4819280789172
-339 15 12.3234611198662
-339 22 13.3553664375777
-339 31 8.41498251991728
-339 32 13.3763958354919
-339 47 8.35186774026289
-339 52 10.4573116225187
-339 54 16.3823640879194
-339 249 12.2878527934437
-339 254 10.5292782082623
-339 256 6.6569721314298
-339 267 6.05086229295593
-339 268 9.24087537378802
-339 309 9.36679076387693
-339 323 12.443611988847
-339 339 111.86168420184
-339 342 5.07260530768554
-340 20 11.1372062995162
-340 33 11.3376251945185
-340 51 8.31792498695818
-340 52 9.0993946440491
-340 54 12.4456956308042
-340 55 15.5184775180814
-340 92 7.11934411027876
-340 225 9.01000929481664
-340 232 14.5290670085825
-340 263 8.35249693614158
-340 268 6.20630917213176
-340 305 7.8689018382264
-340 327 5.49578937365807
-340 335 5.75641457034014
-340 340 92.3985464666685
-340 342 6.40316312189933
-341 82 9.72720565584428
-341 83 9.61289810543377
-341 98 10.795978252502
-341 99 10.7771287243069
-341 152 9.72274439731248
-341 156 5.23280186804278
-341 160 4.91381337083245
-341 161 9.73046543527338
-341 163 10.5995414219208
-341 164 10.2432019678692
-341 165 6.40975817741519
-341 166 10.7693855365462
-341 272 6.37254954977607
-341 296 6.77257098805793
-341 297 6.51681876487655
-341 298 6.78294435414273
-341 301 3.42291594024455
-341 333 8.53843206930136
-341 341 97.9607697197991
-342 8 13.884059010187
-342 32 11.2573596184752
-342 33 13.8926768184505
-342 52 13.5830013948946
-342 54 12.3005123683838
-342 60 13.229135221821
-342 62 12.5495411239752
-342 64 12.4831243986576
-342 91 13.1331319889259
-342 224 9.30354005330781
-342 261 10.1565682222289
-342 291 12.4521484775971
-342 303 11.3002297717202
-342 335 8.52383864015534
-342 339 5.07260530768554
-342 340 6.40316312189933
-342 342 119.68309035891
-343 26 8.97508832349916
-343 27 10.7975835720223
-343 55 13.6302118061672
-343 90 5.99401770659346
-343 91 10.1515250048132
-343 92 14.8888080162329
-343 117 7.12572082358306
-343 118 8.18154555287971
-343 119 9.05906034750793
-343 120 10.8360327621511
-343 232 6.02901342606624
-343 242 5.78378705022229
-343 244 6.39047336222869
-343 335 11.1294052399507
-343 343 85.9815153292787
-344 39 7.30244005853995
-344 72 7.84387209626571
-344 169 8.02584670286984
-344 171 8.13104607396117
-344 174 11.7119462136938
-344 186 7.4824002354914
-344 228 7.81526808070313
-344 229 9.10664797643295
-344 230 5.82898704849495
-344 238 7.39446071650433
-344 239 7.27112514414379
-344 240 6.10530186301774
-344 241 6.18633656642656
-344 275 5.34325820464276
-344 286 6.84080070589507
-344 295 8.69743670280273
-344 344 80.7247829265906
-345 95 8.1592007628085
-345 96 9.34990974440134
-345 179 13.5878154731137
-345 180 12.0738366822136
-345 204 5.50473855039512
-345 205 6.60460234834862
-345 207 7.36751015130946
-345 208 8.25959535416389
-345 229 8.83209836087983
-345 312 13.1465146585805
-345 314 9.70713524945801
-345 330 12.3437490859181
-345 345 76.6244709477271
-346 94 5.25184663978908
-346 95 9.33270438564935
-346 180 9.15878071611981
-346 195 7.5612650444329
-346 196 7.89555536960192
-346 209 5.24821733360089
-346 212 2.44520959578653
-346 213 5.90161286335596
-346 295 6.63473988391483
-346 311 8.56880097479281
-346 330 9.83333720807825
-346 334 8.64700161717944
-346 346 57.6527144215345
diff --git a/examples/turtle/model/turtle-volumetric.obj b/examples/turtle/model/turtle-volumetric.obj
deleted file mode 100644
index 0ed531fb5dd1b04e0be35182a528c417bfe82763..0000000000000000000000000000000000000000
--- a/examples/turtle/model/turtle-volumetric.obj
+++ /dev/null
@@ -1,1231 +0,0 @@
-# Generated by the objfile class
-# Number of vertices: 347
-# Number of texture coordinates: 0
-# Number of normals: 347
-# Number of faces: 530
-# Number of groups: 1
-v -1.07586 -0.298468 -0.0952629
-v 1.14687 2.36868 0.807906
-v -1.52689 0.224941 0.861942
-v 0.732394 -0.049599 1.14023
-v -0.1488 0.686607 0.819301
-v 0.912779 -0.256313 0.99358
-v -1.10349 3.06502 0.576252
-v 0.15638 -0.293391 0.183926
-v 1.36659 1.43824 -0.13223
-v -0.799163 0.521933 1.1138
-v -0.355696 -0.298592 0.0488136
-v 0.571505 -0.294818 0.450653
-v 1.31072 -0.296557 0.241448
-v 0.394326 0.322222 1.11038
-v -0.746481 -0.297871 0.429331
-v 0.440164 1.02524 0.801095
-v -0.14617 -0.280441 0.439071
-v -1.81422 -0.0750205 0.280667
-v -1.04928 0.89413 0.870746
-v -0.12279 0.240891 0.844794
-v 0.125006 0.870122 -0.78
-v 1.45414 0.622585 0.750565
-v 0.756256 0.311578 -0.290053
-v 1.18055 0.216538 0.986384
-v -0.515629 0.407269 0.232091
-v 0.125703 -0.202337 -0.741357
-v -1.11196 1.57291 -0.426935
-v -0.367027 1.758 1.00277
-v 0.503061 -0.294756 0.873759
-v 0.507497 3.56401 -1.4807
-v -1.59062 0.784677 0.165546
-v 0.913687 0.721534 0.920112
-v 1.09097 1.41801 0.60306
-v 0.20791 2.02511 -0.130844
-v 1.20072 2.5498 -0.897716
-v 1.69334 4.41724 0.179419
-v 0.494875 1.85214 1.06304
-v 0.107596 3.20899 1.33689
-v -1.06293 6.78103 1.16639
-v -0.276029 4.89078 0.271272
-v 0.459803 3.4464 -0.0427515
-v 0.673682 -0.293966 -0.164778
-v -0.434716 2.45908 -1.36411
-v -1.15797 4.06886 -1.05429
-v -0.591842 -0.299203 -0.64272
-v -1.28957 0.458428 -0.772923
-v 1.67519 0.0415655 0.632792
-v 1.6179 0.42484 -0.156465
-v 1.32152 -0.159821 -0.642702
-v -0.341747 0.328057 -0.914196
-v -0.64823 1.05657 -0.817318
-v -0.0744169 1.67541 -1.11973
-v 0.697426 1.54283 -0.704255
-v 0.441446 2.36633 -1.30899
-v 0.408346 1.12899 -0.0323087
-v -0.435776 1.25449 -0.0146378
-v -1.5454 2.82687 -0.686709
-v -1.31269 -0.29861 0.419141
-v -1.04037 -0.199519 1.01643
-v -0.455365 -0.263005 0.944565
-v 1.28037 1.97772 0.269872
-v 1.80439 3.06274 0.0390899
-v 1.34987 2.07278 -0.414955
-v 1.3765 2.62142 0.253604
-v 0.568583 2.61017 0.296107
-v 1.14476 3.37464 0.353
-v 0.587054 4.29607 0.6882
-v 1.35338 6.50258 1.40841
-v -0.126695 5.37232 2.17496
-v 1.40335 5.19735 1.20622
-v -0.217956 3.04303 0.398081
-v -0.579337 3.98678 0.776539
-v 0.595838 5.96376 0.379372
-v 0.203774 2.77773 -0.391999
-v -1.35539 5.18403 1.30037
-v -3.54422 3.99078 0.524449
-v -1.85505 4.03707 0.976603
-v -1.32323 4.49114 0.0799895
-v -2.58433 4.09541 -0.614208
-v -1.24307 5.9875 -0.10553
-v 1.76552 2.49515 -0.218198
-v 1.60719 3.03163 -0.620133
-v 3.14934 3.94535 -0.764735
-v 2.31237 3.63023 -0.499494
-v 1.42334 4.06088 -1.04605
-v 1.17093 3.25434 -1.22246
-v 0.238231 5.12013 -1.226
-v 0.269575 5.23574 1.15982
-v -0.0309661 2.37536 1.49917
-v -0.790926 2.48979 1.1255
-v -1.28648 2.12657 0.362332
-v 0.00704813 2.20828 0.554514
-v -0.572671 2.21946 -0.0149638
-v 0.226389 7.09199 0.995003
-v 0.82307 5.69728 1.90788
-v -0.0084901 6.45645 1.89441
-v -0.96959 5.97987 1.90767
-v 2.03767 3.67953 0.921421
-v 3.14679 3.55378 0.402342
-v 2.63428 4.48055 -0.113861
-v 1.12298 2.90838 0.661755
-v 0.671868 3.43441 0.917117
-v 0.670375 2.74933 1.21992
-v 0.0939719 3.86272 1.01997
-v -0.34123 4.57417 1.50179
-v 0.360087 4.44592 1.36191
-v 1.00546 4.62175 1.14847
-v 0.73081 4.96677 1.75709
-v 1.30749 4.90086 0.553709
-v 0.977946 4.89264 -0.473883
-v 1.53141 5.82613 0.501039
-v 0.725754 5.91829 -0.689851
-v 0.220441 6.97838 -0.252861
-v -0.393296 5.98479 -0.855123
-v -0.732116 5.00047 -0.868277
-v -0.24516 4.28284 -1.30236
-v -0.604397 3.40365 -1.48687
-v -0.108955 1.2919 0.681819
-v -0.675808 1.32421 0.781786
-v -1.25862 1.36284 0.389829
-v -0.934451 1.88111 0.801962
-v -0.394786 3.48502 1.09487
-v -0.423778 2.9265 1.31788
-v -0.820867 3.49748 0.753418
-v -1.14599 3.97727 0.446719
-v -0.414753 3.82266 -0.153308
-v -2.02475 3.44735 0.0733384
-v -1.03131 4.61188 0.883202
-v -1.39118 5.25075 0.454226
-v -1.65567 6.04088 0.954185
-v -0.823861 3.04858 1.01213
-v -1.07815 5.13436 -0.216906
-v -1.19853 4.57797 -0.597847
-v -1.81205 4.11002 -0.644368
-v -2.18052 4.5108 0.0207978
-v -3.04069 4.46798 0.0140465
-v -2.69258 4.07617 0.717801
-v -2.93907 3.4676 0.0811426
-v -1.55032 3.50269 -0.894623
-v -1.13201 3.03917 -1.2088
-v -1.02803 2.36078 -0.934456
-v -1.4533 2.18969 -0.398152
-v -1.81449 2.71656 0.0180836
-v -1.72303 4.16081 0.396577
-v -1.82974 3.63029 0.577341
-v -1.39293 3.47704 0.141023
-v -2.44399 3.53783 0.525648
-v 0.592981 4.34654 -1.14796
-v 1.04753 4.39462 0.241342
-v 1.40834 3.92911 0.431606
-v 0.865492 3.82527 0.547644
-v 1.82372 4.25814 0.778357
-v 2.65369 4.19268 0.67403
-v 2.22445 4.52228 0.426497
-v 2.12825 4.49427 -0.0715017
-v 1.60993 4.38855 -0.488155
-v 2.33653 4.21203 -0.537879
-v 1.876 3.93252 -0.628192
-v 1.56727 3.53171 -0.890687
-v 1.81198 3.46349 -0.35362
-v 2.23225 3.41322 0.00410722
-v 2.87877 3.40225 -0.202694
-v 1.77663 3.55788 0.27041
-v 3.59717 3.66961 -0.207039
-v 3.42488 4.38624 0.283982
-v 2.84158 4.27903 -0.533384
-v 3.42268 4.28122 -0.32452
-v -2.20092 4.49816 0.678009
-v -0.837391 5.58386 -0.612758
-v -0.0756911 5.43705 -0.319604
-v -0.829069 6.16651 -0.493318
-v -0.591949 5.76133 0.0289914
-v -0.820351 6.76251 0.247334
-v -0.312763 6.65181 -0.604176
-v 0.147715 6.22609 -0.175256
-v 0.174948 6.56239 0.482201
-v 1.01506 6.6707 0.635009
-v -0.235345 7.17067 0.42761
-v -0.437659 6.99736 1.04524
-v -0.577442 6.67534 1.61608
-v -0.121577 6.31682 1.13209
-v 0.197194 6.29489 -0.843984
-v 0.1451 5.68082 -0.960706
-v 0.605683 5.31345 -0.764086
-v 1.05498 5.49671 -0.385742
-v 1.21782 5.10422 0.0141696
-v 0.522703 5.27832 0.0423676
-v 1.32653 5.62349 0.0538311
-v 1.19699 6.27024 0.136566
-v 0.764346 6.75213 0.0347831
-v -0.241136 5.26661 -0.962378
-v -0.189962 4.80525 -1.22131
-v -0.570957 4.54303 -1.01461
-v 0.374281 7.13454 0.326104
-v 0.640643 6.47454 -0.4823
-v -0.0370509 6.90739 1.50068
-v 0.65681 6.66152 1.62488
-v 0.992671 6.14266 -0.3151
-v -0.988197 6.42373 -0.088252
-v -1.33786 6.40509 0.486226
-v -0.625492 6.75098 -0.219249
-v -1.48501 5.83471 0.425995
-v -1.60474 5.53511 0.836151
-v -1.51201 5.6961 1.33993
-v -1.36648 6.30355 1.45497
-v -1.15483 5.495 1.7131
-v -0.888781 4.89781 1.66152
-v -0.635916 5.51645 1.95523
-v -0.235138 5.93637 2.04905
-v 0.342771 6.0083 2.02001
-v -0.443039 5.01921 1.92928
-v 0.16744 4.77883 2.06898
-v 0.371534 5.48871 2.06837
-v 0.795684 6.21754 1.86358
-v 1.32877 5.96916 1.7027
-v 1.6274 5.98498 1.12475
-v 1.20944 5.454 1.63187
-v 0.636513 4.85218 -1.01313
-v 1.03038 4.50401 -0.857149
-v 1.49684 6.36273 0.709162
-v 0.0756571 2.9637 -1.5585
-v 0.678383 2.92988 -1.4151
-v -2.65669 4.50277 0.390513
-v -3.22174 4.48971 0.582401
-v 0.938403 2.04979 -0.810638
-v 0.460563 1.91114 -1.01969
-v -1.80496 3.27365 -0.430695
-v -2.39441 3.51338 -0.440479
-v -0.352511 6.32985 0.207694
-v -0.186527 5.75837 0.649305
-v -0.733367 5.36887 0.562194
-v -0.579507 1.98158 -1.03757
-v -0.452448 1.54179 -0.781688
-v -1.1503 2.33952 0.801462
-v -1.29517 2.62788 0.414235
-v -3.10918 3.57011 -0.634668
-v -3.59172 3.68133 -0.118607
-v -3.39111 4.20945 -0.540464
-v 0.426754 5.43655 0.614868
-v 0.00104384 5.09795 0.699452
-v 0.256901 4.8841 0.283504
-v 0.00166609 4.90778 -0.200775
-v -1.43796 1.23698 -0.123313
-v -1.20871 1.02465 -0.624764
-v -1.36178 1.74352 0.00626368
-v -1.48266 0.680417 -0.337806
-v -1.548 0.0439621 -0.359122
-v -1.18023 -0.162153 -0.831051
-v -0.851352 0.227733 -0.930284
-v 1.19305 0.874774 -0.719222
-v -0.0507249 3.36775 -0.426507
-v -0.465963 2.84705 -0.267258
-v -1.43749 6.49256 0.994053
-v 1.79785 0.489699 0.351739
-v 1.46809 0.993504 0.269343
-v 1.78714 0.0215539 0.125179
-v 1.45743 0.928724 -0.259584
-v -1.04707 3.57109 -1.22888
-v -0.681886 3.96498 -1.28781
-v -0.223795 3.78481 -1.5075
-v 0.227559 4.05551 -1.40945
-v 0.351888 1.52142 0.695056
-v -2.3412 3.76906 1.0218
-v -0.18037 2.38824 -0.358475
-v 0.733015 -0.260462 -0.771812
-v 0.474904 0.328489 -0.91693
-v 1.00924 0.188998 -0.936223
-v 1.387 0.356678 -0.618714
-v 0.665413 0.838099 -0.859167
-v 0.185781 3.94858 -0.320652
-v 0.0224707 3.69608 0.11188
-v -0.285072 6.49082 0.684253
-v 2.6677 3.50318 0.246386
-v -0.801177 6.83292 0.742706
-v -2.9566 3.64389 0.576316
-v 0.370738 5.73144 -0.218122
-v 0.956027 3.78022 -1.20675
-v -0.680768 2.90478 -1.40722
-v -0.602671 2.69869 0.235155
-v 0.637052 6.98713 0.726978
-v 0.942652 6.82606 1.17005
-v -0.475259 3.31128 0.00722333
-v -0.265889 4.22865 1.11732
-v 2.27494 4.14871 1.06259
-v 1.51453 5.3514 0.719228
-v -3.57058 4.24278 0.00739403
-v -0.462107 5.26681 -0.0260898
-v -1.64335 4.49649 -0.33417
-v -0.89404 4.44535 0.394617
-v -1.77669 0.404991 0.453457
-v 0.773882 2.26545 1.12929
-v 0.935937 1.90161 0.806798
-v -1.31372 5.50672 0.0273056
-v 0.17064 2.73888 0.683378
-v 0.343164 3.08665 0.3256
-v 0.196093 6.08997 0.663695
-v 3.59977 3.8609 0.313374
-v 3.24314 4.02703 0.798848
-v 3.07104 4.46528 0.629037
-v 1.25147 4.57896 -0.184531
-v 2.54816 3.66823 0.746262
-v 3.25834 3.47545 -0.557774
-v -0.244043 7.04475 -0.0626802
-v 0.502134 2.43381 -0.172765
-v -0.0278282 2.16841 -1.25178
-v 0.290654 1.33372 -0.961138
-v 0.03457 1.91341 1.28347
-v -0.436272 2.16979 1.28337
-v -2.60609 4.43979 -0.243887
-v 0.229401 0.450106 0.220919
-v 1.04816 3.84521 -0.387524
-v 0.791516 5.89562 1.13592
-v -0.836907 6.00957 0.978146
-v 0.897235 2.99636 -0.410371
-v -0.451154 5.33738 1.26612
-v -0.601638 3.24834 -0.818229
-v -0.578151 4.60793 -0.34853
-v -0.00243409 4.46322 -0.68875
-v -0.771646 0.578007 -0.363872
-v -0.0543602 4.28881 0.479683
-v -1.23275 3.9575 -0.327673
-v -1.15641 0.316702 0.283457
-v 0.642708 4.39264 -0.255037
-v 0.973872 0.351539 0.352861
-v -0.0203215 2.73611 -0.979132
-v -2.40525 3.93502 0.0526037
-v -0.112997 0.110945 0.15126
-v -0.140535 0.577138 -0.28122
-v -1.0303 3.1819 -0.322734
-v -0.260995 3.86858 -0.794116
-v 0.0651594 5.68657 1.55245
-v 0.523719 3.42167 -0.805447
-v 0.632371 0.163738 0.685345
-v 2.31079 3.98133 0.181166
-v 0.432023 6.5082 1.01801
-v 0.0637229 1.55166 0.18464
-v -1.03566 2.5845 -0.183957
-v 0.94376 5.26526 0.842225
-v 0.462282 2.40835 -0.740862
-v 0.93734 0.854126 0.0285721
-v 0.190753 1.51422 -0.414261
-v 2.816 3.94755 -0.069268
-v 0.704933 1.68718 0.145248
-v -0.789848 1.69653 0.200367
-v 0.0467344 5.50205 0.216918
-v -0.548586 5.88587 1.49006
-v 0.435146 6.06982 1.49594
-vn -0.217974 -0.970405 -0.103928
-vn 0.816813 -0.0800759 0.571317
-vn -0.665218 -0.0736499 0.743008
-vn -0.0183541 -0.252622 0.967391
-vn 0.0111791 0.233962 0.972181
-vn 0.243977 -0.75915 0.603462
-vn -0.787984 0.196938 0.583349
-vn -0.0100807 -0.999733 -0.0208156
-vn 0.967295 0.156815 -0.199371
-vn 0.0171424 0.238189 0.971068
-vn 0.030767 -0.999439 -0.0131972
-vn 0.00605777 -0.999866 0.0152167
-vn 0.322577 -0.945621 0.0417807
-vn -0.197265 0.0457923 0.97928
-vn -0.00636935 -0.998114 0.0610547
-vn -0.0285483 0.317168 0.94794
-vn 0.0384968 -0.967078 0.251551
-vn -0.873467 -0.486408 0.0214949
-vn -0.379638 0.430463 0.818888
-vn 0.0194332 -0.218304 0.975687
-vn -0.084549 0.000145926 -0.996419
-vn 0.619127 0.319224 0.71748
-vn 0 0 0
-vn 0.413212 0.0290042 0.910173
-vn 0 0 0
-vn -0.0305573 -0.766019 -0.642091
-vn -0.709546 -0.0428443 -0.703355
-vn -0.176976 -0.567715 0.803977
-vn -0.21367 -0.850274 0.481018
-vn 0.266162 0.151133 -0.952007
-vn -0.936387 0.345376 0.0624105
-vn 0.1812 0.331439 0.925913
-vn 0.614763 0.164991 0.771261
-vn 0 0 0
-vn 0.629928 -0.351463 -0.692578
-vn -0.183407 0.935926 0.300674
-vn 0.352954 -0.607497 0.711597
-vn 0.125402 0.332739 0.934644
-vn -0.483933 0.845481 0.225768
-vn 0 0 0
-vn 0 0 0
-vn 0.0125017 -0.997805 -0.0650289
-vn -0.256329 -0.370339 -0.892829
-vn -0.444592 0.374329 -0.813766
-vn 0.0260722 -0.867282 -0.497134
-vn -0.62424 0.132835 -0.769856
-vn 0.760515 -0.385763 0.522307
-vn 0.904073 0.167551 -0.393166
-vn 0.572159 -0.622588 -0.533871
-vn 0.0224099 -0.0889441 -0.995784
-vn -0.162408 0.199658 -0.966313
-vn -0.131298 -0.433295 -0.891637
-vn 0.560764 -0.0773016 -0.824359
-vn 0.278938 -0.407771 -0.869435
-vn 0 0 0
-vn 0 0 0
-vn -0.780789 -0.294861 -0.550841
-vn -0.323656 -0.930304 0.172575
-vn -0.250712 -0.633264 0.732203
-vn 0.305095 -0.693656 0.652502
-vn 0.91768 -0.17381 0.357287
-vn 0.895143 -0.169941 0.412115
-vn 0.786972 -0.425617 -0.446682
-vn 0.791998 -0.0479588 0.608637
-vn 0 0 0
-vn 0.558804 0.00537905 0.829283
-vn 0.631846 -0.480164 0.608451
-vn 0.679236 0.598466 0.424825
-vn -0.122412 -0.0295705 0.992039
-vn 0.84144 -0.409691 0.352324
-vn 0 0 0
-vn -0.564489 -0.0593819 0.823302
-vn 0 0 0
-vn 0 0 0
-vn -0.817262 -0.464128 0.341567
-vn -0.761442 -0.140478 0.632828
-vn 0.588248 0.13307 0.797657
-vn -0.599185 0.604732 0.524668
-vn 0.0275649 0.30409 -0.952244
-vn -0.825192 0.210214 -0.524278
-vn 0.937799 -0.345353 -0.0355491
-vn 0.84091 -0.202752 -0.501758
-vn 0.217894 0.0625542 -0.973966
-vn 0.00732963 -0.576499 -0.817065
-vn 0.456384 0.30577 -0.835595
-vn 0.570425 -0.067493 -0.818572
-vn 0.204328 0.190186 -0.960249
-vn 0 0 0
-vn -0.0128337 -0.133613 0.99095
-vn -0.586522 -0.0534266 0.808169
-vn -0.890522 -0.179591 0.417993
-vn 0 0 0
-vn 0 0 0
-vn 0.120617 0.959403 0.254944
-vn 0.383773 -0.0700574 0.920766
-vn -0.0387893 0.486384 0.872883
-vn -0.436329 0.180763 0.881443
-vn -0.243229 -0.629615 0.737851
-vn 0.322257 -0.851146 0.414369
-vn 0.0506402 0.979491 -0.19502
-vn 0.806984 0.244566 0.537554
-vn 0.586123 0.363542 0.724084
-vn 0.535995 0.146652 0.831386
-vn 0.175293 0.016592 0.984376
-vn -0.274197 -0.785932 0.55419
-vn 0.289784 -0.782246 0.551468
-vn 0.597711 -0.706109 0.379673
-vn 0.530329 -0.468755 0.706413
-vn 0.898694 -0.434986 -0.0560028
-vn 0.814462 0.275569 -0.510601
-vn 0.943259 0.0395724 -0.329691
-vn 0.58204 0.167031 -0.795821
-vn 0.220947 0.823074 -0.523194
-vn -0.35247 0.213286 -0.911194
-vn -0.58367 0.249186 -0.77281
-vn -0.176314 0.346206 -0.921442
-vn -0.302935 0.064025 -0.950858
-vn 0.0879075 -0.0909382 0.991969
-vn -0.190247 -0.0284138 0.981325
-vn -0.82963 0.153564 0.536779
-vn -0.613295 -0.272642 0.741307
-vn -0.371724 0.392882 0.841109
-vn -0.381278 0.199342 0.902712
-vn -0.652155 0.24141 0.71862
-vn -0.328812 0.191957 0.924681
-vn 0 0 0
-vn -0.252456 -0.910137 0.328506
-vn -0.748179 -0.630005 0.208141
-vn -0.934737 -0.318813 -0.156926
-vn -0.984034 0.176477 0.0230858
-vn -0.671148 0.264358 0.692586
-vn -0.885961 0.0976303 -0.453367
-vn -0.607549 0.594141 -0.527145
-vn -0.379592 0.362668 -0.851106
-vn 0.0684471 0.992263 -0.103579
-vn -0.183135 0.966498 -0.17984
-vn -0.293162 0.143243 0.945271
-vn -0.194969 -0.974924 0.107289
-vn -0.704108 -0.0423152 -0.708831
-vn -0.570233 -0.185753 -0.800206
-vn -0.578314 -0.383288 -0.720169
-vn -0.848467 -0.402279 -0.343912
-vn -0.912224 -0.268919 0.30908
-vn 0.418012 0.62419 0.66004
-vn 0.382418 -0.701883 0.60093
-vn -0.330068 -0.314744 0.889939
-vn -0.113522 -0.886942 0.447712
-vn 0.335635 0.305707 -0.891007
-vn 0.717347 0.0888368 0.691029
-vn -0.213721 0.0618416 0.974935
-vn 0.51033 0.185427 0.83975
-vn -0.528816 0.535675 0.658335
-vn 0.152415 0.606204 0.780568
-vn 0.065591 0.961528 0.266762
-vn -0.0347809 0.976393 -0.213183
-vn 0.329122 0.837537 -0.436131
-vn 0.0605814 0.463482 -0.884033
-vn 0.435566 0.0113457 -0.900085
-vn 0.717288 -0.147994 -0.680879
-vn 0.69327 -0.585722 -0.419888
-vn -0.0507383 -0.998007 0.0375104
-vn 0.0549765 -0.978762 -0.197489
-vn 0.0689045 -0.784403 0.616413
-vn 0.885212 -0.43152 -0.173752
-vn 0.674124 0.725554 0.138309
-vn -0.0331651 0.678329 -0.73401
-vn 0.65198 0.639544 -0.407315
-vn 0.119305 0.85291 0.508242
-vn -0.681276 0.0899509 -0.726479
-vn 0 0 0
-vn -0.640168 0.330009 -0.693743
-vn 0 0 0
-vn -0.599912 0.77089 -0.214089
-vn -0.275974 0.604451 -0.747313
-vn 0 0 0
-vn 0 0 0
-vn 0.610345 0.763585 -0.210753
-vn -0.236061 0.97051 -0.0488346
-vn -0.270211 0.933296 0.236525
-vn -0.271404 0.676087 0.685016
-vn 0 0 0
-vn 0.175623 0.395545 -0.901499
-vn 0.136065 0.221154 -0.9657
-vn 0.641545 0.199802 -0.740607
-vn 0.788542 0.0158476 -0.614777
-vn 0.941316 -0.121184 -0.315021
-vn 0 0 0
-vn 0.891693 0.00861109 -0.452558
-vn 0.781089 0.448608 -0.434339
-vn 0.644551 0.684208 -0.341193
-vn -0.348774 0.297999 -0.888568
-vn -0.216906 0.210999 -0.953116
-vn -0.481218 0.283513 -0.829488
-vn 0.326483 0.935533 -0.134856
-vn 0.554 0.530728 -0.641414
-vn -0.0283842 0.836147 0.547771
-vn 0.248218 0.699813 0.669813
-vn 0.763474 0.330386 -0.554935
-vn -0.699321 0.543118 -0.46473
-vn -0.765428 0.561233 -0.314862
-vn -0.570735 0.686445 -0.450616
-vn -0.932905 0.058404 -0.355355
-vn -0.955766 -0.287433 -0.0623987
-vn -0.872653 -0.108181 0.476208
-vn -0.707022 0.402576 0.581423
-vn -0.623197 -0.205759 0.754512
-vn -0.496435 -0.583253 0.642937
-vn -0.367032 -0.123607 0.921959
-vn -0.122706 0.235143 0.964184
-vn 0.143875 0.207006 0.967703
-vn -0.387702 -0.44035 0.809802
-vn 0.114754 -0.576471 0.80902
-vn 0.292406 -0.0144904 0.956184
-vn 0.301347 0.337183 0.891907
-vn 0.678339 0.0491047 0.733106
-vn 0.981487 0.0587738 0.182288
-vn 0.666147 -0.303912 0.681091
-vn 0.55642 0.286114 -0.780087
-vn 0.529714 0.543803 -0.650908
-vn 0.822066 0.521201 -0.229253
-vn 0.0350355 -0.142987 -0.989104
-vn 0.373051 -0.201643 -0.905634
-vn -0.0626094 0.95363 0.294398
-vn -0.33191 0.734924 0.591374
-vn 0.561347 -0.427026 -0.708899
-vn 0.389587 -0.36814 -0.844212
-vn -0.769851 -0.386011 -0.508257
-vn -0.0544578 -0.773337 -0.631651
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn -0.412888 -0.444698 -0.794838
-vn -0.360515 -0.297639 -0.883991
-vn -0.807548 -0.103774 0.580601
-vn -0.775259 0.0732924 0.627376
-vn -0.25616 -0.603563 -0.755046
-vn -0.827908 -0.549763 -0.111034
-vn -0.542484 0.461752 -0.701781
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn -0.939003 0.280795 -0.198565
-vn -0.602596 0.316491 -0.732606
-vn -0.99881 -0.0487672 0.00115789
-vn -0.91198 0.172283 -0.372305
-vn -0.847193 -0.361037 -0.389765
-vn -0.40657 -0.591057 -0.696673
-vn -0.0623627 -0.0982155 -0.993209
-vn 0.582845 0.267635 -0.767244
-vn 0 0 0
-vn 0 0 0
-vn -0.771057 0.636427 0.0207683
-vn 0.948077 0.291762 0.126592
-vn 0.887223 0.429839 0.167555
-vn 0.902198 -0.408986 -0.136998
-vn 0.91772 0.246406 -0.311569
-vn -0.491073 0.142464 -0.85939
-vn -0.346595 0.34248 -0.873258
-vn -0.111406 0.226983 -0.967506
-vn 0.157649 0.39222 -0.906262
-vn 0.125094 -0.336334 0.933398
-vn -0.256846 -0.374386 0.890991
-vn 0 0 0
-vn 0.0407064 -0.799296 -0.599557
-vn -0.0506362 -0.0447226 -0.997715
-vn 0.283253 -0.11375 -0.952276
-vn 0.794112 0.0414597 -0.606355
-vn 0.106463 0.115717 -0.98756
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0.0263905 -0.959592 0.280156
-vn -0.496102 0.863043 -0.0950755
-vn -0.237891 -0.64963 0.722073
-vn 0 0 0
-vn 0.382996 0.244294 -0.890862
-vn -0.356729 -0.221364 -0.907603
-vn 0 0 0
-vn 0.482536 0.875835 -0.00848615
-vn 0.445574 0.873414 0.196498
-vn 0 0 0
-vn -0.31997 -0.600569 0.73276
-vn 0.142782 0.310123 0.939913
-vn 0.95793 -0.271414 -0.0933031
-vn -0.840051 0.535024 -0.0897986
-vn 0 0 0
-vn -0.267601 0.9098 -0.31726
-vn -0.816124 -0.197085 0.543231
-vn -0.933973 0.284587 0.216112
-vn 0.535183 -0.224539 0.814347
-vn 0.594583 -0.374138 0.711682
-vn -0.886279 -0.142916 -0.440549
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0.918181 -0.21318 0.333914
-vn 0.440526 -0.0287796 0.897279
-vn 0.0936545 0.819672 0.565125
-vn 0.716252 0.697614 -0.0178336
-vn 0.193846 -0.678579 0.708487
-vn 0.227673 -0.752708 -0.617735
-vn -0.255217 0.897149 -0.360537
-vn 0 0 0
-vn 0.00253991 -0.387101 -0.922034
-vn 0.115966 -0.23213 -0.965747
-vn 0.0399626 -0.602532 0.797093
-vn -0.418091 -0.326764 0.8476
-vn -0.0123857 0.909503 -0.415513
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-vn 0 0 0
-g Default
-f 11//11 15//15 1//1
-f 1//1 45//45 11//11
-f 15//15 58//58 1//1
-f 18//18 1//1 58//58
-f 1//1 18//18 247//247
-f 248//248 45//45 1//1
-f 1//1 247//247 248//248
-f 2//2 61//61 64//64
-f 61//61 2//2 292//292
-f 2//2 64//64 101//101
-f 103//103 2//2 101//101
-f 2//2 103//103 291//291
-f 2//2 291//291 292//292
-f 19//19 3//3 10//10
-f 59//59 10//10 3//3
-f 3//3 18//18 58//58
-f 3//3 290//290 18//18
-f 19//19 290//290 3//3
-f 59//59 3//3 58//58
-f 24//24 4//4 6//6
-f 29//29 6//6 4//4
-f 4//4 24//24 14//14
-f 14//14 29//29 4//4
-f 5//5 19//19 10//10
-f 5//5 10//10 20//20
-f 5//5 14//14 16//16
-f 5//5 20//20 14//14
-f 16//16 118//118 5//5
-f 119//119 19//19 5//5
-f 118//118 119//119 5//5
-f 13//13 6//6 12//12
-f 29//29 12//12 6//6
-f 6//6 13//13 47//47
-f 24//24 6//6 47//47
-f 131//131 124//124 7//7
-f 7//7 124//124 146//146
-f 131//131 7//7 234//234
-f 235//235 7//7 146//146
-f 234//234 7//7 235//235
-f 8//8 17//17 11//11
-f 8//8 11//11 26//26
-f 12//12 17//17 8//8
-f 12//12 8//8 42//42
-f 42//42 8//8 26//26
-f 33//33 9//9 61//61
-f 9//9 33//33 255//255
-f 63//63 9//9 53//53
-f 53//53 9//9 250//250
-f 63//63 61//61 9//9
-f 257//257 250//250 9//9
-f 9//9 255//255 257//257
-f 10//10 60//60 20//20
-f 60//60 10//10 59//59
-f 17//17 15//15 11//11
-f 45//45 26//26 11//11
-f 12//12 42//42 13//13
-f 29//29 17//17 12//12
-f 49//49 13//13 42//42
-f 47//47 13//13 256//256
-f 13//13 49//49 256//256
-f 16//16 14//14 32//32
-f 20//20 29//29 14//14
-f 24//24 32//32 14//14
-f 15//15 17//17 60//60
-f 59//59 58//58 15//15
-f 15//15 60//60 59//59
-f 16//16 32//32 33//33
-f 33//33 262//262 16//16
-f 262//262 118//118 16//16
-f 29//29 20//20 17//17
-f 20//20 60//60 17//17
-f 290//290 247//247 18//18
-f 19//19 120//120 31//31
-f 19//19 31//31 290//290
-f 19//19 119//119 120//120
-f 50//50 51//51 21//21
-f 266//266 50//50 21//21
-f 233//233 21//21 51//51
-f 306//306 21//21 233//233
-f 269//269 266//266 21//21
-f 306//306 269//269 21//21
-f 22//22 32//32 24//24
-f 24//24 47//47 22//22
-f 33//33 32//32 22//22
-f 255//255 33//33 22//22
-f 22//22 47//47 254//254
-f 255//255 22//22 254//254
-f 26//26 265//265 42//42
-f 45//45 50//50 26//26
-f 266//266 26//26 50//50
-f 265//265 26//26 266//266
-f 51//51 27//27 233//233
-f 244//244 27//27 51//51
-f 141//141 27//27 142//142
-f 27//27 141//141 232//232
-f 245//245 142//142 27//27
-f 232//232 233//233 27//27
-f 243//243 27//27 244//244
-f 245//245 27//27 243//243
-f 118//118 28//28 119//119
-f 28//28 118//118 262//262
-f 121//121 119//119 28//28
-f 308//308 121//121 28//28
-f 262//262 307//307 28//28
-f 307//307 308//308 28//28
-f 86//86 222//222 30//30
-f 86//86 30//30 277//277
-f 222//222 221//221 30//30
-f 30//30 221//221 260//260
-f 261//261 30//30 260//260
-f 277//277 30//30 261//261
-f 120//120 243//243 31//31
-f 246//246 31//31 243//243
-f 246//246 247//247 31//31
-f 290//290 31//31 247//247
-f 292//292 33//33 61//61
-f 292//292 262//262 33//33
-f 54//54 222//222 35//35
-f 35//35 225//225 54//54
-f 63//63 35//35 81//81
-f 225//225 35//35 63//63
-f 82//82 81//81 35//35
-f 35//35 86//86 82//82
-f 86//86 35//35 222//222
-f 149//149 150//150 36//36
-f 300//300 149//149 36//36
-f 150//150 152//152 36//36
-f 152//152 154//154 36//36
-f 155//155 36//36 154//154
-f 36//36 155//155 156//156
-f 156//156 300//300 36//36
-f 262//262 292//292 37//37
-f 307//307 262//262 37//37
-f 292//292 291//291 37//37
-f 37//37 291//291 307//307
-f 38//38 89//89 103//103
-f 89//89 38//38 123//123
-f 102//102 38//38 103//103
-f 38//38 102//102 104//104
-f 122//122 38//38 104//104
-f 122//122 123//123 38//38
-f 179//179 39//39 180//180
-f 39//39 179//179 274//274
-f 39//39 205//205 180//180
-f 39//39 253//253 205//205
-f 39//39 274//274 253//253
-f 265//265 49//49 42//42
-f 141//141 43//43 232//232
-f 141//141 278//278 43//43
-f 43//43 278//278 221//221
-f 43//43 221//221 305//305
-f 305//305 232//232 43//43
-f 133//133 44//44 134//134
-f 133//133 193//193 44//44
-f 134//134 44//44 139//139
-f 44//44 258//258 139//139
-f 259//259 44//44 193//193
-f 259//259 258//258 44//44
-f 249//249 50//50 45//45
-f 249//249 45//45 248//248
-f 244//244 51//51 46//46
-f 249//249 46//46 51//51
-f 246//246 244//244 46//46
-f 246//246 46//46 247//247
-f 248//248 247//247 46//46
-f 248//248 46//46 249//249
-f 47//47 256//256 254//254
-f 256//256 49//49 48//48
-f 49//49 268//268 48//48
-f 255//255 254//254 48//48
-f 48//48 254//254 256//256
-f 257//257 255//255 48//48
-f 48//48 268//268 257//257
-f 49//49 265//265 267//267
-f 268//268 49//49 267//267
-f 50//50 249//249 51//51
-f 305//305 226//226 52//52
-f 52//52 226//226 306//306
-f 52//52 233//233 232//232
-f 52//52 232//232 305//305
-f 233//233 52//52 306//306
-f 225//225 63//63 53//53
-f 226//226 225//225 53//53
-f 53//53 306//306 226//226
-f 269//269 53//53 250//250
-f 306//306 53//53 269//269
-f 221//221 222//222 54//54
-f 54//54 305//305 221//221
-f 225//225 226//226 54//54
-f 54//54 226//226 305//305
-f 140//140 57//57 139//139
-f 227//227 139//139 57//57
-f 140//140 141//141 57//57
-f 57//57 141//141 142//142
-f 142//142 143//143 57//57
-f 227//227 57//57 143//143
-f 61//61 63//63 81//81
-f 64//64 61//61 81//81
-f 64//64 62//62 66//66
-f 62//62 64//64 81//81
-f 163//163 66//66 62//62
-f 81//81 82//82 62//62
-f 160//160 62//62 82//82
-f 62//62 160//160 163//163
-f 64//64 66//66 101//101
-f 101//101 66//66 102//102
-f 102//102 66//66 151//151
-f 151//151 66//66 150//150
-f 163//163 150//150 66//66
-f 106//106 104//104 67//67
-f 104//104 151//151 67//67
-f 106//106 67//67 107//107
-f 107//107 67//67 149//149
-f 67//67 151//151 149//149
-f 197//197 214//214 68//68
-f 197//197 68//68 281//281
-f 215//215 68//68 214//214
-f 215//215 216//216 68//68
-f 68//68 216//216 220//220
-f 220//220 281//281 68//68
-f 208//208 69//69 209//209
-f 208//208 211//211 69//69
-f 69//69 213//213 209//209
-f 69//69 211//211 212//212
-f 213//213 69//69 212//212
-f 108//108 107//107 70//70
-f 107//107 109//109 70//70
-f 217//217 108//108 70//70
-f 285//285 70//70 109//109
-f 217//217 70//70 216//216
-f 285//285 216//216 70//70
-f 72//72 122//122 104//104
-f 104//104 283//283 72//72
-f 122//122 72//72 124//124
-f 72//72 125//125 124//124
-f 125//125 72//72 289//289
-f 72//72 283//283 128//128
-f 289//289 72//72 128//128
-f 128//128 75//75 129//129
-f 128//128 207//207 75//75
-f 75//75 203//203 129//129
-f 204//204 203//203 75//75
-f 204//204 75//75 206//206
-f 206//206 75//75 207//207
-f 76//76 137//137 224//224
-f 137//137 76//76 275//275
-f 286//286 76//76 224//224
-f 237//237 275//275 76//76
-f 76//76 286//286 237//237
-f 77//77 145//145 144//144
-f 168//168 77//77 144//144
-f 77//77 263//263 145//145
-f 168//168 263//263 77//77
-f 125//125 78//78 144//144
-f 125//125 289//289 78//78
-f 129//129 132//132 78//78
-f 78//78 289//289 129//129
-f 78//78 132//132 133//133
-f 288//288 78//78 133//133
-f 135//135 144//144 78//78
-f 135//135 78//78 288//288
-f 134//134 228//228 79//79
-f 134//134 79//79 309//309
-f 228//228 236//236 79//79
-f 236//236 238//238 79//79
-f 79//79 238//238 309//309
-f 171//171 169//169 80//80
-f 80//80 169//169 293//293
-f 80//80 199//199 171//171
-f 199//199 80//80 200//200
-f 202//202 200//200 80//80
-f 293//293 202//202 80//80
-f 82//82 86//86 159//159
-f 159//159 160//160 82//82
-f 83//83 162//162 84//84
-f 166//166 83//83 84//84
-f 302//302 162//162 83//83
-f 164//164 83//83 167//167
-f 164//164 302//302 83//83
-f 166//166 167//167 83//83
-f 157//157 84//84 158//158
-f 84//84 157//157 166//166
-f 158//158 84//84 160//160
-f 160//160 84//84 161//161
-f 162//162 161//161 84//84
-f 158//158 85//85 156//156
-f 219//219 156//156 85//85
-f 158//158 159//159 85//85
-f 277//277 85//85 159//159
-f 85//85 277//277 219//219
-f 277//277 159//159 86//86
-f 183//183 184//184 87//87
-f 87//87 191//191 183//183
-f 218//218 87//87 184//184
-f 87//87 192//192 191//191
-f 218//218 192//192 87//87
-f 89//89 291//291 103//103
-f 308//308 89//89 123//123
-f 291//291 89//89 307//307
-f 307//307 89//89 308//308
-f 234//234 121//121 90//90
-f 121//121 308//308 90//90
-f 90//90 123//123 131//131
-f 90//90 308//308 123//123
-f 90//90 131//131 234//234
-f 120//120 121//121 91//91
-f 245//245 120//120 91//91
-f 121//121 234//234 91//91
-f 143//143 142//142 91//91
-f 91//91 142//142 245//245
-f 235//235 143//143 91//91
-f 235//235 91//91 234//234
-f 94//94 178//178 179//179
-f 94//94 194//194 178//178
-f 94//94 179//179 196//196
-f 194//194 94//94 280//280
-f 196//196 197//197 94//94
-f 197//197 281//281 94//94
-f 280//280 94//94 281//281
-f 108//108 95//95 213//213
-f 108//108 217//217 95//95
-f 213//213 95//95 210//210
-f 210//210 95//95 214//214
-f 95//95 215//215 214//214
-f 95//95 217//217 215//215
-f 180//180 96//96 196//196
-f 209//209 96//96 180//180
-f 196//196 96//96 197//197
-f 96//96 214//214 197//197
-f 209//209 210//210 96//96
-f 214//214 96//96 210//210
-f 97//97 180//180 205//205
-f 180//180 97//97 209//209
-f 97//97 205//205 204//204
-f 97//97 204//204 206//206
-f 208//208 97//97 206//206
-f 208//208 209//209 97//97
-f 150//150 98//98 152//152
-f 163//163 98//98 150//150
-f 284//284 152//152 98//98
-f 98//98 163//163 301//301
-f 301//301 284//284 98//98
-f 162//162 164//164 99//99
-f 162//162 99//99 273//273
-f 297//297 99//99 164//164
-f 273//273 99//99 301//301
-f 297//297 298//298 99//99
-f 301//301 99//99 298//298
-f 153//153 100//100 154//154
-f 299//299 100//100 153//153
-f 155//155 154//154 100//100
-f 155//155 100//100 157//157
-f 157//157 100//100 166//166
-f 167//167 100//100 165//165
-f 299//299 165//165 100//100
-f 100//100 167//167 166//166
-f 101//101 102//102 103//103
-f 102//102 151//151 104//104
-f 283//283 104//104 106//106
-f 106//106 212//212 105//105
-f 105//105 283//283 106//106
-f 128//128 105//105 207//207
-f 105//105 128//128 283//283
-f 105//105 211//211 207//207
-f 212//212 211//211 105//105
-f 106//106 107//107 108//108
-f 212//212 106//106 108//108
-f 109//109 107//107 149//149
-f 213//213 212//212 108//108
-f 109//109 149//149 186//186
-f 285//285 109//109 186//186
-f 110//110 184//184 185//185
-f 184//184 110//110 218//218
-f 185//185 186//186 110//110
-f 300//300 110//110 186//186
-f 110//110 219//219 218//218
-f 110//110 300//300 219//219
-f 111//111 188//188 189//189
-f 188//188 111//111 285//285
-f 189//189 220//220 111//111
-f 220//220 216//216 111//111
-f 111//111 216//216 285//285
-f 112//112 183//183 182//182
-f 195//195 112//112 182//182
-f 183//183 112//112 184//184
-f 112//112 185//185 184//184
-f 198//198 185//185 112//112
-f 112//112 195//195 198//198
-f 113//113 182//182 174//174
-f 113//113 174//174 303//303
-f 182//182 113//113 195//195
-f 190//190 113//113 194//194
-f 113//113 190//190 195//195
-f 113//113 303//303 194//194
-f 171//171 114//114 169//169
-f 114//114 191//191 169//169
-f 114//114 171//171 174//174
-f 114//114 174//174 182//182
-f 183//183 114//114 182//182
-f 191//191 114//114 183//183
-f 132//132 115//115 133//133
-f 169//169 115//115 132//132
-f 115//115 193//193 133//133
-f 169//169 191//191 115//115
-f 191//191 192//192 115//115
-f 192//192 193//193 115//115
-f 116//116 192//192 148//148
-f 116//116 148//148 261//261
-f 116//116 193//193 192//192
-f 116//116 259//259 193//193
-f 259//259 116//116 260//260
-f 261//261 260//260 116//116
-f 140//140 258//258 117//117
-f 140//140 117//117 278//278
-f 117//117 260//260 221//221
-f 117//117 221//221 278//278
-f 259//259 117//117 258//258
-f 259//259 260//260 117//117
-f 119//119 121//121 120//120
-f 245//245 243//243 120//120
-f 122//122 131//131 123//123
-f 131//131 122//122 124//124
-f 125//125 146//146 124//124
-f 144//144 145//145 125//125
-f 125//125 145//145 146//146
-f 127//127 147//147 138//138
-f 127//127 138//138 228//228
-f 143//143 146//146 127//127
-f 143//143 127//127 227//227
-f 127//127 146//146 145//145
-f 145//145 147//147 127//127
-f 127//127 228//228 227//227
-f 128//128 129//129 289//289
-f 293//293 132//132 129//129
-f 203//203 202//202 129//129
-f 202//202 293//293 129//129
-f 202//202 130//130 200//200
-f 130//130 253//253 200//200
-f 202//202 203//203 130//130
-f 204//204 130//130 203//203
-f 204//204 205//205 130//130
-f 205//205 253//253 130//130
-f 132//132 293//293 169//169
-f 288//288 133//133 134//134
-f 288//288 134//134 135//135
-f 135//135 134//134 309//309
-f 227//227 134//134 139//139
-f 228//228 134//134 227//227
-f 144//144 135//135 168//168
-f 223//223 168//168 135//135
-f 135//135 309//309 223//223
-f 223//223 136//136 224//224
-f 136//136 223//223 309//309
-f 224//224 136//136 286//286
-f 286//286 136//136 238//238
-f 309//309 238//238 136//136
-f 147//147 263//263 137//137
-f 275//275 147//147 137//137
-f 223//223 137//137 168//168
-f 263//263 168//168 137//137
-f 224//224 137//137 223//223
-f 275//275 138//138 147//147
-f 138//138 236//236 228//228
-f 236//236 138//138 237//237
-f 275//275 237//237 138//138
-f 139//139 258//258 140//140
-f 140//140 278//278 141//141
-f 143//143 235//235 146//146
-f 263//263 147//147 145//145
-f 218//218 148//148 192//192
-f 148//148 218//218 219//219
-f 148//148 219//219 277//277
-f 148//148 277//277 261//261
-f 151//151 150//150 149//149
-f 300//300 186//186 149//149
-f 284//284 154//154 152//152
-f 153//153 154//154 284//284
-f 153//153 284//284 301//301
-f 299//299 153//153 298//298
-f 298//298 153//153 301//301
-f 156//156 155//155 157//157
-f 158//158 156//156 157//157
-f 219//219 300//300 156//156
-f 158//158 160//160 159//159
-f 160//160 161//161 163//163
-f 162//162 273//273 161//161
-f 163//163 161//161 301//301
-f 161//161 273//273 301//301
-f 302//302 164//164 162//162
-f 297//297 164//164 167//167
-f 297//297 167//167 165//165
-f 165//165 298//298 297//297
-f 299//299 298//298 165//165
-f 174//174 171//171 201//201
-f 199//199 201//201 171//171
-f 274//274 178//178 173//173
-f 303//303 173//173 178//178
-f 173//173 199//199 200//200
-f 199//199 173//173 201//201
-f 274//274 173//173 200//200
-f 173//173 303//303 201//201
-f 303//303 174//174 201//201
-f 190//190 177//177 189//189
-f 220//220 189//189 177//177
-f 280//280 177//177 190//190
-f 281//281 220//220 177//177
-f 281//281 177//177 280//280
-f 178//178 274//274 179//179
-f 194//194 303//303 178//178
-f 196//196 179//179 180//180
-f 188//188 186//186 185//185
-f 188//188 185//185 198//198
-f 188//188 285//285 186//186
-f 188//188 198//198 189//189
-f 198//198 190//190 189//189
-f 280//280 190//190 194//194
-f 190//190 198//198 195//195
-f 200//200 253//253 274//274
-f 207//207 208//208 206//206
-f 208//208 207//207 211//211
-f 209//209 213//213 210//210
-f 215//215 217//217 216//216
-f 237//237 238//238 236//236
-f 237//237 286//286 238//238
-f 244//244 246//246 243//243
-f 250//250 257//257 268//268
-f 268//268 267//267 250//250
-f 267//267 269//269 250//250
-f 265//265 266//266 267//267
-f 269//269 267//267 266//266
diff --git a/examples/turtle/model/turtle.interp b/examples/turtle/model/turtle.interp
deleted file mode 100644
index e406ef499f17aa80603d604250d4b80e602ded75..0000000000000000000000000000000000000000
--- a/examples/turtle/model/turtle.interp
+++ /dev/null
@@ -1,1961 +0,0 @@
-0 147 0.054435 310 0.293801 218 0.348853 322 0.302911
-1 224 0.327988 33 0.358870 52 0.244016 342 0.069127
-2 241 0.048137 317 0.261590 217 0.310553 322 0.379721
-3 241 0.082348 317 0.530330 217 0.245572 322 0.141751
-4 92 0.058074 33 0.141816 55 0.054580 340 0.745530
-5 92 0.170163 55 0.066492 232 0.029830 340 0.733514
-6 190 0.084453 86 0.092456 241 0.083473 317 0.739619
-7 303 0.064768 224 0.358282 62 0.131412 342 0.445538
-8 303 0.203875 63 0.222492 62 0.528213 313 0.045420
-9 63 0.206826 81 0.133722 80 0.408484 313 0.250968
-10 61 0.335224 159 0.218901 81 0.092061 313 0.353814
-11 81 0.181107 310 0.432887 158 0.308901 313 0.077105
-12 276 0.014588 84 0.282690 218 0.096174 310 0.606548
-13 217 0.176982 147 0.129124 218 0.054369 322 0.639525
-14 225 0.148053 52 0.358542 33 0.386683 340 0.106722
-15 221 0.397329 331 0.004413 324 0.403907 338 0.194351
-16 220 0.022308 324 0.050429 221 0.526391 331 0.400872
-17 29 0.165511 260 0.270318 259 0.108168 331 0.456003
-18 260 0.021464 329 0.135675 259 0.549684 331 0.293177
-19 304 0.126608 53 0.004699 220 0.199025 324 0.669668
-20 147 0.582824 310 0.141905 269 0.254841 331 0.020430
-21 224 0.256321 34 0.002058 53 0.073708 338 0.667913
-22 34 0.094817 85 0.508936 221 0.022749 313 0.373499
-23 220 0.135346 221 0.039953 29 0.329423 331 0.495278
-24 221 0.446543 331 0.134706 324 0.294576 338 0.124175
-25 221 0.495759 331 0.264999 324 0.185243 338 0.054000
-26 221 0.485273 331 0.266546 324 0.229571 338 0.018610
-27 220 0.063675 324 0.055908 221 0.477665 331 0.402752
-28 221 0.439544 331 0.133351 324 0.334845 338 0.092260
-29 221 0.388054 331 0.016806 324 0.451904 338 0.143237
-30 29 0.178333 260 0.187348 259 0.170140 331 0.464179
-31 220 0.180737 29 0.313040 259 0.004932 331 0.501291
-32 220 0.226102 42 0.001751 304 0.087112 324 0.685036
-33 259 0.537273 329 0.140269 220 0.026848 331 0.295611
-34 29 0.343591 260 0.006157 259 0.243382 331 0.406871
-35 259 0.486672 329 0.124128 220 0.139534 331 0.249666
-36 221 0.449078 324 0.494545 53 0.009322 338 0.047055
-37 220 0.333961 42 0.027776 304 0.029523 324 0.608740
-38 220 0.236190 221 0.329642 29 0.046608 331 0.387560
-39 220 0.217059 29 0.345935 259 0.021099 331 0.415907
-40 220 0.003947 324 0.407998 221 0.501558 331 0.086496
-41 220 0.117802 324 0.191552 221 0.450605 331 0.240042
-42 220 0.646523 315 0.070077 331 0.085653 329 0.197748
-43 259 0.225162 329 0.209897 220 0.425607 331 0.139334
-44 315 0.074652 220 0.475227 331 0.033281 324 0.416841
-45 315 0.175542 220 0.588429 331 0.121113 324 0.114916
-46 221 0.518346 331 0.397250 324 0.022743 338 0.061661
-47 221 0.472739 331 0.276506 324 0.124061 338 0.126693
-48 221 0.427132 331 0.155763 324 0.225380 338 0.191724
-49 221 0.381524 331 0.035022 324 0.326697 338 0.256757
-50 303 0.161152 224 0.230539 62 0.239791 342 0.368518
-51 224 0.200008 34 0.063320 53 0.055318 338 0.681354
-52 61 0.297228 159 0.155614 81 0.157519 313 0.389639
-53 34 0.115919 85 0.448500 221 0.049919 313 0.385662
-54 63 0.104144 81 0.053376 80 0.479778 313 0.362702
-55 303 0.193547 63 0.168834 62 0.523948 313 0.113671
-56 303 0.146747 224 0.290171 62 0.254502 342 0.308579
-57 303 0.141708 63 0.096010 62 0.586077 313 0.176206
-58 80 0.562608 81 0.008826 34 0.034472 313 0.394094
-59 61 0.255282 159 0.068076 81 0.323751 313 0.352891
-60 224 0.215456 34 0.108718 53 0.101540 338 0.574286
-61 221 0.443039 331 0.008624 324 0.246484 338 0.301854
-62 313 0.047641 331 0.305062 221 0.586069 338 0.061228
-63 34 0.189342 85 0.437075 221 0.053693 313 0.319891
-64 221 0.486415 331 0.123457 324 0.150124 338 0.240003
-65 221 0.529790 331 0.238292 324 0.053763 338 0.178154
-66 303 0.138669 313 0.048060 34 0.518505 338 0.294766
-67 34 0.502315 85 0.019273 221 0.078098 313 0.400315
-68 147 0.269081 310 0.084955 218 0.130782 322 0.515182
-69 61 0.002247 159 0.361776 81 0.227329 313 0.408649
-70 34 0.002867 85 0.581326 221 0.021445 313 0.394362
-71 147 0.167636 310 0.383345 218 0.248996 322 0.200022
-72 158 0.431038 310 0.360473 85 0.007178 313 0.201310
-73 276 0.122472 84 0.189465 218 0.086050 310 0.602013
-74 147 0.566912 310 0.173997 269 0.175510 331 0.083581
-75 29 0.194271 260 0.314693 259 0.021975 331 0.469061
-76 220 0.004078 221 0.528482 29 0.025159 331 0.442281
-77 220 0.072317 221 0.073491 29 0.406673 331 0.447519
-78 29 0.205102 276 0.104310 260 0.292703 331 0.397885
-79 221 0.073582 85 0.046369 29 0.484582 331 0.395468
-80 221 0.403945 85 0.110391 29 0.103629 331 0.382034
-81 147 0.245342 310 0.100066 218 0.208416 322 0.446176
-82 147 0.602678 310 0.223003 269 0.079339 331 0.094981
-83 159 0.083765 310 0.205339 81 0.531723 313 0.179173
-84 85 0.646671 313 0.296547 310 0.032248 331 0.024534
-85 276 0.168022 84 0.234919 218 0.068321 310 0.528739
-86 147 0.217858 310 0.416931 218 0.271455 322 0.093755
-87 158 0.386052 310 0.388686 85 0.110818 313 0.114444
-88 85 0.392658 310 0.281711 276 0.164975 331 0.160655
-89 260 0.176594 329 0.008217 259 0.381145 331 0.434044
-90 221 0.193451 220 0.019982 53 0.129898 324 0.656670
-91 221 0.195248 220 0.055370 53 0.080563 324 0.668819
-92 29 0.003805 260 0.102115 259 0.441814 331 0.452266
-93 220 0.037986 29 0.141872 259 0.434536 331 0.385606
-94 221 0.218211 220 0.155362 53 0.054485 324 0.571942
-95 220 0.288742 29 0.102380 259 0.237011 331 0.371867
-96 220 0.558460 29 0.019349 259 0.035064 331 0.387127
-97 220 0.283901 324 0.419094 221 0.208742 331 0.088263
-98 220 0.456277 324 0.190376 221 0.104761 331 0.248586
-99 304 0.183778 53 0.021075 220 0.124709 324 0.670438
-100 221 0.127785 324 0.629640 53 0.220531 338 0.022043
-101 221 0.304868 324 0.390927 53 0.086884 338 0.217321
-102 225 0.081351 224 0.170275 53 0.070094 338 0.678280
-103 263 0.019018 225 0.194805 33 0.260291 340 0.525885
-104 225 0.210770 52 0.277025 33 0.444366 340 0.067839
-105 33 0.409775 224 0.295202 52 0.247455 338 0.047568
-106 303 0.071455 33 0.057705 224 0.530705 342 0.340135
-107 263 0.078386 225 0.078802 33 0.131102 340 0.711710
-108 33 0.242574 224 0.058811 52 0.453651 338 0.244964
-109 263 0.051718 225 0.267716 33 0.121133 340 0.559433
-110 51 0.074252 225 0.077348 263 0.136345 340 0.712056
-111 263 0.170745 232 0.168760 51 0.071043 340 0.589451
-112 224 0.557911 33 0.142348 52 0.062102 342 0.237639
-113 225 0.316677 52 0.209055 33 0.324170 340 0.150098
-114 221 0.180108 324 0.331730 53 0.309829 338 0.178334
-115 225 0.231974 224 0.129212 53 0.091940 338 0.546875
-116 221 0.017742 220 0.000493 53 0.431965 324 0.549799
-117 304 0.327775 53 0.028804 220 0.098786 324 0.544635
-118 304 0.187297 263 0.132026 225 0.368183 338 0.312494
-119 225 0.185062 304 0.282861 51 0.175639 263 0.356438
-120 260 0.112456 329 0.222702 259 0.422276 331 0.242567
-121 260 0.250210 329 0.109216 259 0.272028 331 0.368546
-122 29 0.008618 260 0.387496 259 0.113964 331 0.489922
-123 217 0.122505 317 0.095409 147 0.191153 322 0.590932
-124 147 0.599563 310 0.077329 269 0.309408 331 0.013700
-125 241 0.034794 317 0.617803 217 0.212088 322 0.135315
-126 190 0.070977 86 0.079807 241 0.041434 317 0.807782
-127 217 0.219843 317 0.374514 147 0.024664 322 0.380980
-128 217 0.230962 317 0.407095 147 0.106578 322 0.255364
-129 217 0.257408 317 0.639609 147 0.055118 322 0.047866
-130 191 0.124431 86 0.114745 190 0.007737 317 0.753087
-131 217 0.196021 317 0.122214 147 0.186621 322 0.495144
-132 29 0.050763 260 0.485916 259 0.086711 331 0.376610
-133 147 0.688265 317 0.017974 269 0.266657 322 0.027104
-134 260 0.384261 329 0.062426 259 0.249646 331 0.303667
-135 260 0.226006 329 0.188231 259 0.413474 331 0.172289
-136 269 0.000605 317 0.268154 147 0.539981 329 0.191260
-137 260 0.230577 317 0.562542 115 0.182788 329 0.024093
-138 263 0.009073 33 0.221107 232 0.297231 340 0.472589
-139 109 0.067437 241 0.283645 217 0.647048 322 0.001870
-140 86 0.418114 217 0.077220 183 0.209881 317 0.294785
-141 92 0.031800 33 0.058588 55 0.151439 340 0.758173
-142 92 0.087807 55 0.161354 232 0.035177 340 0.715662
-143 191 0.037849 86 0.389175 190 0.352444 317 0.220532
-144 303 0.039021 224 0.180123 62 0.246903 342 0.533953
-145 60 0.021970 303 0.156042 63 0.234474 62 0.587514
-146 63 0.223107 81 0.179675 80 0.455060 313 0.142158
-147 61 0.387699 159 0.238658 81 0.118619 313 0.255023
-148 159 0.110045 158 0.362817 81 0.204923 310 0.322215
-149 155 0.069165 218 0.084109 84 0.538076 310 0.308650
-150 218 0.509243 310 0.157518 299 0.105526 322 0.227714
-151 109 0.329521 241 0.142213 217 0.384439 322 0.143828
-152 241 0.399114 183 0.043679 109 0.320300 217 0.236907
-153 183 0.233035 241 0.297156 86 0.277003 317 0.192805
-154 33 0.081860 335 0.226542 55 0.159847 340 0.531751
-155 33 0.174697 335 0.080899 55 0.292841 340 0.451562
-156 190 0.290864 86 0.292999 241 0.329079 317 0.087059
-157 60 0.037924 62 0.285997 8 0.180793 342 0.495286
-158 60 0.262676 303 0.038609 63 0.261775 62 0.436941
-159 63 0.484559 81 0.242817 80 0.268411 313 0.004212
-160 65 0.051570 159 0.191333 61 0.523040 313 0.234057
-161 159 0.305815 158 0.098682 81 0.176615 310 0.418888
-162 155 0.224805 218 0.068305 84 0.281043 310 0.425848
-163 218 0.217956 310 0.173959 299 0.294705 322 0.313379
-164 109 0.558014 241 0.309042 217 0.101515 322 0.031429
-165 241 0.130026 317 0.061224 217 0.068079 322 0.740671
-166 241 0.149277 317 0.474522 217 0.163888 322 0.212313
-167 190 0.067703 86 0.074167 241 0.181833 317 0.676297
-168 60 0.251104 303 0.324591 63 0.269973 62 0.154332
-169 64 0.018948 63 0.556606 303 0.088409 313 0.336038
-170 65 0.322760 159 0.007875 61 0.163512 313 0.505853
-171 159 0.054344 310 0.576986 81 0.015168 313 0.353501
-172 185 0.009143 322 0.764942 240 0.166083 186 0.059832
-173 91 0.148425 335 0.772402 27 0.029016 343 0.050157
-174 66 0.047743 239 0.106458 240 0.057312 319 0.788488
-175 117 0.323266 91 0.451029 261 0.061284 335 0.164422
-176 91 0.338512 261 0.208732 306 0.224945 27 0.227811
-177 293 0.423964 88 0.148693 37 0.213330 122 0.214013
-178 70 0.364625 103 0.550235 37 0.006161 294 0.078979
-179 66 0.041437 105 0.049797 239 0.128936 319 0.779830
-180 261 0.018260 335 0.807880 91 0.150897 342 0.022963
-181 66 0.323410 319 0.120975 240 0.199879 322 0.355736
-182 66 0.157375 319 0.473175 240 0.216095 322 0.153355
-183 261 0.370871 335 0.230903 91 0.318538 342 0.079689
-184 91 0.068267 64 0.031924 291 0.539701 342 0.360107
-185 291 0.323757 261 0.267702 91 0.387332 342 0.021209
-186 102 0.409908 64 0.303126 293 0.075013 100 0.211952
-187 88 0.027494 293 0.495204 37 0.139146 102 0.338156
-188 270 0.015799 150 0.557244 294 0.365472 40 0.061485
-189 101 0.063824 103 0.507852 294 0.428189 37 0.000134
-190 270 0.291970 66 0.237125 150 0.434609 322 0.036296
-191 270 0.169784 66 0.496179 150 0.119320 322 0.214717
-192 66 0.416794 240 0.290898 148 0.070630 322 0.221678
-193 66 0.299280 319 0.441812 240 0.220507 322 0.038401
-194 109 0.055994 241 0.275741 217 0.099096 322 0.569169
-195 241 0.299942 317 0.340808 217 0.219499 322 0.139751
-196 33 0.170530 335 0.234477 55 0.134339 340 0.460655
-197 33 0.278598 335 0.059540 55 0.300017 340 0.361844
-198 190 0.084514 86 0.088501 241 0.319147 317 0.507839
-199 60 0.171635 303 0.091781 62 0.275237 342 0.461347
-200 60 0.235064 303 0.259360 63 0.259650 62 0.245926
-201 303 0.018851 63 0.570306 62 0.065432 313 0.345411
-202 65 0.292556 159 0.031186 61 0.230092 313 0.446166
-203 159 0.064488 310 0.597516 81 0.079497 313 0.258499
-204 147 0.125184 310 0.703832 218 0.163883 322 0.007101
-205 218 0.142482 310 0.183426 299 0.058406 322 0.615686
-206 186 0.028276 241 0.223220 109 0.176583 322 0.571921
-207 33 0.323131 335 0.178002 55 0.277218 340 0.221649
-208 33 0.256461 335 0.299611 55 0.174491 340 0.269437
-209 64 0.050177 303 0.025361 60 0.465772 342 0.458690
-210 224 0.242391 33 0.331026 52 0.259974 342 0.166610
-211 33 0.270154 340 0.552568 52 0.130505 342 0.046773
-212 335 0.078527 340 0.700572 33 0.202594 342 0.018306
-213 303 0.040095 224 0.380055 62 0.039977 342 0.539873
-214 33 0.156199 335 0.105749 55 0.088274 340 0.649777
-215 303 0.026298 224 0.244040 62 0.120891 342 0.608771
-216 33 0.126478 335 0.256940 55 0.070052 340 0.546530
-217 8 0.059099 62 0.278568 52 0.024160 342 0.638173
-218 91 0.086153 64 0.002403 291 0.426320 342 0.485124
-219 291 0.317753 261 0.171769 91 0.314281 342 0.196197
-220 91 0.115490 335 0.789409 33 0.014890 342 0.080212
-221 261 0.337154 335 0.226004 91 0.245314 342 0.191529
-222 33 0.165608 335 0.342722 55 0.053281 340 0.438388
-223 60 0.126945 303 0.076829 62 0.226915 342 0.569311
-224 33 0.187528 335 0.437626 55 0.062333 340 0.312512
-225 60 0.380337 303 0.058989 62 0.001150 342 0.559524
-226 33 0.441134 340 0.077105 52 0.471210 342 0.010551
-227 224 0.409750 33 0.453334 52 0.020207 342 0.116709
-228 33 0.405483 335 0.017934 55 0.024614 340 0.551968
-229 225 0.048092 52 0.113997 33 0.498061 340 0.339850
-230 303 0.241681 224 0.242827 62 0.133869 342 0.381623
-231 33 0.336156 335 0.044343 55 0.112037 340 0.507464
-232 303 0.216843 224 0.068690 62 0.250059 342 0.464409
-233 33 0.299305 335 0.150825 55 0.097613 340 0.452256
-234 60 0.054084 303 0.113874 62 0.309293 342 0.522750
-235 91 0.417792 64 0.056158 291 0.357106 342 0.168943
-236 91 0.047197 64 0.186163 291 0.389747 342 0.376892
-237 261 0.178037 335 0.135075 91 0.418356 342 0.268532
-238 91 0.208741 335 0.574209 33 0.162480 342 0.054570
-239 33 0.351701 335 0.214119 55 0.083724 340 0.350456
-240 60 0.206329 303 0.241664 62 0.192791 342 0.359216
-241 33 0.455981 335 0.271827 55 0.099882 340 0.172310
-242 64 0.102269 303 0.114213 60 0.398846 342 0.384672
-243 33 0.524734 224 0.002861 52 0.369754 338 0.102651
-244 224 0.505856 33 0.443916 52 0.003014 342 0.047215
-245 263 0.015299 225 0.185211 33 0.409762 340 0.389728
-246 232 0.096104 33 0.409679 92 0.031501 340 0.462716
-247 303 0.243069 224 0.228514 62 0.238678 342 0.289740
-248 92 0.079283 33 0.262124 55 0.090315 340 0.568277
-249 303 0.217374 224 0.053724 62 0.353427 342 0.375476
-250 33 0.385736 335 0.060279 55 0.205454 340 0.348532
-251 60 0.134888 303 0.096207 62 0.356407 342 0.412498
-252 91 0.021645 64 0.204705 291 0.509131 342 0.264518
-253 91 0.469000 64 0.037165 291 0.466481 342 0.027354
-254 91 0.265702 335 0.597650 33 0.126075 342 0.010573
-255 261 0.260991 335 0.082195 91 0.469585 342 0.187229
-256 33 0.471584 335 0.069032 55 0.179497 340 0.279888
-257 60 0.324490 303 0.246062 62 0.192096 342 0.237353
-258 33 0.556591 335 0.130583 55 0.220749 340 0.092077
-259 64 0.120518 303 0.092303 60 0.511308 342 0.275872
-260 52 0.305247 225 0.118635 33 0.452943 338 0.123176
-261 269 0.018518 310 0.735089 147 0.028441 322 0.217952
-262 218 0.005306 310 0.345348 299 0.064468 322 0.584878
-263 299 0.060436 310 0.159648 148 0.069820 322 0.710095
-264 269 0.006039 310 0.827179 40 0.164237 331 0.002545
-265 299 0.028251 310 0.041209 148 0.262560 322 0.667981
-266 150 0.016793 65 0.059666 40 0.250036 310 0.673505
-267 269 0.119097 150 0.558376 40 0.098443 322 0.224085
-268 270 0.065772 66 0.108102 150 0.404351 322 0.421775
-269 150 0.110180 66 0.106811 148 0.306241 322 0.476768
-270 150 0.376811 65 0.146503 40 0.281955 310 0.194731
-271 147 0.080984 310 0.720259 269 0.195850 331 0.002906
-272 269 0.154606 310 0.339366 147 0.055853 322 0.450175
-273 269 0.144015 310 0.159497 147 0.016673 322 0.679815
-274 310 0.647671 313 0.072107 40 0.118495 331 0.161727
-275 269 0.113825 150 0.121337 40 0.008600 322 0.756237
-276 310 0.522975 313 0.155722 40 0.273460 331 0.047843
-277 150 0.550040 270 0.087415 269 0.342412 40 0.020133
-278 270 0.266113 66 0.140847 150 0.235072 322 0.357968
-279 270 0.108280 66 0.156138 150 0.142874 322 0.592708
-280 150 0.401636 65 0.027432 40 0.449205 310 0.121727
-281 269 0.133994 310 0.292551 147 0.168458 322 0.404998
-282 147 0.155354 310 0.662152 269 0.081619 331 0.100876
-283 269 0.129094 310 0.044888 147 0.107268 322 0.718750
-284 85 0.028297 313 0.174721 310 0.618987 331 0.177995
-285 270 0.078155 319 0.047455 66 0.016173 322 0.858217
-286 310 0.477470 313 0.301265 40 0.180018 331 0.041248
-287 270 0.303229 66 0.255810 150 0.151968 322 0.288993
-288 270 0.257954 150 0.540285 269 0.199176 322 0.002585
-289 270 0.128755 319 0.002967 66 0.307480 322 0.560797
-290 40 0.481971 150 0.379881 294 0.064926 65 0.073223
-291 60 0.614091 64 0.237217 63 0.023246 303 0.125446
-292 60 0.432884 291 0.137172 64 0.156374 342 0.273571
-293 60 0.153926 291 0.439259 64 0.163274 342 0.243541
-294 293 0.287460 88 0.376959 37 0.197286 122 0.138295
-295 88 0.174330 293 0.356977 37 0.013335 102 0.455358
-296 1 0.187126 64 0.307948 102 0.426608 100 0.078318
-297 64 0.277956 63 0.606898 303 0.066171 313 0.048974
-298 88 0.069161 91 0.363923 306 0.524393 307 0.042523
-299 91 0.356439 291 0.076493 290 0.197485 36 0.369583
-300 1 0.229006 64 0.260865 60 0.137951 291 0.372178
-301 88 0.616423 91 0.064673 307 0.058963 293 0.259941
-302 293 0.286222 88 0.266241 290 0.399111 102 0.048426
-303 70 0.338829 103 0.439958 37 0.184261 294 0.036951
-304 101 0.163902 103 0.361440 294 0.336146 37 0.138512
-305 270 0.023284 101 0.309012 294 0.368753 150 0.298951
-306 40 0.113430 310 0.303155 65 0.322679 313 0.260737
-307 293 0.285215 88 0.067067 37 0.428881 122 0.218837
-308 293 0.359259 101 0.092386 37 0.248672 102 0.299683
-309 101 0.076238 293 0.312455 100 0.475429 102 0.135878
-310 65 0.210230 63 0.379058 64 0.230811 313 0.179902
-311 40 0.036838 310 0.008488 65 0.573283 313 0.381391
-312 70 0.315858 121 0.004004 37 0.477836 103 0.202302
-313 101 0.230966 103 0.105345 294 0.333894 37 0.329795
-314 65 0.070163 101 0.441565 294 0.362239 100 0.126033
-315 150 0.249644 270 0.314571 103 0.024085 66 0.411700
-316 270 0.280024 66 0.567983 150 0.076895 322 0.075097
-317 66 0.472152 319 0.187920 240 0.117043 322 0.222885
-318 270 0.318793 101 0.361658 294 0.014642 150 0.304907
-319 66 0.043591 103 0.184210 282 0.004368 319 0.767831
-320 270 0.031577 103 0.080226 66 0.357261 319 0.530937
-321 294 0.010754 270 0.050676 70 0.298236 103 0.640333
-322 270 0.125620 101 0.203483 103 0.480297 294 0.190600
-323 71 0.018744 103 0.394430 270 0.074110 319 0.512716
-324 71 0.064848 103 0.502719 270 0.180063 319 0.252371
-325 270 0.253122 103 0.432048 66 0.267423 319 0.047407
-326 270 0.148800 103 0.284871 66 0.302915 319 0.263413
-327 241 0.007846 317 0.615928 217 0.072865 322 0.303361
-328 190 0.019184 86 0.027385 241 0.103120 317 0.850312
-329 269 0.036883 317 0.159109 241 0.005883 322 0.798125
-330 66 0.351185 319 0.221858 240 0.013600 322 0.413357
-331 66 0.185150 319 0.574057 240 0.029817 322 0.210976
-332 66 0.082565 105 0.000878 239 0.018736 319 0.897822
-333 205 0.155954 312 0.248141 96 0.395688 345 0.200217
-334 203 0.013293 96 0.504032 205 0.189735 312 0.292940
-335 199 0.014454 201 0.139712 171 0.247801 312 0.598032
-336 199 0.070698 201 0.193244 171 0.158167 312 0.577892
-337 199 0.112850 201 0.243884 171 0.059210 312 0.584056
-338 129 0.076870 201 0.217200 199 0.129567 312 0.576364
-339 129 0.256843 201 0.086692 199 0.116514 312 0.539950
-340 252 0.060946 129 0.379971 199 0.037779 312 0.521304
-341 252 0.021185 204 0.154107 129 0.364004 312 0.460705
-342 204 0.292934 203 0.073651 129 0.239633 312 0.393782
-343 204 0.407360 203 0.175596 129 0.070954 312 0.346090
-344 204 0.414654 96 0.083135 203 0.201472 312 0.300738
-345 204 0.338270 96 0.224971 203 0.171617 312 0.265141
-346 204 0.238249 96 0.368970 203 0.138478 312 0.254304
-347 204 0.118430 96 0.509598 203 0.103330 312 0.268642
-348 203 0.047473 96 0.622748 205 0.022766 312 0.307013
-349 205 0.086144 312 0.272205 96 0.462471 345 0.179180
-350 205 0.019173 312 0.278226 96 0.505620 345 0.196981
-351 96 0.387838 312 0.104613 204 0.215420 345 0.292129
-352 179 0.000561 96 0.169804 204 0.437665 345 0.391970
-353 179 0.012142 96 0.031872 204 0.572377 345 0.383609
-354 204 0.596922 312 0.123593 179 0.038104 345 0.241381
-355 204 0.574095 312 0.296722 179 0.063563 345 0.065621
-356 252 0.003920 38 0.096030 204 0.498984 312 0.401066
-357 252 0.198432 38 0.055567 204 0.308217 312 0.437784
-358 252 0.370241 38 0.011202 204 0.119748 312 0.498809
-359 252 0.365584 129 0.020038 199 0.065162 312 0.549216
-360 252 0.163652 129 0.028088 199 0.230256 312 0.578004
-361 129 0.002185 201 0.034303 199 0.338495 312 0.625017
-362 199 0.269584 201 0.044848 171 0.093764 312 0.591804
-363 199 0.178658 201 0.056145 171 0.181968 312 0.583228
-364 199 0.069492 201 0.069819 171 0.259936 312 0.600753
-365 171 0.297525 201 0.042413 230 0.055400 312 0.604662
-366 199 0.105061 201 0.002388 171 0.280728 312 0.611823
-367 171 0.155720 228 0.069147 199 0.184024 312 0.591109
-368 171 0.023378 228 0.133684 199 0.246355 312 0.596583
-369 172 0.122236 273 0.053009 199 0.170137 312 0.654619
-370 252 0.078051 199 0.129975 273 0.206599 312 0.585375
-371 273 0.186151 38 0.075184 252 0.185774 312 0.552892
-372 273 0.012946 38 0.354560 252 0.088075 312 0.544419
-373 204 0.136236 38 0.340122 179 0.049527 312 0.474114
-374 204 0.247289 38 0.191039 179 0.160191 312 0.401481
-375 204 0.356419 38 0.018291 179 0.273525 312 0.351766
-376 204 0.406135 312 0.186597 179 0.240911 345 0.166357
-377 204 0.437065 312 0.024289 179 0.178748 345 0.359898
-378 179 0.127639 96 0.123997 204 0.348902 345 0.399462
-379 179 0.066579 96 0.242459 204 0.250487 345 0.440476
-380 96 0.343634 312 0.130051 204 0.111953 345 0.414363
-381 179 0.005419 96 0.189338 204 0.192352 345 0.612891
-382 179 0.128310 96 0.217369 204 0.173587 345 0.480733
-383 179 0.217260 96 0.087573 204 0.237261 345 0.457906
-384 204 0.255790 312 0.066895 179 0.301349 345 0.375966
-385 204 0.192978 312 0.236697 179 0.385074 345 0.185251
-386 204 0.125257 312 0.422397 179 0.449096 345 0.003251
-387 204 0.002994 38 0.168064 179 0.349569 312 0.479373
-388 179 0.125144 38 0.323923 180 0.128434 312 0.422499
-389 38 0.314699 273 0.084129 180 0.166710 312 0.434462
-390 38 0.113594 273 0.265832 180 0.094091 312 0.526483
-391 271 0.073573 273 0.279535 172 0.032705 312 0.614188
-392 271 0.083846 273 0.040004 172 0.207228 312 0.668922
-393 199 0.083248 228 0.188844 172 0.082000 312 0.645908
-394 171 0.112791 228 0.166616 199 0.114198 312 0.606394
-395 171 0.260945 228 0.047521 199 0.071484 312 0.620050
-396 171 0.259089 228 0.078087 199 0.027192 312 0.635632
-397 171 0.109150 228 0.226576 199 0.027314 312 0.636960
-398 172 0.035982 228 0.268786 271 0.044203 312 0.651029
-399 172 0.068418 228 0.074819 271 0.264926 312 0.591837
-400 180 0.045406 273 0.117622 271 0.308392 312 0.528580
-401 180 0.270923 273 0.241440 271 0.039407 312 0.448230
-402 38 0.165630 273 0.101492 180 0.375548 312 0.357330
-403 179 0.106103 38 0.200554 180 0.358197 312 0.335147
-404 179 0.334010 38 0.046874 180 0.221942 312 0.397173
-405 179 0.422566 312 0.346113 180 0.112027 345 0.119294
-406 179 0.438869 312 0.255287 180 0.011269 345 0.294575
-407 204 0.083089 312 0.092041 179 0.355408 345 0.469462
-408 179 0.255777 96 0.066076 204 0.119741 345 0.558406
-409 179 0.154842 96 0.202562 204 0.092637 345 0.549959
-410 179 0.018945 96 0.181789 204 0.151085 345 0.648181
-411 179 0.012468 96 0.180631 204 0.114034 345 0.692867
-412 179 0.142133 96 0.200291 204 0.019961 345 0.637614
-413 179 0.237329 96 0.062779 204 0.014230 345 0.685662
-414 179 0.279712 312 0.036378 180 0.061095 345 0.622815
-415 179 0.255296 312 0.083619 180 0.192136 345 0.468949
-416 179 0.218589 312 0.155364 180 0.312997 345 0.313050
-417 179 0.171004 312 0.248861 180 0.419032 345 0.161103
-418 179 0.114368 312 0.360514 180 0.506167 345 0.018951
-419 180 0.537174 273 0.065792 271 0.007172 312 0.389862
-420 229 0.023978 180 0.318515 271 0.230926 312 0.426581
-421 229 0.103496 180 0.125875 271 0.328967 312 0.441662
-422 229 0.151463 271 0.305196 228 0.059709 312 0.483632
-423 229 0.138088 271 0.074225 228 0.232526 312 0.555161
-424 171 0.110838 229 0.088422 228 0.204195 312 0.596544
-425 171 0.267171 229 0.026575 228 0.074150 312 0.632104
-426 171 0.280723 229 0.083977 228 0.032954 312 0.602346
-427 171 0.137420 229 0.201023 228 0.123388 312 0.538169
-428 229 0.315581 271 0.020292 228 0.177342 312 0.486785
-429 229 0.372087 180 0.010258 271 0.220239 312 0.397416
-430 229 0.327692 180 0.206357 271 0.120269 312 0.345682
-431 180 0.406812 312 0.319170 229 0.273111 345 0.000908
-432 180 0.444848 312 0.211276 229 0.213402 345 0.130473
-433 180 0.459732 312 0.121359 229 0.148242 345 0.270667
-434 180 0.450890 312 0.052873 229 0.080135 345 0.416102
-435 180 0.418662 312 0.008451 229 0.011697 345 0.561190
-436 179 0.026366 180 0.276256 95 0.039959 345 0.657418
-437 179 0.066409 180 0.113403 95 0.066733 345 0.753455
-438 96 0.005572 179 0.135786 208 0.064738 345 0.793904
-439 96 0.172558 179 0.076840 208 0.034186 345 0.716416
-440 96 0.256814 312 0.065044 204 0.027024 345 0.651118
-441 205 0.053630 312 0.059246 96 0.218793 345 0.668332
-442 208 0.047885 207 0.025650 96 0.148838 345 0.777626
-443 95 0.004898 330 0.003454 208 0.095585 345 0.896064
-444 180 0.054061 330 0.126246 95 0.015416 345 0.804276
-445 229 0.059847 330 0.164769 180 0.109993 345 0.665392
-446 229 0.149006 330 0.166847 180 0.137331 345 0.546816
-447 229 0.248801 330 0.141098 180 0.154948 345 0.455153
-448 229 0.355396 330 0.088516 180 0.162168 345 0.393920
-449 229 0.464696 330 0.011119 180 0.158712 345 0.365473
-450 180 0.138067 312 0.107385 229 0.516484 345 0.238064
-451 180 0.105218 312 0.250292 229 0.544104 345 0.100387
-452 229 0.554155 180 0.030518 271 0.027134 312 0.388193
-453 171 0.075928 229 0.439236 228 0.038765 312 0.446071
-454 171 0.199721 229 0.282564 228 0.011834 312 0.505881
-455 171 0.285291 230 0.027155 229 0.113654 312 0.573900
-456 171 0.257787 230 0.099691 229 0.101289 312 0.541233
-457 171 0.159225 230 0.128847 229 0.264196 312 0.447731
-458 171 0.041679 230 0.162550 229 0.423305 312 0.372465
-459 229 0.504561 312 0.308364 230 0.104354 314 0.082721
-460 312 0.255152 314 0.205348 229 0.531248 345 0.008252
-461 312 0.101394 314 0.203761 229 0.524644 345 0.170201
-462 314 0.203196 330 0.021364 229 0.478908 345 0.296532
-463 314 0.206788 330 0.099110 229 0.370150 345 0.323953
-464 314 0.207398 330 0.149733 229 0.260173 345 0.382696
-465 314 0.204997 330 0.171290 229 0.153205 345 0.470508
-466 314 0.199680 330 0.162952 229 0.053358 345 0.584010
-467 207 0.047434 330 0.116730 314 0.154461 345 0.681374
-468 207 0.146911 330 0.033277 314 0.066037 345 0.753775
-469 96 0.058472 207 0.142926 205 0.044620 345 0.753983
-470 205 0.123439 312 0.035183 96 0.152013 345 0.689365
-471 205 0.190410 312 0.029161 96 0.108862 345 0.671566
-472 207 0.151409 314 0.017278 205 0.153753 345 0.677559
-473 207 0.164754 314 0.171427 205 0.077909 345 0.585910
-474 207 0.136356 314 0.327077 205 0.020507 345 0.516061
-475 207 0.053632 330 0.015364 314 0.470294 345 0.460710
-476 314 0.552408 330 0.017738 229 0.049276 345 0.380578
-477 312 0.016721 314 0.577362 229 0.140339 345 0.265578
-478 312 0.084494 314 0.588663 229 0.209635 345 0.117208
-479 229 0.265341 312 0.154150 230 0.021051 314 0.559457
-480 229 0.300259 312 0.156956 230 0.119742 314 0.423044
-481 229 0.320316 312 0.187754 230 0.209776 314 0.282154
-482 229 0.324742 312 0.245360 230 0.287694 314 0.142203
-483 229 0.313368 312 0.327562 230 0.350502 314 0.008567
-484 171 0.124072 230 0.265099 229 0.193317 312 0.417512
-485 171 0.239865 230 0.169151 229 0.065156 312 0.525827
-486 171 0.234257 230 0.224959 229 0.010754 312 0.530031
-487 171 0.113068 230 0.374572 229 0.086605 312 0.425755
-488 229 0.146430 312 0.337769 230 0.492606 314 0.023195
-489 229 0.112270 312 0.258350 230 0.468559 314 0.160821
-490 229 0.070476 312 0.203027 230 0.422450 314 0.304047
-491 229 0.022651 312 0.173926 230 0.356052 314 0.447370
-492 230 0.252436 312 0.159288 74 0.020168 314 0.568108
-493 230 0.117834 312 0.161162 74 0.057398 314 0.663606
-494 74 0.065616 312 0.173229 205 0.035197 314 0.725958
-495 205 0.162080 314 0.656899 312 0.103678 345 0.077344
-496 205 0.210953 314 0.526295 312 0.016809 345 0.245943
-497 207 0.059854 314 0.370567 205 0.223721 345 0.345859
-498 207 0.104646 314 0.205596 205 0.237573 345 0.452185
-499 207 0.110008 314 0.040815 205 0.263730 345 0.585446
-500 205 0.244345 312 0.042100 96 0.095916 345 0.617639
-501 205 0.277036 312 0.072026 96 0.115139 345 0.535799
-502 207 0.041621 314 0.023569 205 0.389041 345 0.545769
-503 207 0.005365 314 0.180560 205 0.419494 345 0.394580
-504 205 0.420749 314 0.329635 312 0.049364 345 0.200252
-505 74 0.001317 312 0.126298 205 0.404609 314 0.467776
-506 74 0.146892 312 0.137532 205 0.211383 314 0.504192
-507 74 0.282499 312 0.172374 205 0.018026 314 0.527101
-508 230 0.131540 312 0.152240 74 0.264062 314 0.452157
-509 230 0.265878 312 0.150539 74 0.222861 314 0.360722
-510 230 0.383742 312 0.175618 74 0.175377 314 0.265263
-511 230 0.480601 312 0.226516 74 0.123433 314 0.169449
-512 230 0.552733 312 0.301277 74 0.069027 314 0.076963
-513 230 0.592809 202 0.009954 74 0.005029 312 0.392209
-514 171 0.103696 201 0.028462 230 0.426643 312 0.441198
-515 171 0.209125 201 0.038455 230 0.239761 312 0.512659
-516 171 0.189342 201 0.084752 230 0.223447 312 0.502459
-517 171 0.064891 201 0.119278 230 0.394641 312 0.421191
-518 230 0.492354 201 0.051728 202 0.091665 312 0.364253
-519 230 0.481340 202 0.107880 74 0.093189 312 0.317591
-520 230 0.438286 202 0.035924 74 0.236077 312 0.289712
-521 230 0.355000 312 0.265160 74 0.337517 314 0.042323
-522 230 0.235366 312 0.245595 74 0.394989 314 0.124050
-523 230 0.100430 312 0.249161 74 0.439562 314 0.210848
-524 74 0.422417 312 0.249513 205 0.057002 314 0.271068
-525 74 0.278692 312 0.210195 205 0.248098 314 0.263015
-526 74 0.119934 312 0.191693 205 0.437651 314 0.250722
-527 205 0.563906 314 0.205866 312 0.165882 345 0.064345
-528 205 0.534756 314 0.084046 312 0.087566 345 0.293631
-529 205 0.451612 312 0.043454 96 0.053570 345 0.451364
-530 205 0.283503 312 0.114387 96 0.163611 345 0.438499
-531 205 0.262764 312 0.162730 96 0.233946 345 0.340560
-532 205 0.410929 312 0.138286 96 0.191542 345 0.259243
-533 205 0.556989 312 0.200102 96 0.081004 345 0.161905
-534 205 0.662456 314 0.026262 312 0.287390 345 0.023892
-535 74 0.155241 312 0.313255 205 0.513263 314 0.018240
-536 74 0.317923 312 0.345267 205 0.332113 314 0.004696
-537 74 0.461051 203 0.005315 205 0.143658 312 0.389975
-538 230 0.016804 202 0.027162 74 0.532016 312 0.424017
-539 230 0.109711 202 0.121941 74 0.397384 312 0.370963
-540 230 0.197335 202 0.200702 74 0.260258 312 0.341705
-541 230 0.276304 202 0.260420 74 0.125908 312 0.337368
-542 230 0.343432 201 0.000641 202 0.297896 312 0.358031
-543 230 0.361452 201 0.145741 202 0.109550 312 0.383257
-544 171 0.055342 201 0.196526 230 0.313886 312 0.434245
-545 171 0.184474 201 0.124132 230 0.182280 312 0.509113
-546 171 0.195263 201 0.150601 230 0.122526 312 0.531610
-547 171 0.076505 201 0.248445 230 0.196675 312 0.478374
-548 230 0.221578 201 0.261306 202 0.069910 312 0.447205
-549 230 0.165406 201 0.147729 202 0.247444 312 0.439422
-550 230 0.105744 201 0.012154 202 0.427071 312 0.455031
-551 230 0.011073 202 0.404855 74 0.109782 312 0.474289
-552 202 0.274074 203 0.156583 74 0.146993 312 0.422350
-553 202 0.112519 203 0.328749 74 0.178828 312 0.379903
-554 74 0.159213 203 0.426608 205 0.068609 312 0.345570
-555 74 0.038073 203 0.388960 205 0.265181 312 0.307787
-556 203 0.306403 96 0.078479 205 0.338389 312 0.276729
-557 203 0.193327 96 0.188636 205 0.355646 312 0.262390
-558 203 0.063266 96 0.290349 205 0.373849 312 0.272536
-559 205 0.323071 312 0.230417 96 0.351400 345 0.095112
-560 205 0.217974 312 0.209699 96 0.315443 345 0.256884
-561 203 0.196941 96 0.470839 205 0.079775 312 0.252446
-562 204 0.013574 96 0.403045 203 0.347047 312 0.236335
-563 204 0.074081 96 0.265039 203 0.416867 312 0.244012
-564 204 0.121101 96 0.127658 203 0.473980 312 0.277262
-565 204 0.148553 203 0.512351 129 0.005310 312 0.333786
-566 204 0.029056 203 0.417003 129 0.172703 312 0.381237
-567 129 0.230335 203 0.236437 202 0.105097 312 0.428130
-568 129 0.237101 203 0.035404 202 0.238364 312 0.489131
-569 202 0.201682 201 0.144074 129 0.131409 312 0.522835
-570 230 0.003065 201 0.299852 202 0.131461 312 0.565622
-571 171 0.016444 201 0.359318 230 0.077815 312 0.546423
-572 171 0.125155 201 0.267134 230 0.060854 312 0.546857
-573 171 0.220065 201 0.160128 230 0.053285 312 0.566522
-574 205 0.209647 312 0.135835 96 0.378019 345 0.276499
-575 205 0.322215 312 0.222823 96 0.357834 345 0.097128
-576 205 0.410073 312 0.130692 96 0.197975 345 0.261260
-577 205 0.254437 312 0.088866 96 0.296522 345 0.360174
-578 205 0.275176 312 0.040523 96 0.226187 345 0.458114
-579 205 0.450756 312 0.035861 96 0.060003 345 0.453380
-580 207 0.053678 314 0.017393 205 0.386311 345 0.542617
-581 96 0.178169 207 0.002347 205 0.267181 345 0.552302
-582 96 0.166341 207 0.040568 205 0.209617 345 0.583474
-583 207 0.122065 314 0.034640 205 0.261001 345 0.582294
-584 207 0.163467 314 0.011102 205 0.151024 345 0.674407
-585 96 0.182485 207 0.057092 205 0.144928 345 0.615496
-586 205 0.095204 312 0.068641 96 0.379295 345 0.456860
-587 96 0.224147 207 0.049401 205 0.082962 345 0.643490
-588 96 0.066782 207 0.152624 205 0.037452 345 0.743142
-589 208 0.054351 207 0.027654 96 0.153237 345 0.764759
-590 96 0.284981 207 0.018670 205 0.033153 345 0.663197
-591 179 0.008256 96 0.322331 204 0.015938 345 0.653475
-592 96 0.178195 179 0.075692 208 0.041652 345 0.704461
-593 179 0.144322 96 0.214303 204 0.012672 345 0.628704
-594 179 0.033753 96 0.316921 204 0.043129 345 0.606197
-595 179 0.040230 96 0.318079 204 0.080180 345 0.561511
-596 179 0.157030 96 0.216573 204 0.085348 345 0.541049
-597 179 0.130498 96 0.231381 204 0.166298 345 0.471823
-598 179 0.026705 96 0.325628 204 0.121447 345 0.526220
-599 96 0.364305 312 0.023791 204 0.138771 345 0.473133
-600 179 0.068767 96 0.256470 204 0.243197 345 0.431565
-601 96 0.389963 312 0.093689 204 0.218177 345 0.298171
-602 205 0.010846 312 0.204362 96 0.568196 345 0.216596
-603 205 0.077817 312 0.198341 96 0.525047 345 0.198795
-604 203 0.045802 96 0.629781 205 0.023378 312 0.301039
-605 203 0.011623 96 0.511066 205 0.190347 312 0.286965
-606 205 0.147627 312 0.174277 96 0.458264 345 0.219832
-607 205 0.323071 312 0.230417 96 0.351400 345 0.095112
-608 203 0.013293 96 0.504032 205 0.189735 312 0.292940
-609 205 0.410929 312 0.138286 96 0.191542 345 0.259243
-610 205 0.451612 312 0.043454 96 0.053570 345 0.451364
-611 207 0.041621 314 0.023569 205 0.389041 345 0.545769
-612 207 0.110008 314 0.040815 205 0.263730 345 0.585446
-613 207 0.151409 314 0.017278 205 0.153753 345 0.677559
-614 96 0.058472 207 0.142926 205 0.044620 345 0.753983
-615 208 0.047885 207 0.025650 96 0.148838 345 0.777626
-616 96 0.172558 179 0.076840 208 0.034186 345 0.716416
-617 179 0.142133 96 0.200291 204 0.019961 345 0.637614
-618 179 0.154842 96 0.202562 204 0.092637 345 0.549959
-619 179 0.128310 96 0.217369 204 0.173587 345 0.480733
-620 179 0.066579 96 0.242459 204 0.250487 345 0.440476
-621 96 0.387838 312 0.104613 204 0.215420 345 0.292129
-622 203 0.047473 96 0.622748 205 0.022766 312 0.307013
-623 205 0.217509 312 0.205575 96 0.318937 345 0.257979
-624 205 0.326342 312 0.259428 96 0.326823 345 0.087408
-625 205 0.414200 312 0.167296 96 0.166965 345 0.251539
-626 205 0.262299 312 0.158605 96 0.237441 345 0.341655
-627 205 0.283038 312 0.110262 96 0.167105 345 0.439594
-628 205 0.454882 312 0.072465 96 0.028993 345 0.443660
-629 205 0.397163 314 0.046557 312 0.003297 345 0.552983
-630 205 0.276571 312 0.067901 96 0.118633 345 0.536894
-631 205 0.243880 312 0.037975 96 0.099410 345 0.618734
-632 207 0.063945 314 0.064409 205 0.274157 345 0.597489
-633 207 0.105346 314 0.040872 205 0.164180 345 0.689602
-634 205 0.189945 312 0.025037 96 0.112357 345 0.672662
-635 205 0.104327 312 0.149569 96 0.310735 345 0.435370
-636 205 0.122974 312 0.031059 96 0.155507 345 0.690460
-637 96 0.026726 207 0.105875 205 0.072003 345 0.795396
-638 208 0.023184 207 0.017998 96 0.132035 345 0.826784
-639 205 0.053165 312 0.055121 96 0.222287 345 0.669427
-640 96 0.257968 312 0.059110 204 0.028522 345 0.654400
-641 96 0.151021 179 0.081229 208 0.005664 345 0.762086
-642 179 0.133773 96 0.146762 204 0.047810 345 0.671655
-643 179 0.013656 96 0.188241 204 0.110075 345 0.688027
-644 179 0.020134 96 0.189400 204 0.147126 345 0.643341
-645 179 0.146482 96 0.149033 204 0.120486 345 0.583999
-646 179 0.119950 96 0.163841 204 0.201436 345 0.514774
-647 179 0.006608 96 0.196948 204 0.188393 345 0.608051
-648 96 0.344788 312 0.124117 204 0.113450 345 0.417645
-649 179 0.058219 96 0.188930 204 0.278335 345 0.474516
-650 96 0.379719 312 0.146347 204 0.204887 345 0.269047
-651 205 0.018708 312 0.274101 96 0.509115 345 0.198076
-652 205 0.085679 312 0.268080 96 0.465965 345 0.180275
-653 203 0.053854 96 0.595877 205 0.020430 312 0.329839
-654 203 0.019674 96 0.477162 205 0.187399 312 0.315765
-655 205 0.155489 312 0.244016 96 0.399182 345 0.201312
-656 214 0.147898 94 0.306713 213 0.146423 311 0.398967
-657 214 0.321268 94 0.272628 213 0.085818 311 0.320285
-658 110 0.195203 219 0.036076 72 0.446634 311 0.322086
-659 110 0.200434 219 0.125439 72 0.355022 311 0.319105
-660 110 0.197910 219 0.209735 72 0.249806 311 0.342549
-661 110 0.187726 219 0.285726 72 0.135032 311 0.391516
-662 110 0.170277 219 0.350489 72 0.015108 311 0.464126
-663 215 0.152561 219 0.343694 110 0.056882 311 0.446863
-664 215 0.267728 67 0.064948 219 0.251625 311 0.415700
-665 215 0.330104 67 0.179066 219 0.099113 311 0.391717
-666 215 0.319820 214 0.062746 67 0.243324 311 0.374110
-667 215 0.193571 214 0.231595 67 0.223894 311 0.350940
-668 215 0.059561 214 0.391806 67 0.195695 311 0.352939
-669 67 0.117210 214 0.461290 213 0.076281 311 0.345219
-670 67 0.000951 214 0.454304 213 0.208908 311 0.335836
-671 214 0.300978 94 0.146518 213 0.235410 311 0.317094
-672 214 0.137554 94 0.242422 213 0.222684 311 0.397340
-673 214 0.101100 94 0.197006 213 0.299259 311 0.402635
-674 214 0.229471 94 0.057433 213 0.385616 311 0.327480
-675 67 0.103067 214 0.236779 213 0.340100 311 0.320053
-676 67 0.247178 214 0.184434 213 0.243257 311 0.325132
-677 67 0.381450 214 0.124945 213 0.137385 311 0.356220
-678 67 0.500719 214 0.060600 213 0.026555 311 0.412127
-679 311 0.426712 219 0.082615 280 0.000640 67 0.490033
-680 311 0.412161 219 0.208576 280 0.048351 67 0.330913
-681 311 0.420476 219 0.326238 280 0.094264 67 0.159022
-682 219 0.413371 311 0.449558 280 0.113222 176 0.023849
-683 72 0.054269 219 0.370710 176 0.139444 311 0.435577
-684 72 0.197347 219 0.371028 176 0.071143 311 0.360482
-685 72 0.332844 219 0.356759 176 0.000168 311 0.310230
-686 110 0.064007 219 0.226876 72 0.412289 311 0.296827
-687 110 0.125652 219 0.087789 72 0.475830 311 0.310729
-688 110 0.120823 311 0.308146 72 0.523308 337 0.047723
-689 110 0.064208 219 0.117917 72 0.510398 311 0.307477
-690 72 0.441893 219 0.196278 176 0.062483 311 0.299346
-691 72 0.313010 219 0.164877 176 0.193600 311 0.328513
-692 72 0.172103 219 0.126810 176 0.317335 311 0.383752
-693 72 0.024586 219 0.083539 176 0.428935 311 0.462940
-694 219 0.115173 311 0.449813 280 0.154311 176 0.280703
-695 219 0.157617 311 0.435629 280 0.333314 176 0.073441
-696 311 0.433204 219 0.092254 280 0.365532 67 0.109010
-697 67 0.252599 196 0.041060 280 0.275042 311 0.431299
-698 67 0.328223 196 0.201273 280 0.066173 311 0.404331
-699 213 0.138166 196 0.183521 67 0.309937 311 0.368376
-700 213 0.334114 196 0.081186 67 0.242492 311 0.342207
-701 67 0.147196 214 0.025279 213 0.488314 311 0.339210
-702 214 0.117637 94 0.018934 213 0.513568 311 0.349861
-703 214 0.044088 94 0.177380 213 0.364487 311 0.414045
-704 213 0.378672 311 0.405517 94 0.179396 346 0.036415
-705 213 0.578784 311 0.363677 94 0.031849 346 0.025691
-706 213 0.460239 196 0.141767 67 0.020362 311 0.377632
-707 213 0.261261 196 0.292441 67 0.057962 311 0.388336
-708 213 0.052500 196 0.431930 67 0.092952 311 0.422618
-709 67 0.031275 196 0.359760 280 0.166416 311 0.442549
-710 280 0.304656 311 0.428293 196 0.196722 334 0.070329
-711 280 0.392288 311 0.414220 196 0.020162 334 0.173330
-712 176 0.151496 311 0.388836 280 0.246056 334 0.213612
-713 176 0.316333 311 0.382634 280 0.062211 334 0.238822
-714 72 0.086555 311 0.359110 176 0.361003 334 0.193331
-715 72 0.213236 311 0.339629 176 0.337422 334 0.109713
-716 72 0.331795 311 0.345531 176 0.300471 334 0.022203
-717 72 0.451598 219 0.067119 176 0.157864 311 0.323419
-718 110 0.020222 219 0.121873 72 0.545079 311 0.312826
-719 110 0.000390 219 0.099058 72 0.574591 311 0.325961
-720 72 0.491360 311 0.324120 176 0.145903 334 0.038617
-721 72 0.409731 311 0.269359 176 0.147065 334 0.173846
-722 72 0.312430 311 0.242679 176 0.142172 334 0.302719
-723 72 0.203195 311 0.245108 176 0.131413 334 0.420283
-724 72 0.086224 311 0.276553 176 0.115204 334 0.522019
-725 176 0.051873 311 0.319672 280 0.048742 334 0.579713
-726 280 0.044788 311 0.321044 196 0.125326 334 0.508842
-727 196 0.258258 334 0.382922 311 0.311135 346 0.047685
-728 196 0.329429 334 0.221893 311 0.295365 346 0.153313
-729 196 0.387624 334 0.052346 311 0.306250 346 0.253780
-730 196 0.288185 311 0.284258 213 0.147029 346 0.280528
-731 196 0.114825 311 0.263391 213 0.352964 346 0.268821
-732 213 0.444691 311 0.263368 94 0.063854 346 0.228087
-733 213 0.310314 311 0.354381 94 0.195712 346 0.139594
-734 213 0.222724 311 0.311640 94 0.236526 346 0.229109
-735 213 0.272878 311 0.179531 94 0.143917 346 0.403675
-736 213 0.286208 311 0.154463 94 0.000315 346 0.559014
-737 196 0.141643 311 0.145662 213 0.062698 346 0.649996
-738 196 0.119252 334 0.132743 311 0.103411 346 0.644594
-739 196 0.031229 334 0.311224 311 0.069982 346 0.587565
-740 311 0.072135 334 0.396607 295 0.055520 346 0.475737
-741 311 0.109664 334 0.422857 295 0.138943 346 0.328537
-742 311 0.180987 334 0.432427 295 0.217324 346 0.169262
-743 311 0.283362 334 0.424949 295 0.287655 346 0.004035
-744 295 0.208574 311 0.244719 72 0.166218 334 0.380489
-745 295 0.118777 311 0.231039 72 0.329646 334 0.320538
-746 295 0.025188 311 0.247065 72 0.479837 334 0.247910
-747 72 0.554057 311 0.305417 176 0.035600 334 0.104927
-748 110 0.007732 219 0.052944 72 0.594442 311 0.344882
-749 110 0.030532 311 0.359270 72 0.601985 337 0.008213
-750 295 0.120519 311 0.346583 72 0.516143 334 0.016755
-751 295 0.299738 311 0.326045 72 0.342100 334 0.032117
-752 295 0.468211 311 0.331563 72 0.154341 334 0.045885
-753 311 0.322542 334 0.052672 295 0.586167 346 0.038618
-754 311 0.183012 334 0.038228 295 0.553148 346 0.225612
-755 311 0.074456 334 0.021883 295 0.499172 346 0.404488
-756 311 0.001046 334 0.004269 295 0.426313 346 0.568372
-757 180 0.026016 330 0.015328 295 0.304153 346 0.654504
-758 180 0.014632 330 0.030316 295 0.199824 346 0.755228
-759 295 0.090214 330 0.044234 311 0.035320 346 0.830232
-760 311 0.083317 330 0.039152 94 0.040431 346 0.837100
-761 213 0.019986 311 0.075186 94 0.168621 346 0.736207
-762 213 0.089500 311 0.124924 94 0.259848 346 0.525727
-763 213 0.129240 311 0.283803 94 0.295628 346 0.291329
-764 213 0.044091 311 0.275105 94 0.364018 346 0.316786
-765 311 0.142633 330 0.059757 94 0.347004 346 0.450605
-766 311 0.150207 330 0.171509 94 0.228500 346 0.449785
-767 311 0.190209 330 0.277050 94 0.101221 346 0.431521
-768 295 0.026631 330 0.358051 311 0.231552 346 0.383767
-769 295 0.142268 330 0.377420 311 0.202312 346 0.278001
-770 295 0.252431 330 0.382668 311 0.203505 346 0.161396
-771 295 0.352886 330 0.373596 311 0.235082 346 0.038436
-772 87 0.047159 311 0.238580 295 0.440294 330 0.273967
-773 87 0.113629 311 0.245470 295 0.511009 330 0.129892
-774 87 0.147045 238 0.026711 295 0.547846 311 0.278398
-775 72 0.028326 295 0.456769 238 0.203724 311 0.311182
-776 72 0.242886 295 0.286375 238 0.163434 311 0.307305
-777 72 0.447525 295 0.105350 238 0.117171 311 0.329954
-778 110 0.007320 311 0.326228 72 0.598129 337 0.068323
-779 110 0.001011 311 0.292462 72 0.581355 337 0.125172
-780 72 0.421722 295 0.018983 238 0.232910 311 0.326386
-781 72 0.205426 295 0.160988 238 0.331462 311 0.302124
-782 87 0.015143 238 0.395979 295 0.290782 311 0.298096
-783 87 0.190916 238 0.215596 295 0.341651 311 0.251837
-784 87 0.359811 238 0.026577 295 0.379573 311 0.234039
-785 87 0.338896 311 0.229226 295 0.315797 330 0.116082
-786 87 0.276412 311 0.251085 295 0.225718 330 0.246784
-787 87 0.203393 311 0.301400 295 0.126958 330 0.368249
-788 87 0.122640 311 0.378235 295 0.023313 330 0.475812
-789 94 0.091059 311 0.363004 87 0.058488 330 0.487448
-790 311 0.336648 330 0.455434 94 0.204739 346 0.003178
-791 311 0.265264 330 0.311665 94 0.309833 346 0.113239
-792 311 0.221886 330 0.156299 94 0.403028 346 0.218787
-793 311 0.295733 330 0.015227 94 0.419307 346 0.269732
-794 311 0.340897 330 0.040533 94 0.455309 346 0.163261
-795 311 0.310477 330 0.205934 94 0.473647 346 0.009941
-796 94 0.411070 311 0.269283 87 0.103680 330 0.215967
-797 94 0.332634 311 0.249315 87 0.208887 330 0.209163
-798 94 0.241423 311 0.257862 87 0.306152 330 0.194564
-799 94 0.140941 311 0.294592 87 0.391737 330 0.172730
-800 94 0.035048 311 0.358098 87 0.462353 330 0.144500
-801 87 0.532116 311 0.354273 295 0.064394 330 0.049217
-802 87 0.471597 238 0.109633 295 0.099153 311 0.319616
-803 87 0.318606 238 0.284158 295 0.093273 311 0.303964
-804 87 0.153831 238 0.447413 295 0.083988 311 0.314769
-805 72 0.020950 295 0.064729 238 0.569735 311 0.344586
-806 72 0.230379 311 0.318763 238 0.428570 337 0.022288
-807 72 0.410408 311 0.275066 238 0.204554 337 0.109971
-808 110 0.012562 311 0.263113 72 0.554218 337 0.170108
-809 110 0.040217 311 0.242649 72 0.520846 337 0.196287
-810 72 0.431212 311 0.218846 238 0.089579 337 0.260363
-811 72 0.260583 311 0.237144 238 0.261648 337 0.240624
-812 72 0.079466 311 0.284920 238 0.424362 337 0.211252
-813 238 0.400469 311 0.294552 87 0.091708 337 0.213270
-814 238 0.231920 311 0.281457 87 0.249591 337 0.237031
-815 238 0.054386 311 0.295842 87 0.398296 337 0.251476
-816 87 0.480761 311 0.277017 216 0.079096 337 0.163125
-817 87 0.522851 311 0.259706 216 0.189528 337 0.027916
-818 216 0.170726 87 0.486580 94 0.121520 311 0.221173
-819 216 0.113767 87 0.416781 94 0.269888 311 0.199564
-820 216 0.052190 87 0.331206 94 0.408176 311 0.208428
-821 94 0.517660 311 0.245156 87 0.225768 330 0.011417
-822 94 0.547134 311 0.300377 87 0.078671 330 0.073817
-823 311 0.383945 330 0.038072 94 0.493270 346 0.084713
-824 311 0.418326 330 0.008223 94 0.527412 346 0.046039
-825 216 0.052746 87 0.086649 94 0.552830 311 0.307774
-826 216 0.174213 87 0.175483 94 0.413452 311 0.236851
-827 216 0.288738 87 0.257815 94 0.258476 311 0.194970
-828 216 0.391919 87 0.330482 94 0.093860 311 0.183740
-829 87 0.354940 311 0.173416 216 0.405430 337 0.066214
-830 87 0.320840 311 0.156454 216 0.309179 337 0.213527
-831 87 0.274793 311 0.171741 216 0.201092 337 0.352374
-832 87 0.218569 311 0.218692 216 0.085322 337 0.477418
-833 238 0.053361 311 0.269890 87 0.132463 337 0.544285
-834 72 0.015718 311 0.274311 238 0.214217 337 0.495755
-835 72 0.182279 311 0.211759 238 0.120548 337 0.485414
-836 72 0.341362 311 0.179661 238 0.022944 337 0.456032
-837 110 0.035313 311 0.189718 72 0.430701 337 0.344269
-838 110 0.079765 311 0.234185 72 0.486324 337 0.199725
-839 110 0.125186 311 0.239010 72 0.455904 337 0.179900
-840 110 0.124409 311 0.199181 72 0.371030 337 0.305380
-841 110 0.118522 311 0.190194 72 0.271948 337 0.419336
-842 110 0.107752 311 0.212390 72 0.162465 337 0.517393
-843 110 0.092512 311 0.264919 72 0.046790 337 0.595780
-844 69 0.057138 311 0.329613 284 0.068298 337 0.544951
-845 216 0.124168 311 0.262454 69 0.071124 337 0.542254
-846 87 0.028505 311 0.191897 216 0.292652 337 0.486946
-847 87 0.079285 311 0.176221 216 0.398981 337 0.345514
-848 87 0.127401 311 0.192036 216 0.490021 337 0.190542
-849 87 0.171002 311 0.238732 216 0.562276 337 0.027991
-850 216 0.460347 87 0.135041 94 0.152026 311 0.252586
-851 216 0.309045 87 0.079019 94 0.329816 311 0.282120
-852 216 0.145621 87 0.020205 94 0.495219 311 0.338955
-853 214 0.027202 94 0.509737 213 0.025049 311 0.438012
-854 214 0.088175 94 0.451383 213 0.039463 311 0.420979
-855 216 0.133232 94 0.425074 214 0.107231 311 0.334463
-856 216 0.335314 94 0.263328 214 0.070904 311 0.330455
-857 216 0.524478 94 0.091925 214 0.031437 311 0.352160
-858 69 0.073729 216 0.543751 215 0.011564 311 0.370956
-859 69 0.243937 216 0.349254 215 0.022483 311 0.384326
-860 69 0.404748 216 0.141547 215 0.032187 311 0.421518
-861 215 0.019450 284 0.068455 69 0.448810 311 0.463285
-862 69 0.262650 311 0.454241 284 0.227585 337 0.055523
-863 69 0.059673 311 0.446288 284 0.354397 337 0.139642
-864 284 0.129742 311 0.355072 110 0.186818 337 0.328368
-865 110 0.268600 311 0.275381 72 0.083868 337 0.372151
-866 110 0.244900 311 0.239685 72 0.210194 337 0.305221
-867 110 0.211460 311 0.233272 72 0.328493 337 0.226775
-868 110 0.169563 311 0.256389 72 0.434219 337 0.139829
-869 110 0.206143 311 0.283677 72 0.424570 337 0.085610
-870 110 0.283212 311 0.286799 72 0.309566 337 0.120422
-871 110 0.349069 311 0.317394 72 0.182716 337 0.150821
-872 110 0.401182 311 0.374286 72 0.048895 337 0.175638
-873 284 0.246666 311 0.485865 110 0.257254 337 0.010214
-874 215 0.141918 110 0.066262 284 0.337991 311 0.453829
-875 215 0.236717 284 0.235300 69 0.114025 311 0.413959
-876 215 0.297173 284 0.033269 69 0.284375 311 0.385184
-877 69 0.192470 216 0.177534 215 0.294062 311 0.335934
-878 69 0.043977 216 0.383154 215 0.269165 311 0.303705
-879 216 0.411661 214 0.127557 215 0.155124 311 0.305658
-880 216 0.357210 214 0.303049 215 0.002277 311 0.337464
-881 216 0.204891 94 0.188866 214 0.286445 311 0.319798
-882 216 0.043395 94 0.373785 214 0.255698 311 0.327122
-883 214 0.130559 94 0.380089 213 0.082085 311 0.407267
-884 216 0.008013 94 0.173979 214 0.478996 311 0.339012
-885 216 0.098173 214 0.530013 215 0.017429 311 0.354385
-886 216 0.107064 214 0.394440 215 0.172940 311 0.325556
-887 216 0.112016 214 0.243739 215 0.321435 311 0.322811
-888 216 0.112839 214 0.083699 215 0.457208 311 0.346255
-889 69 0.066231 216 0.008126 215 0.526130 311 0.399513
-890 215 0.427000 110 0.097186 284 0.062886 311 0.412927
-891 215 0.276803 110 0.275585 284 0.015445 311 0.432167
-892 215 0.097703 219 0.034611 110 0.398890 311 0.468796
-893 110 0.430808 219 0.048597 72 0.064803 311 0.455792
-894 110 0.388898 219 0.023425 72 0.194628 311 0.393050
-895 110 0.328743 311 0.351613 72 0.317129 337 0.002515
-896 110 0.229354 311 0.316718 72 0.428426 337 0.025502
-897 214 0.127253 94 0.432052 213 0.114857 311 0.325838
-898 216 0.039774 94 0.382696 214 0.257991 311 0.319539
-899 216 0.129612 94 0.433985 214 0.109524 311 0.326879
-900 214 0.084869 94 0.503346 213 0.072235 311 0.339549
-901 214 0.023896 94 0.561700 213 0.057821 311 0.356583
-902 216 0.143197 87 0.018861 94 0.505087 311 0.332856
-903 216 0.050323 87 0.085306 94 0.562697 311 0.301675
-904 213 0.018136 311 0.328871 94 0.584892 346 0.068101
-905 311 0.286356 330 0.015868 94 0.561744 346 0.136032
-906 94 0.554210 311 0.293805 87 0.075791 330 0.076194
-907 311 0.300444 330 0.203652 94 0.480687 346 0.015217
-908 311 0.243308 330 0.018329 94 0.523783 346 0.214580
-909 213 0.217169 311 0.312034 94 0.415377 346 0.055420
-910 213 0.009050 311 0.202203 94 0.482295 346 0.306451
-911 311 0.211853 330 0.154017 94 0.410068 346 0.224063
-912 311 0.132600 330 0.057475 94 0.354044 346 0.455881
-913 213 0.072895 311 0.190435 94 0.415031 346 0.321640
-914 213 0.158044 311 0.199133 94 0.346640 346 0.296183
-915 213 0.092462 311 0.116219 94 0.265093 346 0.526226
-916 213 0.275839 311 0.170826 94 0.149161 346 0.404174
-917 213 0.251529 311 0.226970 94 0.287539 346 0.233963
-918 213 0.339118 311 0.269711 94 0.246724 346 0.144447
-919 213 0.447652 311 0.254664 94 0.069098 346 0.228586
-920 213 0.581745 311 0.354972 94 0.037094 346 0.026190
-921 213 0.407476 311 0.320847 94 0.230409 346 0.041268
-922 214 0.040782 94 0.229344 213 0.397259 311 0.332615
-923 214 0.117297 94 0.024276 213 0.516937 311 0.341490
-924 214 0.229132 94 0.062775 213 0.388985 311 0.319108
-925 214 0.097794 94 0.248970 213 0.332031 311 0.321205
-926 214 0.134248 94 0.294385 213 0.255456 311 0.315911
-927 214 0.300638 94 0.151861 213 0.238779 311 0.308722
-928 214 0.320929 94 0.277970 213 0.089187 311 0.311914
-929 214 0.144592 94 0.358676 213 0.179194 311 0.317538
-930 216 0.043395 94 0.373785 214 0.255698 311 0.327122
-931 214 0.321268 94 0.272628 213 0.085818 311 0.320285
-932 216 0.133232 94 0.425074 214 0.107231 311 0.334463
-933 216 0.145621 87 0.020205 94 0.495219 311 0.338955
-934 216 0.052746 87 0.086649 94 0.552830 311 0.307774
-935 94 0.547134 311 0.300377 87 0.078671 330 0.073817
-936 311 0.310477 330 0.205934 94 0.473647 346 0.009941
-937 311 0.221886 330 0.156299 94 0.403028 346 0.218787
-938 311 0.142633 330 0.059757 94 0.347004 346 0.450605
-939 213 0.089500 311 0.124924 94 0.259848 346 0.525727
-940 213 0.272878 311 0.179531 94 0.143917 346 0.403675
-941 213 0.444691 311 0.263368 94 0.063854 346 0.228087
-942 213 0.578784 311 0.363677 94 0.031849 346 0.025691
-943 214 0.117637 94 0.018934 213 0.513568 311 0.349861
-944 214 0.229471 94 0.057433 213 0.385616 311 0.327480
-945 214 0.300978 94 0.146518 213 0.235410 311 0.317094
-946 214 0.130375 94 0.382990 213 0.083915 311 0.402720
-947 216 0.057226 94 0.339742 214 0.246939 311 0.356094
-948 216 0.147063 94 0.391031 214 0.098471 311 0.363434
-949 214 0.087991 94 0.454284 213 0.041293 311 0.416431
-950 214 0.027017 94 0.512639 213 0.026879 311 0.433465
-951 216 0.154879 87 0.025339 94 0.457524 311 0.362258
-952 216 0.062004 87 0.091784 94 0.515135 311 0.331077
-953 311 0.412876 330 0.006983 94 0.531236 346 0.048905
-954 311 0.378496 330 0.036832 94 0.497094 346 0.087579
-955 94 0.520104 311 0.325486 87 0.089672 330 0.064738
-956 94 0.446685 311 0.342106 87 0.005575 330 0.205635
-957 311 0.335447 330 0.039293 94 0.459132 346 0.166127
-958 213 0.185611 311 0.404801 94 0.359486 346 0.050102
-959 311 0.290284 330 0.013988 94 0.423131 346 0.272598
-960 311 0.260214 330 0.165020 94 0.376134 346 0.198631
-961 311 0.180961 330 0.068478 94 0.320111 346 0.430450
-962 213 0.045699 311 0.270377 94 0.366867 346 0.317057
-963 213 0.130848 311 0.279075 94 0.298477 346 0.291600
-964 213 0.078187 311 0.158179 94 0.239813 346 0.523821
-965 213 0.261565 311 0.212785 94 0.123881 346 0.401768
-966 213 0.224333 311 0.306912 94 0.239375 346 0.229380
-967 213 0.311922 311 0.349653 94 0.198560 346 0.139865
-968 213 0.433378 311 0.296623 94 0.043818 346 0.226181
-969 213 0.567471 311 0.396931 94 0.011814 346 0.023784
-970 213 0.380281 311 0.400789 94 0.182245 346 0.036686
-971 214 0.043904 94 0.180282 213 0.366317 311 0.409497
-972 67 0.001165 214 0.117638 213 0.499706 311 0.381491
-973 214 0.230770 94 0.037024 213 0.372745 311 0.359461
-974 214 0.100915 94 0.199908 213 0.301089 311 0.398088
-975 214 0.137369 94 0.245324 213 0.224514 311 0.392793
-976 214 0.302276 94 0.126109 213 0.222539 311 0.349076
-977 214 0.322567 94 0.252219 213 0.072947 311 0.352267
-978 214 0.147713 94 0.309615 213 0.148253 311 0.394420
-979 211 0.027279 87 0.333237 212 0.441051 107 0.198433
-980 210 0.426975 314 0.264091 207 0.164176 330 0.144758
-981 210 0.082972 68 0.279493 211 0.299457 330 0.338078
-982 94 0.020133 212 0.248568 209 0.139771 346 0.591528
-983 95 0.007162 330 0.005699 208 0.405131 345 0.582009
-984 346 0.604885 213 0.121848 95 0.165609 209 0.107659
-985 95 0.228046 208 0.109362 179 0.156710 345 0.505882
-986 212 0.152776 68 0.070969 208 0.327510 330 0.448745
-987 209 0.099601 208 0.266068 95 0.175294 330 0.459037
-988 180 0.093173 330 0.314642 95 0.584508 345 0.007677
-989 180 0.275009 195 0.511619 95 0.091195 346 0.122177
-990 87 0.221245 311 0.241847 216 0.451407 337 0.085501
-991 74 0.161308 312 0.206326 205 0.285792 314 0.346574
-992 94 0.443059 311 0.272024 87 0.157395 330 0.127522
-993 207 0.117968 314 0.194257 205 0.149705 345 0.538070
-994 311 0.171473 330 0.132769 94 0.289286 346 0.406472
-995 208 0.054048 207 0.034674 96 0.058620 345 0.852658
-996 196 0.016883 311 0.137021 213 0.164081 346 0.682015
-997 179 0.233085 312 0.004309 180 0.022665 345 0.739941
-998 176 0.247220 72 0.360328 189 0.113434 188 0.279019
-999 171 0.096491 228 0.384466 199 0.517590 312 0.001452
-1000 72 0.374001 110 0.252652 187 0.167395 188 0.205952
-1001 199 0.214194 201 0.354795 171 0.391157 312 0.039854
-1002 72 0.052110 187 0.445368 110 0.083439 337 0.419083
-1003 292 0.118646 230 0.403829 201 0.306838 128 0.170687
-1004 69 0.023323 284 0.239260 108 0.126507 337 0.610910
-1005 74 0.322086 128 0.286822 230 0.341858 127 0.049235
-1006 107 0.050733 216 0.160678 69 0.315054 337 0.473535
-1007 230 0.215298 312 0.024523 74 0.662512 314 0.097667
-1008 87 0.168580 216 0.426890 107 0.238043 337 0.166487
-1009 74 0.214304 205 0.247891 206 0.167861 314 0.369944
-1010 87 0.209936 212 0.092086 94 0.482895 330 0.215083
-1011 207 0.499211 330 0.117803 314 0.180670 345 0.202316
-1012 195 0.203283 334 0.190945 196 0.285540 346 0.320232
-1013 179 0.559792 180 0.354292 95 0.021477 345 0.064439
-1014 280 0.188389 196 0.037298 93 0.067792 334 0.706521
-1015 180 0.441105 38 0.266483 178 0.133393 273 0.159018
-1016 175 0.245934 72 0.067160 176 0.495145 334 0.191761
-1017 271 0.278948 273 0.295820 172 0.236467 312 0.188764
-1018 87 0.332629 107 0.512965 106 0.115129 337 0.039277
-1019 104 0.159623 127 0.019132 206 0.394553 314 0.426692
-1020 87 0.297551 106 0.302043 66 0.328996 337 0.071411
-1021 104 0.193422 239 0.221409 127 0.542381 314 0.042788
-1022 240 0.134028 87 0.146891 66 0.553426 337 0.165654
-1023 127 0.411718 239 0.107633 282 0.188464 39 0.292184
-1024 66 0.054616 108 0.138508 148 0.255783 240 0.551093
-1025 230 0.073310 131 0.035240 39 0.430530 288 0.460920
-1026 68 0.463800 212 0.070618 211 0.021384 330 0.444199
-1027 87 0.319837 212 0.403236 94 0.244170 330 0.032758
-1028 210 0.091418 314 0.219981 207 0.491047 330 0.197554
-1029 87 0.097809 216 0.132348 107 0.557669 337 0.212174
-1030 74 0.097303 205 0.063227 206 0.481221 314 0.358249
-1031 106 0.386012 107 0.197298 69 0.095850 337 0.320840
-1032 206 0.134165 127 0.397027 74 0.253704 314 0.215104
-1033 239 0.146135 105 0.332764 282 0.433270 319 0.087832
-1034 105 0.341027 87 0.103631 239 0.113253 104 0.442089
-1035 87 0.491842 105 0.481060 211 0.021100 104 0.005998
-1036 104 0.568263 239 0.202848 127 0.017818 314 0.211070
-1037 105 0.339025 87 0.508490 211 0.032054 107 0.120431
-1038 104 0.511283 239 0.098487 127 0.003651 314 0.386579
-1039 105 0.192397 87 0.505062 211 0.221581 107 0.080961
-1040 87 0.142951 104 0.411125 210 0.151841 314 0.294082
-1041 87 0.137151 105 0.200037 211 0.230342 104 0.432470
-1042 87 0.518881 105 0.404188 211 0.049653 104 0.027278
-1043 87 0.043428 239 0.166572 104 0.546785 314 0.243215
-1044 105 0.139129 87 0.344065 239 0.097874 104 0.418932
-1045 105 0.336710 87 0.535728 211 0.072617 107 0.054945
-1046 87 0.047320 239 0.095557 104 0.508180 314 0.348943
-1047 87 0.537227 105 0.249854 211 0.204687 104 0.008232
-1048 87 0.184050 104 0.470660 210 0.062949 314 0.282340
-1049 210 0.074959 87 0.490567 211 0.096763 104 0.337710
-1050 105 0.063152 87 0.479580 239 0.011717 104 0.445551
-1051 87 0.532887 105 0.314629 211 0.071432 104 0.081052
-1052 87 0.148867 239 0.100396 104 0.520474 314 0.230264
-1053 87 0.545226 105 0.303527 211 0.090863 104 0.060384
-1054 87 0.149673 239 0.080643 104 0.508790 314 0.260893
-1055 87 0.551841 105 0.264742 211 0.101508 104 0.081910
-1056 87 0.194663 239 0.051053 104 0.496976 314 0.257308
-1057 87 0.508871 105 0.010125 211 0.045822 104 0.435182
-1058 239 0.053456 105 0.253508 282 0.253710 319 0.439327
-1059 66 0.152678 106 0.052212 108 0.377595 337 0.417515
-1060 74 0.022343 128 0.192733 230 0.261953 127 0.522970
-1061 180 0.309961 93 0.447284 178 0.136247 195 0.106508
-1062 66 0.113166 87 0.416234 105 0.282720 106 0.187879
-1063 104 0.415862 239 0.185513 127 0.254756 314 0.143869
-1064 239 0.173133 104 0.266452 282 0.177414 105 0.383000
-1065 106 0.044702 87 0.426248 105 0.203968 107 0.325081
-1066 104 0.405084 127 0.027492 206 0.110358 314 0.457065
-1067 211 0.268924 87 0.422924 212 0.039619 107 0.268533
-1068 87 0.080755 104 0.175144 210 0.455222 314 0.288878
-1069 87 0.139180 210 0.291838 211 0.534819 330 0.034163
-1070 39 0.061222 241 0.015877 240 0.215387 319 0.707515
-1071 93 0.447570 271 0.224510 177 0.230792 175 0.097128
-1072 93 0.144253 175 0.346228 279 0.406268 334 0.103251
-1073 271 0.396748 177 0.222421 273 0.177511 178 0.203320
-1074 72 0.035500 176 0.480784 189 0.070617 175 0.413098
-1075 271 0.344410 273 0.110190 172 0.452319 312 0.093081
-1076 302 0.136607 175 0.362812 193 0.143816 177 0.356765
-1077 175 0.416060 189 0.145082 112 0.046406 193 0.392453
-1078 228 0.343664 302 0.020564 172 0.111126 177 0.524646
-1079 189 0.503780 72 0.349737 197 0.082592 188 0.063890
-1080 198 0.386058 199 0.135430 228 0.434600 172 0.043912
-1081 187 0.244116 186 0.171254 185 0.373168 337 0.211461
-1082 230 0.321255 292 0.168488 131 0.317670 128 0.192587
-1083 72 0.299189 187 0.323965 197 0.250401 188 0.126445
-1084 199 0.130469 79 0.379738 171 0.409827 201 0.079966
-1085 186 0.356450 241 0.212993 109 0.306824 322 0.123733
-1086 114 0.034487 286 0.471311 131 0.074972 316 0.419230
-1087 184 0.501997 183 0.086380 186 0.405832 275 0.005792
-1088 286 0.215796 168 0.276942 131 0.337904 171 0.169358
-1089 197 0.325139 174 0.398293 189 0.220458 194 0.056110
-1090 228 0.394097 173 0.060714 170 0.427941 200 0.117248
-1091 184 0.136929 72 0.083574 197 0.468043 275 0.311455
-1092 168 0.103467 79 0.112859 171 0.431510 170 0.352164
-1093 175 0.109670 174 0.230133 302 0.427819 112 0.232378
-1094 189 0.214616 175 0.069681 112 0.419843 174 0.295860
-1095 228 0.352918 302 0.348698 173 0.267505 200 0.030879
-1096 241 0.339796 183 0.576051 169 0.050276 186 0.033878
-1097 190 0.026025 316 0.128330 169 0.512179 114 0.333466
-1098 275 0.419405 182 0.056145 111 0.047111 183 0.477338
-1099 190 0.061852 168 0.497273 169 0.370832 113 0.070043
-1100 111 0.406674 174 0.042533 181 0.163824 275 0.386968
-1101 170 0.055585 174 0.019024 171 0.352226 113 0.573165
-1102 111 0.253257 174 0.408414 194 0.084874 181 0.253454
-1103 170 0.310320 174 0.317411 113 0.268990 173 0.103279
-1104 112 0.177774 174 0.388753 181 0.359732 194 0.073741
-1105 170 0.119767 174 0.357199 113 0.058053 173 0.464981
-1106 174 0.303149 112 0.078665 181 0.257326 173 0.360860
-1107 183 0.036575 241 0.627675 190 0.215252 86 0.120498
-1108 113 0.071872 182 0.484284 169 0.393026 190 0.050818
-1109 182 0.417213 169 0.000771 174 0.311316 113 0.270700
-1110 174 0.332554 182 0.085661 113 0.298527 181 0.283258
-1111 241 0.185580 317 0.311591 217 0.056943 322 0.445885
-1112 269 0.084915 316 0.583997 241 0.072809 317 0.258280
-1113 269 0.040221 316 0.011143 241 0.312002 317 0.636635
-1114 240 0.052976 319 0.098299 241 0.097698 322 0.751028
-1115 125 0.111424 316 0.668204 288 0.055502 319 0.164869
-1116 66 0.164493 319 0.326241 240 0.070823 322 0.438443
-1117 125 0.068811 316 0.293895 288 0.173540 319 0.463753
-1118 241 0.108630 319 0.783385 269 0.059087 322 0.048897
-1119 105 0.147873 87 0.010488 239 0.675244 104 0.166395
-1120 66 0.181387 87 0.348561 239 0.357776 105 0.112277
-1121 104 0.221415 239 0.587799 127 0.138301 314 0.052486
-1122 66 0.188514 87 0.395234 239 0.323680 105 0.092572
-1123 104 0.210084 239 0.568656 127 0.137841 314 0.083418
-1124 66 0.150674 87 0.378012 239 0.353527 105 0.117787
-1125 104 0.217995 239 0.573051 127 0.112178 314 0.096776
-1126 105 0.102223 87 0.103578 239 0.602656 104 0.191543
-1127 105 0.257593 87 0.126616 239 0.594597 104 0.021194
-1128 104 0.297615 239 0.679276 127 0.015522 314 0.007587
-1129 147 0.261125 310 0.184831 269 0.539899 331 0.014145
-1130 125 0.146794 320 0.109604 316 0.208770 329 0.534832
-1131 269 0.323299 317 0.064234 147 0.117034 329 0.495433
-1132 270 0.394125 66 0.106254 150 0.248911 322 0.250710
-1133 288 0.077138 71 0.240981 125 0.560932 319 0.120949
-1134 71 0.017649 103 0.153666 270 0.531450 319 0.297236
-1135 73 0.120066 331 0.044837 313 0.591517 338 0.243580
-1136 251 0.486108 315 0.149730 140 0.223015 328 0.141147
-1137 73 0.127591 251 0.007444 250 0.162933 324 0.702032
-1138 65 0.120029 63 0.307785 64 0.208214 313 0.363972
-1139 6 0.263412 328 0.205290 278 0.107166 336 0.424132
-1140 101 0.015938 293 0.350543 100 0.627021 102 0.006499
-1141 130 0.369968 278 0.367579 89 0.118161 6 0.144293
-1142 293 0.360932 88 0.163245 37 0.273082 122 0.202740
-1143 310 0.230519 313 0.179502 40 0.145814 331 0.444165
-1144 125 0.228471 320 0.110581 315 0.489224 328 0.171724
-1145 315 0.300323 329 0.131276 250 0.144026 331 0.424376
-1146 40 0.352855 310 0.161571 65 0.245150 313 0.240424
-1147 145 0.144938 320 0.122690 281 0.504008 328 0.228364
-1148 40 0.170127 150 0.370408 294 0.375795 65 0.083671
-1149 123 0.426629 125 0.054559 71 0.039854 281 0.478958
-1150 70 0.427849 103 0.391166 37 0.023831 294 0.157154
-1151 303 0.307282 224 0.442822 33 0.148871 338 0.101024
-1152 263 0.068887 140 0.219768 231 0.259805 92 0.451541
-1153 225 0.231532 304 0.170723 51 0.055980 263 0.541765
-1154 60 0.475811 303 0.448735 63 0.019659 62 0.055794
-1155 244 0.154601 90 0.208234 92 0.434983 336 0.202183
-1156 291 0.494640 91 0.098674 290 0.103764 64 0.302922
-1157 91 0.130052 278 0.247292 120 0.526675 89 0.095982
-1158 88 0.398985 91 0.332834 293 0.005425 306 0.262756
-1159 263 0.100836 225 0.114234 33 0.283866 340 0.501063
-1160 232 0.363625 33 0.253827 92 0.196805 340 0.185742
-1161 263 0.318485 232 0.092771 51 0.048592 340 0.540152
-1162 261 0.218108 335 0.291081 91 0.344530 342 0.146281
-1163 91 0.267824 335 0.360925 27 0.159641 343 0.211610
-1164 117 0.263217 91 0.488041 261 0.032580 335 0.216162
-1165 33 0.259636 335 0.453705 55 0.075788 340 0.210871
-1166 33 0.436220 92 0.014245 55 0.415707 335 0.133828
-1167 33 0.375086 335 0.295913 55 0.254734 340 0.074267
-1168 150 0.353539 310 0.076917 40 0.411618 322 0.157927
-1169 281 0.194232 124 0.321561 123 0.038726 125 0.445480
-1170 150 0.157500 310 0.432499 40 0.387837 322 0.022164
-1171 125 0.444026 281 0.157630 145 0.200295 320 0.198048
-1172 269 0.223884 310 0.468351 40 0.228536 331 0.079229
-1173 125 0.529597 320 0.270762 315 0.186737 328 0.012905
-1174 270 0.205854 66 0.000281 150 0.465348 322 0.328517
-1175 71 0.191465 288 0.181566 125 0.510182 124 0.116787
-1176 269 0.353936 310 0.351483 147 0.114034 322 0.180547
-1177 125 0.236212 320 0.260565 316 0.212214 329 0.291009
-1178 270 0.046613 66 0.354537 150 0.031234 322 0.567616
-1179 125 0.240155 316 0.205773 288 0.419602 319 0.134469
-1180 66 0.053445 240 0.022048 148 0.102027 322 0.822480
-1181 288 0.233906 316 0.535637 125 0.159373 320 0.071085
-1182 269 0.091215 310 0.029703 147 0.072003 322 0.807079
-1183 125 0.201332 320 0.122691 316 0.625292 329 0.050685
-1184 162 0.162236 159 0.300058 157 0.096510 333 0.441197
-1185 143 0.098212 133 0.200166 126 0.491492 325 0.210129
-1186 157 0.223742 154 0.270894 35 0.219991 333 0.285373
-1187 134 0.301530 133 0.268752 143 0.219632 325 0.210086
-1188 55 0.118563 335 0.341529 54 0.273524 340 0.266383
-1189 55 0.472368 335 0.262480 54 0.027300 340 0.237852
-1190 54 0.158721 335 0.392990 261 0.398784 342 0.049506
-1191 117 0.354570 335 0.303987 55 0.067410 343 0.274033
-1192 52 0.019150 340 0.805119 54 0.104195 342 0.071536
-1193 92 0.079867 55 0.349807 232 0.265121 340 0.305205
-1194 261 0.154222 291 0.211391 32 0.043573 342 0.590813
-1195 118 0.049866 120 0.035830 27 0.173130 343 0.741174
-1196 60 0.097572 62 0.159234 8 0.136881 342 0.606313
-1197 244 0.236589 26 0.161784 92 0.105577 343 0.496050
-1198 224 0.156080 33 0.078071 52 0.385882 342 0.379967
-1199 92 0.199507 26 0.239230 232 0.367236 343 0.194026
-1200 54 0.219693 15 0.388713 117 0.182704 309 0.208891
-1201 118 0.050149 4 0.498679 55 0.402573 117 0.048599
-1202 31 0.068021 15 0.262545 32 0.431489 339 0.237945
-1203 118 0.142862 119 0.176921 55 0.263196 18 0.417021
-1204 32 0.135169 8 0.290061 254 0.180579 339 0.394191
-1205 242 0.175757 119 0.540846 30 0.032142 318 0.251254
-1206 22 0.170163 326 0.325587 309 0.033310 327 0.470941
-1207 25 0.002700 326 0.365855 22 0.107480 327 0.523965
-1208 22 0.147010 265 0.475461 25 0.056375 327 0.321154
-1209 49 0.459616 318 0.033649 44 0.098995 327 0.407739
-1210 13 0.216336 309 0.523353 15 0.220738 332 0.039573
-1211 24 0.453980 4 0.484381 9 0.020433 19 0.041206
-1212 254 0.068875 21 0.020246 31 0.461123 323 0.449756
-1213 9 0.145572 18 0.371858 24 0.116554 321 0.366015
-1214 22 0.311682 267 0.198315 266 0.321618 249 0.168385
-1215 45 0.356810 247 0.181280 248 0.072035 318 0.389875
-1216 47 0.457977 323 0.166055 254 0.185977 339 0.189992
-1217 245 0.209804 318 0.100615 30 0.386100 321 0.303481
-1218 25 0.269888 41 0.072631 22 0.028520 326 0.628961
-1219 10 0.003430 7 0.023886 25 0.310184 326 0.662499
-1220 22 0.262446 265 0.059767 25 0.628468 327 0.049319
-1221 49 0.117364 44 0.427637 25 0.218663 327 0.236337
-1222 19 0.109578 28 0.077382 16 0.301693 332 0.511346
-1223 59 0.282384 16 0.250691 24 0.209170 19 0.257756
-1224 23 0.237304 12 0.178013 5 0.331374 323 0.253310
-1225 14 0.066173 58 0.534609 9 0.012910 321 0.386309
-1226 264 0.155954 22 0.236869 48 0.416352 266 0.190826
-1227 247 0.476128 0 0.184716 248 0.174121 318 0.165035
-1228 48 0.032271 47 0.398856 12 0.546498 255 0.022375
-1229 17 0.318565 321 0.239615 0 0.200636 246 0.241183
-1230 11 0.010240 22 0.486819 7 0.500786 309 0.002155
-1231 10 0.601238 318 0.266617 24 0.045438 326 0.086706
-1232 25 0.089930 41 0.389170 22 0.386350 326 0.134550
-1233 44 0.184810 0 0.011245 10 0.460104 318 0.343841
-1234 22 0.221495 309 0.200336 11 0.556546 323 0.021623
-1235 24 0.426545 10 0.257971 14 0.299119 0 0.016364
-1236 41 0.216132 11 0.252677 12 0.070974 323 0.460217
-1237 0 0.176826 14 0.370498 24 0.248227 321 0.204449
-1238 22 0.471081 48 0.086680 12 0.082878 41 0.359361
-1239 44 0.114730 0 0.410693 10 0.130841 318 0.343736
-1240 12 0.350086 22 0.319345 41 0.169141 323 0.161429
-1241 24 0.180728 318 0.061617 0 0.563200 321 0.194455
-1242 22 0.489993 11 0.227821 7 0.269360 41 0.012826
-1243 10 0.481256 0 0.129829 24 0.232170 318 0.156746
-1244 12 0.067863 22 0.143599 41 0.463377 323 0.325162
-1245 10 0.044862 0 0.541917 24 0.357256 318 0.055965
-1246 157 0.148923 35 0.018163 162 0.435064 333 0.397849
-1247 143 0.275110 133 0.081624 126 0.438146 325 0.205119
-1248 83 0.039271 157 0.214292 159 0.344106 333 0.402331
-1249 126 0.403777 133 0.345701 227 0.046763 325 0.203759
-1250 157 0.037752 35 0.253249 162 0.272232 333 0.436767
-1251 143 0.465925 133 0.059744 126 0.155992 325 0.318339
-1252 157 0.545709 154 0.116231 35 0.000669 333 0.337392
-1253 143 0.045904 133 0.515318 126 0.042983 325 0.395795
-1254 157 0.137120 35 0.406272 162 0.008406 333 0.448202
-1255 134 0.161928 133 0.165961 143 0.402485 325 0.269626
-1256 157 0.338505 154 0.292574 35 0.121308 333 0.247613
-1257 134 0.235863 133 0.373315 143 0.139971 325 0.250851
-1258 160 0.014783 159 0.166206 162 0.200059 333 0.618952
-1259 143 0.189728 133 0.025841 126 0.369580 325 0.414851
-1260 157 0.022252 35 0.150391 162 0.178739 333 0.648618
-1261 143 0.369170 133 0.004054 126 0.100420 325 0.526357
-1262 83 0.218117 157 0.127019 159 0.214573 333 0.440291
-1263 126 0.275848 133 0.252765 227 0.135891 325 0.335496
-1264 83 0.002413 156 0.207843 157 0.366126 333 0.423618
-1265 227 0.007944 133 0.426444 78 0.026573 325 0.539039
-1266 157 0.085660 154 0.147753 35 0.199861 333 0.566727
-1267 134 0.226484 133 0.088153 143 0.262216 325 0.423146
-1268 156 0.058867 154 0.334115 157 0.244967 333 0.362051
-1269 134 0.277764 133 0.288596 143 0.039322 325 0.394318
-1270 157 0.178184 154 0.383078 35 0.043589 333 0.395150
-1271 134 0.335902 133 0.200078 143 0.109349 325 0.354671
-1272 83 0.033577 159 0.349099 160 0.034972 333 0.582352
-1273 143 0.034021 133 0.154119 126 0.411708 325 0.400151
-1274 162 0.266042 97 0.066890 300 0.010823 333 0.656245
-1275 126 0.003031 144 0.346433 143 0.075841 325 0.574695
-1276 151 0.204353 97 0.032920 162 0.120210 333 0.642517
-1277 167 0.079075 143 0.380618 146 0.222644 325 0.317662
-1278 83 0.444397 159 0.131440 160 0.054635 333 0.369528
-1279 126 0.199831 133 0.141733 227 0.322961 325 0.335475
-1280 83 0.097450 156 0.401015 157 0.143958 333 0.357577
-1281 78 0.200309 133 0.298915 134 0.022828 325 0.477948
-1282 35 0.200645 154 0.043032 153 0.221029 333 0.535294
-1283 134 0.232848 143 0.236009 167 0.156361 325 0.374782
-1284 156 0.277175 154 0.245471 157 0.065902 333 0.411452
-1285 78 0.083895 133 0.171733 134 0.329014 325 0.415358
-1286 35 0.050584 154 0.408313 153 0.007096 333 0.534007
-1287 134 0.429638 133 0.011916 143 0.142503 325 0.415943
-1288 160 0.341456 159 0.051759 162 0.141222 333 0.465562
-1289 126 0.444020 144 0.071642 143 0.018104 325 0.466234
-1290 160 0.089550 162 0.028843 300 0.233611 333 0.647996
-1291 167 0.064810 143 0.091694 146 0.370410 325 0.473086
-1292 283 0.022621 152 0.206832 300 0.165352 333 0.605196
-1293 167 0.199676 146 0.234899 136 0.162184 325 0.403241
-1294 156 0.477942 333 0.067370 83 0.088434 341 0.366254
-1295 78 0.528308 134 0.006385 308 0.049009 325 0.416298
-1296 83 0.417002 333 0.006973 160 0.190881 341 0.385145
-1297 137 0.129697 227 0.426653 78 0.118251 325 0.325399
-1298 99 0.189806 152 0.242446 153 0.153629 333 0.414119
-1299 136 0.015662 222 0.287333 167 0.195451 325 0.501553
-1300 99 0.235668 333 0.294007 156 0.310130 341 0.160196
-1301 78 0.167775 134 0.088576 308 0.301494 325 0.442155
-1302 153 0.104517 154 0.023180 99 0.402723 333 0.469580
-1303 308 0.115424 134 0.239031 222 0.211172 325 0.434373
-1304 160 0.327673 333 0.469541 272 0.060864 341 0.141922
-1305 146 0.067439 126 0.210376 137 0.094907 325 0.627278
-1306 151 0.114878 152 0.001276 283 0.574875 333 0.308970
-1307 146 0.098801 167 0.514975 262 0.299314 144 0.086910
-1308 151 0.029729 283 0.537248 97 0.163924 333 0.269099
-1309 146 0.074671 167 0.403896 262 0.404241 144 0.117192
-1310 283 0.435605 300 0.046660 97 0.306125 333 0.211610
-1311 146 0.060316 167 0.294568 262 0.481980 144 0.163137
-1312 151 0.399699 283 0.270090 97 0.091307 333 0.238905
-1313 262 0.135599 144 0.281729 167 0.484711 76 0.097961
-1314 151 0.332359 283 0.212406 97 0.261335 333 0.193900
-1315 262 0.200487 144 0.294433 167 0.360980 76 0.144099
-1316 151 0.243454 283 0.163401 97 0.426862 333 0.166283
-1317 262 0.280088 144 0.320292 167 0.250556 76 0.149064
-1318 65 0.165202 162 0.239371 159 0.057642 310 0.537784
-1319 145 0.462055 320 0.459601 281 0.015093 328 0.063251
-1320 159 0.412045 162 0.006269 149 0.205792 310 0.375894
-1321 145 0.198305 226 0.142033 126 0.120950 320 0.538713
-1322 159 0.500158 162 0.000518 149 0.344519 310 0.154805
-1323 126 0.363610 143 0.007656 145 0.188388 320 0.440346
-1324 149 0.083875 148 0.123129 299 0.496042 310 0.296954
-1325 77 0.433629 316 0.132537 288 0.017954 320 0.415879
-1326 299 0.254127 155 0.161383 35 0.263933 310 0.320557
-1327 77 0.321972 287 0.242248 132 0.016848 320 0.418931
-1328 299 0.033808 155 0.229331 35 0.488798 310 0.248062
-1329 287 0.375285 77 0.136762 143 0.203991 320 0.283962
-1330 299 0.511299 310 0.466262 148 0.009476 322 0.012962
-1331 132 0.105997 316 0.153979 77 0.204742 320 0.535282
-1332 299 0.110003 155 0.321486 35 0.131358 310 0.437152
-1333 77 0.069127 287 0.242720 132 0.081441 320 0.606712
-1334 35 0.078204 155 0.589185 149 0.178144 310 0.154467
-1335 287 0.440939 143 0.118617 133 0.037653 320 0.402791
-1336 149 0.282347 299 0.228463 35 0.215378 310 0.273813
-1337 77 0.309029 124 0.115008 143 0.172413 320 0.403549
-1338 299 0.012515 218 0.064398 155 0.244773 310 0.678314
-1339 43 0.139099 316 0.051954 132 0.037123 320 0.771825
-1340 155 0.364143 157 0.057481 159 0.087860 310 0.490516
-1341 43 0.033673 132 0.027288 133 0.238432 320 0.700608
-1342 155 0.506918 159 0.253655 149 0.017523 310 0.221903
-1343 133 0.439021 143 0.058566 126 0.021721 320 0.480691
-1344 155 0.139740 159 0.016641 149 0.648511 310 0.195108
-1345 143 0.294057 124 0.249451 145 0.110355 320 0.346138
-1346 65 0.073987 310 0.692662 159 0.219695 313 0.013656
-1347 226 0.062818 320 0.673886 145 0.015626 328 0.247670
-1348 155 0.094804 159 0.357880 149 0.050812 310 0.496504
-1349 145 0.017626 226 0.263901 126 0.008206 320 0.710267
-1350 155 0.111481 159 0.516166 149 0.098440 310 0.273913
-1351 126 0.175232 226 0.234368 133 0.060180 320 0.530221
-1352 159 0.078559 162 0.222358 149 0.352070 310 0.347013
-1353 143 0.193547 124 0.033559 145 0.450904 320 0.321990
-1354 153 0.108306 152 0.460179 151 0.279311 333 0.152205
-1355 167 0.604391 146 0.195182 136 0.117892 325 0.082535
-1356 283 0.467159 152 0.010604 300 0.175090 333 0.347147
-1357 167 0.407846 146 0.335651 262 0.197449 136 0.059053
-1358 283 0.088253 300 0.321278 97 0.278220 333 0.312249
-1359 146 0.501953 167 0.236695 262 0.160284 144 0.101068
-1360 153 0.149302 152 0.052093 151 0.510776 333 0.287829
-1361 167 0.466580 143 0.365504 146 0.165674 325 0.002242
-1362 151 0.105123 97 0.456698 162 0.144185 333 0.293994
-1363 146 0.221294 167 0.230339 262 0.013040 144 0.535328
-1364 151 0.424099 97 0.261241 162 0.030555 333 0.284105
-1365 146 0.022492 167 0.432499 262 0.024364 144 0.520645
-1366 153 0.321203 152 0.321149 151 0.009739 333 0.347908
-1367 136 0.145370 222 0.086187 167 0.392768 325 0.375675
-1368 283 0.290366 152 0.065298 300 0.164764 333 0.479572
-1369 167 0.399181 146 0.447919 136 0.072538 325 0.080361
-1370 162 0.071035 97 0.089787 300 0.362353 333 0.476825
-1371 167 0.156367 143 0.090397 146 0.586086 325 0.167151
-1372 153 0.245366 151 0.210662 35 0.122230 333 0.421741
-1373 134 0.055893 143 0.312156 167 0.333791 325 0.298159
-1374 151 0.024791 97 0.262555 162 0.232032 333 0.480622
-1375 143 0.289512 144 0.203520 146 0.321435 325 0.185532
-1376 151 0.309795 97 0.182241 162 0.047088 333 0.460876
-1377 167 0.205349 146 0.362538 143 0.409332 144 0.022781
-1378 148 0.217551 310 0.175904 150 0.035626 322 0.570919
-1379 288 0.232694 316 0.345799 125 0.143690 320 0.277816
-1380 299 0.023339 310 0.310686 148 0.037799 322 0.628176
-1381 288 0.041040 316 0.420403 125 0.177833 320 0.360725
-1382 269 0.182458 310 0.492574 147 0.131363 322 0.193604
-1383 125 0.098154 320 0.431359 316 0.180543 329 0.289943
-1384 150 0.517111 310 0.008639 40 0.118245 322 0.356006
-1385 71 0.057542 288 0.160745 125 0.455525 124 0.326188
-1386 148 0.137318 310 0.060730 150 0.298498 322 0.503454
-1387 288 0.447725 316 0.082373 125 0.290659 320 0.179243
-1388 299 0.210460 310 0.278000 148 0.273587 322 0.237953
-1389 77 0.084685 316 0.263208 288 0.217936 320 0.434171
-1390 299 0.287963 310 0.395057 148 0.022440 322 0.294540
-1391 77 0.030308 316 0.345814 288 0.064511 320 0.559368
-1392 147 0.024991 310 0.677638 218 0.112025 322 0.185346
-1393 316 0.137583 320 0.652434 43 0.023423 329 0.186560
-1394 148 0.218894 310 0.302873 150 0.439122 322 0.039111
-1395 125 0.252682 124 0.494678 288 0.092076 320 0.160564
-1396 148 0.491956 310 0.285089 150 0.102095 322 0.120859
-1397 288 0.474411 316 0.007681 125 0.106155 320 0.411753
-1398 268 0.002963 20 0.577812 54 0.383207 340 0.036018
-1399 20 0.361278 55 0.319231 50 0.194412 327 0.125078
-1400 54 0.353425 309 0.093522 55 0.224108 327 0.328945
-1401 54 0.296680 309 0.096783 55 0.270950 327 0.335587
-1402 117 0.348777 54 0.391073 261 0.046581 15 0.213569
-1403 4 0.134051 55 0.378463 117 0.470058 309 0.017428
-1404 54 0.216251 261 0.182222 32 0.594245 342 0.007282
-1405 119 0.257354 118 0.451070 55 0.150208 343 0.141368
-1406 52 0.415745 268 0.078659 249 0.224679 339 0.280916
-1407 26 0.105638 50 0.262187 55 0.230134 243 0.402040
-1408 8 0.505743 339 0.166432 32 0.061694 342 0.266131
-1409 55 0.099697 242 0.521908 119 0.019743 343 0.358651
-1410 117 0.065914 15 0.396610 4 0.099103 309 0.438373
-1411 24 0.149387 55 0.209137 4 0.613955 309 0.027521
-1412 31 0.558494 15 0.000881 32 0.120543 339 0.320082
-1413 24 0.278309 18 0.521745 119 0.195977 321 0.003969
-1414 254 0.336414 256 0.235629 47 0.069541 339 0.358416
-1415 242 0.115742 119 0.187603 30 0.422043 318 0.274611
-1416 23 0.196857 323 0.079404 5 0.443606 332 0.280133
-1417 14 0.279213 58 0.392296 9 0.292905 321 0.035586
-1418 28 0.407219 19 0.102566 13 0.112083 332 0.378132
-1419 59 0.600144 19 0.097290 24 0.181465 9 0.121101
-1420 31 0.352118 323 0.002183 23 0.291179 332 0.354520
-1421 14 0.079129 58 0.043787 9 0.635697 321 0.241387
-1422 13 0.389774 309 0.089797 15 0.144754 332 0.375675
-1423 59 0.018995 19 0.382389 24 0.206028 9 0.392588
-1424 12 0.229720 46 0.209796 255 0.408867 323 0.151617
-1425 321 0.294311 17 0.537684 0 0.023033 57 0.144971
-1426 12 0.355660 23 0.460680 46 0.181105 323 0.002555
-1427 2 0.352142 58 0.172019 57 0.313471 321 0.162368
-1428 253 0.492997 47 0.052221 255 0.144089 323 0.310693
-1429 30 0.091668 289 0.479411 246 0.095310 321 0.333611
-1430 46 0.181620 23 0.160755 21 0.377768 323 0.279857
-1431 289 0.010570 18 0.174581 2 0.463280 321 0.351568
-1432 311 0.020170 330 0.139376 94 0.328178 346 0.512277
-1433 208 0.185602 207 0.027728 96 0.059962 345 0.726709
-1434 346 0.719947 213 0.183869 95 0.075430 209 0.020754
-1435 95 0.124765 208 0.067485 179 0.208249 345 0.599501
-1436 72 0.359493 219 0.220566 188 0.131700 176 0.288242
-1437 171 0.033042 228 0.232876 199 0.556076 312 0.178007
-1438 219 0.120762 72 0.382174 188 0.026598 110 0.470466
-1439 199 0.175438 201 0.436213 171 0.190706 312 0.197644
-1440 72 0.075907 187 0.055393 110 0.511286 337 0.357414
-1441 230 0.322522 201 0.403611 202 0.226501 312 0.047366
-1442 69 0.067700 311 0.118603 284 0.487036 337 0.326662
-1443 202 0.362257 230 0.341677 74 0.250932 128 0.045134
-1444 216 0.217052 311 0.074969 69 0.276449 337 0.431529
-1445 230 0.161052 312 0.132161 74 0.629791 314 0.076997
-1446 87 0.309065 311 0.006703 216 0.591179 337 0.093052
-1447 74 0.225596 312 0.027705 205 0.320608 314 0.426091
-1448 94 0.512875 311 0.154974 87 0.180210 330 0.151941
-1449 207 0.289140 314 0.191156 205 0.079284 345 0.440420
-1450 195 0.065890 334 0.254885 196 0.324239 346 0.354985
-1451 179 0.528054 312 0.087565 180 0.266883 345 0.117499
-1452 280 0.207076 311 0.058426 196 0.089178 334 0.645320
-1453 38 0.414750 273 0.088176 180 0.487450 312 0.009625
-1454 72 0.046798 311 0.106848 176 0.496409 334 0.349946
-1455 271 0.091561 273 0.515513 172 0.026368 312 0.366559
-1456 72 0.070978 219 0.588090 176 0.118106 311 0.222826
-1457 252 0.182649 129 0.074801 199 0.421148 312 0.321402
-1458 215 0.016775 219 0.190581 110 0.539308 311 0.253335
-1459 129 0.242368 201 0.448945 199 0.025309 312 0.283377
-1460 215 0.100216 110 0.300945 284 0.347478 311 0.251361
-1461 230 0.045664 201 0.149431 202 0.547200 312 0.257705
-1462 215 0.033131 284 0.404889 69 0.268993 311 0.292986
-1463 230 0.113874 202 0.364313 74 0.331123 312 0.190689
-1464 69 0.533039 311 0.261386 284 0.044746 337 0.160828
-1465 230 0.139440 202 0.050716 74 0.603828 312 0.206016
-1466 196 0.568338 334 0.245934 311 0.171055 346 0.014673
-1467 179 0.606584 38 0.052623 180 0.097992 312 0.242801
-1468 280 0.451394 311 0.196256 196 0.174104 334 0.178246
-1469 179 0.188735 38 0.479160 180 0.173390 312 0.158715
-1470 219 0.102207 311 0.218536 280 0.355443 176 0.323814
-1471 273 0.324006 38 0.197606 252 0.199337 312 0.279051
-1472 215 0.201682 67 0.248818 219 0.297613 311 0.251887
-1473 252 0.260046 204 0.206053 129 0.253562 312 0.280339
-1474 215 0.444410 219 0.038278 110 0.247599 311 0.269714
-1475 129 0.472667 203 0.037419 202 0.161513 312 0.328401
-1476 215 0.403929 284 0.304128 69 0.001585 311 0.290358
-1477 129 0.091533 203 0.225771 202 0.372141 312 0.310555
-1478 215 0.187292 284 0.123271 69 0.358425 311 0.331012
-1479 202 0.199168 203 0.137242 74 0.335607 312 0.327983
-1480 216 0.156968 311 0.323898 69 0.479380 337 0.039754
-1481 74 0.584070 312 0.305583 205 0.084271 314 0.026076
-1482 196 0.573507 334 0.008793 311 0.270944 346 0.146756
-1483 204 0.053555 312 0.175078 179 0.552176 345 0.219190
-1484 67 0.171604 196 0.357348 280 0.183289 311 0.287759
-1485 204 0.205254 38 0.128522 179 0.412981 312 0.253243
-1486 311 0.276899 219 0.013753 280 0.214869 67 0.494479
-1487 204 0.420226 38 0.297913 179 0.058820 312 0.223040
-1488 67 0.432531 196 0.001437 280 0.040744 311 0.525288
-1489 204 0.439667 312 0.449793 179 0.091217 345 0.019323
-1490 213 0.010595 196 0.317901 67 0.162578 311 0.508925
-1491 204 0.227088 312 0.339125 179 0.276797 345 0.156990
-1492 196 0.331314 334 0.049114 311 0.395611 346 0.223961
-1493 179 0.369657 312 0.317762 180 0.003403 345 0.309178
-1494 87 0.011181 311 0.314219 216 0.332704 337 0.341897
-1495 74 0.387647 312 0.356175 205 0.053580 314 0.202598
-1496 215 0.081313 284 0.147634 69 0.254095 311 0.516958
-1497 202 0.101392 203 0.017247 74 0.376652 312 0.504710
-1498 215 0.237329 110 0.030220 284 0.224262 311 0.508189
-1499 202 0.254151 203 0.171181 74 0.051654 312 0.523013
-1500 215 0.270164 110 0.194092 284 0.050830 311 0.484914
-1501 129 0.148140 203 0.119576 202 0.206783 312 0.525501
-1502 215 0.259638 67 0.111320 219 0.158584 311 0.470458
-1503 204 0.212475 203 0.060411 129 0.235393 312 0.491721
-1504 176 0.372176 311 0.434643 280 0.010288 334 0.182893
-1505 38 0.060507 273 0.287424 180 0.041650 312 0.610419
-1506 280 0.149192 311 0.362196 196 0.093258 334 0.395354
-1507 179 0.120959 38 0.224712 180 0.275310 312 0.379019
-1508 196 0.266628 334 0.195961 311 0.226316 346 0.311096
-1509 179 0.375092 312 0.258242 180 0.139525 345 0.227141
-1510 216 0.182077 311 0.328542 69 0.164952 337 0.324429
-1511 230 0.059745 312 0.338286 74 0.463835 314 0.138134
-1512 69 0.077015 311 0.479728 284 0.320578 337 0.122679
-1513 230 0.174830 202 0.164223 74 0.278110 312 0.382837
-1514 284 0.020281 311 0.418078 110 0.368117 337 0.193524
-1515 230 0.163751 201 0.045902 202 0.336412 312 0.453935
-1516 110 0.337214 219 0.089056 72 0.155276 311 0.418454
-1517 230 0.028704 201 0.359855 202 0.008191 312 0.603249
-1518 72 0.226514 219 0.296760 176 0.106340 311 0.370385
-1519 171 0.028108 228 0.015476 199 0.314901 312 0.641515
-1520 272 0.047521 152 0.543089 98 0.165924 341 0.243465
-1521 135 0.176196 136 0.347540 274 0.287693 325 0.188571
-1522 82 0.129867 165 0.305177 83 0.100628 341 0.464327
-1523 135 0.241768 235 0.115185 78 0.470248 137 0.172799
-1524 161 0.327555 82 0.096168 83 0.144263 341 0.432013
-1525 235 0.051048 78 0.396918 137 0.531857 227 0.020176
-1526 99 0.146479 298 0.224106 152 0.311144 341 0.318271
-1527 135 0.424910 136 0.410481 274 0.005148 325 0.159462
-1528 272 0.305642 152 0.271397 98 0.062547 341 0.360414
-1529 135 0.130118 274 0.398559 137 0.074249 325 0.397074
-1530 272 0.386088 161 0.041089 160 0.006121 341 0.566702
-1531 135 0.106830 137 0.525167 78 0.023850 325 0.344154
-1532 166 0.055711 99 0.262918 165 0.106857 341 0.574513
-1533 135 0.430092 137 0.143392 78 0.277319 325 0.149197
-1534 99 0.309595 298 0.164016 152 0.058315 341 0.468074
-1535 135 0.548105 136 0.017079 274 0.061052 325 0.373763
-1536 164 0.441793 296 0.043600 98 0.506814 341 0.007793
-1537 285 0.386905 274 0.446868 135 0.009950 75 0.156276
-1538 164 0.551656 296 0.102936 98 0.260995 341 0.084413
-1539 285 0.285438 274 0.227388 135 0.197052 75 0.290122
-1540 98 0.136616 297 0.295175 164 0.461352 341 0.106858
-1541 135 0.271744 136 0.138282 75 0.553484 274 0.036490
-1542 98 0.275686 297 0.355175 164 0.289515 341 0.079623
-1543 135 0.165701 136 0.097862 75 0.494346 274 0.242091
-1544 163 0.269501 296 0.374204 166 0.062313 341 0.293982
-1545 236 0.212799 285 0.442021 274 0.029980 137 0.315199
-1546 163 0.099267 296 0.347593 166 0.333265 341 0.219874
-1547 135 0.281790 236 0.408611 137 0.097761 285 0.211838
-1548 166 0.061514 296 0.631226 164 0.008807 341 0.298453
-1549 236 0.179391 285 0.462403 274 0.299829 137 0.058377
-1550 166 0.195519 296 0.423382 164 0.160589 341 0.220510
-1551 274 0.141355 285 0.662855 135 0.040186 137 0.155605
-1552 82 0.009027 163 0.581059 166 0.118263 341 0.291651
-1553 135 0.053752 236 0.411202 237 0.291478 137 0.243568
-1554 82 0.047707 163 0.337058 166 0.405105 341 0.210131
-1555 135 0.142116 236 0.299414 237 0.424845 137 0.133625
-1556 82 0.316278 163 0.305849 166 0.247200 341 0.130674
-1557 137 0.163942 236 0.130558 237 0.588896 235 0.116603
-1558 301 0.094857 163 0.492493 82 0.211821 341 0.200829
-1559 137 0.159019 236 0.317985 237 0.275665 235 0.247331
-1560 164 0.358377 298 0.011350 99 0.097516 341 0.532757
-1561 274 0.052785 285 0.017606 135 0.671812 137 0.257798
-1562 166 0.068611 296 0.135777 164 0.436796 341 0.358816
-1563 274 0.183120 285 0.400981 135 0.339453 137 0.076446
-1564 152 0.036535 297 0.086989 98 0.358631 341 0.517845
-1565 135 0.296687 274 0.286265 137 0.401350 325 0.015697
-1566 164 0.142849 296 0.130843 98 0.473386 341 0.252922
-1567 274 0.402187 285 0.340414 135 0.010087 137 0.247313
-1568 99 0.005820 298 0.575243 152 0.064250 341 0.354687
-1569 136 0.413490 135 0.445600 75 0.089811 223 0.051099
-1570 297 0.188889 298 0.362557 164 0.229900 341 0.218654
-1571 136 0.236729 135 0.298704 75 0.268970 223 0.195597
-1572 152 0.457007 297 0.142355 98 0.284051 341 0.116588
-1573 135 0.174663 136 0.366084 75 0.037365 274 0.421889
-1574 152 0.172777 297 0.478940 98 0.202403 341 0.145879
-1575 135 0.064471 136 0.329367 75 0.313846 274 0.292316
-1576 166 0.335924 99 0.063261 165 0.012832 341 0.587983
-1577 135 0.562689 235 0.220582 78 0.070876 137 0.145854
-1578 163 0.070226 296 0.093482 166 0.462442 341 0.373850
-1579 135 0.349685 236 0.179142 237 0.294573 137 0.176600
-1580 163 0.125731 161 0.165805 98 0.175121 341 0.533344
-1581 135 0.267720 235 0.115520 78 0.009511 137 0.607249
-1582 163 0.402275 161 0.017928 98 0.206861 341 0.372936
-1583 135 0.166434 236 0.337921 237 0.051479 137 0.444166
-1584 152 0.056082 297 0.179606 98 0.273169 341 0.491143
-1585 135 0.325234 274 0.400104 137 0.265000 325 0.009663
-1586 164 0.257523 296 0.078495 98 0.472414 341 0.191567
-1587 274 0.450756 285 0.381808 135 0.024656 137 0.142780
-1588 164 0.280012 298 0.124460 99 0.098403 341 0.497125
-1589 135 0.682405 274 0.156290 137 0.141070 325 0.020235
-1590 166 0.036438 296 0.091017 164 0.503425 341 0.369120
-1591 274 0.213655 285 0.371507 135 0.379275 137 0.035562
-1592 163 0.126150 161 0.076962 98 0.201615 341 0.595273
-1593 137 0.599634 237 0.001331 135 0.313675 235 0.085360
-1594 98 0.180619 296 0.026425 163 0.373555 341 0.419401
-1595 135 0.199223 236 0.317637 237 0.047888 137 0.435252
-1596 99 0.104851 166 0.288371 164 0.040164 341 0.566615
-1597 135 0.580218 235 0.164191 78 0.083476 137 0.172114
-1598 166 0.513620 296 0.057770 164 0.025495 341 0.403115
-1599 135 0.392186 236 0.091689 237 0.331034 137 0.185092
-1600 166 0.055203 165 0.190319 82 0.357864 341 0.396615
-1601 237 0.069247 135 0.311943 235 0.364092 78 0.254718
-1602 82 0.496168 163 0.007358 166 0.251350 341 0.245123
-1603 237 0.366124 135 0.182391 235 0.336635 78 0.114850
-1604 161 0.343892 301 0.187439 82 0.126574 341 0.342095
-1605 135 0.077081 235 0.382806 78 0.139065 137 0.401048
-1606 163 0.120658 301 0.506372 161 0.064274 341 0.308695
-1607 137 0.325170 236 0.089816 237 0.145684 235 0.439329
-1608 311 0.623599 330 0.057574 94 0.140689 346 0.178138
-1609 205 0.078428 314 0.017862 312 0.333867 345 0.569843
-1610 280 0.102056 311 0.729996 196 0.017896 334 0.150053
-1611 179 0.140801 312 0.720895 180 0.023212 345 0.115092
-1612 216 0.092370 311 0.686429 69 0.104665 337 0.116537
-1613 74 0.185593 312 0.569757 205 0.103939 314 0.140711
-1614 110 0.156556 219 0.068663 72 0.049530 311 0.725251
-1615 230 0.006229 202 0.119610 74 0.034688 312 0.839473
-1616 51 0.110487 232 0.307920 305 0.366359 340 0.215233
-1617 51 0.120253 232 0.173105 305 0.410165 340 0.296477
-1618 51 0.001173 232 0.488346 305 0.351978 340 0.158502
-1619 225 0.135082 51 0.303785 305 0.120692 340 0.440440
-1620 263 0.023513 232 0.404146 51 0.304642 340 0.267699
-1621 263 0.009913 232 0.056875 51 0.567530 340 0.365682
-1622 263 0.193407 232 0.099742 51 0.206738 340 0.500113
-1623 335 0.020804 340 0.663667 33 0.299649 342 0.015879
-1624 92 0.267822 55 0.140664 232 0.154815 340 0.436699
-1625 55 0.246378 335 0.325928 54 0.023038 340 0.404656
-1626 55 0.117813 335 0.360675 54 0.097297 340 0.424215
-1627 33 0.092653 335 0.178500 55 0.418116 340 0.310731
-1628 316 0.435106 320 0.255655 43 0.275303 329 0.033936
-1629 232 0.309130 92 0.401550 231 0.169449 26 0.119871
-1630 114 0.052158 316 0.487103 192 0.211491 317 0.249248
-1631 114 0.175877 316 0.178715 192 0.022167 317 0.623241
-1632 92 0.186506 55 0.137090 232 0.119922 340 0.556482
-1633 92 0.170163 55 0.066492 232 0.029830 340 0.733514
-1634 190 0.084453 86 0.092456 241 0.083473 317 0.739619
-1635 140 0.066414 92 0.438193 26 0.476848 231 0.018545
-1636 244 0.112921 92 0.117268 141 0.345298 336 0.424513
-1637 56 0.217328 328 0.051526 142 0.384951 336 0.346195
-1638 142 0.214231 226 0.328057 145 0.039997 328 0.417716
-1639 257 0.032156 320 0.270899 138 0.408523 328 0.288422
-1640 43 0.371583 320 0.559409 315 0.038940 329 0.030069
-1641 192 0.071909 316 0.708580 43 0.165282 329 0.054229
-1642 232 0.611937 33 0.094973 92 0.285269 340 0.007821
-1643 277 0.009095 315 0.259193 42 0.425894 324 0.305819
-1644 116 0.196397 315 0.385673 277 0.315652 324 0.102277
-1645 116 0.142265 315 0.187261 259 0.363388 329 0.307086
-1646 260 0.021464 329 0.135675 259 0.549684 331 0.293177
-1647 304 0.126608 53 0.004699 220 0.199025 324 0.669668
-1648 192 0.197081 316 0.152817 43 0.202723 329 0.447379
-1649 263 0.348898 140 0.357980 42 0.113992 231 0.179130
-1650 140 0.022764 315 0.069518 139 0.588705 328 0.319012
-1651 259 0.243594 116 0.175841 220 0.068171 315 0.512393
-1652 277 0.289670 315 0.257407 42 0.153537 324 0.299386
-1653 116 0.059655 315 0.295668 277 0.407689 324 0.236988
-1654 116 0.108205 315 0.272243 277 0.349692 324 0.269859
-1655 116 0.234437 315 0.355331 277 0.264729 324 0.145502
-1656 277 0.357925 315 0.204644 42 0.068024 324 0.369408
-1657 277 0.127337 315 0.187023 42 0.279787 324 0.405852
-1658 259 0.481233 315 0.297038 220 0.018173 329 0.203557
-1659 259 0.271926 116 0.118780 220 0.096336 315 0.512959
-1660 220 0.226102 42 0.001751 304 0.087112 324 0.685036
-1661 259 0.537273 329 0.140269 220 0.026848 331 0.295611
-1662 259 0.507374 315 0.332864 220 0.062728 329 0.097034
-1663 259 0.486672 329 0.124128 220 0.139534 331 0.249666
-1664 277 0.407732 315 0.022207 42 0.043314 324 0.526747
-1665 220 0.333961 42 0.027776 304 0.029523 324 0.608740
-1666 116 0.447817 315 0.207312 277 0.099516 324 0.245356
-1667 259 0.270863 116 0.162894 220 0.139768 315 0.426476
-1668 116 0.080585 315 0.067013 277 0.410648 324 0.441754
-1669 116 0.232489 315 0.142687 277 0.298825 324 0.325999
-1670 220 0.646523 315 0.070077 331 0.085653 329 0.197748
-1671 259 0.225162 329 0.209897 220 0.425607 331 0.139334
-1672 315 0.074652 220 0.475227 331 0.033281 324 0.416841
-1673 315 0.175542 220 0.588429 331 0.121113 324 0.114916
-1674 116 0.090982 315 0.441206 277 0.390319 324 0.077493
-1675 277 0.378228 315 0.381787 42 0.071181 324 0.168804
-1676 277 0.118220 315 0.383440 42 0.323572 324 0.174768
-1677 42 0.376931 315 0.270910 140 0.054790 324 0.297370
-1678 92 0.474700 140 0.062779 26 0.374528 141 0.087993
-1679 263 0.355204 140 0.410389 42 0.129054 231 0.105353
-1680 56 0.050537 226 0.266216 142 0.202103 328 0.481143
-1681 140 0.067256 315 0.093282 139 0.531725 328 0.307737
-1682 56 0.258062 328 0.053367 142 0.301357 336 0.387214
-1683 92 0.180671 140 0.007212 141 0.447468 336 0.364648
-1684 92 0.414578 140 0.133475 26 0.364419 141 0.087528
-1685 92 0.144156 140 0.082011 141 0.441160 336 0.332673
-1686 142 0.230091 141 0.008081 56 0.361418 336 0.400410
-1687 56 0.192630 226 0.210248 142 0.163918 328 0.433205
-1688 263 0.301749 140 0.454488 42 0.137338 231 0.106425
-1689 42 0.436009 315 0.277846 140 0.076492 324 0.209654
-1690 116 0.003136 315 0.434646 277 0.535359 324 0.026859
-1691 140 0.103031 315 0.057352 139 0.561311 328 0.278307
-1692 277 0.049337 315 0.435685 42 0.473834 324 0.041144
-1693 277 0.296621 315 0.434113 42 0.233793 324 0.035472
-1694 251 0.278579 328 0.017161 140 0.690189 336 0.014070
-1695 140 0.475317 315 0.014303 139 0.145257 328 0.365123
-1696 316 0.574206 320 0.042312 43 0.256695 329 0.126788
-1697 138 0.040342 226 0.340822 56 0.148127 328 0.470709
-1698 139 0.529806 257 0.082786 328 0.310293 315 0.077115
-1699 316 0.339118 320 0.250914 43 0.305420 329 0.104547
-1700 257 0.131433 320 0.264960 138 0.300511 328 0.303097
-1701 43 0.360834 320 0.507998 315 0.082574 329 0.048594
-1702 192 0.140551 316 0.125582 43 0.279311 329 0.454556
-1703 116 0.439421 315 0.003869 259 0.060994 329 0.495715
-1704 116 0.226062 315 0.443471 277 0.292325 324 0.038142
-1705 259 0.237269 116 0.268240 220 0.024571 315 0.469920
-1706 258 0.155831 315 0.023410 116 0.420038 329 0.400721
-1707 258 0.034552 315 0.275413 116 0.547154 329 0.142881
-1708 116 0.318056 139 0.073366 277 0.183663 315 0.424916
-1709 316 0.561397 320 0.029197 43 0.317132 329 0.092274
-1710 192 0.160704 316 0.091799 43 0.351939 329 0.395558
-1711 138 0.211515 226 0.232753 56 0.119887 328 0.435846
-1712 139 0.466363 257 0.215550 328 0.291449 315 0.026638
-1713 43 0.448454 320 0.431850 315 0.107749 329 0.011946
-1714 316 0.296526 320 0.205252 43 0.392054 329 0.106168
-1715 257 0.198042 320 0.264710 138 0.303231 328 0.234018
-1716 315 0.174645 320 0.158474 257 0.560185 328 0.106696
-1717 259 0.352791 329 0.460936 220 0.174887 331 0.011386
-1718 277 0.152812 42 0.141475 220 0.032590 324 0.673124
-1719 277 0.166820 42 0.083872 220 0.069167 324 0.680140
-1720 259 0.322047 329 0.452182 220 0.203776 331 0.021995
-1721 259 0.346598 315 0.047590 220 0.251704 329 0.354109
-1722 277 0.219485 42 0.052320 220 0.153644 324 0.574552
-1723 259 0.276191 315 0.234879 220 0.342239 329 0.146691
-1724 259 0.108959 116 0.041241 220 0.463037 315 0.386764
-1725 220 0.161395 116 0.151999 277 0.162292 324 0.524314
-1726 220 0.181833 315 0.056442 116 0.335965 324 0.425760
-1727 304 0.183778 53 0.021075 220 0.124709 324 0.670438
-1728 277 0.026009 315 0.030887 42 0.304012 324 0.639092
-1729 42 0.361453 315 0.162291 140 0.072682 324 0.403573
-1730 263 0.356220 140 0.280811 42 0.148652 231 0.214318
-1731 232 0.445631 33 0.153458 92 0.193178 340 0.207733
-1732 232 0.569728 263 0.099400 92 0.241738 33 0.089133
-1733 232 0.309616 92 0.423948 231 0.229782 26 0.036654
-1734 140 0.047432 92 0.407539 26 0.352462 231 0.192566
-1735 92 0.232213 55 0.019242 232 0.290102 340 0.458444
-1736 232 0.363968 92 0.351081 231 0.276705 26 0.008246
-1737 232 0.634494 263 0.001724 92 0.043708 33 0.320074
-1738 232 0.474779 33 0.235552 92 0.040277 340 0.249393
-1739 263 0.170745 232 0.168760 51 0.071043 340 0.589451
-1740 140 0.012903 92 0.340840 26 0.339951 231 0.306306
-1741 232 0.637676 263 0.191988 92 0.128046 33 0.042290
-1742 42 0.389549 315 0.036705 140 0.130562 324 0.443184
-1743 263 0.295960 140 0.182481 42 0.150130 231 0.371429
-1744 231 0.044140 304 0.044220 42 0.352935 324 0.558706
-1745 304 0.327775 53 0.028804 220 0.098786 324 0.544635
-1746 231 0.381286 263 0.308107 304 0.288083 324 0.022523
-1747 225 0.185062 304 0.282861 51 0.175639 263 0.356438
-1748 260 0.112456 329 0.222702 259 0.422276 331 0.242567
-1749 259 0.405128 329 0.439524 220 0.081570 331 0.073778
-1750 116 0.081191 315 0.143583 259 0.380581 329 0.394645
-1751 192 0.138238 316 0.636339 43 0.072721 329 0.152702
-1752 192 0.268138 316 0.130824 43 0.128923 329 0.472115
-1753 114 0.109754 316 0.157669 192 0.083070 317 0.649508
-1754 190 0.070977 86 0.079807 241 0.041434 317 0.807782
-1755 192 0.210808 317 0.283921 316 0.459012 329 0.046258
-1756 192 0.343797 317 0.341289 316 0.296472 329 0.018442
-1757 114 0.133857 316 0.011918 192 0.208902 317 0.645323
-1758 191 0.124431 86 0.114745 190 0.007737 317 0.753087
-1759 192 0.300879 316 0.548624 43 0.027997 329 0.122500
-1760 259 0.387322 258 0.035640 116 0.153121 329 0.423918
-1761 192 0.489183 316 0.046852 43 0.053024 329 0.410941
-1762 260 0.002235 329 0.333626 259 0.582695 331 0.081445
-1763 260 0.226006 329 0.188231 259 0.413474 331 0.172289
-1764 115 0.264289 317 0.241931 192 0.243526 329 0.250254
-1765 260 0.230577 317 0.562542 115 0.182788 329 0.024093
-1766 263 0.009073 33 0.221107 232 0.297231 340 0.472589
-1767 190 0.180498 316 0.207488 114 0.431017 317 0.180997
-1768 191 0.244173 190 0.370500 114 0.189810 317 0.195517
-1769 92 0.093703 55 0.213829 232 0.089937 340 0.602531
-1770 92 0.087807 55 0.161354 232 0.035177 340 0.715662
-1771 191 0.037849 86 0.389175 190 0.352444 317 0.220532
-1772 244 0.118969 26 0.417255 92 0.432748 343 0.031029
-1773 244 0.216724 92 0.046025 141 0.328692 336 0.408560
-1774 142 0.464289 141 0.027744 56 0.231481 336 0.276486
-1775 142 0.229024 226 0.400121 145 0.087165 328 0.283690
-1776 138 0.480713 320 0.223906 226 0.141360 328 0.154021
-1777 43 0.555555 132 0.015182 133 0.166943 320 0.262319
-1778 43 0.232525 316 0.363813 132 0.262311 320 0.141351
-1779 114 0.503835 131 0.003281 132 0.003652 316 0.489232
-1780 190 0.069796 316 0.503173 169 0.196862 114 0.230168
-1781 241 0.155168 316 0.138299 190 0.526778 317 0.179755
-1782 92 0.008617 33 0.208962 55 0.400605 340 0.381816
-1783 33 0.174697 335 0.080899 55 0.292841 340 0.451562
-1784 190 0.290864 86 0.292999 241 0.329079 317 0.087059
-1785 244 0.336752 26 0.257090 92 0.158534 343 0.247624
-1786 141 0.366372 90 0.358449 244 0.083640 336 0.191539
-1787 56 0.048490 328 0.020051 142 0.609431 336 0.322028
-1788 142 0.305174 226 0.252182 145 0.260678 328 0.181966
-1789 138 0.195608 320 0.364151 226 0.291699 328 0.148542
-1790 43 0.321019 132 0.097394 133 0.126474 320 0.455113
-1791 132 0.246089 316 0.382224 77 0.033341 320 0.338346
-1792 114 0.300109 286 0.185295 131 0.056366 316 0.458229
-1793 269 0.054528 316 0.846319 125 0.096772 319 0.002381
-1794 241 0.003222 316 0.382313 190 0.072648 317 0.541817
-1795 190 0.067703 86 0.074167 241 0.181833 317 0.676297
-1796 244 0.098064 90 0.282591 92 0.160007 336 0.459338
-1797 234 0.338739 328 0.214958 6 0.006250 336 0.440053
-1798 6 0.046714 234 0.161103 145 0.155177 328 0.637005
-1799 125 0.006826 320 0.407297 315 0.148733 328 0.437144
-1800 125 0.022577 316 0.720684 288 0.162831 319 0.093908
-1801 91 0.148425 335 0.772402 27 0.029016 343 0.050157
-1802 66 0.047743 239 0.106458 240 0.057312 319 0.788488
-1803 117 0.323266 91 0.451029 261 0.061284 335 0.164422
-1804 91 0.338512 261 0.208732 306 0.224945 27 0.227811
-1805 293 0.423964 88 0.148693 37 0.213330 122 0.214013
-1806 70 0.364625 103 0.550235 37 0.006161 294 0.078979
-1807 66 0.041437 105 0.049797 239 0.128936 319 0.779830
-1808 91 0.115852 335 0.717556 27 0.023377 343 0.143215
-1809 288 0.326552 316 0.234442 39 0.053718 319 0.385287
-1810 288 0.094279 316 0.095991 39 0.166642 319 0.643088
-1811 91 0.194746 335 0.366032 27 0.280457 343 0.158766
-1812 120 0.518520 92 0.051381 91 0.095442 343 0.334656
-1813 120 0.155760 91 0.330903 27 0.316395 343 0.196942
-1814 130 0.258683 278 0.285562 70 0.083293 89 0.372463
-1815 293 0.253602 89 0.224987 122 0.374085 70 0.147326
-1816 123 0.443160 125 0.093826 71 0.130587 281 0.332426
-1817 121 0.557159 270 0.297201 103 0.023930 70 0.121710
-1818 124 0.039601 123 0.014036 125 0.364086 71 0.582278
-1819 288 0.326236 71 0.233470 125 0.237268 319 0.203026
-1820 288 0.472267 316 0.088792 39 0.173466 319 0.265474
-1821 282 0.069589 288 0.142933 39 0.198079 319 0.589399
-1822 241 0.100784 316 0.850703 269 0.042474 319 0.006039
-1823 241 0.117023 316 0.339111 190 0.134129 317 0.409738
-1824 92 0.045632 33 0.251128 55 0.382985 340 0.320256
-1825 33 0.278598 335 0.059540 55 0.300017 340 0.361844
-1826 190 0.084514 86 0.088501 241 0.319147 317 0.507839
-1827 244 0.404099 26 0.085346 92 0.395346 343 0.115209
-1828 244 0.207669 90 0.208765 92 0.075963 336 0.507603
-1829 142 0.115730 328 0.168011 234 0.261401 336 0.454858
-1830 234 0.149161 142 0.067033 145 0.233108 328 0.550697
-1831 315 0.030172 320 0.415835 257 0.077247 328 0.476747
-1832 316 0.086434 320 0.533238 43 0.199341 329 0.180988
-1833 125 0.008898 320 0.377168 316 0.564372 329 0.049563
-1834 288 0.023134 316 0.887707 39 0.075162 319 0.013997
-1835 33 0.323131 335 0.178002 55 0.277218 340 0.221649
-1836 33 0.367401 335 0.118882 55 0.366663 340 0.147054
-1837 92 0.120191 90 0.288181 244 0.094602 343 0.497026
-1838 232 0.414752 92 0.441066 231 0.010365 26 0.133817
-1839 92 0.259884 55 0.158247 232 0.277932 340 0.303937
-1840 92 0.222138 55 0.196316 232 0.142778 340 0.438767
-1841 92 0.460603 26 0.452448 232 0.071839 343 0.015109
-1842 92 0.171497 55 0.237362 232 0.055670 340 0.535471
-1843 92 0.382829 26 0.418015 232 0.020186 343 0.178970
-1844 92 0.124636 33 0.111211 55 0.341412 340 0.422741
-1845 244 0.155106 26 0.277353 92 0.204361 343 0.363180
-1846 120 0.359750 91 0.122253 27 0.003510 343 0.514488
-1847 120 0.181932 91 0.288723 27 0.207446 343 0.321899
-1848 92 0.009274 335 0.708167 91 0.091111 343 0.191448
-1849 91 0.135188 335 0.361230 27 0.245957 343 0.257625
-1850 92 0.067070 33 0.282897 55 0.416708 340 0.233324
-1851 244 0.273431 26 0.106277 92 0.330228 343 0.290064
-1852 33 0.418198 335 0.061848 55 0.461904 340 0.058049
-1853 92 0.134425 90 0.161510 244 0.125377 343 0.578688
-1854 92 0.352252 55 0.081516 232 0.487032 340 0.079200
-1855 232 0.233311 92 0.543591 231 0.168669 26 0.054430
-1856 92 0.320597 55 0.104905 232 0.093727 340 0.480771
-1857 92 0.390988 55 0.020560 232 0.232298 340 0.356153
-1858 140 0.086505 92 0.565545 26 0.346528 231 0.001422
-1859 92 0.264309 55 0.163309 232 0.011425 340 0.560958
-1860 92 0.574567 244 0.140067 141 0.003437 26 0.281929
-1861 92 0.194629 33 0.121753 55 0.255101 340 0.428517
-1862 244 0.271728 26 0.182293 92 0.421504 343 0.124475
-1863 120 0.384605 92 0.094799 91 0.422485 343 0.098111
-1864 120 0.469768 92 0.276813 91 0.076566 343 0.176853
-1865 91 0.362437 335 0.219984 27 0.117660 343 0.299919
-1866 92 0.167357 335 0.611883 91 0.151721 343 0.069040
-1867 92 0.155632 33 0.267127 55 0.309092 340 0.268149
-1868 244 0.384581 26 0.031418 92 0.556833 343 0.027169
-1869 92 0.076145 33 0.517155 55 0.387790 340 0.018910
-1870 92 0.301179 90 0.294166 244 0.046869 343 0.357786
-1871 232 0.474712 263 0.048623 92 0.374929 33 0.101735
-1872 232 0.107555 92 0.489822 231 0.327718 26 0.074905
-1873 232 0.421283 33 0.256461 92 0.217783 340 0.104473
-1874 232 0.101995 33 0.023454 92 0.311060 340 0.563491
-1875 92 0.495220 140 0.125476 26 0.295634 141 0.083670
-1876 92 0.269780 55 0.092761 232 0.007489 340 0.629970
-1877 92 0.516322 244 0.085610 141 0.136025 26 0.262044
-1878 92 0.115839 33 0.261958 55 0.267831 340 0.354372
-1879 92 0.428996 244 0.418639 141 0.022588 26 0.129777
-1880 120 0.623082 92 0.314365 91 0.040341 343 0.022212
-1881 278 0.031716 91 0.462662 120 0.483466 92 0.022155
-1882 92 0.123400 335 0.601063 91 0.221329 343 0.054208
-1883 91 0.387498 335 0.198929 27 0.186560 343 0.227013
-1884 92 0.152630 33 0.302116 55 0.250635 340 0.294619
-1885 244 0.419635 90 0.044605 92 0.470971 336 0.064790
-1886 92 0.028006 33 0.597878 55 0.359184 340 0.014931
-1887 92 0.270629 90 0.407725 244 0.053231 343 0.268415
-1888 232 0.511104 263 0.230932 92 0.255414 33 0.002550
-1889 125 0.122591 320 0.643227 316 0.081899 329 0.152283
-1890 288 0.013167 316 0.420531 125 0.157673 320 0.408629
-1891 288 0.110187 316 0.494642 125 0.121200 320 0.273971
-1892 125 0.252059 320 0.553515 315 0.098480 328 0.095947
-1893 288 0.271576 316 0.454767 125 0.073557 320 0.200100
-1894 281 0.128321 320 0.467368 125 0.243600 328 0.160711
-1895 71 0.152862 288 0.054057 125 0.480104 124 0.312977
-1896 71 0.078070 288 0.415970 125 0.425263 124 0.080697
-1897 288 0.536952 316 0.190876 125 0.171943 320 0.100230
-1898 281 0.368325 124 0.387425 125 0.134605 145 0.109646
-1899 315 0.012670 320 0.569715 125 0.142721 329 0.274893
-1900 125 0.213991 320 0.315137 316 0.336589 329 0.134283
-1901 125 0.290720 320 0.181805 316 0.499371 329 0.028104
-1902 125 0.205891 320 0.435197 315 0.254103 328 0.104809
-1903 288 0.156210 316 0.427679 125 0.346853 320 0.069258
-1904 125 0.323880 320 0.242229 315 0.096345 328 0.337546
-1905 124 0.084745 123 0.083749 125 0.587518 71 0.243987
-1906 288 0.270741 71 0.106559 125 0.512078 319 0.110622
-1907 125 0.393857 316 0.184430 288 0.364166 319 0.057547
-1908 281 0.425470 124 0.349105 123 0.042786 125 0.182639
-1909 125 0.047532 320 0.316227 316 0.366141 329 0.270100
-1910 43 0.031951 320 0.560558 315 0.058066 329 0.349424
-1911 125 0.168839 320 0.127200 316 0.586540 329 0.117421
-1912 125 0.064877 320 0.461302 315 0.354210 328 0.119610
-1913 125 0.318540 316 0.534954 288 0.132062 319 0.014443
-1914 125 0.229880 320 0.209304 315 0.130697 328 0.430119
-1915 288 0.217979 71 0.143055 125 0.448842 319 0.190123
-1916 123 0.089988 125 0.500898 71 0.346095 281 0.063019
-1917 125 0.292499 316 0.220900 288 0.319393 319 0.167208
-1918 281 0.396523 124 0.156982 123 0.253530 125 0.192965
-1919 90 0.600420 278 0.053804 92 0.317137 336 0.028639
-1920 120 0.149154 90 0.388097 92 0.183082 343 0.279668
-1921 120 0.520103 90 0.124676 92 0.249836 343 0.105385
-1922 293 0.287460 88 0.376959 37 0.197286 122 0.138295
-1923 293 0.284689 89 0.290545 307 0.078851 122 0.345914
-1924 130 0.095026 278 0.289524 89 0.538382 6 0.077068
-1925 90 0.013438 234 0.484897 278 0.217701 336 0.283963
-1926 88 0.069161 91 0.363923 306 0.524393 307 0.042523
-1927 91 0.344585 307 0.287513 120 0.267519 27 0.100383
-1928 120 0.289991 278 0.061041 92 0.204891 233 0.444077
-1929 88 0.616423 91 0.064673 307 0.058963 293 0.259941
-1930 293 0.236323 89 0.254340 307 0.419497 122 0.089841
-1931 70 0.338829 103 0.439958 37 0.184261 294 0.036951
-1932 121 0.599377 270 0.131947 103 0.057315 70 0.211361
-1933 123 0.644330 270 0.118213 70 0.177556 281 0.059901
-1934 145 0.225063 320 0.177632 281 0.314631 328 0.282674
-1935 293 0.285215 88 0.067067 37 0.428881 122 0.218837
-1936 293 0.063029 89 0.050156 122 0.625927 70 0.260888
-1937 130 0.535873 278 0.307930 70 0.036790 89 0.119406
-1938 6 0.456207 328 0.063540 278 0.025821 336 0.454432
-1939 281 0.174740 6 0.235542 145 0.203385 328 0.386333
-1940 70 0.315858 121 0.004004 37 0.477836 103 0.202302
-1941 122 0.052457 37 0.036973 70 0.353952 121 0.556618
-1942 123 0.421656 70 0.354295 130 0.099229 6 0.124820
-1943 288 0.000180 71 0.594612 125 0.278636 319 0.126572
-1944 288 0.149113 71 0.378324 125 0.211902 319 0.260661
-1945 288 0.417726 316 0.058589 39 0.020196 319 0.503488
-1946 123 0.493871 270 0.291030 281 0.042359 71 0.172740
-1947 66 0.043591 103 0.184210 282 0.004368 319 0.767831
-1948 282 0.094169 71 0.165165 288 0.033502 319 0.707164
-1949 294 0.010754 270 0.050676 70 0.298236 103 0.640333
-1950 121 0.559229 270 0.321531 71 0.070892 103 0.048348
-1951 71 0.018744 103 0.394430 270 0.074110 319 0.512716
-1952 71 0.064848 103 0.502719 270 0.180063 319 0.252371
-1953 71 0.380448 103 0.270887 270 0.171837 319 0.176827
-1954 71 0.322200 103 0.167764 270 0.065094 319 0.444942
-1955 192 0.014543 317 0.547032 316 0.365133 329 0.073291
-1956 190 0.019184 86 0.027385 241 0.103120 317 0.850312
-1957 316 0.711030 317 0.011741 125 0.233936 329 0.043293
-1958 125 0.110042 316 0.162651 288 0.338286 319 0.389021
-1959 125 0.028605 316 0.113956 288 0.114787 319 0.742652
-1960 66 0.082565 105 0.000878 239 0.018736 319 0.897822
diff --git a/examples/turtle/model/turtle.mtl b/examples/turtle/model/turtle.mtl
deleted file mode 100644
index 5f4746e6d1cfa3f0cbd5f4e37773f7f20d5ecec3..0000000000000000000000000000000000000000
--- a/examples/turtle/model/turtle.mtl
+++ /dev/null
@@ -1,64 +0,0 @@
-newmtl Belly
-illum 4
-Kd 0.70 0.63 0.27
-Ka 0.00 0.00 0.00
-Tf 1.00 1.00 1.00
-Ni 1.00
-Ks 0.10 0.10 0.10
-Ns 17.65
-newmtl Eye_Base_antonio_iris_png
-illum 4
-Kd 0.50 0.50 0.50
-Ka 0.20 0.20 0.20
-Tf 1.00 1.00 1.00
-Ni 1.00
-Ks 0.10 0.10 0.10
-Ns 96.08
-newmtl Eye_Cornea
-illum 4
-Kd 0.60 0.69 0.65
-Ka 0.00 0.00 0.00
-Tf 0.20 0.20 0.20
-Ni 1.00
-Ks 1.00 1.00 1.00
-Ns 978.43
-newmtl Eye_Iris_antonio_iris_png
-illum 4
-Kd 0.00 0.00 0.00
-Ka 0.00 0.00 0.00
-Tf 1.00 1.00 1.00
-Ni 1.00
-Ks 0.50 0.50 0.50
-Ns 96.08
-newmtl Eye_Pupil_antonio_iris_png
-illum 4
-Kd 0.00 0.00 0.00
-Ka 0.00 0.00 0.00
-Tf 1.00 1.00 1.00
-Ni 1.00
-Ks 0.50 0.50 0.50
-Ns 96.08
-newmtl Material_004
-illum 4
-Kd 0.35 0.51 0.35
-Ka 0.00 0.00 0.00
-Tf 1.00 1.00 1.00
-Ni 1.00
-Ks 0.10 0.10 0.10
-Ns 17.65
-newmtl Shell
-illum 4
-Kd 0.31 0.26 0.18
-Ka 0.00 0.00 0.00
-Tf 1.00 1.00 1.00
-Ni 1.00
-Ks 0.30 0.30 0.30
-Ns 194.12
-newmtl Shell_Grooves
-illum 4
-Kd 0.34 0.35 0.21
-Ka 0.00 0.00 0.00
-Tf 1.00 1.00 1.00
-Ni 1.00
-Ks 0.10 0.10 0.10
-Ns 17.65
diff --git a/examples/turtle/model/turtle.obj b/examples/turtle/model/turtle.obj
deleted file mode 100644
index 133afb93cf3d1c13f65b8e0cd9e38fdaacdb51ac..0000000000000000000000000000000000000000
--- a/examples/turtle/model/turtle.obj
+++ /dev/null
@@ -1,9586 +0,0 @@
-# This file uses centimeters as units for non-parametric coordinates.
-
-mtllib turtle.mtl
-g default
-v 0.894364 4.268147 -0.552617
-v 0.601310 1.892161 -0.474644
-v 0.441164 4.578611 -0.601308
-v 0.246260 4.585340 -0.666746
-v 0.114655 1.613449 -0.329068
-v 0.000000 1.617775 -0.330704
-v 0.000000 4.628910 -0.720798
-v 0.860199 1.916128 -0.291443
-v 1.162404 2.310402 -0.216621
-v 1.445977 2.718793 -0.222594
-v 1.466933 3.124115 -0.266589
-v 1.298121 3.535574 -0.586840
-v 1.151166 3.968589 -0.630799
-v 0.656268 4.474073 -0.537240
-v 0.418997 1.780795 -0.498279
-v 0.353489 2.752426 -1.105280
-v 0.567702 3.118009 -1.151921
-v 0.360120 3.655849 -1.156421
-v 0.000000 3.695522 -1.202781
-v 0.000000 2.707793 -1.130510
-v 0.552387 4.155087 -0.822221
-v 0.584306 2.313640 -0.800945
-v 1.060325 3.083802 -0.892737
-v 0.463911 3.386929 -1.154171
-v 0.424893 2.874287 -1.120827
-v 0.496298 2.996148 -1.136374
-v 0.472734 3.006776 -1.139966
-v 0.538650 3.119271 -1.154318
-v 0.403863 2.882462 -1.125615
-v 0.329083 2.775878 -1.111263
-v 0.338159 3.627589 -1.158473
-v 0.433973 3.385249 -1.156395
-v 0.000000 2.737630 -1.134553
-v 0.000000 3.667168 -1.201269
-v 0.334391 3.562862 -1.212043
-v 0.000000 3.589972 -1.250787
-v 0.320465 2.804258 -1.166779
-v 0.000000 2.787661 -1.191361
-v 0.468119 3.158023 -1.215750
-v 0.405080 3.379167 -1.217310
-v 0.377556 2.893494 -1.185059
-v 0.436417 3.014798 -1.202140
-v 0.000000 3.201811 -1.290968
-v 0.000000 3.402325 -1.281649
-v 0.000000 2.905322 -1.236671
-v 0.000000 3.042980 -1.270768
-v 0.587727 3.088680 -1.121426
-v 0.521557 2.975751 -1.107019
-v 0.455386 2.862822 -1.092612
-v 0.389216 2.749894 -1.078204
-v 0.880726 1.983561 -0.260701
-v 0.603115 2.343270 -0.796177
-v 1.421045 3.094338 -0.300989
-v 1.044242 3.056981 -0.881240
-v 1.401626 2.718726 -0.260218
-v 1.138838 2.340270 -0.254683
-v 0.907057 2.000102 -0.321363
-v 1.152539 2.339356 -0.315639
-v 1.402467 2.699293 -0.320904
-v 1.420936 3.056525 -0.359679
-v 0.643031 2.342210 -0.830636
-v 0.439599 2.728935 -1.098862
-v 0.628396 3.051143 -1.139969
-v 1.062572 3.020995 -0.911536
-v 0.502531 2.836338 -1.112565
-v 0.565464 2.943741 -1.126267
-v 0.871596 2.513484 -0.727531
-v 1.037863 2.771827 -0.749290
-v 0.714473 4.348294 -0.585307
-v 1.391599 3.173524 -0.436515
-v 1.052518 3.143625 -0.905403
-v 0.886329 4.202788 -0.605435
-v 1.242415 3.534956 -0.614999
-v 1.106430 3.934802 -0.653035
-v 0.594924 4.112163 -0.841818
-v 0.410942 3.656769 -1.142132
-v 0.603221 3.163483 -1.147697
-v 0.496086 3.410296 -1.179317
-v 0.478799 3.673793 -1.162596
-v 0.557249 3.446701 -1.196858
-v 0.655960 3.219294 -1.167724
-v 0.751877 4.349762 -0.612856
-v 0.655602 4.115324 -0.880211
-v 1.382349 3.228545 -0.512461
-v 1.069929 3.200997 -0.944481
-v 1.119602 3.929965 -0.711957
-v 0.906156 4.184588 -0.668252
-v 1.244896 3.561558 -0.676911
-v 0.996913 3.534435 -0.917662
-v 0.180060 3.675685 -1.179601
-v 0.176744 2.730109 -1.117895
-v 0.158615 2.756754 -1.122908
-v 0.163153 3.647378 -1.179871
-v 0.179576 3.582266 -1.234919
-v 0.172214 2.793604 -1.182249
-v 0.215515 3.390077 -1.258412
-v 0.246970 3.181401 -1.263679
-v 0.200795 2.901681 -1.219290
-v 0.231909 3.030675 -1.245981
-v 0.000000 2.652369 -1.108443
-v 0.181435 2.672098 -1.102334
-v 0.337691 2.691828 -1.088923
-v 0.541753 2.303905 -0.815248
-v 0.240721 1.741142 -0.457370
-v 0.395606 1.832826 -0.496262
-v 0.556786 1.931283 -0.502431
-v 0.785665 1.952471 -0.400701
-v 0.185173 1.680984 -0.420441
-v 0.535252 1.901656 -0.580384
-v 0.245870 1.727567 -0.539128
-v 0.141332 1.676054 -0.505866
-v 0.000000 1.679556 -0.516862
-v 0.763972 1.928624 -0.480108
-v 0.387680 1.811511 -0.574736
-v 0.334654 2.597991 -1.117361
-v 0.521488 2.242819 -0.866792
-v 0.191590 2.579928 -1.129640
-v 0.000000 2.561864 -1.135234
-v 0.285006 2.177692 -0.888730
-v 0.000000 2.112565 -0.867228
-v 0.000000 3.745822 -1.167307
-v 0.160569 3.727858 -1.146315
-v 0.323629 3.709894 -1.125324
-v 0.570891 4.446858 -0.559973
-v 0.501240 4.171970 -0.828488
-v 0.220519 4.551630 -0.681881
-v 0.000000 4.591086 -0.730829
-v 0.398505 4.518962 -0.606153
-v 0.373343 4.522595 -0.701857
-v 0.225734 4.553530 -0.776799
-v 0.000000 4.587370 -0.818781
-v 0.553368 4.482742 -0.623283
-v 0.314169 3.768378 -1.194095
-v 0.475044 4.243768 -0.894897
-v 0.174316 3.783786 -1.212099
-v 0.000000 3.799193 -1.230103
-v 0.269740 4.286171 -0.956644
-v 0.000000 4.321912 -0.969626
-v 0.000000 1.643304 -0.460300
-v 0.479479 4.869820 -0.744927
-v 0.275163 4.946365 -0.954242
-v 0.072600 1.527242 -0.324440
-v 0.000000 1.535203 -0.327644
-v 0.000000 5.014968 -1.014430
-v 0.898310 1.876836 -0.177654
-v 1.222305 2.255669 -0.205358
-v 1.526845 2.690965 -0.212472
-v 1.551464 3.137764 -0.247453
-v 1.435115 3.522739 -0.614016
-v 1.287395 4.054248 -0.788321
-v 0.968233 4.382786 -0.575087
-v 0.659630 4.807325 -0.610875
-v 0.491151 4.907478 -0.505309
-v 0.207162 4.975423 -0.710121
-v 0.063231 1.523003 -0.191506
-v 0.000000 1.530438 -0.199272
-v 0.000000 5.035666 -0.765170
-v 1.030829 1.763474 -0.060408
-v 1.305855 2.205369 -0.050705
-v 1.534914 2.688713 -0.087988
-v 1.559499 3.139965 -0.125060
-v 1.431708 3.553847 -0.467891
-v 1.278676 4.072967 -0.627298
-v 0.977141 4.376593 -0.388540
-v 0.631038 4.877497 -0.437345
-v 0.519436 4.495228 -0.326146
-v 0.239866 4.578342 -0.576986
-v 0.000000 4.647167 -0.658392
-v 1.064440 2.314215 0.016113
-v 1.122839 2.730615 -0.006406
-v 1.132661 3.132987 -0.090046
-v 1.044797 3.512056 -0.397286
-v 0.576710 4.533762 -0.145339
-v 0.000000 1.662369 0.264066
-v 0.000000 4.409410 0.501791
-v 0.000000 1.761989 0.543465
-v 0.000000 1.896002 0.849942
-v 0.000000 2.825269 1.079886
-v 0.000000 3.498520 0.740326
-v 0.000000 4.401259 0.560592
-v 0.075157 1.653299 0.248868
-v 0.463266 4.447080 0.246541
-v 0.220743 4.434513 0.357432
-v 0.203639 1.760402 0.488618
-v 0.777611 1.867949 0.535043
-v 0.414898 1.914068 0.665136
-v 0.697963 2.740074 0.781336
-v 0.325313 2.797833 0.978176
-v 0.636333 3.529989 0.423308
-v 0.237559 3.502991 0.716126
-v 0.545244 3.919782 0.424609
-v 0.536370 4.158761 0.371050
-v 0.535874 4.495495 0.329818
-v 0.233006 4.426234 0.470615
-v 0.484104 4.608221 -0.327453
-v 0.229203 4.672074 -0.552975
-v 0.079726 1.575226 -0.171817
-v 0.000000 1.580857 -0.179749
-v 0.000000 4.731133 -0.603687
-v 0.962596 1.911706 -0.016738
-v 1.120579 2.286526 -0.017571
-v 1.192731 2.711494 -0.027523
-v 1.206906 3.136870 -0.081855
-v 1.102845 3.536490 -0.409735
-v 0.985386 4.019822 -0.558741
-v 0.807870 4.318978 -0.361011
-v 0.555419 4.620965 -0.273160
-v 0.000000 1.613965 -0.105292
-v 0.047770 1.611138 -0.092408
-v 0.960971 1.887755 0.202799
-v 0.595045 1.849410 -0.398692
-v 0.285561 1.664060 -0.349371
-v 0.193666 1.623826 -0.299570
-v 0.811316 1.870345 -0.253187
-v 0.124693 1.575049 -0.271382
-v 0.834543 1.841924 -0.164113
-v 0.116394 1.570258 -0.196539
-v 0.923514 1.776398 -0.047729
-v 0.742962 1.825710 0.462902
-v 0.498361 1.890616 0.578522
-v 0.110757 1.645410 0.219499
-v 0.269786 1.728498 0.439919
-v 0.116676 1.597817 -0.140776
-v 0.908747 1.868926 0.009517
-v 0.099325 1.610218 -0.074108
-v 0.912572 1.842171 0.173244
-v 0.442496 1.754897 -0.419981
-v 0.575128 1.986038 -0.388754
-v 0.180010 1.715653 -0.278763
-v 0.270033 1.791023 -0.335277
-v 0.798950 2.007299 -0.238718
-v 0.120693 1.658517 -0.247659
-v 0.838267 1.970413 -0.129454
-v 0.115571 1.647423 -0.200095
-v 0.912436 1.907180 -0.057492
-v 0.488198 2.033300 0.560952
-v 0.736645 1.967176 0.450486
-v 0.263502 1.857369 0.419674
-v 0.110311 1.773043 0.208438
-v 0.117132 1.680169 -0.152889
-v 0.898991 2.001902 -0.013893
-v 0.101467 1.731409 -0.082316
-v 0.897337 1.982730 0.174061
-v 0.417112 1.886196 -0.407429
-v 0.602376 2.020186 -0.463414
-v 0.242077 1.810447 -0.409406
-v 0.111919 1.748384 -0.320884
-v 0.862922 2.043559 -0.284192
-v 0.078138 1.680590 -0.272221
-v 0.901331 2.005241 -0.173224
-v 0.060991 1.660181 -0.186732
-v 0.992902 1.935633 -0.068197
-v 0.779527 1.996574 0.521804
-v 0.480317 2.065906 0.651401
-v 0.073622 1.787246 0.242725
-v 0.232371 1.877480 0.484166
-v 0.077615 1.711110 -0.167532
-v 0.965645 2.039247 -0.000176
-v 0.045409 1.746130 -0.090091
-v 0.964007 2.015889 0.197797
-v 0.418639 1.911581 -0.486463
-v 0.930876 3.980696 -0.379037
-v 0.824033 4.216189 -0.299441
-v 0.772494 4.316643 -0.237270
-v 0.944988 3.779257 -0.331559
-v 0.782904 4.375865 -0.128176
-v 0.903746 3.717081 -0.241430
-v 0.694681 3.929798 0.206244
-v 0.685980 4.106970 0.195626
-v 0.785283 4.320419 0.086162
-v 0.827591 3.656310 0.170557
-v 0.840877 3.904824 -0.437225
-v 0.706884 4.135632 -0.360016
-v 0.640743 4.240607 -0.300506
-v 0.882744 3.668248 -0.415907
-v 0.616157 4.265114 -0.163285
-v 0.838675 3.583704 -0.316795
-v 0.550890 3.848572 0.200350
-v 0.522186 4.060302 0.164144
-v 0.598689 4.221076 0.046650
-v 0.713151 3.645145 0.163261
-v 0.691721 4.165222 -0.453008
-v 0.854157 3.888806 -0.542361
-v 0.596587 4.305797 -0.365237
-v 0.931917 3.604791 -0.489530
-v 0.560254 4.331712 -0.176240
-v 0.875145 3.500219 -0.349580
-v 0.474253 4.070497 0.219494
-v 0.512073 3.817972 0.260218
-v 0.543668 4.272953 0.084412
-v 0.656498 3.561714 0.234422
-v 1.016130 2.199926 0.220190
-v 0.964396 1.986696 0.313532
-v 0.872716 1.976793 0.479654
-v 0.000000 2.720541 1.207575
-v 0.362212 2.686535 1.078628
-v 0.763640 2.647703 0.814621
-v 1.070605 2.624241 0.204687
-v 0.000000 2.063571 1.033100
-v 0.409832 2.064489 0.875264
-v 0.935927 2.203910 0.599761
-v 0.000000 2.446927 1.213294
-v 0.381925 2.453651 1.104526
-v 0.000000 3.435851 0.841994
-v 0.274342 3.441097 0.813600
-v 0.593421 3.429113 0.569790
-v 0.973242 3.426804 -0.115422
-v 0.000000 2.957179 1.157222
-v 0.351031 2.923172 1.028275
-v 0.729526 2.873911 0.763822
-v 1.055085 2.844625 0.164860
-v 1.024304 3.237006 0.040993
-v 0.000000 3.289926 0.975277
-v 0.315143 3.289073 0.868887
-v 0.642833 3.237947 0.631082
-v 0.467087 3.979361 0.479808
-v 0.454547 4.099108 0.445174
-v 0.440282 4.385054 0.391415
-v 0.519069 3.631913 0.539097
-v 0.000000 4.210371 0.591084
-v 0.189118 4.238502 0.585909
-v 0.000000 3.601468 0.781014
-v 0.250078 3.606714 0.752620
-v 0.000000 4.071157 0.671095
-v 0.000000 3.948291 0.704318
-v 0.200703 3.956627 0.675776
-v 0.193622 4.081429 0.642029
-v 0.239866 4.473637 -0.576986
-v 0.000000 4.542462 -0.658392
-v 0.519436 4.390522 -0.326146
-v 0.463266 4.342374 0.246541
-v 0.220743 4.329808 0.357432
-v 0.000000 4.304704 0.501791
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.651759 0.557496
-vt 0.663910 0.496406
-vt 0.951876 0.496406
-vt 0.917804 0.667695
-vt 0.917804 0.667695
-vt 0.820777 0.812908
-vt 0.617154 0.609285
-vt 0.651759 0.557496
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.820777 0.812908
-vt 0.675565 0.909935
-vt 0.565365 0.643890
-vt 0.617154 0.609285
-vt 0.675565 0.909935
-vt 0.504275 0.944007
-vt 0.504275 0.656041
-vt 0.565365 0.643890
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.504275 0.944007
-vt 0.332986 0.909935
-vt 0.443186 0.643890
-vt 0.504275 0.656041
-vt 0.332986 0.909935
-vt 0.187774 0.812908
-vt 0.391397 0.609285
-vt 0.443186 0.643890
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.187774 0.812908
-vt 0.090746 0.667696
-vt 0.356792 0.557496
-vt 0.391397 0.609285
-vt 0.090746 0.667696
-vt 0.056675 0.496406
-vt 0.344641 0.496406
-vt 0.356792 0.557496
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.056675 0.496406
-vt 0.090746 0.325117
-vt 0.356792 0.435317
-vt 0.344641 0.496406
-vt 0.090746 0.325117
-vt 0.187774 0.179905
-vt 0.391397 0.383527
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.187774 0.179905
-vt 0.332986 0.082877
-vt 0.443186 0.348923
-vt 0.391397 0.383527
-vt 0.332986 0.082877
-vt 0.504275 0.048806
-vt 0.504275 0.336771
-vt 0.443186 0.348923
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.504275 0.048806
-vt 0.675565 0.082877
-vt 0.565365 0.348923
-vt 0.504275 0.336771
-vt 0.675565 0.082877
-vt 0.820777 0.179905
-vt 0.617154 0.383527
-vt 0.565365 0.348923
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.820777 0.179905
-vt 0.917804 0.325117
-vt 0.651759 0.435317
-vt 0.617154 0.383527
-vt 0.663910 0.496406
-vt 0.651759 0.435317
-vt 0.917804 0.325117
-vt 0.951876 0.496406
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vn 0.234879 -0.249816 -0.939374
-vn 0.324716 -0.302580 -0.896106
-vn 0.434139 -0.307065 -0.846897
-vn 0.439845 -0.056930 -0.896267
-vn 0.330376 -0.307636 -0.892307
-vn 0.473222 -0.386709 -0.791528
-vn 0.312279 -0.277992 -0.908406
-vn 0.293554 -0.500133 -0.814673
-vn 0.097495 0.272756 -0.957131
-vn -0.038586 -0.018459 -0.999085
-vn 0.498816 0.179717 -0.847871
-vn 0.340736 0.268973 -0.900862
-vn 0.499851 -0.037283 -0.865309
-vn 0.552347 0.212611 -0.806046
-vn 0.444403 0.406358 -0.798360
-vn 0.364715 -0.642595 -0.673836
-vn 0.495119 -0.428711 -0.755688
-vn 0.478896 -0.306423 -0.822657
-vn 0.287941 -0.051232 -0.956277
-vn 0.108097 -0.187883 -0.976225
-vn 0.108094 -0.187884 -0.976225
-vn 0.078858 -0.372664 -0.924610
-vn 0.535833 -0.490887 -0.686960
-vn 0.623946 -0.293267 -0.724352
-vn 0.181821 -0.627765 -0.756870
-vn 0.534659 -0.583508 -0.611276
-vn 0.606803 -0.419325 -0.675246
-vn 0.581221 -0.327703 -0.744844
-vn 0.881061 -0.025386 -0.472321
-vn 0.672830 0.079310 -0.735533
-vn 0.393352 -0.189034 -0.899744
-vn 0.875316 -0.328690 -0.354661
-vn 0.384341 -0.254295 -0.887477
-vn 0.759888 -0.509100 -0.404213
-vn 0.461810 -0.811929 -0.357075
-vn 0.790574 -0.563128 -0.240582
-vn 0.891109 -0.362769 -0.272622
-vn 0.913976 0.144328 -0.379232
-vn -0.129684 -0.352931 -0.926619
-vn 0.092228 -0.687216 -0.720575
-vn 0.655506 0.195307 -0.729497
-vn 0.181412 0.127610 -0.975092
-vn 0.092211 -0.146738 -0.984868
-vn 0.057444 -0.198384 -0.978440
-vn 0.575964 -0.400833 -0.712459
-vn 0.587752 -0.399159 -0.703719
-vn -0.178599 0.311899 -0.933178
-vn -0.191784 -0.067482 -0.979115
-vn 0.129579 -0.298035 -0.945719
-vn 0.218054 0.555398 -0.802487
-vn 0.238766 0.213078 -0.947412
-vn 0.342102 0.771009 -0.537133
-vn -0.054660 0.652793 -0.755562
-vn 0.605976 -0.417071 -0.677381
-vn 0.918950 -0.102511 -0.380818
-vn -0.012355 0.186835 -0.982314
-vn -0.081804 0.013481 -0.996557
-vn 0.764239 0.458157 -0.453906
-vn 0.647401 0.671809 -0.359923
-vn 0.236432 -0.289118 -0.927637
-vn 0.915351 0.253543 -0.312807
-vn -0.087440 0.364907 -0.926929
-vn 0.138913 0.085820 -0.986579
-vn 0.254814 -0.237586 -0.937349
-vn 0.326504 0.857574 -0.397444
-vn 0.147306 0.570312 -0.808112
-vn 0.633152 -0.240098 -0.735847
-vn 0.934620 0.016433 -0.355267
-vn 0.776268 0.515837 -0.362381
-vn 0.717487 0.687658 -0.111078
-vn 0.890800 0.339496 -0.302023
-vn 0.570370 0.211237 -0.793761
-vn 0.092325 -0.213667 -0.972534
-vn 0.108234 -0.534000 -0.838528
-vn 0.087109 -0.221310 -0.971305
-vn 0.100641 -0.506608 -0.856282
-vn 0.153322 0.271612 -0.950115
-vn 0.152296 0.276379 -0.948905
-vn 0.147616 0.304380 -0.941043
-vn 0.136173 0.286659 -0.948306
-vn 0.157458 0.393543 -0.905721
-vn 0.123100 0.385264 -0.914559
-vn 0.114560 -0.618546 -0.777353
-vn 0.086732 -0.600929 -0.794582
-vn 0.108389 0.097421 -0.989324
-vn 0.156486 0.095059 -0.983095
-vn 0.156663 -0.040361 -0.986827
-vn 0.105547 -0.040995 -0.993569
-vn 0.077380 -0.290826 -0.953642
-vn 0.120524 -0.285190 -0.950863
-vn 0.092483 -0.178570 -0.979571
-vn 0.141028 -0.181359 -0.973252
-vn 0.063119 -0.001535 -0.998005
-vn 0.045731 -0.013870 -0.998858
-vn 0.405353 -0.064425 -0.911887
-vn 0.652799 -0.579785 -0.487548
-vn 0.754325 -0.128136 -0.643875
-vn 0.213810 -0.576653 -0.788515
-vn 0.210626 -0.865248 -0.454953
-vn 0.409087 -0.837998 -0.361119
-vn 0.369051 -0.739536 -0.562928
-vn 0.406952 -0.613512 -0.676752
-vn 0.365485 -0.842809 -0.395088
-vn 0.340119 -0.894324 -0.290695
-vn 0.619664 -0.679195 -0.393332
-vn 0.243988 -0.827018 -0.506470
-vn 0.457366 -0.855364 -0.243247
-vn 0.145333 -0.943209 -0.298722
-vn 0.110847 -0.831547 -0.544282
-vn 0.421102 -0.838373 -0.346128
-vn 0.598567 -0.315779 -0.736207
-vn 0.431897 -0.031597 -0.901370
-vn 0.079246 -0.114928 -0.990208
-vn 0.033357 -0.121769 -0.991998
-vn 0.142385 -0.568731 -0.810106
-vn 0.065462 -0.566223 -0.821649
-vn 0.113411 -0.129506 -0.985072
-vn 0.113090 -0.128654 -0.985220
-vn 0.426793 0.067296 -0.901842
-vn 0.654363 0.328410 -0.681143
-vn 0.539360 0.552153 -0.635780
-vn 0.334906 -0.251044 -0.908193
-vn 0.378959 0.672822 -0.635374
-vn 0.162115 -0.209132 -0.964356
-vn 0.281825 -0.260978 -0.923290
-vn 0.315681 0.716496 -0.622077
-vn 0.257751 0.765976 -0.588936
-vn 0.359373 0.869877 -0.337884
-vn 0.277993 0.858959 -0.430012
-vn 0.187031 0.860120 -0.474566
-vn 0.660133 0.670527 -0.338553
-vn 0.529637 -0.200008 -0.824306
-vn 0.726360 0.274535 -0.630104
-vn 0.121403 -0.211240 -0.969866
-vn 0.071151 -0.205179 -0.976135
-vn 0.284229 0.517762 -0.806930
-vn 0.139944 0.484992 -0.863249
-vn 0.103670 -0.586254 -0.803467
-vn 0.279226 -0.705236 -0.651671
-vn 0.557255 0.368306 -0.744189
-vn 0.500648 0.351051 -0.791274
-vn -0.059053 -0.708492 -0.703244
-vn 0.380779 -0.780927 -0.495137
-vn 0.321953 0.358136 -0.876405
-vn 0.404386 -0.645773 -0.647649
-vn 0.543493 -0.376379 -0.750303
-vn 0.636519 -0.224477 -0.737871
-vn 0.736465 -0.197839 -0.646899
-vn 0.647159 -0.207497 -0.733574
-vn 0.341775 0.277366 -0.897919
-vn 0.351045 0.429951 -0.831811
-vn 0.499730 0.354040 -0.790522
-vn -0.051493 0.914573 0.401129
-vn 0.008531 0.879405 0.475998
-vn -0.149748 -0.820050 0.552353
-vn 0.234307 -0.845645 0.479566
-vn 0.110431 0.859742 0.498647
-vn 0.517884 -0.806518 0.285173
-vn 0.785020 -0.466431 0.407658
-vn 0.795565 -0.169824 0.581581
-vn 0.761400 0.323893 0.561572
-vn 0.699725 0.483824 0.525642
-vn 0.725996 0.312566 0.612562
-vn 0.735012 0.376185 0.564129
-vn 0.417087 0.774421 0.475721
-vn 0.232919 -0.817749 0.526341
-vn 0.124182 -0.908287 0.399491
-vn 0.099589 -0.891994 0.440942
-vn 0.207909 -0.806047 0.554132
-vn -0.019616 -0.969080 0.245967
-vn -0.042565 -0.965380 0.257352
-vn -0.720199 0.590260 -0.364563
-vn 0.357270 0.734006 0.577575
-vn -0.037630 0.966807 0.252723
-vn -0.891140 0.150802 0.427935
-vn -0.419511 0.676388 -0.605401
-vn 0.095058 0.825605 0.556184
-vn -0.439697 0.721007 -0.535551
-vn 0.064103 0.790454 0.609157
-vn -0.452978 0.068099 0.888917
-vn -0.662598 0.204101 0.720630
-vn -0.522372 -0.161775 0.837231
-vn 0.036478 -0.781584 0.622732
-vn -0.139882 -0.681440 0.718382
-vn -0.063740 -0.967609 0.244276
-vn -0.109279 -0.959577 0.259365
-vn -0.243797 0.035115 0.969190
-vn -0.341697 -0.202869 0.917653
-vn 0.477766 -0.112622 0.871238
-vn 0.643569 -0.439873 0.626363
-vn 0.206641 0.056805 0.976766
-vn 0.762033 0.096238 0.640347
-vn 0.903945 -0.160775 0.396277
-vn 0.063103 0.396799 0.915734
-vn 0.511192 0.412237 0.754151
-vn 0.371848 0.438486 0.818205
-vn 0.713891 0.361920 0.599477
-vn 0.634539 0.152663 0.757664
-vn 0.209758 0.639054 0.740008
-vn 0.700112 0.197251 0.686248
-vn 0.828212 -0.526137 0.192989
-vn -0.355230 -0.925529 0.131181
-vn 0.140300 -0.869112 0.474299
-vn 0.577436 -0.738094 0.348978
-vn 0.705075 -0.651536 0.279947
-vn 0.869466 -0.472519 0.144067
-vn -0.596707 -0.800786 -0.051801
-vn 0.881647 -0.415355 0.224007
-vn -0.705038 -0.593958 0.387472
-vn 0.453517 -0.751334 0.479395
-vn 0.143364 -0.917289 -0.371522
-vn -0.350679 -0.871621 -0.342493
-vn 0.652432 -0.757292 -0.029006
-vn 0.626139 -0.758613 -0.180157
-vn 0.762949 -0.521227 0.382402
-vn -0.600312 -0.693907 0.397640
-vn 0.689013 -0.686911 0.231116
-vn -0.352320 -0.916584 -0.189062
-vn 0.488718 -0.747010 0.450700
-vn -0.645657 0.503935 0.573739
-vn -0.457435 0.477054 0.750449
-vn 0.134295 0.525491 0.840133
-vn 0.216670 0.371426 0.902827
-vn 0.562700 0.472473 0.678335
-vn -0.644713 0.466330 0.605707
-vn 0.855250 0.498707 -0.140854
-vn -0.884813 0.448698 0.125604
-vn -0.068504 0.534401 -0.842451
-vn -0.537004 0.544743 -0.644113
-vn 0.692082 0.620352 -0.369035
-vn 0.338134 0.576944 -0.743506
-vn 0.853417 0.438193 -0.282253
-vn -0.878054 0.376622 -0.295257
-vn 0.832171 0.548093 -0.084170
-vn -0.846949 0.499533 -0.182052
-vn -0.182903 0.533502 0.825786
-vn -0.261234 0.900561 0.347486
-vn -0.413777 0.870650 0.266000
-vn -0.281436 0.860437 0.424785
-vn -0.172081 0.734651 0.656259
-vn 0.073441 0.824964 0.560394
-vn -0.160159 0.836179 0.524550
-vn 0.341812 0.887457 -0.309167
-vn -0.215833 0.960181 -0.177395
-vn -0.168023 0.906465 -0.387414
-vn -0.160215 0.947885 -0.275402
-vn 0.117473 0.961913 -0.246828
-vn -0.213908 0.903835 -0.370575
-vn 0.385451 0.719835 -0.577291
-vn -0.400035 0.789267 -0.465864
-vn 0.367568 0.899210 -0.237308
-vn -0.435970 0.899214 -0.036661
-vn -0.455357 0.848217 0.270513
-vn 0.527943 -0.041478 0.848266
-vn 0.327060 0.086188 0.941065
-vn 0.808253 -0.080227 0.583345
-vn -0.026841 0.420441 0.906923
-vn 0.988825 -0.101310 0.109369
-vn 0.093035 0.813519 0.574049
-vn 0.673139 0.137049 0.726706
-vn 0.728667 0.191809 0.657460
-vn 0.893588 0.205085 -0.399300
-vn 0.952202 -0.241826 -0.186631
-vn 0.965283 -0.260137 -0.023608
-vn 0.759571 0.243795 0.603005
-vn 0.692978 0.715251 -0.090545
-vn -0.605768 -0.605952 0.515623
-vn -0.837826 -0.348406 0.420310
-vn -0.415642 -0.836615 0.356816
-vn -0.999563 0.029232 -0.004448
-vn -0.186263 -0.981850 -0.035732
-vn -0.908169 0.323953 -0.265112
-vn -0.343450 -0.134214 -0.929531
-vn -0.281411 -0.333767 -0.899671
-vn -0.182906 -0.779842 -0.598658
-vn -0.689726 0.217904 -0.690504
-vn -0.853176 -0.521289 -0.018681
-vn -0.917387 -0.378095 -0.124280
-vn -0.811146 -0.584042 -0.030613
-vn -0.912573 -0.202213 -0.355415
-vn -0.760850 -0.637233 -0.122644
-vn -0.914130 -0.054996 -0.401673
-vn -0.693701 -0.263228 -0.670440
-vn -0.740607 -0.195442 -0.642887
-vn -0.771165 -0.439488 -0.460603
-vn -0.826052 -0.142352 -0.545320
-vn 0.961745 -0.130987 0.240600
-vn 0.908848 -0.310602 0.278426
-vn 0.680486 -0.461229 0.569391
-vn 0.434767 0.205021 0.876895
-vn 0.329279 0.167900 0.929185
-vn 0.312866 0.423830 0.849990
-vn 0.447974 0.375115 0.811547
-vn 0.722258 0.219942 0.655720
-vn 0.747769 0.314746 0.584617
-vn 0.933926 0.155572 0.321837
-vn 0.365741 -0.559356 0.743878
-vn 0.285533 -0.605246 0.743067
-vn 0.793148 -0.188229 0.579212
-vn 0.308803 -0.190968 0.931758
-vn 0.448015 -0.224016 0.865505
-vn 0.357379 0.322146 0.876643
-vn 0.093979 0.321864 0.942110
-vn 0.113415 0.715207 0.689649
-vn 0.305974 0.669551 0.676817
-vn 0.646249 0.536188 0.543015
-vn 0.791484 0.462509 0.399548
-vn 0.445830 0.041243 0.894167
-vn 0.318727 -0.011516 0.947777
-vn 0.734012 0.079204 0.674502
-vn 0.878676 0.088211 0.469199
-vn 0.835100 0.343945 0.429313
-vn 0.253036 0.488275 0.835201
-vn 0.419340 0.446162 0.790629
-vn 0.694297 0.389720 0.605037
-vn 0.559701 0.213446 0.800735
-vn 0.623572 0.230156 0.747119
-vn 0.486319 0.231500 0.842557
-vn 0.654265 0.058729 0.753982
-vn 0.103144 0.378653 0.919773
-vn 0.292865 0.333821 0.895987
-vn 0.375808 -0.041554 0.925766
-vn 0.116385 -0.068904 0.990811
-vn 0.083694 0.341447 0.936167
-vn 0.121523 0.241991 0.962638
-vn 0.356362 0.253684 0.899250
-vn 0.330102 0.302194 0.894266
-vn -0.321377 0.000000 0.946952
-vn -0.504872 0.000000 0.863194
-vn -0.932012 0.000000 0.362428
-vn -0.813964 0.000000 -0.580916
-vn -0.482939 0.000000 -0.875654
-vn -0.547319 0.000000 -0.836924
-s 1
-g polySurface1 Mesh
-usemtl Shell_Grooves
-f 17/1/1 26/2/2 27/3/3 28/4/4
-f 26/2/2 25/5/5 29/6/6 27/7/3
-f 25/8/5 16/9/7 30/10/8 29/11/6
-f 18/12/9 24/13/10 32/14/11 31/15/12
-f 24/16/10 17/17/1 28/18/4 32/19/11
-usemtl Shell
-f 32/20/11 28/21/4 39/22/13 40/23/14
-f 31/24/12 32/25/11 40/26/14 35/27/15
-f 29/28/6 30/29/8 37/30/16 41/31/17
-f 27/32/3 29/33/6 41/34/17 42/35/18
-f 28/36/4 27/3/3 42/37/18 39/38/13
-usemtl Shell_Grooves
-f 26/2/2 17/39/1 47/40/19 48/41/20
-f 25/42/5 26/43/2 48/44/20 49/45/21
-f 16/46/7 25/47/5 49/48/21 50/49/22
-f 8/50/23 22/51/24 52/52/25 51/53/26
-f 23/54/27 11/55/28 53/56/29 54/57/30
-f 17/58/1 23/59/27 54/60/30 47/61/19
-f 22/62/24 16/63/7 50/64/22 52/65/25
-f 11/66/28 10/67/31 55/68/32 53/69/29
-f 10/70/31 9/71/33 56/72/34 55/73/32
-f 9/74/33 8/75/23 51/76/26 56/77/34
-usemtl Shell
-f 56/78/34 51/79/26 57/80/35 58/81/36
-f 55/82/32 56/83/34 58/84/36 59/85/37
-f 53/86/29 55/87/32 59/88/37 60/89/38
-f 52/90/25 50/91/22 62/92/39 61/93/40
-f 47/94/19 54/95/30 64/96/41 63/97/42
-f 54/98/30 53/99/29 60/100/38 64/101/41
-f 51/102/26 52/103/25 61/104/40 57/105/35
-f 50/106/22 49/107/21 65/108/43 62/109/39
-f 49/110/21 48/111/20 66/112/44 65/113/43
-f 48/114/20 47/115/19 63/116/42 66/117/44
-f 62/118/39 65/119/43 67/120/45 61/121/40
-f 65/122/43 66/123/44 68/124/46 67/125/45
-f 66/126/44 63/127/42 64/128/41 68/129/46
-f 59/130/37 68/131/46 64/132/41 60/133/38
-f 58/134/36 67/135/45 68/136/46 59/137/37
-f 57/138/35 61/139/40 67/140/45 58/141/36
-usemtl Shell_Grooves
-f 24/142/10 18/143/9 76/144/47 78/145/48
-f 17/146/1 24/147/10 78/148/48 77/149/49
-f 21/150/50 14/151/51 69/152/52 75/153/53
-f 11/154/28 23/155/27 71/156/54 70/157/55
-f 23/158/27 17/1/1 77/159/49 71/160/54
-f 18/161/9 21/162/50 75/163/53 76/164/47
-f 1/165/56 13/166/57 74/167/58 72/168/59
-f 14/169/51 1/170/56 72/171/59 69/172/52
-f 13/173/57 12/174/60 73/175/61 74/176/58
-f 12/177/60 11/178/28 70/179/55 73/180/61
-usemtl Shell
-f 78/181/48 76/182/47 79/183/62 80/184/63
-f 77/185/49 78/186/48 80/187/63 81/188/64
-f 75/189/53 69/190/52 82/191/65 83/192/66
-f 70/193/55 71/194/54 85/195/67 84/196/68
-f 71/197/54 77/198/49 81/199/64 85/200/67
-f 76/201/47 75/202/53 83/203/66 79/204/62
-f 72/205/59 74/206/58 86/207/69 87/208/70
-f 69/209/52 72/210/59 87/211/70 82/212/65
-f 74/213/58 73/214/61 88/215/71 86/216/69
-f 73/217/61 70/218/55 84/219/68 88/220/71
-f 81/221/64 80/222/63 89/223/72 85/224/67
-f 84/225/68 85/226/67 89/227/72 88/228/71
-f 88/229/71 89/230/72 83/231/66 86/232/69
-f 79/233/62 83/234/66 89/235/72 80/236/63
-f 87/237/70 86/238/69 83/239/66 82/240/65
-usemtl Shell_Grooves
-f 16/241/7 91/242/73 92/243/74 30/244/8
-f 91/245/73 20/246/75 33/247/76 92/248/74
-f 19/249/77 90/250/78 93/251/79 34/252/80
-f 90/253/78 18/254/9 31/255/12 93/256/79
-usemtl Shell
-f 34/257/80 93/258/79 94/259/81 36/260/82
-f 93/261/79 31/262/12 35/263/15 94/264/81
-f 30/265/8 92/266/74 95/267/83 37/268/16
-f 92/269/74 33/270/76 38/271/84 95/272/83
-f 44/273/85 96/274/86 97/275/87 43/276/88
-f 96/277/86 40/278/14 39/279/13 97/280/87
-f 36/281/82 94/282/81 96/283/86 44/284/85
-f 94/285/81 35/286/15 40/287/14 96/288/86
-f 45/289/89 98/290/90 95/291/83 38/292/84
-f 98/293/90 41/294/17 37/295/16 95/296/83
-f 46/297/91 99/298/92 98/299/90 45/300/89
-f 99/301/92 42/302/18 41/303/17 98/304/90
-f 43/305/88 97/306/87 99/307/92 46/308/91
-f 97/309/87 39/310/13 42/311/18 99/312/92
-usemtl Shell_Grooves
-f 20/313/75 91/314/73 101/315/93 100/316/94
-f 91/317/73 16/318/7 102/319/95 101/320/93
-f 22/321/24 8/322/23 107/323/96 103/324/97
-f 16/325/7 22/326/24 103/327/97 102/328/95
-f 15/329/98 108/330/99 104/331/100 105/332/101
-f 2/333/102 15/334/98 105/335/101 106/336/103
-f 8/337/23 2/338/102 106/339/103 107/340/96
-usemtl Shell
-f 107/341/96 106/342/103 109/343/104 113/344/105
-f 104/345/100 108/346/99 111/347/106 110/348/107
-f 108/349/99 139/350/108 112/351/109 111/352/106
-f 106/353/103 105/354/101 114/355/110 109/356/104
-f 105/357/101 104/358/100 110/359/107 114/360/110
-f 102/361/95 103/362/97 116/363/111 115/364/112
-f 103/365/97 107/366/96 113/367/105 116/368/111
-f 101/369/93 102/370/95 115/371/112 117/372/113
-f 100/373/94 101/374/93 117/375/113 118/376/114
-f 118/377/114 117/378/113 119/379/115 120/380/116
-f 117/381/113 115/382/112 116/383/111 119/384/115
-f 113/385/105 109/386/104 116/387/111
-f 111/388/106 112/389/109 120/390/116 119/391/115
-f 110/392/107 111/393/106 119/394/115 114/395/110
-f 109/396/104 114/397/110 119/398/115 116/399/111
-usemtl Shell_Grooves
-f 90/400/78 19/401/77 121/402/117 122/403/118
-f 18/404/9 90/405/78 122/406/118 123/407/119
-f 14/408/51 21/409/50 125/410/120 124/411/121
-f 21/412/50 18/413/9 123/414/119 125/415/120
-f 3/416/122 14/417/51 124/418/121 128/419/123
-f 7/420/124 4/421/125 126/422/126 127/423/127
-f 4/424/125 3/425/122 128/426/123 126/427/126
-usemtl Shell
-f 126/428/126 128/429/123 129/430/128 130/431/129
-f 127/432/127 126/433/126 130/434/129 131/435/130
-f 128/436/123 124/437/121 132/438/131 129/439/128
-f 125/440/120 123/441/119 133/442/132 134/443/133
-f 124/444/121 125/445/120 134/446/133 132/447/131
-f 123/448/119 122/449/118 135/450/134 133/451/132
-f 122/452/118 121/453/117 136/454/135 135/455/134
-f 133/456/132 135/457/134 137/458/136 134/459/133
-f 135/460/134 136/461/135 138/462/137 137/463/136
-f 130/464/129 137/465/136 138/466/137 131/467/130
-f 129/468/128 137/469/136 130/470/129
-f 129/471/128 132/472/131 134/473/133 137/474/136
-f 6/475/138 139/476/108 108/477/99 5/478/139
-f 140/479/140 3/480/122 4/481/125 141/482/141
-f 143/483/142 6/484/138 5/485/139 142/486/143
-f 141/487/141 4/488/125 7/489/124 144/490/144
-f 145/491/145 8/492/23 9/493/33 146/494/146
-f 146/495/146 9/496/33 10/497/31 147/498/147
-f 147/499/147 10/500/31 11/501/28 148/502/148
-f 148/503/148 11/504/28 12/505/60 149/506/149
-f 149/507/149 12/508/60 13/509/57 150/510/150
-f 151/511/151 1/512/56 14/513/51 152/514/152
-f 1/515/56 151/516/151 150/517/150 13/518/57
-f 152/519/152 14/520/51 3/521/122 140/522/140
-f 153/523/153 140/524/140 141/525/141 154/526/154
-f 156/527/155 143/528/142 142/529/143 155/530/156
-f 154/526/154 141/531/141 144/532/144 157/533/157
-f 158/534/158 145/535/145 146/536/146 159/537/159
-f 159/538/159 146/539/146 147/540/147 160/541/160
-f 160/542/160 147/543/147 148/544/148 161/545/161
-f 161/546/161 148/547/148 149/548/149 162/549/162
-f 162/550/162 149/551/149 150/552/150 163/553/163
-f 164/554/164 151/555/151 152/556/152 165/557/165
-f 163/558/163 150/559/150 151/560/151 164/561/164
-f 165/562/165 152/563/152 140/564/140 153/565/153
-usemtl Belly
-f 177/566/166 176/567/167 184/568/168 186/569/169
-f 176/570/167 174/571/170 181/572/171 184/573/168
-f 182/574/172 193/575/173 173/576/174 166/577/175
-f 183/578/176 194/579/177 193/580/173 182/581/172
-f 175/582/178 180/583/179 194/584/177 183/585/176
-usemtl Shell
-f 196/586/180 195/587/181 153/588/153 154/589/154
-f 167/590/182 166/591/175 195/592/181 196/593/180
-f 197/594/183 198/595/184 156/596/155 155/597/156
-usemtl Belly
-f 209/598/185 208/599/186 198/600/184 197/601/183
-usemtl Shell
-f 199/602/187 196/603/180 154/604/154 157/605/157
-f 168/606/188 167/607/182 196/608/180 199/609/187
-f 201/610/189 200/611/190 158/612/158 159/613/159
-f 202/614/191 201/615/189 159/616/159 160/617/160
-usemtl Belly
-f 170/618/192 169/619/193 201/620/189 202/621/191
-usemtl Shell
-f 203/622/194 202/623/191 160/624/160 161/625/161
-usemtl Belly
-f 171/626/195 170/627/192 202/628/191 203/629/194
-usemtl Shell
-f 204/630/196 203/631/194 161/632/161 162/633/162
-usemtl Belly
-f 172/634/197 171/635/195 203/636/194 204/637/196
-usemtl Shell
-f 205/638/198 204/639/196 162/640/162 163/641/163
-f 207/642/199 206/643/200 164/644/164 165/645/165
-f 206/646/200 205/647/198 163/648/163 164/649/164
-f 195/650/181 207/651/199 165/652/165 153/653/153
-usemtl Belly
-f 166/654/175 173/655/174 207/656/199 195/657/181
-f 174/658/170 208/659/186 209/660/185 181/661/171
-f 169/662/193 210/663/201 200/664/190 201/665/189
-usemtl Shell
-f 2/666/102 8/667/23 214/668/202 211/669/203
-f 5/670/139 108/671/99 212/672/204 213/673/205
-f 142/674/143 5/675/139 213/676/205 215/677/206
-f 8/678/23 145/679/145 216/680/207 214/681/202
-f 155/682/156 142/683/143 215/684/206 217/685/208
-f 145/686/145 158/687/158 218/688/209 216/689/207
-usemtl Belly
-f 185/690/210 186/691/169 220/692/211 219/693/212
-f 184/694/168 181/695/171 221/696/213 222/697/214
-usemtl Shell
-f 197/698/183 155/699/156 217/700/208 223/701/215
-f 158/702/158 200/703/190 224/704/216 218/705/209
-usemtl Belly
-f 209/706/185 197/707/183 223/708/215 225/709/217
-f 210/710/201 185/711/210 219/712/212 226/713/218
-f 200/714/190 210/715/201 226/716/218 224/717/216
-usemtl Shell
-f 15/718/98 2/719/102 211/720/203 227/721/219
-f 108/722/99 15/723/98 227/724/219 212/725/204
-usemtl Belly
-f 186/726/169 184/727/168 222/728/214 220/729/211
-f 181/730/171 209/731/185 225/732/217 221/733/213
-usemtl Shell
-f 211/734/203 214/735/202 231/736/220 228/737/221
-f 213/738/205 212/739/204 230/740/222 229/741/223
-f 215/742/206 213/743/205 229/744/223 232/745/224
-f 214/746/202 216/747/207 233/748/225 231/749/220
-f 217/750/208 215/751/206 232/752/224 234/753/226
-f 216/754/207 218/755/209 235/756/227 233/757/225
-usemtl Belly
-f 219/758/212 220/759/211 236/760/228 237/761/229
-f 222/762/214 221/763/213 239/764/230 238/765/231
-usemtl Shell
-f 223/766/215 217/767/208 234/768/226 240/769/232
-f 218/770/209 224/771/216 241/772/233 235/773/227
-usemtl Belly
-f 225/774/217 223/775/215 240/776/232 242/777/234
-f 226/778/218 219/779/212 237/780/229 243/781/235
-f 224/782/216 226/783/218 243/784/235 241/785/233
-usemtl Shell
-f 227/786/219 211/787/203 228/788/221 244/789/236
-f 212/790/204 227/791/219 244/792/236 230/793/222
-usemtl Belly
-f 220/794/211 222/795/214 238/796/231 236/797/228
-f 221/798/213 225/799/217 242/800/234 239/801/230
-usemtl Shell
-f 228/802/221 231/803/220 248/804/237 245/805/238
-f 229/806/223 230/807/222 246/808/239 247/809/240
-f 232/810/224 229/811/223 247/812/240 249/813/241
-f 231/814/220 233/815/225 250/816/242 248/817/237
-f 234/818/226 232/819/224 249/820/241 251/821/243
-f 233/822/225 235/823/227 252/824/244 250/825/242
-usemtl Belly
-f 237/826/229 236/827/228 254/828/245 253/829/246
-f 238/830/231 239/831/230 255/832/247 256/833/248
-usemtl Shell
-f 240/834/232 234/835/226 251/836/243 257/837/249
-f 235/838/227 241/839/233 258/840/250 252/841/244
-usemtl Belly
-f 242/842/234 240/843/232 257/844/249 259/845/251
-f 243/846/235 237/847/229 253/848/246 260/849/252
-f 241/850/233 243/851/235 260/852/252 258/853/250
-usemtl Shell
-f 244/854/236 228/855/221 245/856/238 261/857/253
-f 230/858/222 244/859/236 261/860/253 246/861/239
-usemtl Belly
-f 236/862/228 238/863/231 256/864/248 254/865/245
-f 239/866/230 242/867/234 259/868/251 255/869/247
-f 205/870/198 206/871/200 263/872/254 262/873/255
-f 206/874/200 207/875/199 264/876/256 263/877/254
-f 204/878/196 205/879/198 262/880/255 265/881/257
-f 207/882/199 173/883/174 266/884/258 264/885/256
-f 172/886/197 204/887/196 265/888/257 267/889/259
-f 192/890/260 191/891/261 268/892/262 269/893/263
-f 193/894/173 192/895/260 269/896/263 270/897/264
-f 191/898/261 189/899/265 271/900/266 268/901/262
-f 173/902/174 193/903/173 270/904/264 266/905/258
-f 189/906/265 172/907/197 267/908/259 271/909/266
-f 262/910/255 263/911/254 273/912/267 272/913/268
-f 263/914/254 264/915/256 274/916/269 273/917/267
-f 265/918/257 262/919/255 272/920/268 275/921/270
-f 264/922/256 266/923/258 276/924/271 274/925/269
-f 267/926/259 265/927/257 275/928/270 277/929/272
-f 269/930/263 268/931/262 278/932/273 279/933/274
-f 270/934/264 269/935/263 279/936/274 280/937/275
-f 268/938/262 271/939/266 281/940/276 278/941/273
-f 266/942/258 270/943/264 280/944/275 276/945/271
-f 271/946/266 267/947/259 277/948/272 281/949/276
-f 272/950/268 273/951/267 282/952/277 283/953/278
-f 273/954/267 274/955/269 284/956/279 282/957/277
-f 275/958/270 272/959/268 283/960/278 285/961/280
-f 274/962/269 276/963/271 286/964/281 284/965/279
-f 277/966/272 275/967/270 285/968/280 287/969/282
-f 279/970/274 278/971/273 289/972/283 288/973/284
-f 280/974/275 279/975/274 288/976/284 290/977/285
-f 278/978/273 281/979/276 291/980/286 289/981/283
-f 276/982/271 280/983/275 290/984/285 286/985/281
-f 281/940/276 277/986/272 287/987/282 291/988/286
-f 210/989/201 169/990/193 292/991/287 293/992/288
-f 185/993/210 210/994/201 293/995/288 294/996/289
-f 188/997/290 178/998/291 295/999/292 296/1000/293
-f 187/1001/294 188/1002/290 296/1003/293 297/1004/295
-f 170/1005/192 187/1006/294 297/1007/295 298/1008/296
-f 177/1009/166 186/1010/169 300/1011/297 299/1012/298
-f 186/1013/169 185/1014/210 294/1015/289 300/1016/297
-f 169/1017/193 170/1018/192 298/1019/296 292/1020/287
-f 294/1021/289 293/1022/288 292/1023/287 301/1024/299
-f 295/1025/292 302/1026/300 303/1027/301 296/1028/293
-f 296/1029/293 303/1030/301 301/1031/299 297/1032/295
-f 297/1033/295 301/1034/299 292/1035/287 298/1036/296
-f 302/1037/300 299/1038/298 300/1039/297 303/1040/301
-f 303/1041/301 300/1042/297 294/1043/289 301/1044/299
-f 190/1045/302 179/1046/303 304/1047/304 305/1048/305
-f 189/1049/265 190/1050/302 305/1051/305 306/1052/306
-f 172/1053/197 189/1054/265 306/1055/306 307/1056/307
-f 178/1057/291 188/1058/290 309/1059/308 308/1060/309
-f 188/1061/290 187/1062/294 310/1063/310 309/1064/308
-f 187/1065/294 170/1066/192 311/1067/311 310/1068/310
-f 171/1069/195 172/1070/197 307/1071/307 312/1072/312
-f 170/1073/192 171/1074/195 312/1075/312 311/1076/311
-f 304/1077/304 313/1078/313 314/1079/314 305/1080/305
-f 305/1081/305 314/1082/314 315/1083/315 306/1084/306
-f 306/1085/306 315/1086/315 312/1087/312 307/1088/307
-f 313/1089/313 308/1090/309 309/1091/308 314/1092/314
-f 314/1093/314 309/1094/308 310/1095/310 315/1096/315
-f 315/1097/315 310/1098/310 311/1099/311 312/1100/312
-f 191/1101/261 192/1102/260 317/1103/316 316/1104/317
-f 192/1105/260 193/1106/173 318/1107/318 317/1108/316
-f 189/1109/265 191/1110/261 316/1111/317 319/1112/319
-f 194/1113/177 180/1114/179 320/1115/320 321/1116/321
-f 193/1117/173 194/1118/177 321/1119/321 318/1120/318
-f 179/1121/303 190/1122/302 323/1123/322 322/1124/323
-f 190/1125/302 189/1126/265 319/1127/319 323/1128/322
-f 324/1129/324 325/1130/325 326/1131/326 327/1132/327
-f 327/1133/327 326/1134/326 316/1135/317 317/1136/316
-f 320/1137/320 324/1138/324 327/1139/327 321/1140/321
-f 321/1141/321 327/1142/327 317/1143/316 318/1144/318
-f 325/1145/325 322/1146/323 323/1147/322 326/1148/326
-f 326/1149/326 323/1150/322 319/1151/319 316/1152/317
-usemtl Shell
-f 167/1153/182 168/1154/188 329/1155/328 328/1156/329
-usemtl Belly
-f 182/1157/172 166/1158/175 330/1159/330 331/1160/331
-f 183/1161/176 182/1162/172 331/1163/331 332/1164/332
-f 175/1165/178 183/1166/176 332/1167/332 333/1168/333
-usemtl Shell
-f 166/1169/175 167/1170/182 328/1171/329 330/1172/330
-g default
-v -0.881263 5.892801 1.563060
-v -0.973079 5.892801 1.590912
-v -0.873995 5.929342 0.658692
-v -0.958821 5.964478 0.686544
-v -1.036998 5.996860 0.731773
-v -1.105520 6.025243 0.792641
-v -1.161755 6.048536 0.866809
-v -1.203542 6.065845 0.951427
-v -1.229274 6.076503 1.043243
-v -1.237962 6.080102 1.138728
-v -1.229274 6.076503 1.234213
-v -1.203542 6.065845 1.326029
-v -1.161755 6.048536 1.410646
-v -1.105520 6.025243 1.484814
-v -1.036998 5.996860 1.545683
-v -0.958822 5.964478 1.590912
-v -0.873995 5.929342 1.563060
-v -0.853296 5.960320 1.563060
-v -0.918220 6.025243 1.590912
-v -0.978054 6.085077 1.545683
-v -1.030498 6.137521 1.484814
-v -1.073539 6.180562 1.410646
-v -1.105520 6.212543 1.326029
-v -1.125215 6.232238 1.234213
-v -1.131865 6.238888 1.138728
-v -1.125215 6.232238 1.043243
-v -1.105520 6.212543 0.951427
-v -1.073539 6.180562 0.866809
-v -1.030498 6.137521 0.792641
-v -0.978054 6.085077 0.731773
-v -0.918220 6.025243 0.686544
-v -0.853296 5.960320 0.658692
-v -0.785778 5.892801 0.649287
-v -0.822319 5.981018 0.658692
-v -0.857455 6.065845 0.686544
-v -0.889837 6.144021 0.731773
-v -0.918220 6.212543 0.792641
-v -0.941513 6.268778 0.866809
-v -0.958822 6.310565 0.951427
-v -0.969480 6.336297 1.043243
-v -0.973079 6.344985 1.138728
-v -0.969480 6.336297 1.234213
-v -0.958821 6.310565 1.326029
-v -0.941513 6.268778 1.410646
-v -0.918220 6.212543 1.484814
-v -0.889837 6.144021 1.545683
-v -0.857455 6.065845 1.590912
-v -0.822319 5.981018 1.563060
-v -0.785778 5.988286 1.563060
-v -0.785778 6.080102 1.590912
-v -0.785778 6.164720 1.545683
-v -0.785778 6.238888 1.484814
-v -0.785778 6.299756 1.410646
-v -0.785778 6.344985 1.326029
-v -0.785778 6.372837 1.234213
-v -0.785778 6.382242 1.138728
-v -0.785778 6.372837 1.043243
-v -0.785778 6.344985 0.951427
-v -0.785778 6.299756 0.866809
-v -0.785778 6.238888 0.792641
-v -0.785778 6.164720 0.731773
-v -0.785778 6.080102 0.686544
-v -0.785778 5.988286 0.658692
-v -0.749238 5.981018 0.658692
-v -0.714101 6.065845 0.686544
-v -0.681720 6.144021 0.731773
-v -0.653337 6.212543 0.792641
-v -0.630043 6.268778 0.866809
-v -0.612735 6.310565 0.951427
-v -0.602076 6.336297 1.043243
-v -0.598478 6.344985 1.138728
-v -0.602076 6.336297 1.234213
-v -0.612735 6.310565 1.326029
-v -0.630043 6.268778 1.410646
-v -0.653337 6.212543 1.484814
-v -0.681720 6.144021 1.545683
-v -0.714101 6.065845 1.590912
-v -0.749238 5.981018 1.563060
-v -0.718260 5.960320 1.563060
-v -0.653337 6.025243 1.590912
-v -0.593503 6.085077 1.545683
-v -0.541058 6.137521 1.484814
-v -0.498018 6.180562 1.410646
-v -0.466036 6.212543 1.326029
-v -0.446342 6.232238 1.234213
-v -0.439692 6.238888 1.138728
-v -0.446342 6.232238 1.043243
-v -0.466036 6.212543 0.951427
-v -0.498018 6.180562 0.866809
-v -0.541058 6.137521 0.792641
-v -0.593503 6.085076 0.731773
-v -0.653337 6.025243 0.686544
-v -0.718260 5.960320 0.658692
-v -0.697562 5.929342 0.658692
-v -0.612735 5.964478 0.686544
-v -0.534558 5.996860 0.731773
-v -0.466036 6.025243 0.792641
-v -0.409801 6.048536 0.866809
-v -0.368015 6.065845 0.951427
-v -0.342283 6.076503 1.043243
-v -0.333594 6.080102 1.138728
-v -0.342283 6.076503 1.234213
-v -0.368015 6.065845 1.326029
-v -0.409801 6.048536 1.410646
-v -0.466036 6.025243 1.484814
-v -0.534558 5.996860 1.545683
-v -0.612735 5.964478 1.590912
-v -0.697562 5.929342 1.563060
-v -0.690293 5.892801 1.563060
-v -0.598478 5.892801 1.590912
-v -0.513860 5.892801 1.545683
-v -0.439692 5.892801 1.484814
-v -0.378824 5.892801 1.410646
-v -0.333594 5.892801 1.326029
-v -0.305743 5.892801 1.234213
-v -0.296338 5.892801 1.138728
-v -0.305743 5.892801 1.043243
-v -0.333594 5.892801 0.951427
-v -0.378824 5.892801 0.866809
-v -0.439692 5.892801 0.792641
-v -0.513860 5.892801 0.731773
-v -0.598478 5.892801 0.686544
-v -0.690293 5.892801 0.658692
-v -0.697562 5.856261 0.658692
-v -0.612735 5.821125 0.686544
-v -0.534558 5.788743 0.731773
-v -0.466036 5.760360 0.792641
-v -0.409801 5.737066 0.866809
-v -0.368015 5.719758 0.951427
-v -0.342283 5.709099 1.043243
-v -0.333594 5.705501 1.138728
-v -0.342283 5.709099 1.234213
-v -0.368015 5.719758 1.326029
-v -0.409801 5.737066 1.410646
-v -0.466036 5.760360 1.484814
-v -0.534558 5.788743 1.545683
-v -0.612735 5.821124 1.590912
-v -0.697562 5.856261 1.563060
-v -0.718260 5.825283 1.563060
-v -0.653337 5.760360 1.590912
-v -0.593503 5.700526 1.545683
-v -0.541058 5.648081 1.484814
-v -0.498018 5.605041 1.410646
-v -0.466036 5.573059 1.326029
-v -0.446342 5.553365 1.234213
-v -0.439692 5.546715 1.138728
-v -0.446342 5.553365 1.043243
-v -0.466036 5.573059 0.951427
-v -0.498018 5.605041 0.866809
-v -0.541058 5.648081 0.792641
-v -0.593503 5.700526 0.731773
-v -0.653337 5.760360 0.686544
-v -0.718260 5.825283 0.658692
-v -0.749238 5.804585 0.658692
-v -0.714101 5.719758 0.686544
-v -0.681720 5.641582 0.731773
-v -0.653337 5.573059 0.792641
-v -0.630043 5.516824 0.866809
-v -0.612735 5.475038 0.951427
-v -0.602077 5.449306 1.043243
-v -0.598478 5.440618 1.138728
-v -0.602077 5.449306 1.234213
-v -0.612735 5.475038 1.326029
-v -0.630043 5.516824 1.410646
-v -0.653337 5.573059 1.484814
-v -0.681720 5.641582 1.545683
-v -0.714101 5.719758 1.590912
-v -0.749238 5.804585 1.563060
-v -0.785778 5.797316 1.563060
-v -0.785778 5.705501 1.590912
-v -0.785778 5.620883 1.545683
-v -0.785778 5.546715 1.484814
-v -0.785778 5.485847 1.410646
-v -0.785778 5.440618 1.326029
-v -0.785778 5.412766 1.234213
-v -0.785778 5.403361 1.138728
-v -0.785778 5.412766 1.043243
-v -0.785778 5.440618 0.951427
-v -0.785778 5.485847 0.866809
-v -0.785778 5.546715 0.792641
-v -0.785778 5.620883 0.731773
-v -0.785778 5.705501 0.686544
-v -0.785778 5.797317 0.658692
-v -0.822319 5.804585 0.658692
-v -0.857455 5.719758 0.686544
-v -0.889837 5.641582 0.731773
-v -0.918220 5.573059 0.792641
-v -0.941513 5.516824 0.866809
-v -0.958821 5.475038 0.951427
-v -0.969480 5.449306 1.043243
-v -0.973079 5.440618 1.138728
-v -0.969480 5.449306 1.234213
-v -0.958821 5.475038 1.326029
-v -0.941513 5.516824 1.410646
-v -0.918220 5.573059 1.484814
-v -0.889837 5.641582 1.545683
-v -0.857455 5.719758 1.590912
-v -0.822319 5.804585 1.563060
-v -0.853296 5.825283 1.563060
-v -0.918220 5.760360 1.590912
-v -0.978054 5.700526 1.545683
-v -1.030498 5.648081 1.484814
-v -1.073539 5.605041 1.410646
-v -1.105520 5.573059 1.326029
-v -1.125215 5.553365 1.234213
-v -1.131865 5.546715 1.138728
-v -1.125215 5.553365 1.043243
-v -1.105520 5.573059 0.951427
-v -1.073539 5.605041 0.866809
-v -1.030498 5.648081 0.792641
-v -0.978054 5.700526 0.731773
-v -0.918220 5.760360 0.686544
-v -0.853296 5.825283 0.658692
-v -0.873995 5.856261 0.658692
-v -0.958821 5.821125 0.686544
-v -1.036998 5.788743 0.731773
-v -1.105520 5.760360 0.792641
-v -1.161755 5.737066 0.866809
-v -1.203542 5.719758 0.951427
-v -1.229273 5.709100 1.043243
-v -1.237962 5.705501 1.138728
-v -1.229273 5.709100 1.234213
-v -1.203542 5.719758 1.326029
-v -1.161755 5.737066 1.410646
-v -1.105520 5.760360 1.484814
-v -1.036998 5.788743 1.545683
-v -0.958821 5.821125 1.590912
-v -0.873995 5.856261 1.563060
-v -1.057697 5.892801 1.545683
-v -1.131865 5.892801 1.484814
-v -1.192733 5.892801 1.410646
-v -1.237962 5.892801 1.326029
-v -1.265814 5.892801 1.234213
-v -1.275218 5.892801 1.138728
-v -1.265814 5.892801 1.043243
-v -1.237962 5.892801 0.951427
-v -1.192733 5.892801 0.866809
-v -1.131865 5.892801 0.792641
-v -1.057697 5.892801 0.731773
-v -0.973079 5.892801 0.686544
-v -0.881263 5.892801 0.658692
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.650264 0.549198
-vt 0.662163 0.489375
-vt 0.944159 0.489375
-vt 0.910794 0.657113
-vt 0.910794 0.657113
-vt 0.815777 0.799315
-vt 0.616376 0.599914
-vt 0.650264 0.549198
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.815777 0.799315
-vt 0.673576 0.894331
-vt 0.565661 0.633801
-vt 0.616376 0.599914
-vt 0.673576 0.894331
-vt 0.505838 0.927696
-vt 0.505838 0.645700
-vt 0.565661 0.633801
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.505838 0.927696
-vt 0.338100 0.894331
-vt 0.446015 0.633801
-vt 0.505838 0.645700
-vt 0.338100 0.894331
-vt 0.195898 0.799315
-vt 0.395299 0.599914
-vt 0.446015 0.633801
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.195898 0.799315
-vt 0.100882 0.657113
-vt 0.361412 0.549198
-vt 0.395299 0.599914
-vt 0.100882 0.657113
-vt 0.067517 0.489375
-vt 0.349513 0.489375
-vt 0.361412 0.549198
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.067517 0.489375
-vt 0.100882 0.321637
-vt 0.361412 0.429552
-vt 0.349513 0.489375
-vt 0.100882 0.321637
-vt 0.195898 0.179435
-vt 0.395299 0.378836
-vt 0.361412 0.429552
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.195898 0.179435
-vt 0.338100 0.084419
-vt 0.446015 0.344949
-vt 0.395299 0.378836
-vt 0.338100 0.084419
-vt 0.505838 0.051054
-vt 0.505838 0.333050
-vt 0.446015 0.344949
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.505838 0.051054
-vt 0.673576 0.084419
-vt 0.565661 0.344949
-vt 0.505838 0.333050
-vt 0.673576 0.084419
-vt 0.815777 0.179435
-vt 0.616376 0.378836
-vt 0.565661 0.344949
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.014605 0.012578
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.815777 0.179435
-vt 0.910793 0.321637
-vt 0.650263 0.429552
-vt 0.616376 0.378836
-vt 0.662163 0.489375
-vt 0.650263 0.429552
-vt 0.910793 0.321637
-vt 0.944159 0.489375
-vt 0.014605 0.012578
-vn 0.000000 -0.000000 -1.000000
-vn -0.195023 -0.000000 -0.980799
-vn -0.180178 0.074631 -0.980799
-vn -0.382559 -0.000001 -0.923931
-vn -0.353439 0.146399 -0.923931
-vn -0.555426 -0.000001 -0.831566
-vn -0.513147 0.212553 -0.831566
-vn -0.706975 -0.000000 -0.707238
-vn -0.653160 0.270548 -0.707238
-vn -0.831374 -0.000000 -0.555713
-vn -0.768089 0.318153 -0.555714
-vn -0.923830 -0.000001 -0.382803
-vn -0.853507 0.353535 -0.382804
-vn -0.980772 -0.000002 -0.195156
-vn -0.906116 0.375324 -0.195157
-vn -1.000000 -0.000002 0.000000
-vn -0.923880 0.382682 -0.000000
-vn -0.980772 -0.000002 0.195156
-vn -0.906115 0.375324 0.195158
-vn -0.923829 -0.000001 0.382805
-vn -0.853506 0.353534 0.382807
-vn -0.831373 -0.000000 0.555715
-vn -0.768088 0.318153 0.555715
-vn -0.706978 -0.000000 0.707236
-vn -0.653163 0.270550 0.707235
-vn -0.555428 -0.000001 0.831564
-vn -0.513150 0.212554 0.831563
-vn -0.097457 -0.000000 0.995240
-vn -0.090040 0.037296 0.995240
-vn 0.268187 -0.111085 0.956941
-vn 0.290284 0.000002 0.956941
-vn 0.205260 -0.205261 0.956941
-vn -0.068914 0.068913 0.995240
-vn -0.392749 0.392748 0.831563
-vn -0.499909 0.499908 0.707236
-vn -0.587869 0.587869 0.555715
-vn -0.653245 0.653246 0.382807
-vn -0.693510 0.693510 0.195161
-vn -0.707107 0.707106 -0.000001
-vn -0.693511 0.693510 -0.195160
-vn -0.653246 0.653246 -0.382805
-vn -0.587870 0.587869 -0.555715
-vn -0.499907 0.499906 -0.707239
-vn -0.392747 0.392746 -0.831565
-vn -0.270511 0.270510 -0.923931
-vn -0.137901 0.137901 -0.980799
-vn -0.074630 0.180177 -0.980799
-vn -0.146399 0.353440 -0.923931
-vn -0.212553 0.513147 -0.831565
-vn -0.270548 0.653159 -0.707239
-vn -0.318153 0.768089 -0.555714
-vn -0.353535 0.853507 -0.382805
-vn -0.375325 0.906114 -0.195161
-vn -0.382684 0.923879 -0.000001
-vn -0.375325 0.906114 0.195162
-vn -0.353534 0.853506 0.382807
-vn -0.318153 0.768089 0.555715
-vn -0.270549 0.653161 0.707237
-vn -0.212554 0.513149 0.831564
-vn -0.037295 0.090040 0.995240
-vn 0.111086 -0.268188 0.956941
-vn -0.000000 -0.290284 0.956941
-vn 0.000000 0.097459 0.995240
-vn 0.000001 0.555429 0.831564
-vn 0.000001 0.706977 0.707237
-vn 0.000000 0.831373 0.555714
-vn 0.000000 0.923829 0.382806
-vn 0.000000 0.980771 0.195162
-vn 0.000000 1.000000 -0.000000
-vn 0.000000 0.980771 -0.195161
-vn 0.000000 0.923829 -0.382805
-vn 0.000000 0.831374 -0.555714
-vn 0.000000 0.706975 -0.707239
-vn 0.000000 0.555426 -0.831566
-vn 0.000000 0.382560 -0.923931
-vn 0.000000 0.195022 -0.980799
-vn 0.074630 0.180178 -0.980799
-vn 0.146401 0.353440 -0.923930
-vn 0.212555 0.513147 -0.831565
-vn 0.270549 0.653159 -0.707239
-vn 0.318154 0.768090 -0.555713
-vn 0.353535 0.853507 -0.382805
-vn 0.375326 0.906114 -0.195161
-vn 0.382685 0.923879 0.000000
-vn 0.375326 0.906114 0.195161
-vn 0.353535 0.853506 0.382806
-vn 0.318154 0.768089 0.555714
-vn 0.270549 0.653162 0.707236
-vn 0.212555 0.513151 0.831563
-vn 0.037297 0.090041 0.995239
-vn -0.111087 -0.268187 0.956941
-vn -0.205262 -0.205261 0.956941
-vn 0.068914 0.068914 0.995240
-vn 0.392748 0.392749 0.831563
-vn 0.499908 0.499909 0.707236
-vn 0.587869 0.587870 0.555715
-vn 0.653246 0.653245 0.382805
-vn 0.693511 0.693510 0.195159
-vn 0.707107 0.707107 -0.000000
-vn 0.693511 0.693509 -0.195160
-vn 0.653247 0.653246 -0.382803
-vn 0.587870 0.587871 -0.555712
-vn 0.499906 0.499907 -0.707239
-vn 0.392746 0.392747 -0.831565
-vn 0.270511 0.270511 -0.923930
-vn 0.137901 0.137901 -0.980799
-vn 0.180177 0.074631 -0.980799
-vn 0.353438 0.146398 -0.923931
-vn 0.513145 0.212552 -0.831567
-vn 0.653160 0.270547 -0.707238
-vn 0.768090 0.318153 -0.555712
-vn 0.853508 0.353535 -0.382802
-vn 0.906115 0.375325 -0.195159
-vn 0.923880 0.382683 -0.000000
-vn 0.906115 0.375325 0.195159
-vn 0.853507 0.353535 0.382805
-vn 0.768088 0.318152 0.555715
-vn 0.653163 0.270548 0.707236
-vn 0.513149 0.212552 0.831565
-vn 0.090039 0.037295 0.995240
-vn -0.268187 -0.111086 0.956941
-vn -0.290284 0.000000 0.956941
-vn 0.097457 -0.000000 0.995240
-vn 0.555428 -0.000001 0.831564
-vn 0.706978 -0.000000 0.707236
-vn 0.831372 -0.000000 0.555716
-vn 0.923830 -0.000000 0.382804
-vn 0.980772 -0.000000 0.195158
-vn 1.000000 -0.000000 0.000000
-vn 0.980772 -0.000000 -0.195158
-vn 0.923831 -0.000000 -0.382802
-vn 0.831374 -0.000000 -0.555713
-vn 0.706975 -0.000000 -0.707238
-vn 0.555425 -0.000001 -0.831567
-vn 0.382559 -0.000001 -0.923931
-vn 0.195022 -0.000001 -0.980799
-vn 0.180178 -0.074633 -0.980799
-vn 0.353439 -0.146399 -0.923931
-vn 0.513145 -0.212551 -0.831567
-vn 0.653159 -0.270547 -0.707240
-vn 0.768090 -0.318154 -0.555712
-vn 0.853508 -0.353536 -0.382801
-vn 0.906115 -0.375326 -0.195158
-vn 0.923879 -0.382684 -0.000000
-vn 0.906115 -0.375326 0.195157
-vn 0.853507 -0.353536 0.382803
-vn 0.768089 -0.318153 0.555714
-vn 0.653162 -0.270548 0.707236
-vn 0.513149 -0.212553 0.831564
-vn 0.090039 -0.037295 0.995240
-vn -0.268187 0.111087 0.956941
-vn -0.205261 0.205262 0.956941
-vn 0.068914 -0.068914 0.995240
-vn 0.392746 -0.392748 0.831565
-vn 0.499907 -0.499908 0.707237
-vn 0.587871 -0.587871 0.555712
-vn 0.653247 -0.653247 0.382801
-vn 0.693510 -0.693511 0.195158
-vn 0.707105 -0.707108 -0.000000
-vn 0.693509 -0.693511 -0.195158
-vn 0.653247 -0.653248 -0.382799
-vn 0.587872 -0.587872 -0.555710
-vn 0.499905 -0.499906 -0.707241
-vn 0.392743 -0.392744 -0.831568
-vn 0.270511 -0.270511 -0.923930
-vn 0.137902 -0.137902 -0.980799
-vn 0.074630 -0.180177 -0.980799
-vn 0.146399 -0.353439 -0.923931
-vn 0.212552 -0.513145 -0.831567
-vn 0.270546 -0.653158 -0.707241
-vn 0.318153 -0.768092 -0.555710
-vn 0.353534 -0.853509 -0.382800
-vn 0.375325 -0.906115 -0.195158
-vn 0.382684 -0.923879 -0.000000
-vn 0.375325 -0.906115 0.195159
-vn 0.353534 -0.853508 0.382803
-vn 0.318153 -0.768090 0.555713
-vn 0.270547 -0.653160 0.707238
-vn 0.212553 -0.513149 0.831565
-vn 0.037295 -0.090040 0.995240
-vn -0.111088 0.268188 0.956940
-vn -0.000000 0.290284 0.956941
-vn -0.000000 -0.097458 0.995240
-vn 0.000000 -0.555428 0.831565
-vn 0.000000 -0.706976 0.707238
-vn 0.000000 -0.831375 0.555712
-vn 0.000000 -0.923830 0.382803
-vn 0.000000 -0.980772 0.195159
-vn 0.000001 -1.000000 -0.000000
-vn 0.000000 -0.980772 -0.195158
-vn 0.000000 -0.923832 -0.382799
-vn 0.000000 -0.831376 -0.555710
-vn 0.000001 -0.706973 -0.707241
-vn 0.000000 -0.555425 -0.831567
-vn 0.000000 -0.382559 -0.923931
-vn 0.000000 -0.195022 -0.980799
-vn -0.074630 -0.180177 -0.980799
-vn -0.146398 -0.353439 -0.923931
-vn -0.212552 -0.513146 -0.831567
-vn -0.270546 -0.653158 -0.707241
-vn -0.318153 -0.768091 -0.555711
-vn -0.353534 -0.853509 -0.382801
-vn -0.375325 -0.906115 -0.195158
-vn -0.382683 -0.923880 -0.000000
-vn -0.375324 -0.906115 0.195158
-vn -0.353533 -0.853508 0.382803
-vn -0.318153 -0.768090 0.555713
-vn -0.270548 -0.653160 0.707238
-vn -0.212553 -0.513149 0.831565
-vn -0.037295 -0.090040 0.995240
-vn 0.111087 0.268188 0.956941
-vn 0.205263 0.205262 0.956940
-vn -0.068912 -0.068912 0.995240
-vn -0.392747 -0.392746 0.831565
-vn -0.499908 -0.499907 0.707237
-vn -0.587871 -0.587870 0.555713
-vn -0.653246 -0.653247 0.382803
-vn -0.693511 -0.693510 0.195158
-vn -0.707107 -0.707106 -0.000000
-vn -0.693511 -0.693510 -0.195158
-vn -0.653247 -0.653247 -0.382801
-vn -0.587871 -0.587871 -0.555712
-vn -0.499906 -0.499905 -0.707240
-vn -0.392744 -0.392744 -0.831567
-vn -0.270511 -0.270510 -0.923931
-vn -0.137901 -0.137902 -0.980799
-vn -0.180177 -0.074632 -0.980799
-vn -0.353439 -0.146399 -0.923931
-vn -0.513146 -0.212552 -0.831566
-vn -0.653159 -0.270548 -0.707239
-vn -0.768089 -0.318154 -0.555713
-vn -0.853508 -0.353536 -0.382801
-vn -0.906115 -0.375325 -0.195157
-vn -0.923880 -0.382682 0.000000
-vn -0.906115 -0.375325 0.195157
-vn -0.853507 -0.353535 0.382803
-vn -0.768089 -0.318154 0.555714
-vn -0.653162 -0.270549 0.707236
-vn -0.513149 -0.212554 0.831564
-vn -0.090039 -0.037295 0.995240
-vn 0.268188 0.111087 0.956941
-s 1
-g Mesh polySurface2
-usemtl Eye_Base_antonio_iris_png
-f 366/1173/334 574/1175/335 336/1174/336
-f 574/1176/335 573/1179/337 337/1178/338 336/1177/336
-f 573/1180/337 572/1183/339 338/1182/340 337/1181/338
-f 572/1184/339 571/1187/341 339/1186/342 338/1185/340
-f 571/1188/341 570/1191/343 340/1190/344 339/1189/342
-f 570/1192/343 569/1195/345 341/1194/346 340/1193/344
-f 569/1196/345 568/1199/347 342/1198/348 341/1197/346
-f 568/1200/347 567/1203/349 343/1202/350 342/1201/348
-f 567/1204/349 566/1207/351 344/1206/352 343/1205/350
-f 566/1208/351 565/1211/353 345/1210/354 344/1209/352
-f 565/1212/353 564/1215/355 346/1214/356 345/1213/354
-f 564/1216/355 563/1219/357 347/1218/358 346/1217/356
-f 563/1220/357 562/1223/359 348/1222/360 347/1221/358
-f 562/1224/359 335/1227/361 349/1226/362 348/1225/360
-usemtl Eye_Iris_antonio_iris_png
-f 350/1228/363 349/1230/362 335/1227/361 334/1229/364
-f 349/1231/362 350/1228/363 351/1233/365 352/1232/366
-usemtl Eye_Base_antonio_iris_png
-f 348/1234/360 349/1237/362 352/1236/366 353/1235/367
-f 347/1238/358 348/1241/360 353/1240/367 354/1239/368
-f 346/1242/356 347/1245/358 354/1244/368 355/1243/369
-f 345/1246/354 346/1249/356 355/1248/369 356/1247/370
-f 344/1250/352 345/1253/354 356/1252/370 357/1251/371
-f 343/1254/350 344/1257/352 357/1256/371 358/1255/372
-f 342/1258/348 343/1261/350 358/1260/372 359/1259/373
-f 341/1262/346 342/1265/348 359/1264/373 360/1263/374
-f 340/1266/344 341/1269/346 360/1268/374 361/1267/375
-f 339/1270/342 340/1273/344 361/1272/375 362/1271/376
-f 338/1274/340 339/1277/342 362/1276/376 363/1275/377
-f 337/1278/338 338/1281/340 363/1280/377 364/1279/378
-f 336/1282/336 337/1285/338 364/1284/378 365/1283/379
-f 366/1286/334 336/1288/336 365/1287/379
-f 366/1289/334 365/1291/379 367/1290/380
-f 365/1292/379 364/1295/378 368/1294/381 367/1293/380
-f 364/1296/378 363/1299/377 369/1298/382 368/1297/381
-f 363/1300/377 362/1303/376 370/1302/383 369/1301/382
-f 362/1304/376 361/1307/375 371/1306/384 370/1305/383
-f 361/1308/375 360/1311/374 372/1310/385 371/1309/384
-f 360/1312/374 359/1315/373 373/1314/386 372/1313/385
-f 359/1316/373 358/1319/372 374/1318/387 373/1317/386
-f 358/1320/372 357/1323/371 375/1322/388 374/1321/387
-f 357/1324/371 356/1327/370 376/1326/389 375/1325/388
-f 356/1328/370 355/1331/369 377/1330/390 376/1329/389
-f 355/1332/369 354/1335/368 378/1334/391 377/1333/390
-f 354/1336/368 353/1339/367 379/1338/392 378/1337/391
-f 353/1340/367 352/1343/366 380/1342/393 379/1341/392
-usemtl Eye_Iris_antonio_iris_png
-f 352/1344/366 351/1347/365 381/1346/394 380/1345/393
-f 380/1348/393 381/1351/394 382/1350/395 383/1349/396
-usemtl Eye_Base_antonio_iris_png
-f 379/1352/392 380/1355/393 383/1354/396 384/1353/397
-f 378/1356/391 379/1359/392 384/1358/397 385/1357/398
-f 377/1360/390 378/1363/391 385/1362/398 386/1361/399
-f 376/1364/389 377/1367/390 386/1366/399 387/1365/400
-f 375/1368/388 376/1371/389 387/1370/400 388/1369/401
-f 374/1372/387 375/1375/388 388/1374/401 389/1373/402
-f 373/1376/386 374/1379/387 389/1378/402 390/1377/403
-f 372/1380/385 373/1383/386 390/1382/403 391/1381/404
-f 371/1384/384 372/1387/385 391/1386/404 392/1385/405
-f 370/1388/383 371/1391/384 392/1390/405 393/1389/406
-f 369/1392/382 370/1395/383 393/1394/406 394/1393/407
-f 368/1396/381 369/1399/382 394/1398/407 395/1397/408
-f 367/1400/380 368/1403/381 395/1402/408 396/1401/409
-f 366/1404/334 367/1406/380 396/1405/409
-f 366/1407/334 396/1409/409 397/1408/410
-f 396/1410/409 395/1413/408 398/1412/411 397/1411/410
-f 395/1414/408 394/1417/407 399/1416/412 398/1415/411
-f 394/1418/407 393/1421/406 400/1420/413 399/1419/412
-f 393/1422/406 392/1425/405 401/1424/414 400/1423/413
-f 392/1426/405 391/1429/404 402/1428/415 401/1427/414
-f 391/1430/404 390/1433/403 403/1432/416 402/1431/415
-f 390/1434/403 389/1437/402 404/1436/417 403/1435/416
-f 389/1438/402 388/1441/401 405/1440/418 404/1439/417
-f 388/1442/401 387/1445/400 406/1444/419 405/1443/418
-f 387/1446/400 386/1449/399 407/1448/420 406/1447/419
-f 386/1450/399 385/1453/398 408/1452/421 407/1451/420
-f 385/1454/398 384/1457/397 409/1456/422 408/1455/421
-f 384/1458/397 383/1461/396 410/1460/423 409/1459/422
-usemtl Eye_Iris_antonio_iris_png
-f 383/1462/396 382/1465/395 411/1464/424 410/1463/423
-f 410/1466/423 411/1469/424 412/1468/425 413/1467/426
-usemtl Eye_Base_antonio_iris_png
-f 409/1470/422 410/1473/423 413/1472/426 414/1471/427
-f 408/1474/421 409/1477/422 414/1476/427 415/1475/428
-f 407/1478/420 408/1481/421 415/1480/428 416/1479/429
-f 406/1482/419 407/1485/420 416/1484/429 417/1483/430
-f 405/1486/418 406/1489/419 417/1488/430 418/1487/431
-f 404/1490/417 405/1493/418 418/1492/431 419/1491/432
-f 403/1494/416 404/1497/417 419/1496/432 420/1495/433
-f 402/1498/415 403/1501/416 420/1500/433 421/1499/434
-f 401/1502/414 402/1505/415 421/1504/434 422/1503/435
-f 400/1506/413 401/1509/414 422/1508/435 423/1507/436
-f 399/1510/412 400/1513/413 423/1512/436 424/1511/437
-f 398/1514/411 399/1517/412 424/1516/437 425/1515/438
-f 397/1518/410 398/1521/411 425/1520/438 426/1519/439
-f 366/1522/334 397/1524/410 426/1523/439
-f 366/1525/334 426/1527/439 427/1526/440
-f 426/1528/439 425/1531/438 428/1530/441 427/1529/440
-f 425/1532/438 424/1535/437 429/1534/442 428/1533/441
-f 424/1536/437 423/1539/436 430/1538/443 429/1537/442
-f 423/1540/436 422/1543/435 431/1542/444 430/1541/443
-f 422/1544/435 421/1547/434 432/1546/445 431/1545/444
-f 421/1548/434 420/1551/433 433/1550/446 432/1549/445
-f 420/1552/433 419/1555/432 434/1554/447 433/1553/446
-f 419/1556/432 418/1559/431 435/1558/448 434/1557/447
-f 418/1560/431 417/1563/430 436/1562/449 435/1561/448
-f 417/1564/430 416/1567/429 437/1566/450 436/1565/449
-f 416/1568/429 415/1571/428 438/1570/451 437/1569/450
-f 415/1572/428 414/1575/427 439/1574/452 438/1573/451
-f 414/1576/427 413/1579/426 440/1578/453 439/1577/452
-usemtl Eye_Iris_antonio_iris_png
-f 413/1580/426 412/1583/425 441/1582/454 440/1581/453
-f 440/1584/453 441/1587/454 442/1586/455 443/1585/456
-usemtl Eye_Base_antonio_iris_png
-f 439/1588/452 440/1591/453 443/1590/456 444/1589/457
-f 438/1592/451 439/1595/452 444/1594/457 445/1593/458
-f 437/1596/450 438/1599/451 445/1598/458 446/1597/459
-f 436/1600/449 437/1603/450 446/1602/459 447/1601/460
-f 435/1604/448 436/1607/449 447/1606/460 448/1605/461
-f 434/1608/447 435/1611/448 448/1610/461 449/1609/462
-f 433/1612/446 434/1615/447 449/1614/462 450/1613/463
-f 432/1616/445 433/1619/446 450/1618/463 451/1617/464
-f 431/1620/444 432/1623/445 451/1622/464 452/1621/465
-f 430/1624/443 431/1627/444 452/1626/465 453/1625/466
-f 429/1628/442 430/1631/443 453/1630/466 454/1629/467
-f 428/1632/441 429/1635/442 454/1634/467 455/1633/468
-f 427/1636/440 428/1639/441 455/1638/468 456/1637/469
-f 366/1640/334 427/1642/440 456/1641/469
-f 366/1643/334 456/1645/469 457/1644/470
-f 456/1646/469 455/1649/468 458/1648/471 457/1647/470
-f 455/1650/468 454/1653/467 459/1652/472 458/1651/471
-f 454/1654/467 453/1657/466 460/1656/473 459/1655/472
-f 453/1658/466 452/1661/465 461/1660/474 460/1659/473
-f 452/1662/465 451/1665/464 462/1664/475 461/1663/474
-f 451/1666/464 450/1669/463 463/1668/476 462/1667/475
-f 450/1670/463 449/1673/462 464/1672/477 463/1671/476
-f 449/1674/462 448/1677/461 465/1676/478 464/1675/477
-f 448/1678/461 447/1681/460 466/1680/479 465/1679/478
-f 447/1682/460 446/1685/459 467/1684/480 466/1683/479
-f 446/1686/459 445/1689/458 468/1688/481 467/1687/480
-f 445/1690/458 444/1693/457 469/1692/482 468/1691/481
-f 444/1694/457 443/1697/456 470/1696/483 469/1695/482
-usemtl Eye_Iris_antonio_iris_png
-f 443/1698/456 442/1701/455 471/1700/484 470/1699/483
-f 470/1702/483 471/1705/484 472/1704/485 473/1703/486
-usemtl Eye_Base_antonio_iris_png
-f 469/1706/482 470/1709/483 473/1708/486 474/1707/487
-f 468/1710/481 469/1713/482 474/1712/487 475/1711/488
-f 467/1714/480 468/1717/481 475/1716/488 476/1715/489
-f 466/1718/479 467/1721/480 476/1720/489 477/1719/490
-f 465/1722/478 466/1725/479 477/1724/490 478/1723/491
-f 464/1726/477 465/1729/478 478/1728/491 479/1727/492
-f 463/1730/476 464/1733/477 479/1732/492 480/1731/493
-f 462/1734/475 463/1737/476 480/1736/493 481/1735/494
-f 461/1738/474 462/1741/475 481/1740/494 482/1739/495
-f 460/1742/473 461/1745/474 482/1744/495 483/1743/496
-f 459/1746/472 460/1749/473 483/1748/496 484/1747/497
-f 458/1750/471 459/1753/472 484/1752/497 485/1751/498
-f 457/1754/470 458/1757/471 485/1756/498 486/1755/499
-f 366/1758/334 457/1760/470 486/1759/499
-f 366/1761/334 486/1763/499 487/1762/500
-f 486/1764/499 485/1767/498 488/1766/501 487/1765/500
-f 485/1768/498 484/1771/497 489/1770/502 488/1769/501
-f 484/1772/497 483/1775/496 490/1774/503 489/1773/502
-f 483/1776/496 482/1779/495 491/1778/504 490/1777/503
-f 482/1780/495 481/1783/494 492/1782/505 491/1781/504
-f 481/1784/494 480/1787/493 493/1786/506 492/1785/505
-f 480/1788/493 479/1791/492 494/1790/507 493/1789/506
-f 479/1792/492 478/1795/491 495/1794/508 494/1793/507
-f 478/1796/491 477/1799/490 496/1798/509 495/1797/508
-f 477/1800/490 476/1803/489 497/1802/510 496/1801/509
-f 476/1804/489 475/1807/488 498/1806/511 497/1805/510
-f 475/1808/488 474/1811/487 499/1810/512 498/1809/511
-f 474/1812/487 473/1815/486 500/1814/513 499/1813/512
-usemtl Eye_Iris_antonio_iris_png
-f 473/1816/486 472/1819/485 501/1818/514 500/1817/513
-f 500/1820/513 501/1823/514 502/1822/515 503/1821/516
-usemtl Eye_Base_antonio_iris_png
-f 499/1824/512 500/1827/513 503/1826/516 504/1825/517
-f 498/1828/511 499/1831/512 504/1830/517 505/1829/518
-f 497/1832/510 498/1835/511 505/1834/518 506/1833/519
-f 496/1836/509 497/1839/510 506/1838/519 507/1837/520
-f 495/1840/508 496/1843/509 507/1842/520 508/1841/521
-f 494/1844/507 495/1847/508 508/1846/521 509/1845/522
-f 493/1848/506 494/1851/507 509/1850/522 510/1849/523
-f 492/1852/505 493/1855/506 510/1854/523 511/1853/524
-f 491/1856/504 492/1859/505 511/1858/524 512/1857/525
-f 490/1860/503 491/1863/504 512/1862/525 513/1861/526
-f 489/1864/502 490/1867/503 513/1866/526 514/1865/527
-f 488/1868/501 489/1871/502 514/1870/527 515/1869/528
-f 487/1872/500 488/1875/501 515/1874/528 516/1873/529
-f 366/1876/334 487/1878/500 516/1877/529
-f 366/1879/334 516/1881/529 517/1880/530
-f 516/1882/529 515/1885/528 518/1884/531 517/1883/530
-f 515/1886/528 514/1889/527 519/1888/532 518/1887/531
-f 514/1890/527 513/1893/526 520/1892/533 519/1891/532
-f 513/1894/526 512/1897/525 521/1896/534 520/1895/533
-f 512/1898/525 511/1901/524 522/1900/535 521/1899/534
-f 511/1902/524 510/1905/523 523/1904/536 522/1903/535
-f 510/1906/523 509/1909/522 524/1908/537 523/1907/536
-f 509/1910/522 508/1913/521 525/1912/538 524/1911/537
-f 508/1914/521 507/1917/520 526/1916/539 525/1915/538
-f 507/1918/520 506/1921/519 527/1920/540 526/1919/539
-f 506/1922/519 505/1925/518 528/1924/541 527/1923/540
-f 505/1926/518 504/1929/517 529/1928/542 528/1927/541
-f 504/1930/517 503/1933/516 530/1932/543 529/1931/542
-usemtl Eye_Iris_antonio_iris_png
-f 503/1934/516 502/1937/515 531/1936/544 530/1935/543
-f 530/1938/543 531/1941/544 532/1940/545 533/1939/546
-usemtl Eye_Base_antonio_iris_png
-f 529/1942/542 530/1945/543 533/1944/546 534/1943/547
-f 528/1946/541 529/1949/542 534/1948/547 535/1947/548
-f 527/1950/540 528/1953/541 535/1952/548 536/1951/549
-f 526/1954/539 527/1957/540 536/1956/549 537/1955/550
-f 525/1958/538 526/1961/539 537/1960/550 538/1959/551
-f 524/1962/537 525/1965/538 538/1964/551 539/1963/552
-f 523/1966/536 524/1969/537 539/1968/552 540/1967/553
-f 522/1970/535 523/1973/536 540/1972/553 541/1971/554
-f 521/1974/534 522/1977/535 541/1976/554 542/1975/555
-f 520/1978/533 521/1981/534 542/1980/555 543/1979/556
-f 519/1982/532 520/1985/533 543/1984/556 544/1983/557
-f 518/1986/531 519/1989/532 544/1988/557 545/1987/558
-f 517/1990/530 518/1993/531 545/1992/558 546/1991/559
-f 366/1994/334 517/1996/530 546/1995/559
-f 366/1997/334 546/1999/559 547/1998/560
-f 546/2000/559 545/2003/558 548/2002/561 547/2001/560
-f 545/2004/558 544/2007/557 549/2006/562 548/2005/561
-f 544/2008/557 543/2011/556 550/2010/563 549/2009/562
-f 543/2012/556 542/2015/555 551/2014/564 550/2013/563
-f 542/2016/555 541/2019/554 552/2018/565 551/2017/564
-f 541/2020/554 540/2023/553 553/2022/566 552/2021/565
-f 540/2024/553 539/2027/552 554/2026/567 553/2025/566
-f 539/2028/552 538/2031/551 555/2030/568 554/2029/567
-f 538/2032/551 537/2035/550 556/2034/569 555/2033/568
-f 537/2036/550 536/2039/549 557/2038/570 556/2037/569
-f 536/2040/549 535/2043/548 558/2042/571 557/2041/570
-f 535/2044/548 534/2047/547 559/2046/572 558/2045/571
-f 534/2048/547 533/2051/546 560/2050/573 559/2049/572
-usemtl Eye_Iris_antonio_iris_png
-f 533/2052/546 532/2055/545 561/2054/574 560/2053/573
-f 334/1229/364 335/2058/361 560/2057/573 561/2056/574
-usemtl Eye_Base_antonio_iris_png
-f 559/2059/572 560/2061/573 335/1227/361 562/2060/359
-f 558/2062/571 559/2065/572 562/2064/359 563/2063/357
-f 557/2066/570 558/2069/571 563/2068/357 564/2067/355
-f 556/2070/569 557/2073/570 564/2072/355 565/2071/353
-f 555/2074/568 556/2077/569 565/2076/353 566/2075/351
-f 554/2078/567 555/2081/568 566/2080/351 567/2079/349
-f 553/2082/566 554/2085/567 567/2084/349 568/2083/347
-f 552/2086/565 553/2089/566 568/2088/347 569/2087/345
-f 551/2090/564 552/2093/565 569/2092/345 570/2091/343
-f 550/2094/563 551/2097/564 570/2096/343 571/2095/341
-f 549/2098/562 550/2101/563 571/2100/341 572/2099/339
-f 548/2102/561 549/2105/562 572/2104/339 573/2103/337
-f 547/2106/560 548/2109/561 573/2108/337 574/2107/335
-f 366/2110/334 547/2112/560 574/2111/335
-g default
-v -0.873995 5.856261 1.625147
-v -0.958821 5.821125 1.597295
-v -0.918220 5.760360 1.597295
-v -0.853296 5.825283 1.625147
-v -0.822319 5.804585 1.625147
-v -0.857455 5.719758 1.597295
-v -0.785778 5.705501 1.597295
-v -0.785778 5.797316 1.625147
-v -0.749238 5.804585 1.625147
-v -0.714101 5.719758 1.597295
-v -0.653337 5.760360 1.597295
-v -0.718260 5.825283 1.625147
-v -0.785778 5.892801 1.634551
-v -0.697562 5.856261 1.625147
-v -0.612735 5.821124 1.597295
-v -0.598478 5.892801 1.597295
-v -0.690293 5.892801 1.625147
-v -0.697562 5.929342 1.625147
-v -0.612735 5.964478 1.597295
-v -0.653337 6.025243 1.597295
-v -0.718260 5.960320 1.625147
-v -0.749238 5.981018 1.625147
-v -0.714101 6.065845 1.597295
-v -0.785778 6.080102 1.597295
-v -0.785778 5.988286 1.625147
-v -0.822319 5.981018 1.625147
-v -0.857455 6.065845 1.597295
-v -0.918220 6.025243 1.597295
-v -0.853296 5.960320 1.625147
-v -0.873995 5.929342 1.625147
-v -0.958822 5.964478 1.597295
-v -0.973079 5.892801 1.597295
-v -0.881263 5.892801 1.625147
-v -0.958821 5.821125 1.590912
-v -0.973079 5.892801 1.590912
-v -0.918220 5.760360 1.590912
-v -0.857455 5.719758 1.590912
-v -0.785778 5.705501 1.590912
-v -0.714101 5.719758 1.590912
-v -0.653337 5.760360 1.590912
-v -0.612735 5.821124 1.590912
-v -0.598478 5.892801 1.590912
-v -0.612735 5.964478 1.590912
-v -0.653337 6.025243 1.590912
-v -0.714101 6.065845 1.590912
-v -0.785778 6.080102 1.590912
-v -0.857455 6.065845 1.590912
-v -0.918220 6.025243 1.590912
-v -0.958822 5.964478 1.590912
-vt 0.099449 0.012578
-vt 0.099449 0.097422
-vt 0.014605 0.097422
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vn -0.195017 -0.000001 0.980800
-vn -0.799272 -0.000007 0.600969
-vn -0.738432 -0.305868 0.600969
-vn -0.180172 -0.074630 0.980800
-vn 0.000000 -0.000000 1.000000
-vn -0.137897 -0.137898 0.980800
-vn -0.565176 -0.565166 0.600969
-vn -0.305869 -0.738431 0.600970
-vn -0.074628 -0.180172 0.980800
-vn -0.000000 -0.195017 0.980800
-vn -0.000000 -0.799272 0.600969
-vn 0.305871 -0.738430 0.600970
-vn 0.074628 -0.180173 0.980800
-vn 0.137897 -0.137898 0.980800
-vn 0.565173 -0.565170 0.600969
-vn 0.738431 -0.305868 0.600970
-vn 0.180172 -0.074630 0.980800
-vn 0.195017 -0.000000 0.980800
-vn 0.799273 -0.000001 0.600969
-vn 0.738431 0.305867 0.600970
-vn 0.180172 0.074629 0.980800
-vn 0.137899 0.137897 0.980800
-vn 0.565173 0.565169 0.600969
-vn 0.305871 0.738429 0.600970
-vn 0.074629 0.180172 0.980800
-vn 0.000001 0.195017 0.980800
-vn 0.000001 0.799272 0.600969
-vn -0.305868 0.738431 0.600970
-vn -0.074629 0.180172 0.980800
-vn -0.137897 0.137897 0.980800
-vn -0.565171 0.565170 0.600969
-vn -0.738431 0.305866 0.600971
-vn -0.180171 0.074628 0.980800
-vn -1.000000 -0.000010 0.000000
-vn -0.923880 -0.382682 0.000000
-vn -0.707114 -0.707099 0.000000
-vn -0.382684 -0.923879 0.000000
-vn 0.000000 -1.000000 0.000000
-vn 0.382688 -0.923878 0.000000
-vn 0.707110 -0.707104 0.000000
-vn 0.923879 -0.382684 0.000000
-vn 1.000000 -0.000002 0.000000
-vn 0.923880 0.382682 0.000000
-vn 0.707110 0.707104 0.000000
-vn 0.382688 0.923878 0.000000
-vn 0.000000 1.000000 0.000000
-vn -0.382684 0.923879 0.000000
-vn -0.707107 0.707107 0.000000
-vn -0.923880 0.382683 0.000000
-s 1
-g Mesh polySurface3
-usemtl Eye_Cornea
-f 607/2113/575 606/2116/576 576/2115/577 575/2114/578
-f 575/2117/578 587/2119/579 607/2118/575
-f 578/2120/580 587/2122/579 575/2121/578
-f 577/2123/581 578/2126/580 575/2125/578 576/2124/577
-f 580/2127/582 579/2130/583 578/2129/580 577/2128/581
-f 579/2131/583 587/2133/579 578/2132/580
-f 582/2134/584 587/2136/579 579/2135/583
-f 581/2137/585 582/2140/584 579/2139/583 580/2138/582
-f 584/2141/586 583/2144/587 582/2143/584 581/2142/585
-f 583/2145/587 587/2147/579 582/2146/584
-f 586/2148/588 587/2150/579 583/2149/587
-f 585/2151/589 586/2154/588 583/2153/587 584/2152/586
-f 589/2155/590 588/2158/591 586/2157/588 585/2156/589
-f 588/2159/591 587/2161/579 586/2160/588
-f 591/2162/592 587/2164/579 588/2163/591
-f 590/2165/593 591/2168/592 588/2167/591 589/2166/590
-f 593/2169/594 592/2172/595 591/2171/592 590/2170/593
-f 592/2173/595 587/2175/579 591/2174/592
-f 595/2176/596 587/2178/579 592/2177/595
-f 594/2179/597 595/2182/596 592/2181/595 593/2180/594
-f 597/2183/598 596/2186/599 595/2185/596 594/2184/597
-f 596/2187/599 587/2189/579 595/2188/596
-f 599/2190/600 587/2192/579 596/2191/599
-f 598/2193/601 599/2196/600 596/2195/599 597/2194/598
-f 601/2197/602 600/2200/603 599/2199/600 598/2198/601
-f 600/2201/603 587/2203/579 599/2202/600
-f 603/2204/604 587/2206/579 600/2205/603
-f 602/2207/605 603/2210/604 600/2209/603 601/2208/602
-f 605/2211/606 604/2214/607 603/2213/604 602/2212/605
-f 604/2215/607 587/2217/579 603/2216/604
-f 607/2218/575 587/2220/579 604/2219/607
-f 604/2221/607 605/2224/606 606/2223/576 607/2222/575
-f 606/2225/576 609/2228/608 608/2227/609 576/2226/577
-f 576/2229/577 608/2232/609 610/2231/610 577/2230/581
-f 577/2233/581 610/2236/610 611/2235/611 580/2234/582
-f 580/2237/582 611/2240/611 612/2239/612 581/2238/585
-f 581/2241/585 612/2244/612 613/2243/613 584/2242/586
-f 584/2245/586 613/2248/613 614/2247/614 585/2246/589
-f 585/2249/589 614/2252/614 615/2251/615 589/2250/590
-f 589/2253/590 615/2256/615 616/2255/616 590/2254/593
-f 590/2257/593 616/2260/616 617/2259/617 593/2258/594
-f 593/2261/594 617/2264/617 618/2263/618 594/2262/597
-f 594/2265/597 618/2268/618 619/2267/619 597/2266/598
-f 597/2269/598 619/2272/619 620/2271/620 598/2270/601
-f 598/2273/601 620/2276/620 621/2275/621 601/2274/602
-f 601/2277/602 621/2280/621 622/2279/622 602/2278/605
-f 602/2281/605 622/2284/622 623/2283/623 605/2282/606
-f 605/2285/606 623/2288/623 609/2287/608 606/2286/576
-g default
-v -0.873995 5.856261 1.566527
-v -0.958821 5.821125 1.566527
-v -0.918220 5.760360 1.566527
-v -0.853296 5.825283 1.566527
-v -0.822319 5.804585 1.566527
-v -0.857455 5.719758 1.566527
-v -0.785778 5.705501 1.566527
-v -0.785778 5.797316 1.566527
-v -0.749238 5.804585 1.566527
-v -0.714101 5.719758 1.566527
-v -0.653337 5.760360 1.566527
-v -0.718260 5.825283 1.566527
-v -0.785778 5.892801 1.566527
-v -0.697562 5.856261 1.566527
-v -0.612735 5.821124 1.566527
-v -0.598478 5.892801 1.566527
-v -0.690293 5.892801 1.566527
-v -0.697562 5.929342 1.566527
-v -0.612735 5.964478 1.566527
-v -0.653337 6.025243 1.566527
-v -0.718260 5.960320 1.566527
-v -0.749238 5.981018 1.566527
-v -0.714101 6.065845 1.566527
-v -0.785778 6.080102 1.566527
-v -0.785778 5.988286 1.566527
-v -0.822319 5.981018 1.566527
-v -0.857455 6.065845 1.566527
-v -0.918220 6.025243 1.566527
-v -0.853296 5.960320 1.566527
-v -0.873995 5.929342 1.566527
-v -0.958822 5.964478 1.566527
-v -0.973079 5.892801 1.566527
-v -0.881263 5.892801 1.566527
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vt 0.134778 0.100437
-vt 0.053904 0.100437
-vt 0.053904 0.019563
-vt 0.134778 0.019563
-vn 0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000001 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn -0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000001 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000002 0.000000 -1.000000
-vn -0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000001 0.000000 -1.000000
-vn -0.000002 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn -0.000001 0.000000 -1.000000
-vn -0.000001 0.000000 -1.000000
-vn -0.000003 0.000000 -1.000000
-vn -0.000001 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn -0.000002 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-vn -0.000001 0.000000 -1.000000
-vn 0.000001 0.000000 -1.000000
-vn 0.000001 0.000000 -1.000000
-vn -0.000000 0.000000 -1.000000
-vn 0.000001 0.000000 -1.000000
-vn 0.000000 0.000000 -1.000000
-s 1
-g Mesh polySurface4
-usemtl Eye_Pupil_antonio_iris_png
-f 656/2289/624 624/2290/625 625/2291/626 655/2292/627
-f 624/2293/625 656/2294/624 636/2295/628
-f 627/2296/629 624/2297/625 636/2298/628
-f 626/2299/630 625/2300/626 624/2301/625 627/2302/629
-f 629/2303/631 626/2304/630 627/2305/629 628/2306/632
-f 628/2307/632 627/2308/629 636/2309/628
-f 631/2310/633 628/2311/632 636/2312/628
-f 630/2313/634 629/2314/631 628/2315/632 631/2316/633
-f 633/2317/635 630/2318/634 631/2319/633 632/2320/636
-f 632/2321/636 631/2322/633 636/2323/628
-f 635/2324/637 632/2325/636 636/2326/628
-f 634/2327/638 633/2328/635 632/2329/636 635/2330/637
-f 638/2331/639 634/2332/638 635/2333/637 637/2334/640
-f 637/2335/640 635/2336/637 636/2337/628
-f 640/2338/641 637/2339/640 636/2340/628
-f 639/2341/642 638/2342/639 637/2343/640 640/2344/641
-f 642/2345/643 639/2346/642 640/2347/641 641/2348/644
-f 641/2349/644 640/2350/641 636/2351/628
-f 644/2352/645 641/2353/644 636/2354/628
-f 643/2355/646 642/2356/643 641/2357/644 644/2358/645
-f 646/2359/647 643/2360/646 644/2361/645 645/2362/648
-f 645/2363/648 644/2364/645 636/2365/628
-f 648/2366/649 645/2367/648 636/2368/628
-f 647/2369/650 646/2370/647 645/2371/648 648/2372/649
-f 650/2373/651 647/2374/650 648/2375/649 649/2376/652
-f 649/2377/652 648/2378/649 636/2379/628
-f 652/2380/653 649/2381/652 636/2382/628
-f 651/2383/654 650/2384/651 649/2385/652 652/2386/653
-f 654/2387/655 651/2388/654 652/2389/653 653/2390/656
-f 653/2391/656 652/2392/653 636/2393/628
-f 656/2394/624 653/2395/656 636/2396/628
-f 653/2397/656 656/2398/624 655/2399/627 654/2400/655
-g default
-v 0.881263 5.892801 1.563060
-v 0.973079 5.892801 1.590912
-v 0.873995 5.929342 0.658692
-v 0.958821 5.964478 0.686544
-v 1.036998 5.996860 0.731773
-v 1.105520 6.025243 0.792641
-v 1.161755 6.048536 0.866809
-v 1.203542 6.065845 0.951427
-v 1.229274 6.076503 1.043243
-v 1.237962 6.080102 1.138728
-v 1.229274 6.076503 1.234213
-v 1.203542 6.065845 1.326029
-v 1.161755 6.048536 1.410646
-v 1.105520 6.025243 1.484814
-v 1.036998 5.996860 1.545683
-v 0.958822 5.964478 1.590912
-v 0.873995 5.929342 1.563060
-v 0.853296 5.960320 1.563060
-v 0.918220 6.025243 1.590912
-v 0.978054 6.085077 1.545683
-v 1.030498 6.137521 1.484814
-v 1.073539 6.180562 1.410646
-v 1.105520 6.212543 1.326029
-v 1.125215 6.232238 1.234213
-v 1.131865 6.238888 1.138728
-v 1.125215 6.232238 1.043243
-v 1.105520 6.212543 0.951427
-v 1.073539 6.180562 0.866809
-v 1.030498 6.137521 0.792641
-v 0.978054 6.085077 0.731773
-v 0.918220 6.025243 0.686544
-v 0.853296 5.960320 0.658692
-v 0.785778 5.892801 0.649287
-v 0.822319 5.981018 0.658692
-v 0.857455 6.065845 0.686544
-v 0.889837 6.144021 0.731773
-v 0.918220 6.212543 0.792641
-v 0.941513 6.268778 0.866809
-v 0.958822 6.310565 0.951427
-v 0.969480 6.336297 1.043243
-v 0.973079 6.344985 1.138728
-v 0.969480 6.336297 1.234213
-v 0.958821 6.310565 1.326029
-v 0.941513 6.268778 1.410646
-v 0.918220 6.212543 1.484814
-v 0.889837 6.144021 1.545683
-v 0.857455 6.065845 1.590912
-v 0.822319 5.981018 1.563060
-v 0.785778 5.988286 1.563060
-v 0.785778 6.080102 1.590912
-v 0.785778 6.164720 1.545683
-v 0.785778 6.238888 1.484814
-v 0.785778 6.299756 1.410646
-v 0.785778 6.344985 1.326029
-v 0.785778 6.372837 1.234213
-v 0.785778 6.382242 1.138728
-v 0.785778 6.372837 1.043243
-v 0.785778 6.344985 0.951427
-v 0.785778 6.299756 0.866809
-v 0.785778 6.238888 0.792641
-v 0.785778 6.164720 0.731773
-v 0.785778 6.080102 0.686544
-v 0.785778 5.988286 0.658692
-v 0.749238 5.981018 0.658692
-v 0.714101 6.065845 0.686544
-v 0.681720 6.144021 0.731773
-v 0.653337 6.212543 0.792641
-v 0.630043 6.268778 0.866809
-v 0.612735 6.310565 0.951427
-v 0.602076 6.336297 1.043243
-v 0.598478 6.344985 1.138728
-v 0.602076 6.336297 1.234213
-v 0.612735 6.310565 1.326029
-v 0.630043 6.268778 1.410646
-v 0.653337 6.212543 1.484814
-v 0.681720 6.144021 1.545683
-v 0.714101 6.065845 1.590912
-v 0.749238 5.981018 1.563060
-v 0.718260 5.960320 1.563060
-v 0.653337 6.025243 1.590912
-v 0.593503 6.085077 1.545683
-v 0.541058 6.137521 1.484814
-v 0.498018 6.180562 1.410646
-v 0.466036 6.212543 1.326029
-v 0.446342 6.232238 1.234213
-v 0.439692 6.238888 1.138728
-v 0.446342 6.232238 1.043243
-v 0.466036 6.212543 0.951427
-v 0.498018 6.180562 0.866809
-v 0.541058 6.137521 0.792641
-v 0.593503 6.085076 0.731773
-v 0.653337 6.025243 0.686544
-v 0.718260 5.960320 0.658692
-v 0.697562 5.929342 0.658692
-v 0.612735 5.964478 0.686544
-v 0.534558 5.996860 0.731773
-v 0.466036 6.025243 0.792641
-v 0.409801 6.048536 0.866809
-v 0.368015 6.065845 0.951427
-v 0.342283 6.076503 1.043243
-v 0.333594 6.080102 1.138728
-v 0.342283 6.076503 1.234213
-v 0.368015 6.065845 1.326029
-v 0.409801 6.048536 1.410646
-v 0.466036 6.025243 1.484814
-v 0.534558 5.996860 1.545683
-v 0.612735 5.964478 1.590912
-v 0.697562 5.929342 1.563060
-v 0.690293 5.892801 1.563060
-v 0.598478 5.892801 1.590912
-v 0.513860 5.892801 1.545683
-v 0.439692 5.892801 1.484814
-v 0.378824 5.892801 1.410646
-v 0.333594 5.892801 1.326029
-v 0.305743 5.892801 1.234213
-v 0.296338 5.892801 1.138728
-v 0.305743 5.892801 1.043243
-v 0.333594 5.892801 0.951427
-v 0.378824 5.892801 0.866809
-v 0.439692 5.892801 0.792641
-v 0.513860 5.892801 0.731773
-v 0.598478 5.892801 0.686544
-v 0.690293 5.892801 0.658692
-v 0.697562 5.856261 0.658692
-v 0.612735 5.821125 0.686544
-v 0.534558 5.788743 0.731773
-v 0.466036 5.760360 0.792641
-v 0.409801 5.737066 0.866809
-v 0.368015 5.719758 0.951427
-v 0.342283 5.709099 1.043243
-v 0.333594 5.705501 1.138728
-v 0.342283 5.709099 1.234213
-v 0.368015 5.719758 1.326029
-v 0.409801 5.737066 1.410646
-v 0.466036 5.760360 1.484814
-v 0.534558 5.788743 1.545683
-v 0.612735 5.821124 1.590912
-v 0.697562 5.856261 1.563060
-v 0.718260 5.825283 1.563060
-v 0.653337 5.760360 1.590912
-v 0.593503 5.700526 1.545683
-v 0.541058 5.648081 1.484814
-v 0.498018 5.605041 1.410646
-v 0.466036 5.573059 1.326029
-v 0.446342 5.553365 1.234213
-v 0.439692 5.546715 1.138728
-v 0.446342 5.553365 1.043243
-v 0.466036 5.573059 0.951427
-v 0.498018 5.605041 0.866809
-v 0.541058 5.648081 0.792641
-v 0.593503 5.700526 0.731773
-v 0.653337 5.760360 0.686544
-v 0.718260 5.825283 0.658692
-v 0.749238 5.804585 0.658692
-v 0.714101 5.719758 0.686544
-v 0.681720 5.641582 0.731773
-v 0.653337 5.573059 0.792641
-v 0.630043 5.516824 0.866809
-v 0.612735 5.475038 0.951427
-v 0.602077 5.449306 1.043243
-v 0.598478 5.440618 1.138728
-v 0.602077 5.449306 1.234213
-v 0.612735 5.475038 1.326029
-v 0.630043 5.516824 1.410646
-v 0.653337 5.573059 1.484814
-v 0.681720 5.641582 1.545683
-v 0.714101 5.719758 1.590912
-v 0.749238 5.804585 1.563060
-v 0.785778 5.797316 1.563060
-v 0.785778 5.705501 1.590912
-v 0.785778 5.620883 1.545683
-v 0.785778 5.546715 1.484814
-v 0.785778 5.485847 1.410646
-v 0.785778 5.440618 1.326029
-v 0.785778 5.412766 1.234213
-v 0.785778 5.403361 1.138728
-v 0.785778 5.412766 1.043243
-v 0.785778 5.440618 0.951427
-v 0.785778 5.485847 0.866809
-v 0.785778 5.546715 0.792641
-v 0.785778 5.620883 0.731773
-v 0.785778 5.705501 0.686544
-v 0.785778 5.797317 0.658692
-v 0.822319 5.804585 0.658692
-v 0.857455 5.719758 0.686544
-v 0.889837 5.641582 0.731773
-v 0.918220 5.573059 0.792641
-v 0.941513 5.516824 0.866809
-v 0.958821 5.475038 0.951427
-v 0.969480 5.449306 1.043243
-v 0.973079 5.440618 1.138728
-v 0.969480 5.449306 1.234213
-v 0.958821 5.475038 1.326029
-v 0.941513 5.516824 1.410646
-v 0.918220 5.573059 1.484814
-v 0.889837 5.641582 1.545683
-v 0.857455 5.719758 1.590912
-v 0.822319 5.804585 1.563060
-v 0.853296 5.825283 1.563060
-v 0.918220 5.760360 1.590912
-v 0.978054 5.700526 1.545683
-v 1.030498 5.648081 1.484814
-v 1.073539 5.605041 1.410646
-v 1.105520 5.573059 1.326029
-v 1.125215 5.553365 1.234213
-v 1.131865 5.546715 1.138728
-v 1.125215 5.553365 1.043243
-v 1.105520 5.573059 0.951427
-v 1.073539 5.605041 0.866809
-v 1.030498 5.648081 0.792641
-v 0.978054 5.700526 0.731773
-v 0.918220 5.760360 0.686544
-v 0.853296 5.825283 0.658692
-v 0.873995 5.856261 0.658692
-v 0.958821 5.821125 0.686544
-v 1.036998 5.788743 0.731773
-v 1.105520 5.760360 0.792641
-v 1.161755 5.737066 0.866809
-v 1.203542 5.719758 0.951427
-v 1.229273 5.709100 1.043243
-v 1.237962 5.705501 1.138728
-v 1.229273 5.709100 1.234213
-v 1.203542 5.719758 1.326029
-v 1.161755 5.737066 1.410646
-v 1.105520 5.760360 1.484814
-v 1.036998 5.788743 1.545683
-v 0.958821 5.821125 1.590912
-v 0.873995 5.856261 1.563060
-v 1.057697 5.892801 1.545683
-v 1.131865 5.892801 1.484814
-v 1.192733 5.892801 1.410646
-v 1.237962 5.892801 1.326029
-v 1.265814 5.892801 1.234213
-v 1.275218 5.892801 1.138728
-v 1.265814 5.892801 1.043243
-v 1.237962 5.892801 0.951427
-v 1.192733 5.892801 0.866809
-v 1.131865 5.892801 0.792641
-v 1.057697 5.892801 0.731773
-v 0.973079 5.892801 0.686544
-v 0.881263 5.892801 0.658692
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vn -0.000000 -0.000000 -1.000000
-vn 0.180178 0.074631 -0.980799
-vn 0.195023 0.000000 -0.980799
-vn 0.353439 0.146399 -0.923931
-vn 0.382559 -0.000000 -0.923931
-vn 0.513147 0.212553 -0.831566
-vn 0.555425 -0.000001 -0.831567
-vn 0.653160 0.270548 -0.707238
-vn 0.706975 -0.000000 -0.707239
-vn 0.768089 0.318153 -0.555714
-vn 0.831374 -0.000000 -0.555713
-vn 0.853507 0.353535 -0.382804
-vn 0.923830 -0.000001 -0.382803
-vn 0.906116 0.375324 -0.195157
-vn 0.980772 -0.000002 -0.195156
-vn 0.923880 0.382682 -0.000000
-vn 1.000000 -0.000002 0.000000
-vn 0.906116 0.375324 0.195157
-vn 0.980772 -0.000002 0.195156
-vn 0.853506 0.353534 0.382806
-vn 0.923829 -0.000001 0.382805
-vn 0.768088 0.318153 0.555715
-vn 0.831373 -0.000000 0.555715
-vn 0.653163 0.270550 0.707235
-vn 0.706978 -0.000000 0.707235
-vn 0.513151 0.212554 0.831563
-vn 0.555429 -0.000001 0.831564
-vn 0.090041 0.037297 0.995239
-vn 0.097457 0.000000 0.995240
-vn -0.268185 -0.111085 0.956941
-vn -0.290284 0.000002 0.956941
-vn 0.068915 0.068914 0.995240
-vn -0.205260 -0.205261 0.956941
-vn 0.392749 0.392749 0.831563
-vn 0.499909 0.499908 0.707236
-vn 0.587870 0.587870 0.555714
-vn 0.653245 0.653246 0.382805
-vn 0.693510 0.693510 0.195161
-vn 0.707107 0.707106 -0.000000
-vn 0.693511 0.693510 -0.195160
-vn 0.653246 0.653246 -0.382805
-vn 0.587870 0.587869 -0.555715
-vn 0.499907 0.499906 -0.707239
-vn 0.392747 0.392746 -0.831565
-vn 0.270511 0.270510 -0.923931
-vn 0.137901 0.137901 -0.980799
-vn 0.074630 0.180177 -0.980799
-vn 0.146399 0.353440 -0.923931
-vn 0.212553 0.513147 -0.831566
-vn 0.270548 0.653159 -0.707239
-vn 0.318153 0.768089 -0.555714
-vn 0.353535 0.853507 -0.382804
-vn 0.375326 0.906115 -0.195160
-vn 0.382684 0.923879 -0.000000
-vn 0.375325 0.906114 0.195161
-vn 0.353534 0.853507 0.382805
-vn 0.318153 0.768089 0.555714
-vn 0.270550 0.653162 0.707236
-vn 0.212554 0.513150 0.831564
-vn 0.037296 0.090040 0.995240
-vn -0.111086 -0.268188 0.956941
-vn 0.000000 0.097458 0.995240
-vn 0.000002 -0.290284 0.956941
-vn -0.000001 0.555428 0.831564
-vn -0.000000 0.706977 0.707236
-vn -0.000000 0.831373 0.555715
-vn 0.000000 0.923829 0.382807
-vn -0.000000 0.980771 0.195161
-vn -0.000000 1.000000 -0.000000
-vn -0.000000 0.980771 -0.195160
-vn -0.000000 0.923830 -0.382803
-vn -0.000000 0.831374 -0.555713
-vn -0.000000 0.706974 -0.707239
-vn -0.000001 0.555426 -0.831566
-vn -0.000001 0.382561 -0.923930
-vn -0.000000 0.195022 -0.980799
-vn -0.074630 0.180177 -0.980799
-vn -0.146401 0.353440 -0.923930
-vn -0.212555 0.513148 -0.831565
-vn -0.270549 0.653159 -0.707239
-vn -0.318154 0.768090 -0.555712
-vn -0.353535 0.853507 -0.382804
-vn -0.375326 0.906114 -0.195160
-vn -0.382685 0.923879 0.000001
-vn -0.375326 0.906114 0.195161
-vn -0.353535 0.853506 0.382807
-vn -0.318153 0.768088 0.555715
-vn -0.270549 0.653162 0.707236
-vn -0.212555 0.513150 0.831563
-vn -0.037297 0.090040 0.995240
-vn 0.111087 -0.268188 0.956941
-vn -0.068913 0.068914 0.995240
-vn 0.205262 -0.205261 0.956941
-vn -0.392748 0.392749 0.831564
-vn -0.499908 0.499909 0.707237
-vn -0.587869 0.587870 0.555714
-vn -0.653246 0.653246 0.382805
-vn -0.693511 0.693510 0.195160
-vn -0.707107 0.707107 0.000001
-vn -0.693511 0.693510 -0.195159
-vn -0.653247 0.653246 -0.382804
-vn -0.587870 0.587871 -0.555712
-vn -0.499905 0.499907 -0.707239
-vn -0.392745 0.392747 -0.831565
-vn -0.270511 0.270511 -0.923930
-vn -0.137901 0.137901 -0.980799
-vn -0.180177 0.074631 -0.980799
-vn -0.353438 0.146398 -0.923931
-vn -0.513145 0.212552 -0.831567
-vn -0.653160 0.270547 -0.707239
-vn -0.768090 0.318153 -0.555713
-vn -0.853508 0.353535 -0.382803
-vn -0.906115 0.375325 -0.195159
-vn -0.923880 0.382683 0.000000
-vn -0.906115 0.375325 0.195159
-vn -0.853507 0.353534 0.382804
-vn -0.768089 0.318152 0.555715
-vn -0.653163 0.270548 0.707236
-vn -0.513149 0.212553 0.831565
-vn -0.090038 0.037295 0.995240
-vn 0.268188 -0.111086 0.956940
-vn -0.097457 -0.000000 0.995240
-vn 0.290284 0.000000 0.956940
-vn -0.555429 -0.000001 0.831564
-vn -0.706979 -0.000000 0.707235
-vn -0.831373 -0.000000 0.555715
-vn -0.923830 -0.000000 0.382804
-vn -0.980772 -0.000000 0.195158
-vn -1.000000 -0.000000 0.000000
-vn -0.980772 -0.000000 -0.195158
-vn -0.923831 -0.000000 -0.382802
-vn -0.831374 -0.000000 -0.555713
-vn -0.706976 -0.000000 -0.707238
-vn -0.555425 -0.000001 -0.831566
-vn -0.382559 -0.000001 -0.923931
-vn -0.195023 -0.000001 -0.980799
-vn -0.180179 -0.074633 -0.980799
-vn -0.353439 -0.146399 -0.923931
-vn -0.513145 -0.212551 -0.831567
-vn -0.653159 -0.270547 -0.707239
-vn -0.768090 -0.318154 -0.555712
-vn -0.853508 -0.353536 -0.382801
-vn -0.906115 -0.375326 -0.195157
-vn -0.923879 -0.382684 -0.000000
-vn -0.906115 -0.375326 0.195157
-vn -0.853507 -0.353536 0.382803
-vn -0.768089 -0.318153 0.555713
-vn -0.653162 -0.270548 0.707236
-vn -0.513148 -0.212552 0.831565
-vn -0.090038 -0.037295 0.995240
-vn 0.268187 0.111087 0.956941
-vn -0.068913 -0.068913 0.995240
-vn 0.205261 0.205263 0.956940
-vn -0.392745 -0.392747 0.831565
-vn -0.499907 -0.499908 0.707237
-vn -0.587871 -0.587871 0.555712
-vn -0.653247 -0.653247 0.382802
-vn -0.693510 -0.693511 0.195158
-vn -0.707105 -0.707108 -0.000000
-vn -0.693510 -0.693511 -0.195157
-vn -0.653247 -0.653248 -0.382799
-vn -0.587871 -0.587871 -0.555711
-vn -0.499905 -0.499905 -0.707241
-vn -0.392743 -0.392745 -0.831567
-vn -0.270511 -0.270512 -0.923930
-vn -0.137902 -0.137902 -0.980799
-vn -0.074630 -0.180177 -0.980799
-vn -0.146399 -0.353439 -0.923931
-vn -0.212552 -0.513146 -0.831567
-vn -0.270546 -0.653157 -0.707241
-vn -0.318153 -0.768091 -0.555711
-vn -0.353534 -0.853509 -0.382800
-vn -0.375325 -0.906115 -0.195159
-vn -0.382684 -0.923879 -0.000000
-vn -0.375325 -0.906115 0.195159
-vn -0.353534 -0.853508 0.382802
-vn -0.318153 -0.768090 0.555713
-vn -0.270547 -0.653161 0.707238
-vn -0.212553 -0.513149 0.831565
-vn -0.037296 -0.090039 0.995240
-vn 0.111087 0.268188 0.956940
-vn -0.000000 -0.097458 0.995240
-vn -0.000001 0.290284 0.956941
-vn -0.000000 -0.555428 0.831565
-vn -0.000001 -0.706976 0.707238
-vn 0.000000 -0.831375 0.555713
-vn -0.000000 -0.923831 0.382802
-vn -0.000000 -0.980772 0.195158
-vn -0.000001 -1.000000 -0.000000
-vn -0.000000 -0.980772 -0.195158
-vn -0.000000 -0.923831 -0.382800
-vn 0.000000 -0.831376 -0.555711
-vn -0.000000 -0.706973 -0.707241
-vn -0.000000 -0.555425 -0.831567
-vn 0.000000 -0.382560 -0.923931
-vn -0.000000 -0.195022 -0.980799
-vn 0.074630 -0.180176 -0.980799
-vn 0.146398 -0.353439 -0.923931
-vn 0.212552 -0.513146 -0.831566
-vn 0.270546 -0.653158 -0.707241
-vn 0.318153 -0.768091 -0.555711
-vn 0.353534 -0.853509 -0.382800
-vn 0.375325 -0.906115 -0.195158
-vn 0.382683 -0.923880 -0.000000
-vn 0.375324 -0.906115 0.195158
-vn 0.353533 -0.853508 0.382802
-vn 0.318152 -0.768090 0.555713
-vn 0.270548 -0.653160 0.707238
-vn 0.212553 -0.513149 0.831564
-vn 0.037295 -0.090040 0.995240
-vn -0.111088 0.268188 0.956940
-vn 0.068912 -0.068912 0.995240
-vn -0.205263 0.205262 0.956940
-vn 0.392748 -0.392746 0.831565
-vn 0.499908 -0.499907 0.707237
-vn 0.587870 -0.587870 0.555714
-vn 0.653246 -0.653247 0.382803
-vn 0.693511 -0.693510 0.195158
-vn 0.707107 -0.707106 -0.000000
-vn 0.693511 -0.693510 -0.195158
-vn 0.653247 -0.653248 -0.382800
-vn 0.587871 -0.587871 -0.555712
-vn 0.499906 -0.499905 -0.707241
-vn 0.392745 -0.392744 -0.831567
-vn 0.270511 -0.270510 -0.923931
-vn 0.137901 -0.137901 -0.980799
-vn 0.180177 -0.074632 -0.980799
-vn 0.353439 -0.146399 -0.923931
-vn 0.513146 -0.212552 -0.831567
-vn 0.653158 -0.270548 -0.707240
-vn 0.768089 -0.318154 -0.555713
-vn 0.853508 -0.353536 -0.382801
-vn 0.906115 -0.375325 -0.195157
-vn 0.923880 -0.382682 -0.000000
-vn 0.906115 -0.375325 0.195157
-vn 0.853507 -0.353535 0.382803
-vn 0.768088 -0.318154 0.555714
-vn 0.653162 -0.270549 0.707236
-vn 0.513149 -0.212554 0.831564
-vn 0.090038 -0.037295 0.995240
-vn -0.268188 0.111087 0.956940
-s 1
-g Mesh polySurface5
-usemtl Eye_Base_antonio_iris_png
-f 689/2401/657 659/2402/658 897/2403/659
-f 897/2404/659 659/2405/658 660/2406/660 896/2407/661
-f 896/2408/661 660/2409/660 661/2410/662 895/2411/663
-f 895/2412/663 661/2413/662 662/2414/664 894/2415/665
-f 894/2416/665 662/2417/664 663/2418/666 893/2419/667
-f 893/2420/667 663/2421/666 664/2422/668 892/2423/669
-f 892/2424/669 664/2425/668 665/2426/670 891/2427/671
-f 891/2428/671 665/2429/670 666/2430/672 890/2431/673
-f 890/2432/673 666/2433/672 667/2434/674 889/2435/675
-f 889/2436/675 667/2437/674 668/2438/676 888/2439/677
-f 888/2440/677 668/2441/676 669/2442/678 887/2443/679
-f 887/2444/679 669/2445/678 670/2446/680 886/2447/681
-f 886/2448/681 670/2449/680 671/2450/682 885/2451/683
-f 885//683 671//682 672//684 658//685
-usemtl Eye_Iris_antonio_iris_png
-f 673//686 657//687 658//685 672//684
-f 672//684 675//688 674//689 673//686
-usemtl Eye_Base_antonio_iris_png
-f 671//682 676//690 675//688 672//684
-f 670//680 677//691 676//690 671//682
-f 669//678 678//692 677//691 670//680
-f 668//676 679//693 678//692 669//678
-f 667//674 680//694 679//693 668//676
-f 666//672 681//695 680//694 667//674
-f 665//670 682//696 681//695 666//672
-f 664//668 683//697 682//696 665//670
-f 663//666 684//698 683//697 664//668
-f 662//664 685//699 684//698 663//666
-f 661//662 686//700 685//699 662//664
-f 660//660 687//701 686//700 661//662
-f 659//658 688//702 687//701 660//660
-f 689//657 688//702 659//658
-f 689//657 690//703 688//702
-f 688//702 690//703 691//704 687//701
-f 687//701 691//704 692//705 686//700
-f 686//700 692//705 693//706 685//699
-f 685//699 693//706 694//707 684//698
-f 684//698 694//707 695//708 683//697
-f 683//697 695//708 696//709 682//696
-f 682//696 696//709 697//710 681//695
-f 681//695 697//710 698//711 680//694
-f 680//694 698//711 699//712 679//693
-f 679//693 699//712 700//713 678//692
-f 678//692 700//713 701//714 677//691
-f 677//691 701//714 702//715 676//690
-f 676//690 702//715 703//716 675//688
-usemtl Eye_Iris_antonio_iris_png
-f 675//688 703//716 704//717 674//689
-f 703//716 706//718 705//719 704//717
-usemtl Eye_Base_antonio_iris_png
-f 702//715 707//720 706//718 703//716
-f 701//714 708//721 707//720 702//715
-f 700//713 709//722 708//721 701//714
-f 699//712 710//723 709//722 700//713
-f 698//711 711//724 710//723 699//712
-f 697//710 712//725 711//724 698//711
-f 696//709 713//726 712//725 697//710
-f 695//708 714//727 713//726 696//709
-f 694//707 715//728 714//727 695//708
-f 693//706 716//729 715//728 694//707
-f 692//705 717//730 716//729 693//706
-f 691//704 718//731 717//730 692//705
-f 690//703 719//732 718//731 691//704
-f 689//657 719//732 690//703
-f 689//657 720//733 719//732
-f 719//732 720//733 721//734 718//731
-f 718//731 721//734 722//735 717//730
-f 717//730 722//735 723//736 716//729
-f 716//729 723//736 724//737 715//728
-f 715//728 724//737 725//738 714//727
-f 714//727 725//738 726//739 713//726
-f 713//726 726//739 727//740 712//725
-f 712//725 727//740 728//741 711//724
-f 711//724 728//741 729//742 710//723
-f 710//723 729//742 730//743 709//722
-f 709//722 730//743 731//744 708//721
-f 708//721 731//744 732//745 707//720
-f 707//720 732//745 733//746 706//718
-usemtl Eye_Iris_antonio_iris_png
-f 706//718 733//746 734//747 705//719
-f 733//746 736//748 735//749 734//747
-usemtl Eye_Base_antonio_iris_png
-f 732//745 737//750 736//748 733//746
-f 731//744 738//751 737//750 732//745
-f 730//743 739//752 738//751 731//744
-f 729//742 740//753 739//752 730//743
-f 728//741 741//754 740//753 729//742
-f 727//740 742//755 741//754 728//741
-f 726//739 743//756 742//755 727//740
-f 725//738 744//757 743//756 726//739
-f 724//737 745//758 744//757 725//738
-f 723//736 746//759 745//758 724//737
-f 722//735 747//760 746//759 723//736
-f 721//734 748//761 747//760 722//735
-f 720//733 749//762 748//761 721//734
-f 689//657 749//762 720//733
-f 689//657 750//763 749//762
-f 749//762 750//763 751//764 748//761
-f 748//761 751//764 752//765 747//760
-f 747//760 752//765 753//766 746//759
-f 746//759 753//766 754//767 745//758
-f 745//758 754//767 755//768 744//757
-f 744//757 755//768 756//769 743//756
-f 743//756 756//769 757//770 742//755
-f 742//755 757//770 758//771 741//754
-f 741//754 758//771 759//772 740//753
-f 740//753 759//772 760//773 739//752
-f 739//752 760//773 761//774 738//751
-f 738//751 761//774 762//775 737//750
-f 737//750 762//775 763//776 736//748
-usemtl Eye_Iris_antonio_iris_png
-f 736//748 763//776 764//777 735//749
-f 763//776 766//778 765//779 764//777
-usemtl Eye_Base_antonio_iris_png
-f 762//775 767//780 766//778 763//776
-f 761//774 768//781 767//780 762//775
-f 760//773 769//782 768//781 761//774
-f 759//772 770//783 769//782 760//773
-f 758//771 771//784 770//783 759//772
-f 757//770 772//785 771//784 758//771
-f 756//769 773//786 772//785 757//770
-f 755//768 774//787 773//786 756//769
-f 754//767 775//788 774//787 755//768
-f 753//766 776//789 775//788 754//767
-f 752//765 777//790 776//789 753//766
-f 751//764 778//791 777//790 752//765
-f 750//763 779//792 778//791 751//764
-f 689//657 779//792 750//763
-f 689//657 780//793 779//792
-f 779//792 780//793 781//794 778//791
-f 778//791 781//794 782//795 777//790
-f 777//790 782//795 783//796 776//789
-f 776//789 783//796 784//797 775//788
-f 775//788 784//797 785//798 774//787
-f 774//787 785//798 786//799 773//786
-f 773//786 786//799 787//800 772//785
-f 772//785 787//800 788//801 771//784
-f 771//784 788//801 789//802 770//783
-f 770//783 789//802 790//803 769//782
-f 769//782 790//803 791//804 768//781
-f 768//781 791//804 792//805 767//780
-f 767//780 792//805 793//806 766//778
-usemtl Eye_Iris_antonio_iris_png
-f 766//778 793//806 794//807 765//779
-f 793//806 796//808 795//809 794//807
-usemtl Eye_Base_antonio_iris_png
-f 792//805 797//810 796//808 793//806
-f 791//804 798//811 797//810 792//805
-f 790//803 799//812 798//811 791//804
-f 789//802 800//813 799//812 790//803
-f 788//801 801//814 800//813 789//802
-f 787//800 802//815 801//814 788//801
-f 786//799 803//816 802//815 787//800
-f 785//798 804//817 803//816 786//799
-f 784//797 805//818 804//817 785//798
-f 783//796 806//819 805//818 784//797
-f 782//795 807//820 806//819 783//796
-f 781//794 808//821 807//820 782//795
-f 780//793 809//822 808//821 781//794
-f 689//657 809//822 780//793
-f 689//657 810//823 809//822
-f 809//822 810//823 811//824 808//821
-f 808//821 811//824 812//825 807//820
-f 807//820 812//825 813//826 806//819
-f 806//819 813//826 814//827 805//818
-f 805//818 814//827 815//828 804//817
-f 804//817 815//828 816//829 803//816
-f 803//816 816//829 817//830 802//815
-f 802//815 817//830 818//831 801//814
-f 801//814 818//831 819//832 800//813
-f 800//813 819//832 820//833 799//812
-f 799//812 820//833 821//834 798//811
-f 798//811 821//834 822//835 797//810
-f 797//810 822//835 823//836 796//808
-usemtl Eye_Iris_antonio_iris_png
-f 796//808 823//836 824//837 795//809
-f 823//836 826//838 825//839 824//837
-usemtl Eye_Base_antonio_iris_png
-f 822//835 827//840 826//838 823//836
-f 821//834 828//841 827//840 822//835
-f 820//833 829//842 828//841 821//834
-f 819//832 830//843 829//842 820//833
-f 818//831 831//844 830//843 819//832
-f 817//830 832//845 831//844 818//831
-f 816//829 833//846 832//845 817//830
-f 815//828 834//847 833//846 816//829
-f 814//827 835//848 834//847 815//828
-f 813//826 836//849 835//848 814//827
-f 812//825 837//850 836//849 813//826
-f 811//824 838//851 837//850 812//825
-f 810//823 839//852 838//851 811//824
-f 689//657 839//852 810//823
-f 689//657 840//853 839//852
-f 839//852 840//853 841//854 838//851
-f 838//851 841//854 842//855 837//850
-f 837//850 842//855 843//856 836//849
-f 836//849 843//856 844//857 835//848
-f 835//848 844//857 845//858 834//847
-f 834//847 845//858 846//859 833//846
-f 833//846 846//859 847//860 832//845
-f 832//845 847//860 848//861 831//844
-f 831//844 848//861 849//862 830//843
-f 830//843 849//862 850//863 829//842
-f 829//842 850//863 851//864 828//841
-f 828//841 851//864 852//865 827//840
-f 827//840 852//865 853//866 826//838
-usemtl Eye_Iris_antonio_iris_png
-f 826//838 853//866 854//867 825//839
-f 853//866 856//868 855//869 854//867
-usemtl Eye_Base_antonio_iris_png
-f 852//865 857//870 856//868 853//866
-f 851//864 858//871 857//870 852//865
-f 850//863 859//872 858//871 851//864
-f 849//862 860//873 859//872 850//863
-f 848//861 861//874 860//873 849//862
-f 847//860 862//875 861//874 848//861
-f 846//859 863//876 862//875 847//860
-f 845//858 864//877 863//876 846//859
-f 844//857 865//878 864//877 845//858
-f 843//856 866//879 865//878 844//857
-f 842//855 867//880 866//879 843//856
-f 841//854 868//881 867//880 842//855
-f 840//853 869//882 868//881 841//854
-f 689//657 869//882 840//853
-f 689//657 870//883 869//882
-f 869//882 870//883 871//884 868//881
-f 868//881 871//884 872//885 867//880
-f 867//880 872//885 873//886 866//879
-f 866//879 873//886 874//887 865//878
-f 865//878 874//887 875//888 864//877
-f 864//877 875//888 876//889 863//876
-f 863//876 876//889 877//890 862//875
-f 862//875 877//890 878//891 861//874
-f 861//874 878//891 879//892 860//873
-f 860//873 879//892 880//893 859//872
-f 859//872 880//893 881//894 858//871
-f 858//871 881//894 882//895 857//870
-f 857//870 882//895 883//896 856//868
-usemtl Eye_Iris_antonio_iris_png
-f 856//868 883//896 884//897 855//869
-f 657//687 884//897 883//896 658//685
-usemtl Eye_Base_antonio_iris_png
-f 882//895 885//683 658//685 883//896
-f 881//894 886//681 885//683 882//895
-f 880//893 887//679 886//681 881//894
-f 879//892 888//677 887//679 880//893
-f 878//891 889//675 888//677 879//892
-f 877//890 890//673 889//675 878//891
-f 876//889 891//671 890//673 877//890
-f 875//888 892//669 891//671 876//889
-f 874//887 893//667 892//669 875//888
-f 873//886 894//665 893//667 874//887
-f 872//885 895//663 894//665 873//886
-f 871//884 896//661 895//663 872//885
-f 870//883 897//659 896//661 871//884
-f 689//657 897//659 870//883
-g default
-v 0.873995 5.856261 1.625147
-v 0.958821 5.821125 1.597295
-v 0.918220 5.760360 1.597295
-v 0.853296 5.825283 1.625147
-v 0.822319 5.804585 1.625147
-v 0.857455 5.719758 1.597295
-v 0.785778 5.705501 1.597295
-v 0.785778 5.797316 1.625147
-v 0.749238 5.804585 1.625147
-v 0.714101 5.719758 1.597295
-v 0.653337 5.760360 1.597295
-v 0.718260 5.825283 1.625147
-v 0.785778 5.892801 1.634551
-v 0.697562 5.856261 1.625147
-v 0.612735 5.821124 1.597295
-v 0.598478 5.892801 1.597295
-v 0.690293 5.892801 1.625147
-v 0.697562 5.929342 1.625147
-v 0.612735 5.964478 1.597295
-v 0.653337 6.025243 1.597295
-v 0.718260 5.960320 1.625147
-v 0.749238 5.981018 1.625147
-v 0.714101 6.065845 1.597295
-v 0.785778 6.080102 1.597295
-v 0.785778 5.988286 1.625147
-v 0.822319 5.981018 1.625147
-v 0.857455 6.065845 1.597295
-v 0.918220 6.025243 1.597295
-v 0.853296 5.960320 1.625147
-v 0.873995 5.929342 1.625147
-v 0.958822 5.964478 1.597295
-v 0.973079 5.892801 1.597295
-v 0.881263 5.892801 1.625147
-v 0.958821 5.821125 1.590912
-v 0.973079 5.892801 1.590912
-v 0.918220 5.760360 1.590912
-v 0.857455 5.719758 1.590912
-v 0.785778 5.705501 1.590912
-v 0.714101 5.719758 1.590912
-v 0.653337 5.760360 1.590912
-v 0.612735 5.821124 1.590912
-v 0.598478 5.892801 1.590912
-v 0.612735 5.964478 1.590912
-v 0.653337 6.025243 1.590912
-v 0.714101 6.065845 1.590912
-v 0.785778 6.080102 1.590912
-v 0.857455 6.065845 1.590912
-v 0.918220 6.025243 1.590912
-v 0.958822 5.964478 1.590912
-vn 0.195017 -0.000001 0.980800
-vn 0.180172 -0.074630 0.980800
-vn 0.738432 -0.305868 0.600969
-vn 0.799272 -0.000007 0.600970
-vn 0.000000 -0.000000 1.000000
-vn 0.137897 -0.137898 0.980800
-vn 0.565175 -0.565166 0.600969
-vn 0.305868 -0.738431 0.600970
-vn 0.074628 -0.180172 0.980800
-vn -0.000000 -0.195017 0.980800
-vn -0.000000 -0.799272 0.600969
-vn -0.305871 -0.738430 0.600970
-vn -0.074628 -0.180172 0.980800
-vn -0.137898 -0.137898 0.980800
-vn -0.565173 -0.565169 0.600968
-vn -0.738431 -0.305868 0.600970
-vn -0.180173 -0.074630 0.980800
-vn -0.195017 -0.000000 0.980800
-vn -0.799273 -0.000001 0.600968
-vn -0.738431 0.305867 0.600970
-vn -0.180173 0.074629 0.980800
-vn -0.137899 0.137897 0.980800
-vn -0.565173 0.565169 0.600969
-vn -0.305870 0.738430 0.600970
-vn -0.074628 0.180173 0.980800
-vn -0.000000 0.195017 0.980800
-vn -0.000000 0.799272 0.600969
-vn 0.305868 0.738431 0.600970
-vn 0.074629 0.180172 0.980800
-vn 0.137898 0.137897 0.980800
-vn 0.565171 0.565170 0.600970
-vn 0.738431 0.305866 0.600971
-vn 0.180171 0.074628 0.980800
-vn 0.923880 -0.382682 0.000000
-vn 1.000000 -0.000010 0.000000
-vn 0.707114 -0.707099 0.000000
-vn 0.382684 -0.923879 0.000000
-vn -0.000000 -1.000000 0.000000
-vn -0.382688 -0.923878 0.000000
-vn -0.707110 -0.707104 0.000000
-vn -0.923879 -0.382684 0.000000
-vn -1.000000 -0.000002 0.000000
-vn -0.923880 0.382682 0.000000
-vn -0.707110 0.707104 0.000000
-vn -0.382688 0.923878 0.000000
-vn -0.000000 1.000000 0.000000
-vn 0.382684 0.923879 0.000000
-vn 0.707107 0.707107 0.000000
-vn 0.923880 0.382683 0.000000
-s 1
-g Mesh polySurface6
-usemtl Eye_Cornea
-f 930//898 898//899 899//900 929//901
-f 898//899 930//898 910//902
-f 901//903 898//899 910//902
-f 900//904 899//900 898//899 901//903
-f 903//905 900//904 901//903 902//906
-f 902//906 901//903 910//902
-f 905//907 902//906 910//902
-f 904//908 903//905 902//906 905//907
-f 907//909 904//908 905//907 906//910
-f 906//910 905//907 910//902
-f 909//911 906//910 910//902
-f 908//912 907//909 906//910 909//911
-f 912//913 908//912 909//911 911//914
-f 911//914 909//911 910//902
-f 914//915 911//914 910//902
-f 913//916 912//913 911//914 914//915
-f 916//917 913//916 914//915 915//918
-f 915//918 914//915 910//902
-f 918//919 915//918 910//902
-f 917//920 916//917 915//918 918//919
-f 920//921 917//920 918//919 919//922
-f 919//922 918//919 910//902
-f 922//923 919//922 910//902
-f 921//924 920//921 919//922 922//923
-f 924//925 921//924 922//923 923//926
-f 923//926 922//923 910//902
-f 926//927 923//926 910//902
-f 925//928 924//925 923//926 926//927
-f 928//929 925//928 926//927 927//930
-f 927//930 926//927 910//902
-f 930//898 927//930 910//902
-f 927//930 930//898 929//901 928//929
-f 929//901 899//900 931//931 932//932
-f 899//900 900//904 933//933 931//931
-f 900//904 903//905 934//934 933//933
-f 903//905 904//908 935//935 934//934
-f 904//908 907//909 936//936 935//935
-f 907//909 908//912 937//937 936//936
-f 908//912 912//913 938//938 937//937
-f 912//913 913//916 939//939 938//938
-f 913//916 916//917 940//940 939//939
-f 916//917 917//920 941//941 940//940
-f 917//920 920//921 942//942 941//941
-f 920//921 921//924 943//943 942//942
-f 921//924 924//925 944//944 943//943
-f 924//925 925//928 945//945 944//944
-f 925//928 928//929 946//946 945//945
-f 928//929 929//901 932//932 946//946
-g default
-v 0.873995 5.856261 1.566527
-v 0.958821 5.821125 1.566527
-v 0.918220 5.760360 1.566527
-v 0.853296 5.825283 1.566527
-v 0.822319 5.804585 1.566527
-v 0.857455 5.719758 1.566527
-v 0.785778 5.705501 1.566527
-v 0.785778 5.797316 1.566527
-v 0.749238 5.804585 1.566527
-v 0.714101 5.719758 1.566527
-v 0.653337 5.760360 1.566527
-v 0.718260 5.825283 1.566527
-v 0.785778 5.892801 1.566527
-v 0.697562 5.856261 1.566527
-v 0.612735 5.821124 1.566527
-v 0.598478 5.892801 1.566527
-v 0.690293 5.892801 1.566527
-v 0.697562 5.929342 1.566527
-v 0.612735 5.964478 1.566527
-v 0.653337 6.025243 1.566527
-v 0.718260 5.960320 1.566527
-v 0.749238 5.981018 1.566527
-v 0.714101 6.065845 1.566527
-v 0.785778 6.080102 1.566527
-v 0.785778 5.988286 1.566527
-v 0.822319 5.981018 1.566527
-v 0.857455 6.065845 1.566527
-v 0.918220 6.025243 1.566527
-v 0.853296 5.960320 1.566527
-v 0.873995 5.929342 1.566527
-v 0.958822 5.964478 1.566527
-v 0.973079 5.892801 1.566527
-v 0.881263 5.892801 1.566527
-vn 0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000001 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn -0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000001 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000002 0.000000 1.000000
-vn -0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000001 0.000000 1.000000
-vn -0.000002 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn -0.000001 0.000000 1.000000
-vn -0.000001 0.000000 1.000000
-vn -0.000003 0.000000 1.000000
-vn -0.000001 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn -0.000002 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-vn -0.000001 0.000000 1.000000
-vn 0.000001 0.000000 1.000000
-vn 0.000001 0.000000 1.000000
-vn -0.000000 0.000000 1.000000
-vn 0.000001 0.000000 1.000000
-vn 0.000000 0.000000 1.000000
-s 1
-g Mesh polySurface7
-usemtl Eye_Pupil_antonio_iris_png
-f 979//947 947//948 948//949 978//950
-f 947//948 979//947 959//951
-f 950//952 947//948 959//951
-f 949//953 948//949 947//948 950//952
-f 952//954 949//953 950//952 951//955
-f 951//955 950//952 959//951
-f 954//956 951//955 959//951
-f 953//957 952//954 951//955 954//956
-f 956//958 953//957 954//956 955//959
-f 955//959 954//956 959//951
-f 958//960 955//959 959//951
-f 957//961 956//958 955//959 958//960
-f 961//962 957//961 958//960 960//963
-f 960//963 958//960 959//951
-f 963//964 960//963 959//951
-f 962//965 961//962 960//963 963//964
-f 965//966 962//965 963//964 964//967
-f 964//967 963//964 959//951
-f 967//968 964//967 959//951
-f 966//969 965//966 964//967 967//968
-f 969//970 966//969 967//968 968//971
-f 968//971 967//968 959//951
-f 971//972 968//971 959//951
-f 970//973 969//970 968//971 971//972
-f 973//974 970//973 971//972 972//975
-f 972//975 971//972 959//951
-f 975//976 972//975 959//951
-f 974//977 973//974 972//975 975//976
-f 977//978 974//977 975//976 976//979
-f 976//979 975//976 959//951
-f 979//947 976//979 959//951
-f 976//979 979//947 978//950 977//978
-g default
-v 0.403282 5.281476 1.703856
-v -0.403282 5.281476 1.703856
-v 0.000000 5.271539 1.912381
-v 0.414233 5.909278 1.719773
-v -0.414233 5.909278 1.719773
-v 0.395662 6.145228 1.663149
-v -0.395662 6.145228 1.663149
-v 0.000000 5.715853 1.838089
-v 0.000000 5.920035 1.791091
-v 0.000000 6.196824 1.712682
-v 0.000000 6.601523 1.434642
-v 0.877710 5.496379 1.339971
-v -0.877710 5.496379 1.339971
-v 0.630719 5.677225 1.534821
-v -0.630719 5.677225 1.534821
-v 0.559352 5.881298 1.560878
-v -0.559352 5.881298 1.560878
-v 0.546876 6.080182 1.509111
-v -0.546876 6.080182 1.509111
-v 0.886325 6.313471 0.335735
-v -0.886325 6.313471 0.335735
-v 1.078335 5.935148 0.305611
-v -1.078335 5.935148 0.305611
-v 1.145136 5.508001 0.438512
-v -1.145136 5.508001 0.438512
-v 1.137056 5.238186 0.784787
-v -1.137056 5.238186 0.784787
-v 1.120441 5.259047 1.130195
-v -1.120441 5.259047 1.130195
-v 0.892831 5.269801 1.450634
-v -0.892831 5.269801 1.450634
-v 0.502278 5.578877 1.689167
-v -0.502278 5.578877 1.689167
-v 0.401854 6.492745 1.442463
-v -0.401854 6.492745 1.442463
-v 0.522664 6.613373 1.067726
-v -0.522664 6.613373 1.067726
-v 0.668489 6.565423 0.653704
-v -0.668489 6.565423 0.653704
-v 0.617374 5.028239 1.452418
-v -0.617374 5.028239 1.452418
-v 0.644437 4.743249 0.978551
-v -0.644437 4.743249 0.978551
-v 0.555259 4.673462 0.728751
-v -0.555259 4.673462 0.728751
-v 0.622677 4.729105 0.332248
-v -0.622677 4.729105 0.332248
-v 0.000000 5.507439 1.888647
-v 0.439139 5.465209 1.721695
-v -0.439139 5.465209 1.721695
-v 0.794225 5.120893 1.487988
-v -0.794225 5.120893 1.487988
-v 0.969614 4.951456 1.175829
-v -0.969614 4.951456 1.175829
-v 0.000000 4.433268 1.081641
-v 0.000000 4.658312 1.327781
-v 0.307298 4.842180 1.278272
-v -0.307298 4.842180 1.278272
-v 0.352534 4.920933 1.329406
-v -0.352534 4.920933 1.329406
-v 0.301700 4.960763 1.448509
-v -0.301700 4.960763 1.448509
-v 0.000000 4.686392 1.557555
-v 0.284426 4.875771 1.295973
-v -0.284426 4.875771 1.295973
-v 0.000000 4.835214 1.286141
-v 0.317977 4.921842 1.326703
-v -0.317977 4.921842 1.326703
-v 0.266256 4.939431 1.399221
-v -0.266256 4.939431 1.399221
-v 0.000000 4.951878 1.420958
-v 0.000000 4.889484 1.319553
-v 0.241250 4.900980 1.316064
-v -0.241250 4.900980 1.316064
-v 0.250885 4.914544 1.324418
-v -0.250885 4.914544 1.324418
-v 0.233139 4.926073 1.333619
-v -0.233139 4.926073 1.333619
-v 0.000000 4.918903 1.352345
-v 0.000000 4.356627 0.876857
-v 1.029863 4.946091 0.725756
-v -1.029863 4.946091 0.725756
-v 0.000000 6.819163 1.098198
-v 0.469350 4.790747 1.161451
-v -0.469350 4.790747 1.161451
-v 0.000000 4.554434 1.241094
-v 0.470871 4.959758 1.394694
-v -0.470871 4.959758 1.394694
-v 0.370005 5.050661 1.600697
-v -0.370005 5.050661 1.600697
-v 0.000000 4.943586 1.884027
-v 0.000000 4.463706 0.413866
-v 0.000000 6.923741 0.744479
-v 0.396650 6.805749 0.710942
-v -0.396650 6.805749 0.710942
-v 0.635424 6.606611 0.520423
-v -0.635424 6.606611 0.520423
-v 0.000000 6.927581 0.365841
-v 0.340799 6.833764 0.321917
-v -0.340799 6.833764 0.321917
-v 0.751912 6.395282 0.132904
-v -0.751912 6.395282 0.132904
-v 1.067363 5.294851 0.203782
-v -1.067363 5.294851 0.203782
-v 1.007937 5.937074 0.069310
-v -1.007937 5.937074 0.069310
-v 0.566254 4.971474 -0.204617
-v -0.566254 4.971474 -0.204617
-v 0.796192 5.393610 -0.243712
-v -0.796192 5.393610 -0.243712
-v 0.586043 6.328874 -0.191648
-v -0.586043 6.328874 -0.191648
-v 0.774334 5.911183 -0.236529
-v -0.774334 5.911183 -0.236529
-v 0.000000 6.788026 -0.073024
-v 0.312485 6.678264 -0.116948
-v -0.312485 6.678264 -0.116948
-v 0.363373 5.180629 -0.523008
-v -0.363373 5.180629 -0.523008
-v 0.486943 5.537878 -0.542648
-v -0.486943 5.537878 -0.542648
-v 0.477197 5.920773 -0.510670
-v -0.477197 5.920773 -0.510670
-v 0.348485 6.186662 -0.501133
-v -0.348485 6.186662 -0.501133
-v 0.214792 6.402898 -0.452257
-v -0.214792 6.402898 -0.452257
-v 0.000000 6.456598 -0.508222
-v 0.000000 5.025444 -0.508852
-v 0.000000 5.585810 -0.701233
-v 0.000000 5.932668 -0.687107
-v 0.000000 6.126835 -0.634920
-v 0.322370 4.536399 -0.423277
-v -0.322370 4.536399 -0.423277
-v 0.000000 4.582835 -0.517905
-v 0.491120 4.458797 -0.148984
-v -0.491120 4.458797 -0.148984
-v 0.378817 4.377687 0.177956
-v -0.378817 4.377687 0.177956
-v 0.000000 4.341020 0.322549
-v 0.000000 4.915823 0.935745
-v 0.241250 4.927320 0.932256
-v -0.241250 4.927320 0.932256
-v 0.250885 4.940884 0.940609
-v -0.250885 4.940884 0.940609
-v 0.233139 4.952413 0.949810
-v -0.233139 4.952413 0.949810
-v 0.000000 4.945243 0.968537
-v 0.120277 4.936337 0.945391
-v -0.120277 4.936337 0.945391
-v 0.456286 4.025938 -0.555900
-v -0.456286 4.025938 -0.555900
-v 0.000000 3.988576 -0.675689
-v 0.447797 3.966623 0.189593
-v -0.447797 3.966623 0.189593
-v 0.000000 3.902997 0.372477
-v 0.691281 2.845954 -0.506380
-v -0.691281 2.845954 -0.506380
-v 0.000000 2.845161 -0.808879
-v 1.006026 2.845954 0.032716
-v -1.006026 2.845954 0.032716
-v 0.779013 2.856313 0.677032
-v -0.779013 2.856313 0.677032
-v 0.000000 2.845954 1.123654
-v 0.702340 3.446568 -0.526979
-v -0.702340 3.446568 -0.526979
-v 0.000000 3.420520 -0.753221
-v 0.827951 3.385044 -0.089823
-v -0.827951 3.385044 -0.089823
-v 0.623552 3.445540 0.347473
-v -0.623552 3.445540 0.347473
-v 0.000000 3.374475 0.652325
-v 0.647496 2.200342 -0.506380
-v -0.647496 2.200342 -0.506380
-v 0.000000 2.200342 -0.706690
-v 0.936916 2.200342 0.032716
-v -0.936916 2.200342 0.032716
-v 0.716185 2.184262 0.660668
-v -0.716185 2.184262 0.660668
-v 0.000000 2.200342 1.123654
-v 0.189022 1.792717 -0.397344
-v -0.189022 1.792717 -0.397344
-v 0.000000 1.802970 -0.464861
-v 0.200845 1.791113 0.417636
-v -0.200845 1.791113 0.417636
-v 0.000000 1.802758 0.512649
-v 0.090090 1.644165 -0.038665
-v -0.090090 1.644165 -0.038665
-v 0.000000 1.650763 -0.028935
-v 0.677370 3.760457 0.105932
-v -0.677370 3.760457 0.105932
-v 0.782217 3.699529 -0.103583
-v -0.782217 3.699529 -0.103583
-v 0.679076 3.743654 -0.326871
-v -0.679076 3.743654 -0.326871
-v 0.618686 3.985199 0.194285
-v -0.618686 3.985199 0.194285
-v 0.617824 4.037802 -0.426651
-v -0.617824 4.037802 -0.426651
-v 0.601024 4.308213 0.121549
-v -0.601024 4.308213 0.121549
-v 0.672530 4.398517 -0.142108
-v -0.672530 4.398517 -0.142108
-v 0.609492 4.332556 -0.329251
-v -0.609492 4.332556 -0.329251
-v 2.032496 3.752539 -0.042933
-v -2.032496 3.752539 -0.042933
-v 2.028226 4.205258 -0.068752
-v -2.028226 4.205258 -0.068752
-v 0.132602 1.390840 -0.057865
-v -0.132602 1.390840 -0.057865
-v 0.265081 1.479222 0.351801
-v -0.265081 1.479222 0.351801
-v 0.259910 1.486998 -0.339992
-v -0.259910 1.486998 -0.339992
-v 0.716139 1.695218 0.389835
-v -0.716139 1.695218 0.389835
-v 0.954344 1.742856 0.030223
-v -0.954344 1.742856 0.030223
-v 0.699673 1.714458 -0.353309
-v -0.699673 1.714458 -0.353309
-v 0.288821 0.976613 0.475017
-v -0.288821 0.976613 0.475017
-v 0.871490 1.133342 0.539923
-v -0.871490 1.133342 0.539923
-v 1.178457 1.124943 0.103061
-v -1.178457 1.124943 0.103061
-v 0.033354 0.375932 -0.125187
-v -0.033354 0.375932 -0.125187
-v 0.298929 0.375932 -0.610714
-v -0.298929 0.375932 -0.610714
-v 0.327550 0.538062 0.559787
-v -0.327550 0.538062 0.559787
-v 0.989882 0.571855 0.616733
-v -0.989882 0.571855 0.616733
-v 1.036256 0.375932 -0.635317
-v -1.036256 0.375932 -0.635317
-v 1.353794 0.599987 0.042457
-v -1.353794 0.599987 0.042457
-v 0.033354 0.002707 -0.125187
-v -0.033354 0.002707 -0.125187
-v 0.298929 0.002707 -0.610714
-v -0.298929 0.002707 -0.610714
-v 0.304735 0.002707 0.643098
-v -0.304735 0.002707 0.643098
-v 1.062636 0.002707 0.695683
-v -1.062636 0.002707 0.695683
-v 1.036256 0.002707 -0.635317
-v -1.036256 0.002707 -0.635317
-v 1.444249 0.002707 0.051604
-v -1.444249 0.002707 0.051604
-v 0.452819 0.002707 -0.044005
-v -0.452819 0.002707 -0.044005
-v 0.550457 0.002707 -0.222507
-v -0.550457 0.002707 -0.222507
-v 0.552591 0.002707 0.238452
-v -0.552591 0.002707 0.238452
-v 0.831230 0.002707 0.257785
-v -0.831230 0.002707 0.257785
-v 0.821532 0.002707 -0.231552
-v -0.821532 0.002707 -0.231552
-v 0.971529 0.002707 0.020992
-v -0.971529 0.002707 0.020992
-v 0.551524 0.002707 0.007973
-v -0.551524 0.002707 0.007973
-v 0.826381 0.002707 0.013117
-v -0.826381 0.002707 0.013117
-v 2.002430 3.797751 0.099429
-v -2.002430 3.797751 0.099429
-v 2.046036 3.778891 -0.203026
-v -2.046036 3.778891 -0.203026
-v 1.992591 3.974605 0.174464
-v -1.992591 3.974605 0.174464
-v 2.051891 4.014605 -0.289877
-v -2.051891 4.014605 -0.289877
-v 1.995828 4.148176 0.070227
-v -1.995828 4.148176 0.070227
-v 2.035303 4.167760 -0.166942
-v -2.035303 4.167760 -0.166942
-v 2.119859 3.802149 0.107518
-v -2.119859 3.802149 0.107518
-v 2.112780 3.970114 0.178845
-v -2.112780 3.970114 0.178845
-v 2.148876 3.787435 -0.184852
-v -2.148876 3.787435 -0.184852
-v 2.156955 4.010562 -0.266252
-v -2.156955 4.010562 -0.266252
-v 2.123170 4.140059 0.074155
-v -2.123170 4.140059 0.074155
-v 2.144806 4.154335 -0.143848
-v -2.144806 4.154335 -0.143848
-v 2.136476 4.188130 -0.059916
-v -2.136476 4.188130 -0.059916
-v 2.133961 3.768896 -0.034574
-v -2.133961 3.768896 -0.034574
-v 2.152980 3.845099 0.260540
-v -2.152980 3.845099 0.260540
-v 2.138052 3.977059 0.338301
-v -2.138052 3.977059 0.338301
-v 2.241637 3.726199 -0.201283
-v -2.241637 3.726199 -0.201283
-v 2.258674 4.032603 -0.290026
-v -2.258674 4.032603 -0.290026
-v 2.159962 4.210432 0.224168
-v -2.159962 4.210432 0.224168
-v 2.244462 4.167970 -0.133496
-v -2.244462 4.167970 -0.133496
-v 2.204410 4.216659 0.079651
-v -2.204410 4.216659 0.079651
-v 2.182718 3.700742 0.105631
-v -2.182718 3.700742 0.105631
-v 2.343801 3.845099 0.299897
-v -2.343801 3.845099 0.299897
-v 2.420150 3.977059 0.396484
-v -2.420150 3.977059 0.396484
-v 2.508267 4.048171 -0.314412
-v -2.508267 4.048171 -0.314412
-v 2.491036 3.713470 -0.232921
-v -2.491036 3.713470 -0.232921
-v 2.442060 4.210432 0.282351
-v -2.442060 4.210432 0.282351
-v 2.475941 4.165116 -0.151478
-v -2.475941 4.165116 -0.151478
-v 2.427811 4.250806 0.082136
-v -2.427811 4.250806 0.082136
-v 2.378477 3.761280 0.091576
-v -2.378477 3.761280 0.091576
-v 2.234664 4.109622 0.757108
-v -2.234664 4.109622 0.757108
-v 2.232278 4.030012 0.793809
-v -2.232278 4.030012 0.793809
-v 2.222640 3.947244 0.818097
-v -2.222640 3.947244 0.818097
-v 2.081488 4.109622 0.725516
-v -2.081488 4.109622 0.725516
-v 2.069917 4.030012 0.760322
-v -2.069917 4.030012 0.760322
-v 2.069768 3.947244 0.786567
-v -2.069768 3.947244 0.786567
-v 1.282522 3.676689 -0.105743
-v -1.282522 3.676689 -0.105743
-v 1.441577 3.703389 -0.200859
-v -1.441577 3.703389 -0.200859
-v 1.554657 3.683046 -0.088020
-v -1.554657 3.683046 -0.088020
-v 1.179143 4.283866 -0.140695
-v -1.179143 4.283866 -0.140695
-v 1.360771 4.270339 -0.202543
-v -1.360771 4.270339 -0.202543
-v 1.499228 4.274229 -0.126618
-v -1.499228 4.274229 -0.126618
-v 1.146851 4.232678 -0.276057
-v -1.146851 4.232678 -0.276057
-v 1.335876 4.175742 -0.323073
-v -1.335876 4.175742 -0.323073
-v 1.493766 4.225019 -0.256554
-v -1.493766 4.225019 -0.256554
-v 1.335262 4.159736 0.012238
-v -1.335262 4.159736 0.012238
-v 1.187066 4.029813 -0.439858
-v -1.187066 4.029813 -0.439858
-v 1.367419 4.014544 -0.435023
-v -1.367419 4.014544 -0.435023
-v 1.532990 4.025284 -0.415582
-v -1.532990 4.025284 -0.415582
-v 1.372953 3.969194 0.130193
-v -1.372953 3.969194 0.130193
-v 1.221054 3.714940 -0.325598
-v -1.221054 3.714940 -0.325598
-v 1.393076 3.764374 -0.343309
-v -1.393076 3.764374 -0.343309
-v 1.540501 3.717010 -0.300607
-v -1.540501 3.717010 -0.300607
-v 1.396955 3.780871 0.049828
-v -1.396955 3.780871 0.049828
-v 2.323190 4.214493 0.601344
-v -2.323190 4.214493 0.601344
-v 2.339239 4.006944 0.697100
-v -2.339239 4.006944 0.697100
-v 2.307900 3.811543 0.646461
-v -2.307900 3.811543 0.646461
-v 2.066978 4.214493 0.548500
-v -2.066978 4.214493 0.548500
-v 2.057836 3.811543 0.594885
-v -2.057836 3.811543 0.594885
-v 2.016552 4.006944 0.630546
-v -2.016552 4.006944 0.630546
-v 2.388435 4.225656 0.424066
-v -2.388435 4.225656 0.424066
-v 2.361880 3.992145 0.562392
-v -2.361880 3.992145 0.562392
-v 2.334334 3.810700 0.458735
-v -2.334334 3.810700 0.458735
-v 2.111526 4.225656 0.366954
-v -2.111526 4.225656 0.366954
-v 2.103063 3.810700 0.411036
-v -2.103063 3.810700 0.411036
-v 2.084971 3.992145 0.505280
-v -2.084971 3.992145 0.505280
-v 0.810035 4.276563 -0.141758
-v -0.810035 4.276563 -0.141758
-v 0.798186 4.226985 -0.275791
-v -0.798186 4.226985 -0.275791
-v 0.752521 4.035912 -0.449566
-v -0.752521 4.035912 -0.449566
-v 0.739787 3.982630 0.183995
-v -0.739787 3.982630 0.183995
-v 0.789421 4.190308 0.044677
-v -0.789421 4.190308 0.044677
-v 0.994298 4.280209 -0.141227
-v -0.994298 4.280209 -0.141227
-v 0.987270 4.230072 -0.275935
-v -0.987270 4.230072 -0.275935
-v 0.959644 4.033005 -0.434582
-v -0.959644 4.033005 -0.434582
-v 0.951951 3.978127 0.165966
-v -0.951951 3.978127 0.165966
-v 0.980198 4.179622 0.033339
-v -0.980198 4.179622 0.033339
-v 0.237553 0.992426 -0.480541
-v -0.237553 0.992426 -0.480541
-v 0.021885 0.912096 -0.086544
-v -0.021885 0.912096 -0.086544
-v 0.232089 1.181931 0.428633
-v -0.232089 1.181931 0.428633
-v 0.805864 1.376313 0.479091
-v -0.805864 1.376313 0.479091
-v 0.873660 1.143830 -0.513940
-v -0.873660 1.143830 -0.513940
-v 1.102058 1.406028 0.013741
-v -1.102058 1.406028 0.013741
-v 0.253208 0.757134 0.540704
-v -0.253208 0.757134 0.540704
-v 0.942211 0.848197 0.596423
-v -0.942211 0.848197 0.596423
-v 1.285767 0.888739 0.028805
-v -1.285767 0.888739 0.028805
-v 0.891791 0.002707 0.854941
-v -0.891791 0.002707 0.854941
-v 0.475579 0.002707 0.826064
-v -0.475579 0.002707 0.826064
-v 0.891791 0.375932 0.854941
-v -0.891791 0.375932 0.854941
-v 0.475579 0.375932 0.826064
-v -0.475579 0.375932 0.826064
-v 1.530905 0.002707 0.292904
-v -1.530905 0.002707 0.292904
-v 1.315900 0.002707 0.655784
-v -1.315900 0.002707 0.655784
-v 1.530905 0.375932 0.292904
-v -1.530905 0.375932 0.292904
-v 1.315900 0.375932 0.655784
-v -1.315900 0.375932 0.655784
-v 0.518075 5.890634 1.631745
-v -0.518075 5.890634 1.631745
-v 0.466057 6.124871 1.604473
-v -0.466057 6.124871 1.604473
-v 0.994577 6.295892 0.493820
-v -0.994577 6.295892 0.493820
-v 1.160789 5.955342 0.469980
-v -1.160789 5.955342 0.469980
-v 1.239010 5.624889 0.588976
-v -1.239010 5.624889 0.588976
-v 1.234803 5.377378 0.841796
-v -1.234803 5.377378 0.841796
-v 1.117066 5.334710 1.136263
-v -1.117066 5.334710 1.136263
-v 0.891437 5.371941 1.409173
-v -0.891437 5.371941 1.409173
-v 0.603277 5.643217 1.599433
-v -0.603277 5.643217 1.599433
-v 0.475109 6.428597 1.416243
-v -0.475109 6.428597 1.416243
-v 0.578812 6.551901 1.110500
-v -0.578812 6.551901 1.110500
-v 0.767525 6.497934 0.810595
-v -0.767525 6.497934 0.810595
-v 1.218823 6.266702 0.772088
-v -1.218823 6.266702 0.772088
-v 1.338991 5.948666 0.712003
-v -1.338991 5.948666 0.712003
-v 1.349184 5.694559 0.798944
-v -1.349184 5.694559 0.798944
-v 1.276530 5.490403 0.985745
-v -1.276530 5.490403 0.985745
-v 1.174485 5.397684 1.107512
-v -1.174485 5.397684 1.107512
-v 0.621317 6.484120 1.390098
-v -0.621317 6.484120 1.390098
-v 0.772207 6.558151 1.215438
-v -0.772207 6.558151 1.215438
-v 0.989712 6.525063 0.942231
-v -0.989712 6.525063 0.942231
-v 1.309814 6.203684 1.074458
-v -1.309814 6.203684 1.074458
-v 1.373186 5.936008 0.957424
-v -1.373186 5.936008 0.957424
-v 1.350012 5.765097 1.004791
-v -1.350012 5.765097 1.004791
-v 1.256494 5.594994 1.107658
-v -1.256494 5.594994 1.107658
-v 1.156470 5.466505 1.235792
-v -1.156470 5.466505 1.235792
-v 0.658801 6.365822 1.468140
-v -0.658801 6.365822 1.468140
-v 0.867499 6.444010 1.363664
-v -0.867499 6.444010 1.363664
-v 1.111521 6.402097 1.272124
-v -1.111521 6.402097 1.272124
-v 1.040504 6.197162 1.255872
-v -1.040504 6.197162 1.255872
-v 0.840084 6.241192 1.343370
-v -0.840084 6.241192 1.343370
-v 0.649417 6.218476 1.372758
-v -0.649417 6.218476 1.372758
-v 0.976777 5.525796 1.200777
-v -0.976777 5.525796 1.200777
-v 1.121690 5.645116 1.091355
-v -1.121690 5.645116 1.091355
-v 1.174400 5.792682 1.020633
-v -1.174400 5.792682 1.020633
-v 1.197700 5.878614 0.988495
-v -1.197700 5.878614 0.988495
-v 1.182943 6.060466 1.095675
-v -1.182943 6.060466 1.095675
-v 0.810520 6.305696 0.928277
-v -0.810520 6.305696 0.928277
-v 0.659375 6.348046 1.139993
-v -0.659375 6.348046 1.139993
-v 0.574288 6.274066 1.355185
-v -0.574288 6.274066 1.355185
-v 1.017926 5.495524 1.142533
-v -1.017926 5.495524 1.142533
-v 1.089096 5.590048 0.971720
-v -1.089096 5.590048 0.971720
-v 1.108010 5.737014 0.836920
-v -1.108010 5.737014 0.836920
-v 1.073448 5.924368 0.766349
-v -1.073448 5.924368 0.766349
-v 0.980276 6.132098 0.784638
-v -0.980276 6.132098 0.784638
-v 2.775690 3.994225 0.427661
-v -2.775690 3.994225 0.427661
-v 2.816417 4.016494 -0.344517
-v -2.816417 4.016494 -0.344517
-v 2.795962 3.722946 -0.241920
-v -2.795962 3.722946 -0.241920
-v 2.796036 4.217921 0.311967
-v -2.796036 4.217921 0.311967
-v 2.747313 3.853631 0.258436
-v -2.747313 3.853631 0.258436
-v 2.757749 3.750309 0.047569
-v -2.757749 3.750309 0.047569
-v 2.804755 4.141697 -0.144807
-v -2.804755 4.141697 -0.144807
-v 2.792106 4.211776 0.074805
-v -2.792106 4.211776 0.074805
-v 3.286820 3.938014 0.342497
-v -3.286820 3.938014 0.342497
-v 3.318905 4.077865 0.288080
-v -3.318905 4.077865 0.288080
-v 3.268180 4.119606 0.414380
-v -3.268180 4.119606 0.414380
-v 3.235184 3.994230 0.471353
-v -3.235184 3.994230 0.471353
-v 3.357620 3.861013 0.020883
-v -3.357620 3.861013 0.020883
-v 3.368163 4.001042 -0.035007
-v -3.368163 4.001042 -0.035007
-v 3.353418 3.917244 0.159675
-v -3.353418 3.917244 0.159675
-v 3.364231 4.046552 0.099557
-v -3.364231 4.046552 0.099557
-v 3.344663 3.825492 -0.185786
-v -3.344663 3.825492 -0.185786
-v 3.340971 3.988935 -0.252287
-v -3.340971 3.988935 -0.252287
-v 3.310319 3.944330 -0.394464
-v -3.310319 3.944330 -0.394464
-v 3.313288 3.765419 -0.330772
-v -3.313288 3.765419 -0.330772
-v 3.019383 4.162620 0.060906
-v -3.019383 4.162620 0.060906
-v 3.229999 4.150297 0.119471
-v -3.229999 4.150297 0.119471
-v 2.965858 3.822203 0.202539
-v -2.965858 3.822203 0.202539
-v 3.162120 3.812474 0.254513
-v -3.162120 3.812474 0.254513
-v 2.951224 4.264223 0.379924
-v -2.951224 4.264223 0.379924
-v 3.129130 4.251125 0.429097
-v -3.129130 4.251125 0.429097
-v 2.896590 3.959040 0.527966
-v -2.896590 3.959040 0.527966
-v 3.059484 3.948269 0.570388
-v -3.059484 3.948269 0.570388
-v 3.008631 4.097611 -0.163790
-v -3.008631 4.097611 -0.163790
-v 3.224681 4.074235 -0.161212
-v -3.224681 4.074235 -0.161212
-v 2.982553 3.753235 -0.026124
-v -2.982553 3.753235 -0.026124
-v 3.199798 3.744511 -0.029524
-v -3.199798 3.744511 -0.029524
-v 2.973976 3.868008 0.257166
-v -2.973976 3.868008 0.257166
-v 3.190593 3.867699 0.274533
-v -3.190593 3.867699 0.274533
-v 3.000354 4.187275 0.112169
-v -3.000354 4.187275 0.112169
-v 3.215968 4.172670 0.134093
-v -3.215968 4.172670 0.134093
-v 2.986068 3.791132 -0.001833
-v -2.986068 3.791132 -0.001833
-v 3.188268 3.770313 -0.025440
-v -3.188268 3.770313 -0.025440
-v 2.996350 4.117277 -0.133363
-v -2.996350 4.117277 -0.133363
-v 3.188405 4.125109 -0.169259
-v -3.188405 4.125109 -0.169259
-v 2.973649 4.028270 -0.420571
-v -2.973649 4.028270 -0.420571
-v 3.139630 4.028282 -0.479508
-v -3.139630 4.028282 -0.479508
-v 2.962690 3.671258 -0.294745
-v -2.962690 3.671258 -0.294745
-v 3.138278 3.639908 -0.341833
-v -3.138278 3.639908 -0.341833
-v 0.690653 5.886715 1.332639
-v -0.690653 5.886715 1.332639
-v 0.750587 6.096205 1.130458
-v -0.750587 6.096205 1.130458
-v 0.911899 5.708286 1.154860
-v -0.911899 5.708286 1.154860
-v 0.946088 5.920192 0.969749
-v -0.946088 5.920192 0.969749
-v 0.000000 1.474390 -0.805698
-v 0.088500 1.464340 -0.787009
-v -0.088500 1.464340 -0.787009
-v 0.158702 1.595017 -0.776359
-v -0.158702 1.595017 -0.776359
-v 0.000000 1.615931 -0.834980
-v 0.000000 1.719333 -0.585967
-v 0.201416 1.670830 -0.307991
-v -0.201416 1.670830 -0.307991
-v 0.000000 1.453553 -0.111805
-v 0.092294 1.459639 -0.114009
-v -0.092294 1.459639 -0.114009
-vn 0.480857 0.098116 0.871292
-vn 0.440340 0.171403 0.881318
-vn 0.000000 0.167679 0.985842
-vn 0.000000 0.126392 0.991980
-vn -0.440340 0.171403 0.881318
-vn -0.480857 0.098116 0.871292
-vn 0.418886 0.374592 0.827173
-vn 0.439896 0.645972 0.623869
-vn 0.000000 0.736146 0.676822
-vn -0.000000 0.411366 0.911470
-vn -0.439896 0.645972 0.623869
-vn -0.418886 0.374592 0.827173
-vn 0.420761 -0.322530 0.847900
-vn 0.396537 -0.083301 0.914232
-vn -0.000000 0.080979 0.996716
-vn -0.000000 -0.140642 0.990061
-vn -0.396537 -0.083301 0.914232
-vn -0.420761 -0.322531 0.847900
-vn 0.302040 -0.673977 0.674186
-vn 0.483717 -0.429783 0.762433
-vn -0.302040 -0.673977 0.674186
-vn -0.483717 -0.429783 0.762433
-vn 0.694556 -0.010406 0.719364
-vn -0.694556 -0.010406 0.719364
-vn 0.395954 -0.832497 0.387517
-vn 0.666250 -0.657183 0.352451
-vn -0.666250 -0.657183 0.352451
-vn -0.395954 -0.832497 0.387517
-vn 0.885791 -0.265738 0.380470
-vn -0.885791 -0.265738 0.380470
-vn 0.000000 -0.235977 0.971759
-vn 0.119069 -0.459496 0.880163
-vn 0.006360 -0.393698 0.919218
-vn 0.000000 -0.226478 0.974016
-vn -0.006360 -0.393697 0.919218
-vn -0.119069 -0.459495 0.880163
-vn 0.046356 -0.811794 0.582101
-vn -0.004302 -0.790105 0.612957
-vn 0.004301 -0.790104 0.612958
-vn -0.046356 -0.811794 0.582101
-vn 0.271428 -0.956991 0.102446
-vn 0.038448 -0.998269 0.044512
-vn -0.038448 -0.998269 0.044513
-vn -0.271428 -0.956991 0.102446
-vn -0.000000 -0.954485 -0.298261
-vn -0.000000 -0.955480 -0.295056
-vn -0.396544 0.226252 0.889698
-vn 0.000000 0.448054 0.894006
-vn 0.396543 0.226253 0.889698
-vn -0.546291 -0.707850 0.447789
-vn 0.546291 -0.707849 0.447790
-vn -0.143607 -0.980176 0.136496
-vn 0.143607 -0.980176 0.136497
-vn 0.000000 -0.991552 0.129713
-vn 0.000000 -0.901183 0.433438
-vn 0.000000 -0.999697 0.024605
-vn 0.519078 -0.854030 -0.034515
-vn -0.519078 -0.854030 -0.034515
-vn 0.923501 -0.380608 -0.047783
-vn 0.797660 -0.600323 -0.057887
-vn -0.923501 -0.380608 -0.047784
-vn -0.797660 -0.600323 -0.057888
-vn 0.498342 0.813751 0.299106
-vn 0.000000 0.915018 0.403414
-vn -0.498342 0.813751 0.299106
-vn 0.303461 -0.713503 0.631526
-vn 0.000000 -0.687399 0.726280
-vn -0.303460 -0.713503 0.631526
-vn 0.214255 -0.780990 0.586643
-vn -0.214255 -0.780990 0.586642
-vn 0.444855 -0.641386 0.625083
-vn -0.444855 -0.641386 0.625082
-vn -0.000000 -0.596418 0.802674
-vn 0.000000 -0.921920 0.387381
-vn 0.700537 -0.712971 -0.030333
-vn -0.700537 -0.712971 -0.030333
-vn 0.502667 0.860066 0.087247
-vn 0.000000 0.984283 0.176596
-vn -0.502667 0.860066 0.087247
-vn 0.634125 0.773221 -0.003887
-vn 0.688260 0.724637 -0.034644
-vn -0.688260 0.724637 -0.034644
-vn -0.634125 0.773221 -0.003887
-vn 0.472894 0.857428 -0.202947
-vn 0.000000 0.987760 -0.155983
-vn -0.472894 0.857428 -0.202948
-vn 0.731568 0.617347 -0.289295
-vn -0.731568 0.617346 -0.289295
-vn 0.745636 0.611383 -0.265024
-vn -0.745636 0.611383 -0.265024
-vn 0.872847 -0.327104 -0.362135
-vn -0.872847 -0.327104 -0.362135
-vn 0.932802 -0.158653 -0.323589
-vn -0.932802 -0.158653 -0.323589
-vn 0.882974 0.241379 -0.402608
-vn 0.864891 0.244317 -0.438489
-vn -0.882974 0.241379 -0.402608
-vn -0.864891 0.244317 -0.438489
-vn 0.805388 -0.364585 -0.467363
-vn 0.745249 -0.219339 -0.629678
-vn -0.805388 -0.364585 -0.467363
-vn -0.745249 -0.219339 -0.629679
-vn 0.742828 0.215643 -0.633802
-vn 0.654256 0.509838 -0.558583
-vn -0.742828 0.215643 -0.633802
-vn -0.654256 0.509838 -0.558583
-vn 0.436893 0.747003 -0.501110
-vn 0.000000 0.871725 -0.489995
-vn -0.436893 0.747003 -0.501110
-vn 0.464202 -0.219855 -0.858010
-vn 0.481111 -0.120724 -0.868308
-vn -0.464202 -0.219855 -0.858010
-vn -0.481111 -0.120724 -0.868308
-vn 0.516383 0.177690 -0.837720
-vn 0.469200 0.375897 -0.799095
-vn -0.516383 0.177690 -0.837720
-vn -0.469200 0.375897 -0.799095
-vn 0.375756 0.563724 -0.735543
-vn 0.000000 0.591237 -0.806498
-vn -0.375756 0.563724 -0.735543
-vn -0.000000 -0.095533 -0.995426
-vn -0.000000 0.148933 -0.988847
-vn 0.000000 0.302827 -0.953046
-vn -0.000000 -0.169230 -0.985577
-vn 0.515729 0.270586 -0.812900
-vn 0.000000 0.097490 -0.995237
-vn -0.515729 0.270586 -0.812900
-vn 0.891198 0.415463 -0.182090
-vn -0.891198 0.415463 -0.182090
-vn 0.580016 0.054810 0.812759
-vn -0.580016 0.054810 0.812759
-vn 0.000000 -0.268264 0.963345
-vn -0.402511 0.206162 0.891898
-vn 0.000000 -0.226878 0.973923
-vn 0.402511 0.206162 0.891898
-vn -0.516199 -0.553914 0.653236
-vn 0.516199 -0.553914 0.653236
-vn -0.119916 -0.908937 0.399316
-vn 0.119916 -0.908937 0.399316
-vn 0.000000 -0.887952 0.459936
-vn 0.058412 -0.644601 0.762284
-vn -0.058412 -0.644601 0.762284
-vn 0.492074 0.235614 -0.838063
-vn 0.000000 0.195615 -0.980681
-vn -0.492074 0.235614 -0.838063
-vn -0.000000 0.273872 0.961766
-vn 0.344103 0.222708 0.912138
-vn -0.344102 0.222708 0.912138
-vn 0.719197 0.133150 -0.681929
-vn 0.000000 0.065985 -0.997821
-vn -0.719197 0.133150 -0.681929
-vn 0.668956 -0.014745 -0.743156
-vn 0.000000 -0.032757 -0.999463
-vn -0.668956 -0.014745 -0.743156
-vn 0.975578 0.219554 0.006671
-vn 0.994425 0.081162 -0.067318
-vn -0.994425 0.081162 -0.067318
-vn -0.975578 0.219554 0.006671
-vn 0.638303 0.377425 0.670909
-vn 0.747388 0.230230 0.623222
-vn -0.747388 0.230230 0.623222
-vn -0.638303 0.377425 0.670910
-vn 0.000000 0.528774 0.848763
-vn -0.000000 0.322298 0.946638
-vn 0.528080 -0.207068 -0.823562
-vn 0.000000 -0.319097 -0.947722
-vn -0.528080 -0.207068 -0.823562
-vn 0.994189 -0.081894 -0.069864
-vn -0.994189 -0.081894 -0.069864
-vn 0.617682 -0.328917 0.714341
-vn -0.617682 -0.328917 0.714341
-vn -0.000000 -0.506256 0.862383
-vn 0.000000 -0.904049 0.427430
-vn -0.187344 -0.738487 0.647719
-vn 0.187344 -0.738487 0.647719
-vn 0.000000 -0.743699 0.668514
-vn -0.328639 -0.706788 0.626456
-vn 0.328639 -0.706788 0.626456
-vn 0.750839 -0.643659 0.148134
-vn 0.392729 -0.306761 0.866984
-vn -0.750839 -0.643659 0.148134
-vn -0.392729 -0.306761 0.866984
-vn 0.466617 -0.366174 -0.805099
-vn -0.466617 -0.366174 -0.805099
-vn 0.291255 0.091029 0.952305
-vn -0.291255 0.091029 0.952305
-vn 0.440289 0.171705 -0.881285
-vn -0.440289 0.171705 -0.881285
-vn 0.401630 0.562448 0.722735
-vn -0.401630 0.562448 0.722735
-vn 0.493649 0.621543 -0.608272
-vn -0.493649 0.621543 -0.608272
-vn 0.542337 0.839538 0.032366
-vn -0.542337 0.839538 0.032366
-vn 0.353321 -0.265977 -0.896895
-vn -0.566764 -0.004103 -0.823870
-vn -0.998350 -0.012699 -0.056002
-vn 0.566764 -0.004103 -0.823870
-vn -0.353321 -0.265977 -0.896895
-vn 0.998350 -0.012699 -0.056002
-vn -0.640564 -0.055447 0.765900
-vn 0.640564 -0.055447 0.765900
-vn 0.463659 0.027785 0.885578
-vn -0.463659 0.027785 0.885578
-vn 0.459356 0.106885 -0.881798
-vn -0.459356 0.106885 -0.881798
-vn 0.983975 0.176509 -0.025258
-vn -0.983975 0.176509 -0.025258
-vn -0.997864 -0.012039 -0.064209
-vn -0.531289 0.052793 -0.845544
-vn -0.343769 -0.754972 -0.558426
-vn -0.680698 -0.731097 -0.046343
-vn 0.343769 -0.754972 -0.558426
-vn 0.531289 0.052793 -0.845544
-vn 0.997864 -0.012039 -0.064209
-vn 0.680698 -0.731097 -0.046343
-vn -0.680634 0.213713 0.700760
-vn -0.497313 -0.823460 0.273118
-vn 0.680634 0.213713 0.700760
-vn 0.497313 -0.823460 0.273118
-vn 0.475574 0.140793 -0.868335
-vn 0.327914 -0.749152 -0.575538
-vn -0.327914 -0.749152 -0.575538
-vn -0.475574 0.140793 -0.868335
-vn 0.933232 0.333165 -0.134458
-vn 0.509093 -0.818854 -0.265146
-vn -0.509092 -0.818854 -0.265146
-vn -0.933232 0.333165 -0.134458
-vn 0.000000 -1.000000 -0.000000
-vn 0.000000 -1.000000 -0.000000
-vn 0.000000 -1.000000 0.000000
-vn 0.000000 -1.000000 -0.000000
-vn 0.000000 -1.000000 -0.000000
-vn 0.000000 -1.000000 -0.000000
-vn 0.196770 -0.904587 0.378159
-vn 0.000000 -1.000000 0.000000
-vn -0.196770 -0.904587 0.378159
-vn 0.000000 -1.000000 0.000000
-vn 0.000000 -1.000000 0.000000
-vn 0.000000 -1.000000 0.000000
-vn 0.000000 -1.000000 0.000000
-vn 0.000000 -1.000000 -0.000000
-vn 0.000000 -1.000000 0.000000
-vn 0.000000 -1.000000 -0.000000
-vn 0.000000 -1.000000 -0.000000
-vn 0.000000 -1.000000 0.000000
-vn -0.050425 0.069426 0.996312
-vn -0.003226 -0.713706 0.700438
-vn -0.565091 -0.651377 0.506341
-vn -0.680791 0.039622 0.731405
-vn 0.565091 -0.651377 0.506341
-vn 0.003226 -0.713706 0.700438
-vn 0.050425 0.069426 0.996312
-vn 0.680791 0.039623 0.731405
-vn 0.201984 -0.763224 -0.613752
-vn 0.234452 0.167793 -0.957537
-vn -0.003621 0.206385 -0.978464
-vn -0.153153 -0.737875 -0.657332
-vn 0.003621 0.206385 -0.978464
-vn -0.234452 0.167793 -0.957537
-vn -0.201984 -0.763224 -0.613752
-vn 0.153153 -0.737875 -0.657332
-vn 0.004481 0.733020 0.680192
-vn -0.504281 0.743614 0.439020
-vn -0.004481 0.733020 0.680192
-vn 0.504281 0.743614 0.439020
-vn 0.191818 0.802491 -0.564991
-vn 0.058015 0.839850 -0.539709
-vn -0.058015 0.839851 -0.539708
-vn -0.191818 0.802490 -0.564991
-vn 0.115672 0.991729 0.055624
-vn -0.120283 0.990389 -0.068284
-vn 0.120283 0.990388 -0.068284
-vn -0.115672 0.991729 0.055624
-vn 0.101218 -0.987721 0.119002
-vn -0.346816 -0.936754 0.047018
-vn -0.101218 -0.987721 0.119003
-vn 0.346816 -0.936754 0.047018
-vn -0.725211 -0.678356 0.117910
-vn -0.997939 -0.002895 -0.064107
-vn 0.725211 -0.678356 0.117910
-vn 0.997939 -0.002895 -0.064107
-vn -0.176340 0.273404 -0.945597
-vn -0.259339 -0.733185 -0.628635
-vn 0.176340 0.273404 -0.945597
-vn 0.259339 -0.733185 -0.628635
-vn -0.689157 0.723917 -0.031730
-vn 0.689157 0.723917 -0.031730
-vn -0.083010 0.868837 -0.488090
-vn 0.083010 0.868837 -0.488090
-vn -0.225456 0.967087 -0.117949
-vn 0.225456 0.967087 -0.117949
-vn -0.351740 -0.918120 0.182577
-vn 0.351740 -0.918120 0.182577
-vn -0.061312 0.329902 -0.942022
-vn -0.018740 -0.822995 -0.567740
-vn 0.018740 -0.822994 -0.567740
-vn 0.061312 0.329903 -0.942022
-vn -0.003671 0.890729 -0.454520
-vn 0.003671 0.890729 -0.454520
-vn -0.008022 0.994386 -0.105512
-vn 0.008022 0.994386 -0.105512
-vn 0.215031 0.914041 0.343936
-vn -0.215031 0.914040 0.343936
-vn 0.066004 -0.954108 0.292100
-vn -0.066004 -0.954108 0.292100
-vn 0.258887 -0.889903 0.375566
-vn -0.258887 -0.889903 0.375566
-vn -0.554039 -0.289150 0.780662
-vn 0.190478 -0.296099 0.935972
-vn 0.313601 0.256382 0.914288
-vn -0.661764 0.258851 0.703608
-vn -0.313601 0.256382 0.914288
-vn -0.190478 -0.296099 0.935972
-vn 0.554039 -0.289150 0.780662
-vn 0.661764 0.258851 0.703608
-vn 0.153203 0.607989 0.779024
-vn -0.459781 0.606745 0.648431
-vn -0.153203 0.607989 0.779024
-vn 0.459781 0.606745 0.648431
-vn 0.023478 -0.997617 0.064878
-vn 0.014134 -0.758258 0.651802
-vn -0.014134 -0.758258 0.651802
-vn -0.023478 -0.997617 0.064878
-vn 0.020403 -0.995067 0.097090
-vn -0.020403 -0.995066 0.097091
-vn 0.013822 -0.744873 -0.667063
-vn -0.013822 -0.744873 -0.667063
-vn 0.043421 -0.732381 -0.679509
-vn 0.004284 -0.996551 -0.082873
-vn -0.004284 -0.996551 -0.082873
-vn -0.043421 -0.732381 -0.679509
-vn 0.117341 -0.753779 -0.646567
-vn -0.117341 -0.753779 -0.646567
-vn 0.008738 0.081462 0.996638
-vn -0.008738 0.081462 0.996638
-vn -0.000561 0.158386 -0.987377
-vn 0.076600 0.153645 -0.985153
-vn -0.076600 0.153645 -0.985153
-vn 0.000561 0.158386 -0.987377
-vn 0.186385 0.161973 -0.969033
-vn -0.186385 0.161972 -0.969033
-vn 0.015305 0.746237 0.665504
-vn -0.015305 0.746237 0.665504
-vn 0.005141 0.782209 -0.622995
-vn 0.058793 0.750242 -0.658544
-vn -0.058793 0.750242 -0.658544
-vn -0.005141 0.782209 -0.622996
-vn 0.137257 0.776772 -0.614642
-vn -0.137257 0.776772 -0.614643
-vn 0.072984 0.996622 0.037655
-vn -0.072984 0.996622 0.037655
-vn 0.015455 0.998816 0.046123
-vn 0.042358 0.978187 -0.203362
-vn -0.042358 0.978187 -0.203362
-vn -0.015455 0.998816 0.046122
-vn 0.488236 0.733469 0.472915
-vn 0.891629 0.016633 0.452461
-vn -0.488236 0.733469 0.472915
-vn -0.891629 0.016633 0.452461
-vn 0.524087 -0.720989 0.453330
-vn -0.524087 -0.720989 0.453330
-vn -0.663827 0.727742 0.172409
-vn 0.663827 0.727742 0.172410
-vn -0.700297 -0.691693 0.176476
-vn 0.700298 -0.691693 0.176477
-vn -0.998776 0.032935 -0.036898
-vn 0.998776 0.032935 -0.036897
-vn 0.643249 0.741848 0.189454
-vn 0.965642 -0.106820 0.236907
-vn 0.634653 -0.203306 0.745575
-vn -0.965642 -0.106820 0.236907
-vn -0.643249 0.741848 0.189454
-vn -0.634653 -0.203306 0.745575
-vn 0.591669 -0.805373 0.036099
-vn -0.591669 -0.805373 0.036098
-vn -0.677928 0.691614 -0.249166
-vn 0.677928 0.691614 -0.249166
-vn -0.657072 -0.690112 -0.303318
-vn 0.657072 -0.690112 -0.303318
-vn -0.945672 -0.009422 -0.324986
-vn 0.945672 -0.009422 -0.324986
-vn 0.191409 0.188690 0.963203
-vn 0.292977 0.659943 0.691838
-vn -0.292977 0.659943 0.691838
-vn -0.191409 0.188689 0.963203
-vn 0.218555 0.742831 -0.632800
-vn 0.088620 0.273511 -0.957778
-vn -0.088620 0.273511 -0.957778
-vn -0.218555 0.742831 -0.632800
-vn 0.343313 0.938604 0.034055
-vn -0.343313 0.938604 0.034055
-vn 0.087755 0.101377 0.990970
-vn 0.061426 0.719672 0.691592
-vn -0.061426 0.719671 0.691592
-vn -0.087755 0.101377 0.990970
-vn -0.003843 0.815337 -0.578975
-vn -0.007932 0.160959 -0.986929
-vn 0.007932 0.160959 -0.986929
-vn 0.003843 0.815336 -0.578975
-vn 0.011867 0.996055 0.087944
-vn -0.011867 0.996055 0.087944
-vn -0.561332 0.179769 -0.807831
-vn -0.996213 0.076955 0.040478
-vn 0.996213 0.076955 0.040478
-vn 0.561332 0.179769 -0.807831
-vn -0.626748 0.197974 0.753653
-vn 0.626748 0.197974 0.753653
-vn 0.355638 0.331108 0.874008
-vn -0.355638 0.331108 0.874008
-vn -0.444539 0.178930 0.877707
-vn 0.377511 0.301857 0.875424
-vn -0.377511 0.301857 0.875424
-vn 0.444539 0.178930 0.877707
-vn 0.530841 0.308814 -0.789204
-vn -0.530841 0.308814 -0.789204
-vn 0.917899 0.395361 0.033927
-vn -0.917899 0.395361 0.033927
-vn 0.909854 0.339575 0.238443
-vn -0.909854 0.339575 0.238443
-vn -0.595987 0.067957 0.800113
-vn 0.595987 0.067957 0.800113
-vn 0.413765 0.217019 0.884139
-vn -0.413765 0.217019 0.884139
-vn 0.401652 0.420296 0.813650
-vn -0.401652 0.420296 0.813650
-vn 0.958139 0.283256 0.041665
-vn -0.958139 0.283256 0.041665
-vn 0.267678 0.376013 0.887109
-vn 0.370685 -0.452806 0.810900
-vn -0.267678 0.376013 0.887109
-vn -0.370685 -0.452806 0.810900
-vn -0.426887 -0.454314 0.781899
-vn 0.426887 -0.454314 0.781899
-vn -0.372113 0.349773 0.859762
-vn 0.372113 0.349773 0.859762
-vn 0.917533 0.363732 0.160723
-vn 0.884807 -0.464738 0.033685
-vn -0.917533 0.363732 0.160723
-vn -0.884807 -0.464738 0.033685
-vn 0.451298 -0.463163 0.762765
-vn -0.451299 -0.463163 0.762765
-vn 0.560300 0.363777 0.744130
-vn -0.560300 0.363777 0.744130
-vn 0.750662 0.199460 0.629859
-vn 0.632575 0.252733 0.732103
-vn -0.632576 0.252733 0.732103
-vn -0.750662 0.199460 0.629859
-vn 0.838439 0.219919 0.498653
-vn 0.924581 -0.049180 0.377799
-vn -0.924581 -0.049180 0.377799
-vn -0.838439 0.219919 0.498653
-vn 0.698458 0.192676 0.689226
-vn 0.824475 -0.393624 0.406573
-vn -0.698458 0.192676 0.689226
-vn -0.824474 -0.393625 0.406573
-vn 0.795491 0.302745 0.524918
-vn 0.727670 0.239850 0.642626
-vn 0.570398 0.596969 0.564158
-vn -0.727670 0.239850 0.642626
-vn -0.795491 0.302745 0.524918
-vn -0.570398 0.596969 0.564158
-vn 0.267996 0.726915 0.632276
-vn -0.267996 0.726915 0.632276
-vn 0.649310 0.636673 -0.415986
-vn 0.737501 0.600139 -0.309718
-vn 0.924063 0.163220 -0.345640
-vn 0.830798 0.194885 -0.521340
-vn -0.924063 0.163220 -0.345640
-vn -0.737501 0.600139 -0.309718
-vn -0.649310 0.636673 -0.415986
-vn -0.830798 0.194885 -0.521340
-vn 0.968168 -0.192417 -0.160083
-vn 0.894937 -0.205209 -0.396203
-vn -0.968168 -0.192417 -0.160083
-vn -0.894937 -0.205209 -0.396204
-vn 0.902016 -0.403980 0.152212
-vn 0.888797 -0.457010 -0.034372
-vn -0.902016 -0.403980 0.152212
-vn -0.888797 -0.457010 -0.034372
-vn 0.765066 -0.517746 0.382901
-vn -0.765066 -0.517746 0.382901
-vn 0.248876 0.950007 0.188538
-vn 0.087679 0.956155 0.279427
-vn 0.395114 0.914523 -0.086795
-vn 0.393851 0.905568 -0.157567
-vn -0.395114 0.914523 -0.086795
-vn -0.087679 0.956155 0.279427
-vn -0.248876 0.950007 0.188538
-vn -0.393851 0.905568 -0.157567
-vn 0.864544 0.129865 0.485487
-vn 0.835631 0.071554 0.544610
-vn -0.835631 0.071554 0.544610
-vn -0.864545 0.129865 0.485487
-vn 0.730140 0.134324 0.669964
-vn -0.730140 0.134324 0.669964
-vn 0.660707 0.149409 0.735625
-vn -0.660707 0.149409 0.735625
-vn 0.625957 0.131251 0.768733
-vn -0.625957 0.131251 0.768733
-vn -0.035250 0.781876 0.622437
-vn 0.401966 0.215771 0.889869
-vn 0.395176 0.354520 0.847438
-vn -0.395176 0.354520 0.847438
-vn -0.401966 0.215771 0.889869
-vn 0.035250 0.781876 0.622437
-vn 0.655240 0.377324 0.654436
-vn -0.655240 0.377324 0.654436
-vn 0.396946 -0.653417 0.644577
-vn 0.083235 -0.282972 0.955510
-vn -0.171183 -0.615342 0.769448
-vn -0.017981 -0.774396 0.632445
-vn 0.171183 -0.615342 0.769448
-vn -0.083235 -0.282972 0.955510
-vn -0.396946 -0.653417 0.644577
-vn 0.017981 -0.774396 0.632445
-vn 0.235383 -0.968475 -0.081554
-vn 0.097490 -0.883822 0.457553
-vn -0.235383 -0.968475 -0.081554
-vn -0.097490 -0.883822 0.457553
-vn 0.798777 -0.570409 -0.191283
-vn 0.370248 -0.926419 0.068294
-vn -0.798777 -0.570409 -0.191283
-vn -0.370248 -0.926419 0.068294
-vn -0.109775 0.843923 0.525113
-vn -0.697527 0.670018 -0.254033
-vn -0.543472 0.802794 0.245273
-vn -0.532427 0.654971 0.536222
-vn 0.543472 0.802795 0.245273
-vn 0.697526 0.670018 -0.254034
-vn 0.109775 0.843924 0.525112
-vn 0.532426 0.654972 0.536222
-vn -0.119548 0.570597 0.812482
-vn -0.510180 0.374545 0.774231
-vn 0.119549 0.570597 0.812482
-vn 0.510180 0.374545 0.774230
-vn -0.565121 0.048345 0.823591
-vn -0.420354 -0.110891 0.900559
-vn 0.565121 0.048345 0.823591
-vn 0.420354 -0.110891 0.900558
-vn -0.083758 -0.855818 0.510450
-vn -0.256125 -0.221431 0.940940
-vn 0.083758 -0.855818 0.510450
-vn 0.256125 -0.221431 0.940940
-vn -0.145404 0.386464 -0.910771
-vn -0.139386 -0.856529 -0.496920
-vn 0.145404 0.386464 -0.910771
-vn 0.139386 -0.856529 -0.496920
-vn -0.150313 0.872573 0.464783
-vn 0.150313 0.872572 0.464783
-vn 0.025542 0.920918 -0.388918
-vn -0.025542 0.920918 -0.388918
-vn 0.013795 0.983356 -0.181166
-vn -0.013795 0.983356 -0.181166
-vn 0.006299 -0.952461 0.304597
-vn -0.006299 -0.952461 0.304597
-vn 0.697347 -0.201276 0.687891
-vn 0.864141 -0.485322 0.133128
-vn 0.989053 0.130129 -0.069580
-vn 0.780194 0.388087 0.490597
-vn -0.989053 0.130129 -0.069580
-vn -0.864141 -0.485322 0.133128
-vn -0.697348 -0.201276 0.687891
-vn -0.780194 0.388087 0.490597
-vn 0.829838 -0.223050 0.511485
-vn 0.842180 -0.513009 -0.165996
-vn 0.897318 0.151880 -0.414431
-vn 0.861737 0.435158 0.260856
-vn -0.897318 0.151879 -0.414431
-vn -0.842180 -0.513009 -0.165997
-vn -0.829838 -0.223050 0.511485
-vn -0.861738 0.435158 0.260856
-vn 0.916501 -0.239478 0.320432
-vn 0.779572 -0.506193 -0.368832
-vn 0.771146 0.173011 -0.612700
-vn 0.901249 0.426395 0.077060
-vn -0.771146 0.173011 -0.612700
-vn -0.779572 -0.506192 -0.368831
-vn -0.916500 -0.239478 0.320433
-vn -0.901249 0.426395 0.077060
-vn 0.071302 0.453554 -0.888372
-vn -0.131735 -0.927397 -0.350116
-vn 0.131735 -0.927397 -0.350117
-vn -0.071302 0.453554 -0.888372
-vn 0.674262 0.348940 -0.650855
-vn 0.449639 -0.867562 -0.212513
-vn -0.449639 -0.867561 -0.212513
-vn -0.674262 0.348941 -0.650855
-vn -0.163973 0.912046 0.375880
-vn 0.163973 0.912046 0.375880
-vn 0.331373 0.811800 0.480805
-vn -0.331373 0.811800 0.480804
-vn -0.376626 -0.314813 0.871232
-vn 0.376626 -0.314813 0.871232
-vn 0.179251 -0.331017 0.926443
-vn -0.179252 -0.331017 0.926443
-vn 0.004142 0.424105 -0.905604
-vn -0.067496 -0.924823 -0.374362
-vn 0.067496 -0.924823 -0.374361
-vn -0.004142 0.424105 -0.905604
-vn 0.431311 0.362018 -0.826386
-vn 0.333103 -0.873325 -0.355452
-vn -0.333103 -0.873325 -0.355452
-vn -0.431311 0.362018 -0.826386
-vn -0.022159 -0.353365 0.935223
-vn 0.046197 0.915849 0.398857
-vn -0.046197 0.915849 0.398857
-vn 0.022159 -0.353365 0.935223
-vn 0.311555 -0.353544 0.882009
-vn 0.374862 0.837396 0.397801
-vn -0.374862 0.837396 0.397801
-vn -0.311555 -0.353544 0.882009
-vn 0.073459 -0.411808 0.908305
-vn 0.027595 0.917632 0.396472
-vn -0.027595 0.917632 0.396472
-vn -0.073459 -0.411808 0.908305
-vn 0.456800 -0.380160 0.804247
-vn 0.426241 0.833674 0.351151
-vn -0.426241 0.833674 0.351151
-vn -0.456800 -0.380159 0.804247
-vn -0.287041 0.438239 -0.851795
-vn 0.287041 0.438239 -0.851795
-vn 0.154769 0.408153 -0.899699
-vn -0.154769 0.408153 -0.899699
-vn -0.250744 -0.879695 -0.404059
-vn 0.250744 -0.879695 -0.404059
-vn 0.176014 -0.880312 -0.440533
-vn -0.176014 -0.880312 -0.440533
-vn 0.760634 0.351820 0.545581
-vn 0.800100 0.288117 0.526146
-vn -0.800100 0.288117 0.526146
-vn -0.760634 0.351820 0.545581
-vn 0.776680 0.336584 0.532428
-vn 0.803473 0.298035 0.515369
-vn -0.803474 0.298035 0.515369
-vn -0.776680 0.336584 0.532428
-vn 0.000000 0.529930 -0.848042
-vn 0.784952 0.140196 -0.603486
-vn 0.520225 -0.729679 -0.443774
-vn -0.000000 -0.761130 -0.648599
-vn -0.520225 -0.729679 -0.443774
-vn -0.784952 0.140196 -0.603486
-vn 0.642172 -0.622700 0.447056
-vn 0.000000 -0.836868 0.547404
-vn -0.642173 -0.622700 0.447056
-vn 0.957384 0.210330 -0.197932
-vn -0.957384 0.210330 -0.197932
-vn 0.000000 0.729084 -0.684424
-vn 0.000000 -0.221613 -0.975135
-s 1
-g Mesh polySurface8
-usemtl Material_004
-f 1011//980 983//981 988//982 987//983
-f 988//982 984//984 1012//985 987//983
-f 985//986 1013//987 990//988 989//989
-f 990//988 1014//990 986//991 989//989
-f 983//981 985//986 989//989 988//982
-f 989//989 986//991 984//984 988//982
-f 980//992 1028//993 1027//994 982//995
-f 1027//994 1029//996 981//997 982//995
-f 1028//993 1011//980 987//983 1027//994
-f 987//983 1012//985 1029//996 1027//994
-f 1028//993 980//992 1019//998 1030//999
-f 1020//1000 981//997 1029//996 1031//1001
-f 1030//999 1009//1002 1011//980 1028//993
-f 1012//985 1010//1003 1031//1001 1029//996
-f 1019//998 1021//1004 1032//1005 1030//999
-f 1033//1006 1022//1007 1020//1000 1031//1001
-f 1007//1008 1009//1002 1030//999 1032//1005
-f 1031//1001 1010//1003 1008//1009 1033//1006
-f 1035//1010 1036//1011 1043//1012 1045//1013
-f 1044//1014 1037//1015 1035//1010 1045//1013
-f 1036//1011 1038//1016 1046//1017 1043//1012
-f 1047//1018 1039//1019 1037//1015 1044//1014
-f 1038//1016 1040//1020 1048//1021 1046//1017
-f 1049//1022 1041//1023 1039//1019 1047//1018
-f 1040//1020 1042//1024 1050//1025 1048//1021
-f 1050//1025 1042//1024 1041//1023 1049//1022
-f 1045//1013 1043//1012 1052//1026 1051//1027
-f 1053//1028 1044//1014 1045//1013 1051//1027
-f 1043//1012 1046//1017 1054//1029 1052//1026
-f 1055//1030 1047//1018 1044//1014 1053//1028
-f 1046//1017 1048//1021 1056//1031 1054//1029
-f 1057//1032 1049//1022 1047//1018 1055//1030
-f 1048//1021 1050//1025 1058//1033 1056//1031
-f 1058//1033 1050//1025 1049//1022 1057//1032
-f 1021//1004 1034//1034 1059//1035 1023//1036
-f 1059//1035 1034//1034 1022//1007 1024//1037
-f 1005//1038 1007//1008 1032//1005 1060//1039
-f 1033//1006 1008//1009 1006//1040 1061//1041
-f 1021//1004 1023//1036 1060//1039 1032//1005
-f 1061//1041 1024//1037 1022//1007 1033//1006
-f 990//988 1013//987 1015//1042 1062//1043
-f 1016//1044 1014//990 990//988 1062//1043
-f 1021//1004 1063//1045 1065//1046 1034//1034
-f 1065//1046 1064//1047 1022//1007 1034//1034
-f 1063//1045 1036//1011 1035//1010 1065//1046
-f 1035//1010 1037//1015 1064//1047 1065//1046
-f 1019//998 1066//1048 1063//1045 1021//1004
-f 1064//1047 1067//1049 1020//1000 1022//1007
-f 1066//1048 1038//1016 1036//1011 1063//1045
-f 1037//1015 1039//1019 1067//1049 1064//1047
-f 980//992 1068//1050 1066//1048 1019//998
-f 1067//1049 1069//1051 981//997 1020//1000
-f 1068//1050 1040//1020 1038//1016 1066//1048
-f 1039//1019 1041//1023 1069//1051 1067//1049
-f 1068//1050 980//992 982//995 1070//1052
-f 982//995 981//997 1069//1051 1070//1052
-f 1070//1052 1042//1024 1040//1020 1068//1050
-f 1041//1023 1042//1024 1070//1052 1069//1051
-f 1023//1036 1059//1035 1071//1053 1025//1054
-f 1071//1053 1059//1035 1024//1037 1026//1055
-f 1015//1042 1073//1056 1072//1057 1062//1043
-f 1072//1057 1074//1058 1016//1044 1062//1043
-f 1015//1042 1017//1059 1075//1060 1073//1056
-f 1076//1061 1018//1062 1016//1044 1074//1058
-f 1072//1057 1073//1056 1078//1063 1077//1064
-f 1079//1065 1074//1058 1072//1057 1077//1064
-f 1073//1056 1075//1060 1080//1066 1078//1063
-f 1081//1067 1076//1061 1074//1058 1079//1065
-f 999//1068 1080//1066 1075//1060 1017//1059
-f 1076//1061 1081//1067 1000//1069 1018//1062
-f 1023//1036 1025//1054 1082//1070 1060//1039
-f 1083//1071 1026//1055 1024//1037 1061//1041
-f 1003//1072 1005//1038 1060//1039 1082//1070
-f 1061//1041 1006//1040 1004//1073 1083//1071
-f 1001//1074 1003//1072 1082//1070 1084//1075
-f 1083//1071 1004//1073 1002//1076 1085//1077
-f 999//1068 1001//1074 1084//1075 1080//1066
-f 1085//1077 1002//1076 1000//1069 1081//1067
-f 1082//1070 1025//1054 1086//1078 1088//1079
-f 1087//1080 1026//1055 1083//1071 1089//1081
-f 1080//1066 1084//1075 1092//1082 1090//1083
-f 1093//1084 1085//1077 1081//1067 1091//1085
-f 1082//1070 1088//1079 1092//1082 1084//1075
-f 1093//1084 1089//1081 1083//1071 1085//1077
-f 1077//1064 1078//1063 1095//1086 1094//1087
-f 1096//1088 1079//1065 1077//1064 1094//1087
-f 1078//1063 1080//1066 1090//1083 1095//1086
-f 1091//1085 1081//1067 1079//1065 1096//1088
-f 1088//1079 1086//1078 1097//1089 1099//1090
-f 1098//1091 1087//1080 1089//1081 1100//1092
-f 1090//1083 1092//1082 1101//1093 1103//1094
-f 1102//1095 1093//1084 1091//1085 1104//1096
-f 1092//1082 1088//1079 1099//1090 1101//1093
-f 1100//1092 1089//1081 1093//1084 1102//1095
-f 1094//1087 1095//1086 1105//1097 1107//1098
-f 1106//1099 1096//1088 1094//1087 1107//1098
-f 1095//1086 1090//1083 1103//1094 1105//1097
-f 1104//1096 1091//1085 1096//1088 1106//1099
-f 1101//1093 1099//1090 1109//1100 1110//1101
-f 1109//1100 1100//1092 1102//1095 1110//1101
-f 1103//1094 1111//1102 1107//1098 1105//1097
-f 1107//1098 1111//1102 1104//1096 1106//1099
-f 1101//1093 1110//1101 1111//1102 1103//1094
-f 1111//1102 1110//1101 1102//1095 1104//1096
-f 1097//1089 1108//1103 1109//1100 1099//1090
-f 1109//1100 1108//1103 1098//1091 1100//1092
-f 1108//1103 1097//1089 1112//1104 1114//1105
-f 1113//1106 1098//1091 1108//1103 1114//1105
-f 1097//1089 1086//1078 1115//1107 1112//1104
-f 1116//1108 1087//1080 1098//1091 1113//1106
-f 1086//1078 1025//1054 1117//1109 1115//1107
-f 1118//1110 1026//1055 1087//1080 1116//1108
-f 1025//1054 1071//1053 1119//1111 1117//1109
-f 1119//1111 1071//1053 1026//1055 1118//1110
-f 1051//1027 1052//1026 1121//1112 1120//1113
-f 1122//1114 1053//1028 1051//1027 1120//1113
-f 1052//1026 1054//1029 1123//1115 1121//1112
-f 1124//1116 1055//1030 1053//1028 1122//1114
-f 1054//1029 1056//1031 1125//1117 1123//1115
-f 1126//1118 1057//1032 1055//1030 1124//1116
-f 1056//1031 1058//1033 1127//1119 1125//1117
-f 1127//1119 1058//1033 1057//1032 1126//1118
-f 1120//1113 1121//1112 1128//1120
-f 1129//1121 1122//1114 1120//1113
-f 1121//1112 1123//1115 1128//1120
-f 1129//1121 1124//1116 1122//1114
-f 1123//1115 1125//1117 1128//1120
-f 1129//1121 1126//1118 1124//1116
-f 1125//1117 1127//1119 1128//1120
-f 1129//1121 1127//1119 1126//1118
-f 1114//1105 1112//1104 1130//1122 1132//1123
-f 1131//1124 1113//1106 1114//1105 1132//1123
-f 1117//1109 1119//1111 1135//1125 1133//1126
-f 1135//1125 1119//1111 1118//1110 1134//1127
-f 1130//1122 1144//1128 1146//1129 1132//1123
-f 1146//1129 1145//1130 1131//1124 1132//1123
-f 1144//1128 1136//1131 1138//1132 1146//1129
-f 1138//1132 1137//1133 1145//1130 1146//1129
-f 1147//1134 1139//1135 1136//1131 1144//1128
-f 1137//1133 1140//1136 1148//1137 1145//1130
-f 1149//1138 1141//1139 1139//1135 1147//1134
-f 1140//1136 1142//1140 1150//1141 1148//1137
-f 1135//1125 1151//1142 1149//1138 1133//1126
-f 1150//1141 1151//1142 1135//1125 1134//1127
-f 1151//1142 1143//1143 1141//1139 1149//1138
-f 1142//1140 1143//1143 1151//1142 1150//1141
-f 1138//1132 1136//1131 1152//1144 1154//1145
-f 1153//1146 1137//1133 1138//1132 1154//1145
-f 1136//1131 1139//1135 1155//1147 1152//1144
-f 1156//1148 1140//1136 1137//1133 1153//1146
-f 1139//1135 1141//1139 1157//1149 1155//1147
-f 1158//1150 1142//1140 1140//1136 1156//1148
-f 1141//1139 1143//1143 1159//1151 1157//1149
-f 1159//1151 1143//1143 1142//1140 1158//1150
-f 1157//1149 1159//1151 1165//1152 1163//1153
-f 1165//1152 1159//1151 1158//1150 1164//1154
-f 1163//1153 1165//1152 1168//1155 1166//1156
-f 1168//1155 1165//1152 1164//1154 1167//1157
-f 1149//1138 1147//1134 1171//1158 1169//1159
-f 1172//1160 1148//1137 1150//1141 1170//1161
-f 1147//1134 1144//1128 1173//1162 1171//1158
-f 1174//1163 1145//1130 1148//1137 1172//1160
-f 1133//1126 1149//1138 1169//1159 1175//1164
-f 1170//1161 1150//1141 1134//1127 1176//1165
-f 1144//1128 1130//1122 1177//1166 1173//1162
-f 1178//1167 1131//1124 1145//1130 1174//1163
-f 1117//1109 1133//1126 1175//1164 1179//1168
-f 1176//1165 1134//1127 1118//1110 1180//1169
-f 1130//1122 1112//1104 1183//1170 1177//1166
-f 1184//1171 1113//1106 1131//1124 1178//1167
-f 1115//1107 1117//1109 1179//1168 1181//1172
-f 1180//1169 1118//1110 1116//1108 1182//1173
-f 1112//1104 1115//1107 1181//1172 1183//1170
-f 1182//1173 1116//1108 1113//1106 1184//1171
-f 1166//1156 1160//1174 1193//1175 1189//1176
-f 1194//1177 1161//1178 1167//1157 1190//1179
-f 1163//1153 1166//1156 1189//1176 1191//1180
-f 1190//1179 1167//1157 1164//1154 1192//1181
-f 1157//1149 1163//1153 1191//1180 1195//1182
-f 1192//1181 1164//1154 1158//1150 1196//1183
-f 1160//1174 1152//1144 1199//1184 1193//1175
-f 1200//1185 1153//1146 1161//1178 1194//1177
-f 1155//1147 1157//1149 1195//1182 1197//1186
-f 1196//1183 1158//1150 1156//1148 1198//1187
-f 1152//1144 1155//1147 1197//1186 1199//1184
-f 1198//1187 1156//1148 1153//1146 1200//1185
-f 1207//1188 1209//1189 1221//1190 1219//1191
-f 1222//1192 1210//1193 1208//1194 1220//1195
-f 1211//1196 1207//1188 1219//1191 1223//1197
-f 1220//1195 1208//1194 1212//1198 1224//1199
-f 1209//1189 1215//1200 1227//1201 1221//1190
-f 1228//1202 1216//1203 1210//1193 1222//1192
-f 1215//1200 1217//1204 1229//1205 1227//1201
-f 1230//1206 1218//1207 1216//1203 1228//1202
-f 1219//1191 1221//1190 1233//1208 1231//1209
-f 1234//1210 1222//1192 1220//1195 1232//1211
-f 1223//1197 1219//1191 1231//1209 1235//1212
-f 1232//1211 1220//1195 1224//1199 1236//1213
-f 1225//1214 1223//1197 1235//1212 1237//1215
-f 1236//1213 1224//1199 1226//1216 1238//1217
-f 1221//1190 1227//1201 1239//1218 1233//1208
-f 1240//1219 1228//1202 1222//1192 1234//1210
-f 1229//1205 1225//1214 1237//1215 1241//1220
-f 1238//1217 1226//1216 1230//1206 1242//1221
-f 1227//1201 1229//1205 1241//1220 1239//1218
-f 1242//1221 1230//1206 1228//1202 1240//1219
-f 1239//1218 1245//1222 1243//1223 1233//1208
-f 1244//1224 1246//1225 1240//1219 1234//1210
-f 1245//1222 1237//1215 1235//1212 1243//1223
-f 1236//1213 1238//1217 1246//1225 1244//1224
-f 1231//1209 1233//1208 1243//1223 1235//1212
-f 1244//1224 1234//1210 1232//1211 1236//1213
-f 1241//1220 1237//1215 1245//1222 1239//1218
-f 1246//1225 1238//1217 1242//1221 1240//1219
-f 1251//1226 1247//1227 1259//1228 1261//1229
-f 1260//1230 1248//1231 1252//1232 1262//1233
-f 1249//1234 1253//1235 1265//1236 1263//1237
-f 1266//1238 1254//1239 1250//1240 1264//1241
-f 1255//1242 1251//1226 1261//1229 1267//1243
-f 1262//1233 1252//1232 1256//1244 1268//1245
-f 1253//1235 1257//1246 1269//1247 1265//1236
-f 1270//1248 1258//1249 1254//1239 1266//1238
-f 1257//1246 1187//1250 1271//1251 1269//1247
-f 1272//1252 1188//1253 1258//1249 1270//1248
-f 1187//1250 1255//1242 1267//1243 1271//1251
-f 1268//1245 1256//1244 1188//1253 1272//1252
-f 1185//1254 1249//1234 1263//1237 1273//1255
-f 1264//1241 1250//1240 1186//1256 1274//1257
-f 1247//1227 1185//1254 1273//1255 1259//1228
-f 1274//1257 1186//1256 1248//1231 1260//1230
-f 1261//1229 1259//1228 1275//1258 1277//1259
-f 1276//1260 1260//1230 1262//1233 1278//1261
-f 1263//1237 1265//1236 1281//1262 1279//1263
-f 1282//1264 1266//1238 1264//1241 1280//1265
-f 1267//1243 1261//1229 1277//1259 1283//1266
-f 1278//1261 1262//1233 1268//1245 1284//1267
-f 1265//1236 1269//1247 1285//1268 1281//1262
-f 1286//1269 1270//1248 1266//1238 1282//1264
-f 1269//1247 1271//1251 1287//1270 1285//1268
-f 1288//1271 1272//1252 1270//1248 1286//1269
-f 1271//1251 1267//1243 1283//1266 1287//1270
-f 1284//1267 1268//1245 1272//1252 1288//1271
-f 1273//1255 1263//1237 1279//1263 1289//1272
-f 1280//1265 1264//1241 1274//1257 1290//1273
-f 1259//1228 1273//1255 1289//1272 1275//1258
-f 1290//1273 1274//1257 1260//1230 1276//1260
-f 1281//1262 1295//1274 1297//1275 1279//1263
-f 1298//1276 1296//1277 1282//1264 1280//1265
-f 1285//1268 1301//1278 1295//1274 1281//1262
-f 1296//1277 1302//1279 1286//1269 1282//1264
-f 1287//1270 1303//1280 1301//1278 1285//1268
-f 1302//1279 1304//1281 1288//1271 1286//1269
-f 1283//1266 1299//1282 1303//1280 1287//1270
-f 1304//1281 1300//1283 1284//1267 1288//1271
-f 1279//1263 1297//1275 1305//1284 1289//1272
-f 1306//1285 1298//1276 1280//1265 1290//1273
-f 1289//1272 1305//1284 1291//1286 1275//1258
-f 1292//1287 1306//1285 1290//1273 1276//1260
-f 1317//1288 1311//1289 1309//1290 1315//1291
-f 1310//1292 1312//1293 1318//1294 1316//1295
-f 1315//1291 1309//1290 1307//1296 1313//1297
-f 1308//1298 1310//1292 1316//1295 1314//1299
-f 1171//1158 1319//1300 1353//1301 1169//1159
-f 1354//1302 1320//1303 1172//1160 1170//1161
-f 1323//1304 1185//1254 1247//1227 1353//1301
-f 1248//1231 1186//1256 1324//1305 1354//1302
-f 1173//1162 1347//1306 1319//1300 1171//1158
-f 1320//1303 1348//1307 1174//1163 1172//1160
-f 1347//1306 1349//1308 1321//1309 1319//1300
-f 1322//1310 1350//1311 1348//1307 1320//1303
-f 1349//1308 1351//1312 1323//1304 1321//1309
-f 1324//1305 1352//1313 1350//1311 1322//1310
-f 1351//1312 1249//1234 1185//1254 1323//1304
-f 1186//1256 1250//1240 1352//1313 1324//1305
-f 1353//1301 1247//1227 1251//1226 1345//1314
-f 1252//1232 1248//1231 1354//1302 1346//1315
-f 1339//1316 1341//1317 1349//1308 1347//1306
-f 1350//1311 1342//1318 1340//1319 1348//1307
-f 1341//1317 1343//1320 1351//1312 1349//1308
-f 1352//1313 1344//1321 1342//1318 1350//1311
-f 1343//1320 1253//1235 1249//1234 1351//1312
-f 1250//1240 1254//1239 1344//1321 1352//1313
-f 1345//1314 1251//1226 1255//1242 1337//1322
-f 1256//1244 1252//1232 1346//1315 1338//1323
-f 1331//1324 1333//1325 1341//1317 1339//1316
-f 1342//1318 1334//1326 1332//1327 1340//1319
-f 1333//1325 1335//1328 1343//1320 1341//1317
-f 1344//1321 1336//1329 1334//1326 1342//1318
-f 1335//1328 1257//1246 1253//1235 1343//1320
-f 1254//1239 1258//1249 1336//1329 1344//1321
-f 1337//1322 1255//1242 1187//1250 1329//1330
-f 1188//1253 1256//1244 1338//1323 1330//1331
-f 1325//1332 1327//1333 1333//1325 1331//1324
-f 1334//1326 1328//1334 1326//1335 1332//1327
-f 1327//1333 1329//1330 1335//1328 1333//1325
-f 1336//1329 1330//1331 1328//1334 1334//1326
-f 1329//1330 1187//1250 1257//1246 1335//1328
-f 1258//1249 1188//1253 1330//1331 1336//1329
-f 1355//1336 1307//1296 1309//1290 1357//1337
-f 1310//1292 1308//1298 1356//1338 1358//1339
-f 1357//1337 1309//1290 1311//1289 1359//1340
-f 1312//1293 1310//1292 1358//1339 1360//1341
-f 1361//1342 1313//1297 1307//1296 1355//1336
-f 1308//1298 1314//1299 1362//1343 1356//1338
-f 1359//1340 1311//1289 1317//1288 1363//1344
-f 1318//1294 1312//1293 1360//1341 1364//1345
-f 1365//1346 1315//1291 1313//1297 1361//1342
-f 1314//1299 1316//1295 1366//1347 1362//1343
-f 1363//1344 1317//1288 1315//1291 1365//1346
-f 1316//1295 1318//1294 1364//1345 1366//1347
-f 1299//1282 1367//1348 1369//1349 1293//1350
-f 1370//1351 1368//1352 1300//1283 1294//1353
-f 1367//1348 1355//1336 1357//1337 1369//1349
-f 1358//1339 1356//1338 1368//1352 1370//1351
-f 1293//1350 1369//1349 1371//1354 1291//1286
-f 1372//1355 1370//1351 1294//1353 1292//1287
-f 1369//1349 1357//1337 1359//1340 1371//1354
-f 1360//1341 1358//1339 1370//1351 1372//1355
-f 1283//1266 1373//1356 1367//1348 1299//1282
-f 1368//1352 1374//1357 1284//1267 1300//1283
-f 1373//1356 1361//1342 1355//1336 1367//1348
-f 1356//1338 1362//1343 1374//1357 1368//1352
-f 1291//1286 1371//1354 1375//1358 1275//1258
-f 1376//1359 1372//1355 1292//1287 1276//1260
-f 1371//1354 1359//1340 1363//1344 1375//1358
-f 1364//1345 1360//1341 1372//1355 1376//1359
-f 1277//1259 1377//1360 1373//1356 1283//1266
-f 1374//1357 1378//1361 1278//1261 1284//1267
-f 1377//1360 1365//1346 1361//1342 1373//1356
-f 1362//1343 1366//1347 1378//1361 1374//1357
-f 1275//1258 1375//1358 1377//1360 1277//1259
-f 1378//1361 1376//1359 1276//1260 1278//1261
-f 1375//1358 1363//1344 1365//1346 1377//1360
-f 1366//1347 1364//1345 1376//1359 1378//1361
-f 1325//1332 1337//1322 1329//1330 1327//1333
-f 1330//1331 1338//1323 1326//1335 1328//1334
-f 1319//1300 1321//1309 1323//1304 1353//1301
-f 1324//1305 1322//1310 1320//1303 1354//1302
-f 1175//1164 1385//1362 1387//1363 1179//1168
-f 1388//1364 1386//1365 1176//1165 1180//1169
-f 1183//1170 1381//1366 1383//1367 1177//1166
-f 1384//1368 1382//1369 1184//1171 1178//1167
-f 1179//1168 1387//1363 1379//1370 1181//1172
-f 1380//1371 1388//1364 1180//1169 1182//1173
-f 1181//1172 1379//1370 1381//1366 1183//1170
-f 1382//1369 1380//1371 1182//1173 1184//1171
-f 1385//1362 1395//1372 1397//1373 1387//1363
-f 1398//1374 1396//1375 1386//1365 1388//1364
-f 1395//1372 1345//1314 1337//1322 1397//1373
-f 1338//1323 1346//1315 1396//1375 1398//1374
-f 1381//1366 1391//1376 1393//1377 1383//1367
-f 1394//1378 1392//1379 1382//1369 1384//1368
-f 1391//1376 1331//1324 1339//1316 1393//1377
-f 1340//1319 1332//1327 1392//1379 1394//1378
-f 1387//1363 1397//1373 1389//1380 1379//1370
-f 1390//1381 1398//1374 1388//1364 1380//1371
-f 1397//1373 1337//1322 1325//1332 1389//1380
-f 1326//1335 1338//1323 1398//1374 1390//1381
-f 1379//1370 1389//1380 1391//1376 1381//1366
-f 1392//1379 1390//1381 1380//1371 1382//1369
-f 1389//1380 1325//1332 1331//1324 1391//1376
-f 1332//1327 1326//1335 1390//1381 1392//1379
-f 1175//1164 1169//1159 1395//1372 1385//1362
-f 1396//1375 1170//1161 1176//1165 1386//1365
-f 1345//1314 1395//1372 1169//1159 1353//1301
-f 1170//1161 1396//1375 1346//1315 1354//1302
-f 1177//1166 1383//1367 1393//1377 1173//1162
-f 1394//1378 1384//1368 1178//1167 1174//1163
-f 1339//1316 1347//1306 1173//1162 1393//1377
-f 1174//1163 1348//1307 1340//1319 1394//1378
-f 1193//1175 1399//1382 1401//1383 1189//1176
-f 1402//1384 1400//1385 1194//1177 1190//1179
-f 1189//1176 1401//1383 1403//1386 1191//1180
-f 1404//1387 1402//1384 1190//1179 1192//1181
-f 1191//1180 1403//1386 1405//1388 1195//1182
-f 1406//1389 1404//1387 1192//1181 1196//1183
-f 1403//1386 1201//1390 1203//1391 1405//1388
-f 1204//1392 1202//1393 1404//1387 1406//1389
-f 1199//1184 1407//1394 1399//1382 1193//1175
-f 1400//1385 1408//1395 1200//1185 1194//1177
-f 1195//1182 1405//1388 1409//1396 1197//1186
-f 1410//1397 1406//1389 1196//1183 1198//1187
-f 1405//1388 1203//1391 1205//1398 1409//1396
-f 1206//1399 1204//1392 1406//1389 1410//1397
-f 1197//1186 1409//1396 1407//1394 1199//1184
-f 1408//1395 1410//1397 1198//1187 1200//1185
-f 1399//1382 1209//1189 1207//1188 1401//1383
-f 1208//1194 1210//1193 1400//1385 1402//1384
-f 1401//1383 1207//1188 1211//1196 1411//1400
-f 1212//1198 1208//1194 1402//1384 1412//1401
-f 1201//1390 1411//1400 1413//1402 1203//1391
-f 1414//1403 1412//1401 1202//1393 1204//1392
-f 1411//1400 1211//1196 1213//1404 1413//1402
-f 1214//1405 1212//1198 1412//1401 1414//1403
-f 1407//1394 1215//1200 1209//1189 1399//1382
-f 1210//1193 1216//1203 1408//1395 1400//1385
-f 1203//1391 1413//1402 1415//1406 1205//1398
-f 1416//1407 1414//1403 1204//1392 1206//1399
-f 1413//1402 1213//1404 1217//1204 1415//1406
-f 1218//1207 1214//1405 1414//1403 1416//1407
-f 1415//1406 1217//1204 1215//1200 1407//1394
-f 1216//1203 1218//1207 1416//1407 1408//1395
-f 1409//1396 1205//1398 1415//1406 1407//1394
-f 1416//1407 1206//1399 1410//1397 1408//1395
-f 1403//1386 1401//1383 1411//1400 1201//1390
-f 1412//1401 1402//1384 1404//1387 1202//1393
-f 1225//1214 1213//1404 1421//1408 1417//1409
-f 1422//1410 1214//1405 1226//1216 1418//1411
-f 1223//1197 1225//1214 1417//1409 1419//1412
-f 1418//1411 1226//1216 1224//1199 1420//1413
-f 1211//1196 1223//1197 1419//1412 1423//1414
-f 1420//1413 1224//1199 1212//1198 1424//1415
-f 1213//1404 1211//1196 1423//1414 1421//1408
-f 1424//1415 1212//1198 1214//1405 1422//1410
-f 1421//1408 1423//1414 1419//1412 1417//1409
-f 1420//1413 1424//1415 1422//1410 1418//1411
-f 1229//1205 1217//1204 1429//1416 1425//1417
-f 1430//1418 1218//1207 1230//1206 1426//1419
-f 1225//1214 1229//1205 1425//1417 1427//1420
-f 1426//1419 1230//1206 1226//1216 1428//1421
-f 1213//1404 1225//1214 1427//1420 1431//1422
-f 1428//1421 1226//1216 1214//1405 1432//1423
-f 1217//1204 1213//1404 1431//1422 1429//1416
-f 1432//1423 1214//1405 1218//1207 1430//1418
-f 1429//1416 1431//1422 1427//1420 1425//1417
-f 1428//1421 1432//1423 1430//1418 1426//1419
-f 983//981 1433//1424 1435//1425 985//986
-f 1436//1426 1434//1427 984//984 986//991
-f 1433//1424 995//1428 997//1429 1435//1425
-f 998//1430 996//1431 1434//1427 1436//1426
-f 1447//1432 1009//1002 1007//1008 1445//1433
-f 1008//1009 1010//1003 1448//1434 1446//1435
-f 993//1436 1449//1437 1447//1432 991//1438
-f 1448//1434 1450//1439 994//1440 992//1441
-f 1449//1437 1011//980 1009//1002 1447//1432
-f 1010//1003 1012//985 1450//1439 1448//1434
-f 995//1428 1433//1424 1449//1437 993//1436
-f 1450//1439 1434//1427 996//1431 994//1440
-f 1433//1424 983//981 1011//980 1449//1437
-f 1012//985 984//984 1434//1427 1450//1439
-f 1451//1442 1013//987 985//986 1435//1425
-f 986//991 1014//990 1452//1443 1436//1426
-f 1437//1444 1457//1445 1459//1446 1439//1447
-f 1460//1448 1458//1449 1438//1450 1440//1451
-f 1439//1447 1459//1446 1461//1452 1441//1453
-f 1462//1454 1460//1448 1440//1451 1442//1455
-f 1441//1453 1461//1452 1463//1456 1443//1457
-f 1464//1458 1462//1454 1442//1455 1444//1459
-f 1443//1457 1463//1456 1465//1460 1445//1433
-f 1466//1461 1464//1458 1444//1459 1446//1435
-f 1453//1462 1469//1463 1471//1464 1455//1465
-f 1472//1466 1470//1467 1454//1468 1456//1469
-f 1455//1465 1471//1464 1457//1445 1437//1444
-f 1458//1449 1472//1466 1456//1469 1438//1450
-f 1457//1445 1473//1470 1475//1471 1459//1446
-f 1476//1472 1474//1473 1458//1449 1460//1448
-f 1459//1446 1475//1471 1477//1474 1461//1452
-f 1478//1475 1476//1472 1460//1448 1462//1454
-f 1461//1452 1477//1474 1479//1476 1463//1456
-f 1480//1477 1478//1475 1462//1454 1464//1458
-f 1463//1456 1479//1476 1481//1478 1465//1460
-f 1482//1479 1480//1477 1464//1458 1466//1461
-f 1467//1480 1483//1481 1485//1482 1469//1463
-f 1486//1483 1484//1484 1468//1485 1470//1467
-f 1469//1463 1485//1482 1487//1486 1471//1464
-f 1488//1487 1486//1483 1470//1467 1472//1466
-f 1471//1464 1487//1486 1473//1470 1457//1445
-f 1474//1473 1488//1487 1472//1466 1458//1449
-f 1445//1433 1465//1460 1481//1478 1447//1432
-f 1482//1479 1466//1461 1446//1435 1448//1434
-f 1505//1488 1519//1489 1503//1490 1489//1491
-f 1504//1492 1520//1493 1506//1494 1490//1495
-f 1507//1496 1505//1488 1489//1491 1491//1497
-f 1490//1495 1506//1494 1508//1498 1492//1499
-f 1509//1500 1507//1496 1491//1497 1493//1501
-f 1492//1499 1508//1498 1510//1502 1494//1503
-f 1513//1504 1511//1505 1495//1506 1497//1507
-f 1496//1508 1512//1509 1514//1510 1498//1511
-f 1515//1512 1513//1504 1497//1507 1499//1513
-f 1498//1511 1514//1510 1516//1514 1500//1515
-f 1517//1516 1515//1512 1499//1513 1501//1517
-f 1500//1515 1516//1514 1518//1518 1502//1519
-f 1519//1489 1517//1516 1501//1517 1503//1490
-f 1502//1519 1518//1518 1520//1493 1504//1492
-f 1495//1506 1481//1478 1479//1476 1497//1507
-f 1480//1477 1482//1479 1496//1508 1498//1511
-f 1497//1507 1479//1476 1477//1474 1499//1513
-f 1478//1475 1480//1477 1498//1511 1500//1515
-f 1499//1513 1477//1474 1475//1471 1501//1517
-f 1476//1472 1478//1475 1500//1515 1502//1519
-f 1501//1517 1475//1471 1473//1470 1503//1490
-f 1474//1473 1476//1472 1502//1519 1504//1492
-f 1503//1490 1473//1470 1487//1486 1489//1491
-f 1488//1487 1474//1473 1504//1492 1490//1495
-f 1489//1491 1487//1486 1485//1482 1491//1497
-f 1486//1483 1488//1487 1490//1495 1492//1499
-f 1491//1497 1485//1482 1483//1481 1493//1501
-f 1484//1484 1486//1483 1492//1499 1494//1503
-f 991//1438 1447//1432 1481//1478 1495//1506
-f 1482//1479 1448//1434 992//1441 1496//1508
-f 991//1438 1495//1506 1511//1505
-f 1512//1509 1496//1508 992//1441
-f 1005//1038 1443//1457 1445//1433 1007//1008
-f 1446//1435 1444//1459 1006//1040 1008//1009
-f 1003//1072 1441//1453 1443//1457 1005//1038
-f 1444//1459 1442//1455 1004//1073 1006//1040
-f 1001//1074 1439//1447 1441//1453 1003//1072
-f 1442//1455 1440//1451 1002//1076 1004//1073
-f 999//1068 1437//1444 1439//1447 1001//1074
-f 1440//1451 1438//1450 1000//1069 1002//1076
-f 999//1068 1017//1059 1455//1465 1437//1444
-f 1456//1469 1018//1062 1000//1069 1438//1450
-f 1015//1042 1453//1462 1455//1465 1017//1059
-f 1456//1469 1454//1468 1016//1044 1018//1062
-f 1013//987 1451//1442 1453//1462 1015//1042
-f 1454//1468 1452//1443 1014//990 1016//1044
-f 997//1429 1493//1501 1483//1481 1435//1425
-f 1484//1484 1494//1503 998//1430 1436//1426
-f 997//1429 1509//1500 1493//1501
-f 1494//1503 1510//1502 998//1430
-f 1120//1113 1128//1120 1127//1119
-f 1127//1119 1129//1121 1120//1113
-f 1467//1480 1469//1463 1453//1462 1451//1442
-f 1454//1468 1470//1467 1468//1485 1452//1443
-f 1451//1442 1435//1425 1483//1481 1467//1480
-f 1484//1484 1436//1426 1452//1443 1468//1485
-f 1293//1350 1291//1286 1529//1520 1521//1521
-f 1530//1522 1292//1287 1294//1353 1522//1523
-f 1297//1275 1295//1274 1523//1524 1525//1525
-f 1524//1526 1296//1277 1298//1276 1526//1527
-f 1299//1282 1293//1350 1521//1521 1527//1528
-f 1522//1523 1294//1353 1300//1283 1528//1529
-f 1295//1274 1301//1278 1533//1530 1523//1524
-f 1534//1531 1302//1279 1296//1277 1524//1526
-f 1301//1278 1303//1280 1535//1532 1533//1530
-f 1536//1533 1304//1281 1302//1279 1534//1531
-f 1303//1280 1299//1282 1527//1528 1535//1532
-f 1528//1529 1300//1283 1304//1281 1536//1533
-f 1305//1284 1297//1275 1525//1525 1531//1534
-f 1526//1527 1298//1276 1306//1285 1532//1535
-f 1291//1286 1305//1284 1531//1534 1529//1520
-f 1532//1535 1306//1285 1292//1287 1530//1522
-f 1543//1536 1537//1537 1539//1538 1541//1539
-f 1540//1540 1538//1541 1544//1542 1542//1543
-f 1549//1544 1545//1545 1547//1546 1551//1547
-f 1548//1548 1546//1549 1550//1550 1552//1551
-f 1553//1552 1559//1553 1557//1554 1555//1555
-f 1558//1556 1560//1557 1554//1558 1556//1559
-f 1535//1532 1561//1560 1565//1561 1529//1520
-f 1566//1562 1562//1563 1536//1533 1530//1522
-f 1561//1560 1563//1564 1567//1565 1565//1561
-f 1568//1566 1564//1567 1562//1563 1566//1562
-f 1563//1564 1539//1538 1537//1537 1567//1565
-f 1538//1541 1540//1540 1564//1567 1568//1566
-f 1527//1528 1569//1568 1561//1560 1535//1532
-f 1562//1563 1570//1569 1528//1529 1536//1533
-f 1569//1568 1571//1570 1563//1564 1561//1560
-f 1564//1567 1572//1571 1570//1569 1562//1563
-f 1571//1570 1541//1539 1539//1538 1563//1564
-f 1540//1540 1542//1543 1572//1571 1564//1567
-f 1529//1520 1565//1561 1573//1572 1521//1521
-f 1574//1573 1566//1562 1530//1522 1522//1523
-f 1565//1561 1567//1565 1575//1574 1573//1572
-f 1576//1575 1568//1566 1566//1562 1574//1573
-f 1567//1565 1537//1537 1543//1536 1575//1574
-f 1544//1542 1538//1541 1568//1566 1576//1575
-f 1521//1521 1573//1572 1569//1568 1527//1528
-f 1570//1569 1574//1573 1522//1523 1528//1529
-f 1573//1572 1575//1574 1571//1570 1569//1568
-f 1572//1571 1576//1575 1574//1573 1570//1569
-f 1575//1574 1543//1536 1541//1539 1571//1570
-f 1542//1543 1544//1542 1576//1575 1572//1571
-f 1533//1530 1577//1576 1581//1577 1531//1534
-f 1582//1578 1578//1579 1534//1531 1532//1535
-f 1577//1576 1579//1580 1583//1581 1581//1577
-f 1584//1582 1580//1583 1578//1579 1582//1578
-f 1579//1580 1547//1546 1545//1545 1583//1581
-f 1546//1549 1548//1548 1580//1583 1584//1582
-f 1529//1520 1585//1584 1589//1585 1535//1532
-f 1590//1586 1586//1587 1530//1522 1536//1533
-f 1585//1584 1587//1588 1591//1589 1589//1585
-f 1592//1590 1588//1591 1586//1587 1590//1586
-f 1587//1588 1549//1544 1551//1547 1591//1589
-f 1552//1551 1550//1550 1588//1591 1592//1590
-f 1535//1532 1589//1585 1577//1576 1533//1530
-f 1578//1579 1590//1586 1536//1533 1534//1531
-f 1589//1585 1591//1589 1579//1580 1577//1576
-f 1580//1583 1592//1590 1590//1586 1578//1579
-f 1591//1589 1551//1547 1547//1546 1579//1580
-f 1548//1548 1552//1551 1592//1590 1580//1583
-f 1531//1534 1581//1577 1585//1584 1529//1520
-f 1586//1587 1582//1578 1532//1535 1530//1522
-f 1581//1577 1583//1581 1587//1588 1585//1584
-f 1588//1591 1584//1582 1582//1578 1586//1587
-f 1583//1581 1545//1545 1549//1544 1587//1588
-f 1550//1550 1546//1549 1584//1582 1588//1591
-f 1531//1534 1593//1592 1597//1593 1533//1530
-f 1598//1594 1594//1595 1532//1535 1534//1531
-f 1593//1592 1595//1596 1599//1597 1597//1593
-f 1600//1598 1596//1599 1594//1595 1598//1594
-f 1595//1596 1553//1552 1555//1555 1599//1597
-f 1556//1559 1554//1558 1596//1599 1600//1598
-f 1533//1530 1597//1593 1601//1600 1523//1524
-f 1602//1601 1598//1594 1534//1531 1524//1526
-f 1597//1593 1599//1597 1603//1602 1601//1600
-f 1604//1603 1600//1598 1598//1594 1602//1601
-f 1599//1597 1555//1555 1557//1554 1603//1602
-f 1558//1556 1556//1559 1600//1598 1604//1603
-f 1525//1525 1605//1604 1593//1592 1531//1534
-f 1594//1595 1606//1605 1526//1527 1532//1535
-f 1605//1604 1607//1606 1595//1596 1593//1592
-f 1596//1599 1608//1607 1606//1605 1594//1595
-f 1607//1606 1559//1553 1553//1552 1595//1596
-f 1554//1558 1560//1557 1608//1607 1596//1599
-f 1523//1524 1601//1600 1605//1604 1525//1525
-f 1606//1605 1602//1601 1524//1526 1526//1527
-f 1601//1600 1603//1602 1607//1606 1605//1604
-f 1608//1607 1604//1603 1602//1601 1606//1605
-f 1603//1602 1557//1554 1559//1553 1607//1606
-f 1560//1557 1558//1556 1604//1603 1608//1607
-f 991//1438 1613//1608 1609//1609 993//1436
-f 1610//1610 1614//1611 992//1441 994//1440
-f 1613//1608 1615//1612 1611//1613 1609//1609
-f 1612//1614 1616//1615 1614//1611 1610//1610
-f 1615//1612 1519//1489 1505//1488 1611//1613
-f 1506//1494 1520//1493 1616//1615 1612//1614
-f 1515//1512 1517//1516 1519//1489 1615//1612
-f 1520//1493 1518//1518 1516//1514 1616//1615
-f 991//1438 1511//1505 1513//1504 1613//1608
-f 1514//1510 1512//1509 992//1441 1614//1611
-f 1513//1504 1515//1512 1615//1612 1613//1608
-f 1616//1615 1516//1514 1514//1510 1614//1611
-f 993//1436 1609//1609 997//1429 995//1428
-f 998//1430 1610//1610 994//1440 996//1431
-f 1505//1488 1507//1496 1509//1500 1611//1613
-f 1510//1502 1508//1498 1506//1494 1612//1614
-f 997//1429 1609//1609 1611//1613 1509//1500
-f 1612//1614 1610//1610 998//1430 1510//1502
-f 1622//1616 1620//1617 1618//1618 1617//1619
-f 1619//1620 1621//1621 1622//1616 1617//1619
-f 1627//1622 1626//1623 1617//1619 1618//1618
-f 1617//1619 1626//1623 1628//1624 1619//1620
-f 1624//1625 1627//1622 1618//1618 1620//1617
-f 1619//1620 1628//1624 1625//1626 1621//1621
-f 1623//1627 1624//1625 1620//1617 1622//1616
-f 1621//1621 1625//1626 1623//1627 1622//1616
-f 1166//1156 1168//1155 1626//1623 1627//1622
-f 1626//1623 1168//1155 1167//1157 1628//1624
-f 1152//1144 1160//1174 1162//1628 1154//1145
-f 1162//1628 1161//1178 1153//1146 1154//1145
-f 1160//1174 1624//1625 1623//1627 1162//1628
-f 1623//1627 1625//1626 1161//1178 1162//1628
-f 1160//1174 1166//1156 1627//1622 1624//1625
-f 1628//1624 1167//1157 1161//1178 1625//1626
-g default
-v -0.894364 4.268147 -0.552617
-v -0.601310 1.892161 -0.474644
-v -0.441164 4.578611 -0.601308
-v -0.246260 4.585340 -0.666746
-v -0.114655 1.613449 -0.329068
-v 0.000000 1.617775 -0.330704
-v 0.000000 4.628910 -0.720798
-v -0.860199 1.916128 -0.291443
-v -1.162404 2.310402 -0.216621
-v -1.445977 2.718793 -0.222594
-v -1.466933 3.124115 -0.266589
-v -1.298121 3.535574 -0.586840
-v -1.151166 3.968589 -0.630799
-v -0.656268 4.474073 -0.537240
-v -0.418997 1.780795 -0.498279
-v -0.353489 2.752426 -1.105280
-v -0.567702 3.118009 -1.151921
-v -0.360120 3.655849 -1.156421
-v 0.000000 3.695522 -1.202781
-v 0.000000 2.707793 -1.130510
-v -0.552387 4.155087 -0.822221
-v -0.584306 2.313640 -0.800945
-v -1.060325 3.083802 -0.892737
-v -0.463911 3.386929 -1.154171
-v -0.424893 2.874287 -1.120827
-v -0.496298 2.996148 -1.136374
-v -0.472734 3.006776 -1.139966
-v -0.538650 3.119271 -1.154318
-v -0.403863 2.882462 -1.125615
-v -0.329083 2.775878 -1.111263
-v -0.338159 3.627589 -1.158473
-v -0.433973 3.385249 -1.156395
-v 0.000000 2.737630 -1.134553
-v 0.000000 3.667168 -1.201269
-v -0.334391 3.562862 -1.212043
-v 0.000000 3.589972 -1.250787
-v -0.320465 2.804258 -1.166779
-v 0.000000 2.787661 -1.191361
-v -0.468119 3.158023 -1.215750
-v -0.405080 3.379167 -1.217310
-v -0.377556 2.893494 -1.185059
-v -0.436417 3.014798 -1.202140
-v 0.000000 3.201811 -1.290968
-v 0.000000 3.402325 -1.281649
-v 0.000000 2.905322 -1.236671
-v 0.000000 3.042980 -1.270768
-v -0.587727 3.088680 -1.121426
-v -0.521557 2.975751 -1.107019
-v -0.455386 2.862822 -1.092612
-v -0.389216 2.749894 -1.078204
-v -0.880726 1.983561 -0.260701
-v -0.603115 2.343270 -0.796177
-v -1.421045 3.094338 -0.300989
-v -1.044242 3.056981 -0.881240
-v -1.401626 2.718726 -0.260218
-v -1.138838 2.340270 -0.254683
-v -0.907057 2.000102 -0.321363
-v -1.152539 2.339356 -0.315639
-v -1.402467 2.699293 -0.320904
-v -1.420936 3.056525 -0.359679
-v -0.643031 2.342210 -0.830636
-v -0.439599 2.728935 -1.098862
-v -0.628396 3.051143 -1.139969
-v -1.062572 3.020995 -0.911536
-v -0.502531 2.836338 -1.112565
-v -0.565464 2.943741 -1.126267
-v -0.871596 2.513484 -0.727531
-v -1.037863 2.771827 -0.749290
-v -0.714473 4.348294 -0.585307
-v -1.391599 3.173524 -0.436515
-v -1.052518 3.143625 -0.905403
-v -0.886329 4.202788 -0.605435
-v -1.242415 3.534956 -0.614999
-v -1.106430 3.934802 -0.653035
-v -0.594924 4.112163 -0.841818
-v -0.410942 3.656769 -1.142132
-v -0.603221 3.163483 -1.147697
-v -0.496086 3.410296 -1.179317
-v -0.478799 3.673793 -1.162596
-v -0.557249 3.446701 -1.196858
-v -0.655960 3.219294 -1.167724
-v -0.751877 4.349762 -0.612856
-v -0.655602 4.115324 -0.880211
-v -1.382349 3.228545 -0.512461
-v -1.069929 3.200997 -0.944481
-v -1.119602 3.929965 -0.711957
-v -0.906156 4.184588 -0.668252
-v -1.244896 3.561558 -0.676911
-v -0.996913 3.534435 -0.917662
-v -0.180060 3.675685 -1.179601
-v -0.176744 2.730109 -1.117895
-v -0.158615 2.756754 -1.122908
-v -0.163153 3.647378 -1.179871
-v -0.179576 3.582266 -1.234919
-v -0.172214 2.793604 -1.182249
-v -0.215515 3.390077 -1.258412
-v -0.246970 3.181401 -1.263679
-v -0.200795 2.901681 -1.219290
-v -0.231909 3.030675 -1.245981
-v 0.000000 2.652369 -1.108443
-v -0.181435 2.672098 -1.102334
-v -0.337691 2.691828 -1.088923
-v -0.541753 2.303905 -0.815248
-v -0.240721 1.741142 -0.457370
-v -0.395606 1.832826 -0.496262
-v -0.556786 1.931283 -0.502431
-v -0.785665 1.952471 -0.400701
-v -0.185173 1.680984 -0.420441
-v -0.535252 1.901656 -0.580384
-v -0.245870 1.727567 -0.539128
-v -0.141332 1.676054 -0.505866
-v 0.000000 1.679556 -0.516862
-v -0.763972 1.928624 -0.480108
-v -0.387680 1.811511 -0.574736
-v -0.334654 2.597991 -1.117361
-v -0.521488 2.242819 -0.866792
-v -0.191590 2.579928 -1.129640
-v 0.000000 2.561864 -1.135234
-v -0.285006 2.177692 -0.888730
-v 0.000000 2.112565 -0.867228
-v 0.000000 3.745822 -1.167307
-v -0.160569 3.727858 -1.146315
-v -0.323629 3.709894 -1.125324
-v -0.570891 4.446858 -0.559973
-v -0.501240 4.171970 -0.828488
-v -0.220519 4.551630 -0.681881
-v 0.000000 4.591086 -0.730829
-v -0.398505 4.518962 -0.606153
-v -0.373343 4.522595 -0.701857
-v -0.225734 4.553530 -0.776799
-v 0.000000 4.587370 -0.818781
-v -0.553368 4.482742 -0.623283
-v -0.314169 3.768378 -1.194095
-v -0.475044 4.243768 -0.894897
-v -0.174316 3.783786 -1.212099
-v 0.000000 3.799193 -1.230103
-v -0.269740 4.286171 -0.956644
-v 0.000000 4.321912 -0.969626
-v 0.000000 1.643304 -0.460300
-v -0.479479 4.869820 -0.744927
-v -0.275163 4.946365 -0.954242
-v -0.072600 1.527242 -0.324440
-v 0.000000 1.535203 -0.327644
-v 0.000000 5.014968 -1.014430
-v -0.898310 1.876836 -0.177654
-v -1.222305 2.255669 -0.205358
-v -1.526845 2.690965 -0.212472
-v -1.551464 3.137764 -0.247453
-v -1.435115 3.522739 -0.614016
-v -1.287395 4.054248 -0.788321
-v -0.968233 4.382786 -0.575087
-v -0.659630 4.807325 -0.610875
-v -0.491151 4.907478 -0.505309
-v -0.207162 4.975423 -0.710121
-v -0.063231 1.523003 -0.191506
-v 0.000000 1.530438 -0.199272
-v 0.000000 5.035666 -0.765170
-v -1.030829 1.763474 -0.060408
-v -1.305855 2.205369 -0.050705
-v -1.534914 2.688713 -0.087988
-v -1.559499 3.139965 -0.125060
-v -1.431708 3.553847 -0.467891
-v -1.278676 4.072967 -0.627298
-v -0.977141 4.376593 -0.388540
-v -0.631038 4.877497 -0.437345
-v -0.519436 4.495228 -0.326146
-v -0.239866 4.578342 -0.576986
-v 0.000000 4.647167 -0.658392
-v -1.064440 2.314215 0.016113
-v -1.122839 2.730615 -0.006406
-v -1.132661 3.132987 -0.090046
-v -1.044797 3.512056 -0.397286
-v -0.576710 4.533762 -0.145339
-v 0.000000 1.662369 0.264066
-v 0.000000 4.409410 0.501791
-v 0.000000 1.761989 0.543465
-v 0.000000 1.896002 0.849942
-v 0.000000 2.825269 1.079886
-v 0.000000 3.498520 0.740326
-v 0.000000 4.401259 0.560592
-v -0.075157 1.653299 0.248868
-v -0.463266 4.447080 0.246541
-v -0.220743 4.434513 0.357432
-v -0.203639 1.760402 0.488618
-v -0.777611 1.867949 0.535043
-v -0.414898 1.914068 0.665136
-v -0.697963 2.740074 0.781336
-v -0.325313 2.797833 0.978176
-v -0.636333 3.529989 0.423308
-v -0.237559 3.502991 0.716126
-v -0.545244 3.919782 0.424609
-v -0.536370 4.158761 0.371050
-v -0.535874 4.495495 0.329818
-v -0.233006 4.426234 0.470615
-v -0.484104 4.608221 -0.327453
-v -0.229203 4.672074 -0.552975
-v -0.079726 1.575226 -0.171817
-v 0.000000 1.580857 -0.179749
-v 0.000000 4.731133 -0.603687
-v -0.962596 1.911706 -0.016738
-v -1.120579 2.286526 -0.017571
-v -1.192731 2.711494 -0.027523
-v -1.206906 3.136870 -0.081855
-v -1.102845 3.536490 -0.409735
-v -0.985386 4.019822 -0.558741
-v -0.807870 4.318978 -0.361011
-v -0.555419 4.620965 -0.273160
-v 0.000000 1.613965 -0.105292
-v -0.047770 1.611138 -0.092408
-v -0.960971 1.887755 0.202799
-v -0.595045 1.849410 -0.398692
-v -0.285561 1.664060 -0.349371
-v -0.193666 1.623826 -0.299570
-v -0.811316 1.870345 -0.253187
-v -0.124693 1.575049 -0.271382
-v -0.834543 1.841924 -0.164113
-v -0.116394 1.570258 -0.196539
-v -0.923514 1.776398 -0.047729
-v -0.742962 1.825710 0.462902
-v -0.498361 1.890616 0.578522
-v -0.110757 1.645410 0.219499
-v -0.269786 1.728498 0.439919
-v -0.116676 1.597817 -0.140776
-v -0.908747 1.868926 0.009517
-v -0.099325 1.610218 -0.074108
-v -0.912572 1.842171 0.173244
-v -0.442496 1.754897 -0.419981
-v -0.575128 1.986038 -0.388754
-v -0.180010 1.715653 -0.278763
-v -0.270033 1.791023 -0.335277
-v -0.798950 2.007299 -0.238718
-v -0.120693 1.658517 -0.247659
-v -0.838267 1.970413 -0.129454
-v -0.115571 1.647423 -0.200095
-v -0.912436 1.907180 -0.057492
-v -0.488198 2.033300 0.560952
-v -0.736645 1.967176 0.450486
-v -0.263502 1.857369 0.419674
-v -0.110311 1.773043 0.208438
-v -0.117132 1.680169 -0.152889
-v -0.898991 2.001902 -0.013893
-v -0.101467 1.731409 -0.082316
-v -0.897337 1.982730 0.174061
-v -0.417112 1.886196 -0.407429
-v -0.602376 2.020186 -0.463414
-v -0.242077 1.810447 -0.409406
-v -0.111919 1.748384 -0.320884
-v -0.862922 2.043559 -0.284192
-v -0.078138 1.680590 -0.272221
-v -0.901331 2.005241 -0.173224
-v -0.060991 1.660181 -0.186732
-v -0.992902 1.935633 -0.068197
-v -0.779527 1.996574 0.521804
-v -0.480317 2.065906 0.651401
-v -0.073622 1.787246 0.242725
-v -0.232371 1.877480 0.484166
-v -0.077615 1.711110 -0.167532
-v -0.965645 2.039247 -0.000176
-v -0.045409 1.746130 -0.090091
-v -0.964007 2.015889 0.197797
-v -0.418639 1.911581 -0.486463
-v -0.930876 3.980696 -0.379037
-v -0.824033 4.216189 -0.299441
-v -0.772494 4.316643 -0.237270
-v -0.944988 3.779257 -0.331559
-v -0.782904 4.375865 -0.128176
-v -0.903746 3.717081 -0.241430
-v -0.694681 3.929798 0.206244
-v -0.685980 4.106970 0.195626
-v -0.785283 4.320419 0.086162
-v -0.827591 3.656310 0.170557
-v -0.840877 3.904824 -0.437225
-v -0.706884 4.135632 -0.360016
-v -0.640743 4.240607 -0.300506
-v -0.882744 3.668248 -0.415907
-v -0.616157 4.265114 -0.163285
-v -0.838675 3.583704 -0.316795
-v -0.550890 3.848572 0.200350
-v -0.522186 4.060302 0.164144
-v -0.598689 4.221076 0.046650
-v -0.713151 3.645145 0.163261
-v -0.691721 4.165222 -0.453008
-v -0.854157 3.888806 -0.542361
-v -0.596587 4.305797 -0.365237
-v -0.931917 3.604791 -0.489530
-v -0.560254 4.331712 -0.176240
-v -0.875145 3.500219 -0.349580
-v -0.474253 4.070497 0.219494
-v -0.512073 3.817972 0.260218
-v -0.543668 4.272953 0.084412
-v -0.656498 3.561714 0.234422
-v -1.016130 2.199926 0.220190
-v -0.964396 1.986696 0.313532
-v -0.872716 1.976793 0.479654
-v 0.000000 2.720541 1.207575
-v -0.362212 2.686535 1.078628
-v -0.763640 2.647703 0.814621
-v -1.070605 2.624241 0.204687
-v 0.000000 2.063571 1.033100
-v -0.409832 2.064489 0.875264
-v -0.935927 2.203910 0.599761
-v 0.000000 2.446927 1.213294
-v -0.381925 2.453651 1.104526
-v 0.000000 3.435851 0.841994
-v -0.274342 3.441097 0.813600
-v -0.593421 3.429113 0.569790
-v -0.973242 3.426804 -0.115422
-v 0.000000 2.957179 1.157222
-v -0.351031 2.923172 1.028275
-v -0.729526 2.873911 0.763822
-v -1.055085 2.844625 0.164860
-v -1.024304 3.237006 0.040993
-v 0.000000 3.289926 0.975277
-v -0.315143 3.289073 0.868887
-v -0.642833 3.237947 0.631082
-v -0.467087 3.979361 0.479808
-v -0.454547 4.099108 0.445174
-v -0.440282 4.385054 0.391415
-v -0.519069 3.631913 0.539097
-v 0.000000 4.210371 0.591084
-v -0.189118 4.238502 0.585909
-v 0.000000 3.601468 0.781014
-v -0.250078 3.606714 0.752620
-v 0.000000 4.071157 0.671095
-v 0.000000 3.948291 0.704318
-v -0.200703 3.956627 0.675776
-v -0.193622 4.081429 0.642029
-v -0.239866 4.473637 -0.576986
-v 0.000000 4.542462 -0.658392
-v -0.519436 4.390522 -0.326146
-v -0.463266 4.342374 0.246541
-v -0.220743 4.329808 0.357432
-v 0.000000 4.304704 0.501791
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.651759 0.557496
-vt 0.663910 0.496406
-vt 0.951876 0.496406
-vt 0.917804 0.667695
-vt 0.917804 0.667695
-vt 0.820777 0.812908
-vt 0.617154 0.609285
-vt 0.651759 0.557496
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.820777 0.812908
-vt 0.675565 0.909935
-vt 0.565365 0.643890
-vt 0.617154 0.609285
-vt 0.675565 0.909935
-vt 0.504275 0.944007
-vt 0.504275 0.656041
-vt 0.565365 0.643890
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.504275 0.944007
-vt 0.332986 0.909935
-vt 0.443186 0.643890
-vt 0.504275 0.656041
-vt 0.332986 0.909935
-vt 0.187774 0.812908
-vt 0.391397 0.609285
-vt 0.443186 0.643890
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.187774 0.812908
-vt 0.090746 0.667696
-vt 0.356792 0.557496
-vt 0.391397 0.609285
-vt 0.090746 0.667696
-vt 0.056675 0.496406
-vt 0.344641 0.496406
-vt 0.356792 0.557496
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.056675 0.496406
-vt 0.090746 0.325117
-vt 0.356792 0.435317
-vt 0.344641 0.496406
-vt 0.090746 0.325117
-vt 0.187774 0.179905
-vt 0.391397 0.383527
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.187774 0.179905
-vt 0.332986 0.082877
-vt 0.443186 0.348923
-vt 0.391397 0.383527
-vt 0.332986 0.082877
-vt 0.504275 0.048806
-vt 0.504275 0.336771
-vt 0.443186 0.348923
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.504275 0.048806
-vt 0.675565 0.082877
-vt 0.565365 0.348923
-vt 0.504275 0.336771
-vt 0.675565 0.082877
-vt 0.820777 0.179905
-vt 0.617154 0.383527
-vt 0.565365 0.348923
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.820777 0.179905
-vt 0.917804 0.325117
-vt 0.651759 0.435317
-vt 0.617154 0.383527
-vt 0.663910 0.496406
-vt 0.651759 0.435317
-vt 0.917804 0.325117
-vt 0.951876 0.496406
-vt 0.014996 0.011797
-vt 0.099840 0.011797
-vt 0.099840 0.096641
-vt 0.014996 0.096641
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.018782
-vt 0.135168 0.018782
-vt 0.135168 0.099656
-vt 0.054295 0.099656
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vt 1.000000 1.000000
-vt 0.000000 1.000000
-vt 0.000000 0.000000
-vt 1.000000 0.000000
-vn -0.234879 -0.249816 -0.939374
-vn -0.324716 -0.302580 -0.896106
-vn -0.434139 -0.307065 -0.846897
-vn -0.439845 -0.056930 -0.896267
-vn -0.330376 -0.307636 -0.892307
-vn -0.473222 -0.386709 -0.791528
-vn -0.312279 -0.277992 -0.908406
-vn -0.293554 -0.500133 -0.814673
-vn -0.097495 0.272756 -0.957131
-vn 0.038586 -0.018459 -0.999085
-vn -0.498816 0.179717 -0.847871
-vn -0.340736 0.268973 -0.900862
-vn -0.499851 -0.037283 -0.865309
-vn -0.552347 0.212611 -0.806046
-vn -0.444403 0.406358 -0.798360
-vn -0.364715 -0.642595 -0.673836
-vn -0.495119 -0.428711 -0.755688
-vn -0.478896 -0.306423 -0.822657
-vn -0.287941 -0.051232 -0.956277
-vn -0.108097 -0.187883 -0.976225
-vn -0.108094 -0.187884 -0.976225
-vn -0.078858 -0.372664 -0.924610
-vn -0.535833 -0.490887 -0.686960
-vn -0.623946 -0.293267 -0.724352
-vn -0.181821 -0.627765 -0.756870
-vn -0.534659 -0.583508 -0.611276
-vn -0.606803 -0.419325 -0.675246
-vn -0.581221 -0.327703 -0.744844
-vn -0.881061 -0.025386 -0.472321
-vn -0.672830 0.079310 -0.735533
-vn -0.393352 -0.189034 -0.899744
-vn -0.875316 -0.328690 -0.354661
-vn -0.384341 -0.254295 -0.887477
-vn -0.759888 -0.509100 -0.404213
-vn -0.461810 -0.811929 -0.357075
-vn -0.790574 -0.563128 -0.240582
-vn -0.891109 -0.362769 -0.272622
-vn -0.913976 0.144328 -0.379232
-vn 0.129684 -0.352931 -0.926619
-vn -0.092228 -0.687216 -0.720575
-vn -0.655506 0.195307 -0.729497
-vn -0.181412 0.127610 -0.975092
-vn -0.092211 -0.146738 -0.984868
-vn -0.057444 -0.198384 -0.978440
-vn -0.575964 -0.400833 -0.712459
-vn -0.587752 -0.399159 -0.703719
-vn 0.178599 0.311899 -0.933178
-vn 0.191784 -0.067482 -0.979115
-vn -0.129579 -0.298035 -0.945719
-vn -0.218054 0.555398 -0.802487
-vn -0.238766 0.213078 -0.947412
-vn -0.342102 0.771009 -0.537133
-vn 0.054660 0.652793 -0.755562
-vn -0.605976 -0.417071 -0.677381
-vn -0.918950 -0.102511 -0.380818
-vn 0.012355 0.186835 -0.982314
-vn 0.081804 0.013481 -0.996557
-vn -0.764239 0.458157 -0.453906
-vn -0.647401 0.671809 -0.359923
-vn -0.236432 -0.289118 -0.927637
-vn -0.915351 0.253543 -0.312807
-vn 0.087440 0.364907 -0.926929
-vn -0.138913 0.085820 -0.986579
-vn -0.254814 -0.237586 -0.937349
-vn -0.326504 0.857574 -0.397444
-vn -0.147306 0.570312 -0.808112
-vn -0.633152 -0.240098 -0.735847
-vn -0.934620 0.016433 -0.355267
-vn -0.776268 0.515837 -0.362381
-vn -0.717487 0.687658 -0.111078
-vn -0.890800 0.339496 -0.302023
-vn -0.570370 0.211237 -0.793761
-vn -0.092325 -0.213667 -0.972534
-vn -0.108234 -0.534000 -0.838528
-vn -0.087109 -0.221310 -0.971305
-vn -0.100641 -0.506608 -0.856282
-vn -0.153322 0.271612 -0.950115
-vn -0.152296 0.276379 -0.948905
-vn -0.147616 0.304380 -0.941043
-vn -0.136173 0.286659 -0.948306
-vn -0.157458 0.393543 -0.905721
-vn -0.123100 0.385264 -0.914559
-vn -0.114560 -0.618546 -0.777353
-vn -0.086732 -0.600929 -0.794582
-vn -0.108389 0.097421 -0.989324
-vn -0.156486 0.095059 -0.983095
-vn -0.156663 -0.040361 -0.986827
-vn -0.105547 -0.040995 -0.993569
-vn -0.077380 -0.290826 -0.953642
-vn -0.120524 -0.285190 -0.950863
-vn -0.092483 -0.178570 -0.979571
-vn -0.141028 -0.181359 -0.973252
-vn -0.063119 -0.001535 -0.998005
-vn -0.045731 -0.013870 -0.998858
-vn -0.405353 -0.064425 -0.911887
-vn -0.652799 -0.579785 -0.487548
-vn -0.754325 -0.128136 -0.643875
-vn -0.213810 -0.576653 -0.788515
-vn -0.210626 -0.865248 -0.454953
-vn -0.409087 -0.837998 -0.361119
-vn -0.369051 -0.739536 -0.562928
-vn -0.406952 -0.613512 -0.676752
-vn -0.365485 -0.842809 -0.395088
-vn -0.340119 -0.894324 -0.290695
-vn -0.619664 -0.679195 -0.393332
-vn -0.243988 -0.827018 -0.506470
-vn -0.457366 -0.855364 -0.243247
-vn -0.145333 -0.943209 -0.298722
-vn -0.110847 -0.831547 -0.544282
-vn -0.421102 -0.838373 -0.346128
-vn -0.598567 -0.315779 -0.736207
-vn -0.431897 -0.031597 -0.901370
-vn -0.079246 -0.114928 -0.990208
-vn -0.033357 -0.121769 -0.991998
-vn -0.142385 -0.568731 -0.810106
-vn -0.065462 -0.566223 -0.821649
-vn -0.113411 -0.129506 -0.985072
-vn -0.113090 -0.128654 -0.985220
-vn -0.426793 0.067296 -0.901842
-vn -0.654363 0.328410 -0.681143
-vn -0.539360 0.552153 -0.635780
-vn -0.334906 -0.251044 -0.908193
-vn -0.378959 0.672822 -0.635374
-vn -0.162115 -0.209132 -0.964356
-vn -0.281825 -0.260978 -0.923290
-vn -0.315681 0.716496 -0.622077
-vn -0.257751 0.765976 -0.588936
-vn -0.359373 0.869877 -0.337884
-vn -0.277993 0.858959 -0.430012
-vn -0.187031 0.860120 -0.474566
-vn -0.660133 0.670527 -0.338553
-vn -0.529637 -0.200008 -0.824306
-vn -0.726360 0.274535 -0.630104
-vn -0.121403 -0.211240 -0.969866
-vn -0.071151 -0.205179 -0.976135
-vn -0.284229 0.517762 -0.806930
-vn -0.139944 0.484992 -0.863249
-vn -0.103670 -0.586254 -0.803467
-vn -0.279226 -0.705236 -0.651671
-vn -0.557255 0.368306 -0.744189
-vn -0.500648 0.351051 -0.791274
-vn 0.059053 -0.708492 -0.703244
-vn -0.380779 -0.780927 -0.495137
-vn -0.321953 0.358136 -0.876405
-vn -0.404386 -0.645773 -0.647649
-vn -0.543493 -0.376379 -0.750303
-vn -0.636519 -0.224477 -0.737871
-vn -0.736465 -0.197839 -0.646899
-vn -0.647159 -0.207497 -0.733574
-vn -0.341775 0.277366 -0.897919
-vn -0.351045 0.429951 -0.831811
-vn -0.499730 0.354040 -0.790522
-vn 0.051493 0.914573 0.401129
-vn -0.008531 0.879405 0.475998
-vn 0.149748 -0.820050 0.552353
-vn -0.234307 -0.845645 0.479566
-vn -0.110431 0.859742 0.498647
-vn -0.517884 -0.806518 0.285173
-vn -0.785020 -0.466431 0.407658
-vn -0.795565 -0.169824 0.581581
-vn -0.761400 0.323893 0.561572
-vn -0.699725 0.483824 0.525642
-vn -0.725996 0.312566 0.612562
-vn -0.735012 0.376185 0.564129
-vn -0.417087 0.774421 0.475721
-vn -0.232919 -0.817749 0.526341
-vn -0.124182 -0.908287 0.399491
-vn -0.099589 -0.891994 0.440942
-vn -0.207909 -0.806047 0.554132
-vn 0.019616 -0.969080 0.245967
-vn 0.042565 -0.965380 0.257352
-vn 0.720199 0.590260 -0.364563
-vn -0.357270 0.734006 0.577575
-vn 0.037630 0.966807 0.252723
-vn 0.891140 0.150802 0.427935
-vn 0.419511 0.676388 -0.605401
-vn -0.095058 0.825605 0.556184
-vn 0.439697 0.721007 -0.535551
-vn -0.064103 0.790454 0.609157
-vn 0.452978 0.068099 0.888917
-vn 0.662598 0.204101 0.720630
-vn 0.522372 -0.161775 0.837231
-vn -0.036478 -0.781584 0.622732
-vn 0.139882 -0.681440 0.718382
-vn 0.063740 -0.967609 0.244276
-vn 0.109279 -0.959577 0.259365
-vn 0.243797 0.035115 0.969190
-vn 0.341697 -0.202869 0.917653
-vn -0.477766 -0.112622 0.871238
-vn -0.643569 -0.439873 0.626363
-vn -0.206641 0.056805 0.976766
-vn -0.762033 0.096238 0.640347
-vn -0.903945 -0.160775 0.396277
-vn -0.063103 0.396799 0.915734
-vn -0.511192 0.412237 0.754151
-vn -0.371848 0.438486 0.818205
-vn -0.713891 0.361920 0.599477
-vn -0.634539 0.152663 0.757664
-vn -0.209758 0.639054 0.740008
-vn -0.700112 0.197251 0.686248
-vn -0.828212 -0.526137 0.192989
-vn 0.355230 -0.925529 0.131181
-vn -0.140300 -0.869112 0.474299
-vn -0.577436 -0.738094 0.348978
-vn -0.705075 -0.651536 0.279947
-vn -0.869466 -0.472519 0.144067
-vn 0.596707 -0.800786 -0.051801
-vn -0.881647 -0.415355 0.224007
-vn 0.705038 -0.593958 0.387472
-vn -0.453517 -0.751334 0.479395
-vn -0.143364 -0.917289 -0.371522
-vn 0.350679 -0.871621 -0.342493
-vn -0.652432 -0.757292 -0.029006
-vn -0.626139 -0.758613 -0.180157
-vn -0.762949 -0.521227 0.382402
-vn 0.600312 -0.693907 0.397640
-vn -0.689013 -0.686911 0.231116
-vn 0.352320 -0.916584 -0.189062
-vn -0.488718 -0.747010 0.450700
-vn 0.645657 0.503935 0.573739
-vn 0.457435 0.477054 0.750449
-vn -0.134295 0.525491 0.840133
-vn -0.216670 0.371426 0.902827
-vn -0.562700 0.472473 0.678335
-vn 0.644713 0.466330 0.605707
-vn -0.855250 0.498707 -0.140854
-vn 0.884813 0.448698 0.125604
-vn 0.068504 0.534401 -0.842451
-vn 0.537004 0.544743 -0.644113
-vn -0.692082 0.620352 -0.369035
-vn -0.338134 0.576944 -0.743506
-vn -0.853417 0.438193 -0.282253
-vn 0.878054 0.376622 -0.295257
-vn -0.832171 0.548093 -0.084170
-vn 0.846949 0.499533 -0.182052
-vn 0.182903 0.533502 0.825786
-vn 0.261234 0.900561 0.347486
-vn 0.413777 0.870650 0.266000
-vn 0.281436 0.860437 0.424785
-vn 0.172081 0.734651 0.656259
-vn -0.073441 0.824964 0.560394
-vn 0.160159 0.836179 0.524550
-vn -0.341812 0.887457 -0.309167
-vn 0.215833 0.960181 -0.177395
-vn 0.168023 0.906465 -0.387414
-vn 0.160215 0.947885 -0.275402
-vn -0.117473 0.961913 -0.246828
-vn 0.213908 0.903835 -0.370575
-vn -0.385451 0.719835 -0.577291
-vn 0.400035 0.789267 -0.465864
-vn -0.367568 0.899210 -0.237308
-vn 0.435970 0.899214 -0.036661
-vn 0.455357 0.848217 0.270513
-vn -0.527943 -0.041478 0.848266
-vn -0.327060 0.086188 0.941065
-vn -0.808253 -0.080227 0.583345
-vn 0.026841 0.420441 0.906923
-vn -0.988825 -0.101310 0.109369
-vn -0.093035 0.813519 0.574049
-vn -0.673139 0.137049 0.726706
-vn -0.728667 0.191809 0.657460
-vn -0.893588 0.205085 -0.399300
-vn -0.952202 -0.241826 -0.186631
-vn -0.965283 -0.260137 -0.023608
-vn -0.759571 0.243795 0.603005
-vn -0.692978 0.715251 -0.090545
-vn 0.605768 -0.605952 0.515623
-vn 0.837826 -0.348406 0.420310
-vn 0.415642 -0.836615 0.356816
-vn 0.999563 0.029232 -0.004448
-vn 0.186263 -0.981850 -0.035732
-vn 0.908169 0.323953 -0.265112
-vn 0.343450 -0.134214 -0.929531
-vn 0.281411 -0.333767 -0.899671
-vn 0.182906 -0.779842 -0.598658
-vn 0.689726 0.217904 -0.690504
-vn 0.853176 -0.521289 -0.018681
-vn 0.917387 -0.378095 -0.124280
-vn 0.811146 -0.584042 -0.030613
-vn 0.912573 -0.202213 -0.355415
-vn 0.760850 -0.637233 -0.122644
-vn 0.914130 -0.054996 -0.401673
-vn 0.693701 -0.263228 -0.670440
-vn 0.740607 -0.195442 -0.642887
-vn 0.771165 -0.439488 -0.460603
-vn 0.826052 -0.142352 -0.545320
-vn -0.961745 -0.130987 0.240600
-vn -0.908848 -0.310602 0.278426
-vn -0.680486 -0.461229 0.569391
-vn -0.434767 0.205021 0.876895
-vn -0.329279 0.167900 0.929185
-vn -0.312866 0.423830 0.849990
-vn -0.447974 0.375115 0.811547
-vn -0.722258 0.219942 0.655720
-vn -0.747769 0.314746 0.584617
-vn -0.933926 0.155572 0.321837
-vn -0.365741 -0.559356 0.743878
-vn -0.285533 -0.605246 0.743067
-vn -0.793148 -0.188229 0.579212
-vn -0.308803 -0.190968 0.931758
-vn -0.448015 -0.224016 0.865505
-vn -0.357379 0.322146 0.876643
-vn -0.093979 0.321864 0.942110
-vn -0.113415 0.715207 0.689649
-vn -0.305974 0.669551 0.676817
-vn -0.646249 0.536188 0.543015
-vn -0.791484 0.462509 0.399548
-vn -0.445830 0.041243 0.894167
-vn -0.318727 -0.011516 0.947777
-vn -0.734012 0.079204 0.674502
-vn -0.878676 0.088211 0.469199
-vn -0.835100 0.343945 0.429313
-vn -0.253036 0.488275 0.835201
-vn -0.419340 0.446162 0.790629
-vn -0.694297 0.389720 0.605037
-vn -0.559701 0.213446 0.800735
-vn -0.623572 0.230156 0.747119
-vn -0.486319 0.231500 0.842557
-vn -0.654265 0.058729 0.753982
-vn -0.103144 0.378653 0.919773
-vn -0.292865 0.333821 0.895987
-vn -0.375808 -0.041554 0.925766
-vn -0.116385 -0.068904 0.990811
-vn -0.083694 0.341447 0.936167
-vn -0.121523 0.241991 0.962638
-vn -0.356362 0.253684 0.899250
-vn -0.330102 0.302194 0.894266
-vn 0.321377 0.000000 0.946952
-vn 0.504872 0.000000 0.863194
-vn 0.932012 0.000000 0.362428
-vn 0.813964 0.000000 -0.580916
-vn 0.482939 0.000000 -0.875654
-vn 0.547319 0.000000 -0.836924
-s 1
-g Mesh polySurface9
-usemtl Shell_Grooves
-f 1645/2452/1629 1654/2453/1630 1655/2454/1631 1656/2455/1632
-f 1654/2453/1630 1653/2456/1633 1657/2457/1634 1655/2458/1631
-f 1653/2459/1633 1644/2460/1635 1658/2461/1636 1657/2462/1634
-f 1646/2463/1637 1652/2464/1638 1660/2465/1639 1659/2466/1640
-f 1652/2467/1638 1645/2468/1629 1656/2469/1632 1660/2470/1639
-usemtl Shell
-f 1660/2471/1639 1656/2472/1632 1667/2473/1641 1668/2474/1642
-f 1659/2475/1640 1660/2476/1639 1668/2477/1642 1663/2478/1643
-f 1657/2479/1634 1658/2480/1636 1665/2481/1644 1669/2482/1645
-f 1655/2483/1631 1657/2484/1634 1669/2485/1645 1670/2486/1646
-f 1656/2487/1632 1655/2454/1631 1670/2488/1646 1667/2489/1641
-usemtl Shell_Grooves
-f 1654/2453/1630 1645/2490/1629 1675/2491/1647 1676/2492/1648
-f 1653/2493/1633 1654/2494/1630 1676/2495/1648 1677/2496/1649
-f 1644/2497/1635 1653/2498/1633 1677/2499/1649 1678/2500/1650
-f 1636/2501/1651 1650/2502/1652 1680/2503/1653 1679/2504/1654
-f 1651/2505/1655 1639/2506/1656 1681/2507/1657 1682/2508/1658
-f 1645/2509/1629 1651/2510/1655 1682/2511/1658 1675/2512/1647
-f 1650/2513/1652 1644/2514/1635 1678/2515/1650 1680/2516/1653
-f 1639/2517/1656 1638/2518/1659 1683/2519/1660 1681/2520/1657
-f 1638/2521/1659 1637/2522/1661 1684/2523/1662 1683/2524/1660
-f 1637/2525/1661 1636/2526/1651 1679/2527/1654 1684/2528/1662
-usemtl Shell
-f 1684/2529/1662 1679/2530/1654 1685/2531/1663 1686/2532/1664
-f 1683/2533/1660 1684/2534/1662 1686/2535/1664 1687/2536/1665
-f 1681/2537/1657 1683/2538/1660 1687/2539/1665 1688/2540/1666
-f 1680/2541/1653 1678/2542/1650 1690/2543/1667 1689/2544/1668
-f 1675/2545/1647 1682/2546/1658 1692/2547/1669 1691/2548/1670
-f 1682/2549/1658 1681/2550/1657 1688/2551/1666 1692/2552/1669
-f 1679/2553/1654 1680/2554/1653 1689/2555/1668 1685/2556/1663
-f 1678/2557/1650 1677/2558/1649 1693/2559/1671 1690/2560/1667
-f 1677/2561/1649 1676/2562/1648 1694/2563/1672 1693/2564/1671
-f 1676/2565/1648 1675/2566/1647 1691/2567/1670 1694/2568/1672
-f 1690/2569/1667 1693/2570/1671 1695/2571/1673 1689/2572/1668
-f 1693/2573/1671 1694/2574/1672 1696/2575/1674 1695/2576/1673
-f 1694/2577/1672 1691/2578/1670 1692/2579/1669 1696/2580/1674
-f 1687/2581/1665 1696/2582/1674 1692/2583/1669 1688/2584/1666
-f 1686/2585/1664 1695/2586/1673 1696/2587/1674 1687/2588/1665
-f 1685/2589/1663 1689/2590/1668 1695/2591/1673 1686/2592/1664
-usemtl Shell_Grooves
-f 1652/2593/1638 1646/2594/1637 1704/2595/1675 1706/2596/1676
-f 1645/2597/1629 1652/2598/1638 1706/2599/1676 1705/2600/1677
-f 1649/2601/1678 1642/2602/1679 1697/2603/1680 1703/2604/1681
-f 1639/2605/1656 1651/2606/1655 1699/2607/1682 1698/2608/1683
-f 1651/2609/1655 1645/2452/1629 1705/2610/1677 1699/2611/1682
-f 1646/2612/1637 1649/2613/1678 1703/2614/1681 1704/2615/1675
-f 1629/2616/1684 1641/2617/1685 1702/2618/1686 1700/2619/1687
-f 1642/2620/1679 1629/2621/1684 1700/2622/1687 1697/2623/1680
-f 1641/2624/1685 1640/2625/1688 1701/2626/1689 1702/2627/1686
-f 1640/2628/1688 1639/2629/1656 1698/2630/1683 1701/2631/1689
-usemtl Shell
-f 1706/2632/1676 1704/2633/1675 1707/2634/1690 1708/2635/1691
-f 1705/2636/1677 1706/2637/1676 1708/2638/1691 1709/2639/1692
-f 1703/2640/1681 1697/2641/1680 1710/2642/1693 1711/2643/1694
-f 1698/2644/1683 1699/2645/1682 1713/2646/1695 1712/2647/1696
-f 1699/2648/1682 1705/2649/1677 1709/2650/1692 1713/2651/1695
-f 1704/2652/1675 1703/2653/1681 1711/2654/1694 1707/2655/1690
-f 1700/2656/1687 1702/2657/1686 1714/2658/1697 1715/2659/1698
-f 1697/2660/1680 1700/2661/1687 1715/2662/1698 1710/2663/1693
-f 1702/2664/1686 1701/2665/1689 1716/2666/1699 1714/2667/1697
-f 1701/2668/1689 1698/2669/1683 1712/2670/1696 1716/2671/1699
-f 1709/2672/1692 1708/2673/1691 1717/2674/1700 1713/2675/1695
-f 1712/2676/1696 1713/2677/1695 1717/2678/1700 1716/2679/1699
-f 1716/2680/1699 1717/2681/1700 1711/2682/1694 1714/2683/1697
-f 1707/2684/1690 1711/2685/1694 1717/2686/1700 1708/2687/1691
-f 1715/2688/1698 1714/2689/1697 1711/2690/1694 1710/2691/1693
-usemtl Shell_Grooves
-f 1644/2692/1635 1719/2693/1701 1720/2694/1702 1658/2695/1636
-f 1719/2696/1701 1648/2697/1703 1661/2698/1704 1720/2699/1702
-f 1647/2700/1705 1718/2701/1706 1721/2702/1707 1662/2703/1708
-f 1718/2704/1706 1646/2705/1637 1659/2706/1640 1721/2707/1707
-usemtl Shell
-f 1662/2708/1708 1721/2709/1707 1722/2710/1709 1664/2711/1710
-f 1721/2712/1707 1659/2713/1640 1663/2714/1643 1722/2715/1709
-f 1658/2716/1636 1720/2717/1702 1723/2718/1711 1665/2719/1644
-f 1720/2720/1702 1661/2721/1704 1666/2722/1712 1723/2723/1711
-f 1672/2724/1713 1724/2725/1714 1725/2726/1715 1671/2727/1716
-f 1724/2728/1714 1668/2729/1642 1667/2730/1641 1725/2731/1715
-f 1664/2732/1710 1722/2733/1709 1724/2734/1714 1672/2735/1713
-f 1722/2736/1709 1663/2737/1643 1668/2738/1642 1724/2739/1714
-f 1673/2740/1717 1726/2741/1718 1723/2742/1711 1666/2743/1712
-f 1726/2744/1718 1669/2745/1645 1665/2746/1644 1723/2747/1711
-f 1674/2748/1719 1727/2749/1720 1726/2750/1718 1673/2751/1717
-f 1727/2752/1720 1670/2753/1646 1669/2754/1645 1726/2755/1718
-f 1671/2756/1716 1725/2757/1715 1727/2758/1720 1674/2759/1719
-f 1725/2760/1715 1667/2761/1641 1670/2762/1646 1727/2763/1720
-usemtl Shell_Grooves
-f 1648/2764/1703 1719/2765/1701 1729/2766/1721 1728/2767/1722
-f 1719/2768/1701 1644/2769/1635 1730/2770/1723 1729/2771/1721
-f 1650/2772/1652 1636/2773/1651 1735/2774/1724 1731/2775/1725
-f 1644/2776/1635 1650/2777/1652 1731/2778/1725 1730/2779/1723
-f 1643/2780/1726 1736/2781/1727 1732/2782/1728 1733/2783/1729
-f 1630/2784/1730 1643/2785/1726 1733/2786/1729 1734/2787/1731
-f 1636/2788/1651 1630/2789/1730 1734/2790/1731 1735/2791/1724
-usemtl Shell
-f 1735/2792/1724 1734/2793/1731 1737/2794/1732 1741/2795/1733
-f 1732/2796/1728 1736/2797/1727 1739/2798/1734 1738/2799/1735
-f 1736/2800/1727 1767/2801/1736 1740/2802/1737 1739/2803/1734
-f 1734/2804/1731 1733/2805/1729 1742/2806/1738 1737/2807/1732
-f 1733/2808/1729 1732/2809/1728 1738/2810/1735 1742/2811/1738
-f 1730/2812/1723 1731/2813/1725 1744/2814/1739 1743/2815/1740
-f 1731/2816/1725 1735/2817/1724 1741/2818/1733 1744/2819/1739
-f 1729/2820/1721 1730/2821/1723 1743/2822/1740 1745/2823/1741
-f 1728/2824/1722 1729/2825/1721 1745/2826/1741 1746/2827/1742
-f 1746/2828/1742 1745/2829/1741 1747/2830/1743 1748/2831/1744
-f 1745/2832/1741 1743/2833/1740 1744/2834/1739 1747/2835/1743
-f 1741/2836/1733 1737/2837/1732 1744/2838/1739
-f 1739/2839/1734 1740/2840/1737 1748/2841/1744 1747/2842/1743
-f 1738/2843/1735 1739/2844/1734 1747/2845/1743 1742/2846/1738
-f 1737/2847/1732 1742/2848/1738 1747/2849/1743 1744/2850/1739
-usemtl Shell_Grooves
-f 1718/2851/1706 1647/2852/1705 1749/2853/1745 1750/2854/1746
-f 1646/2855/1637 1718/2856/1706 1750/2857/1746 1751/2858/1747
-f 1642/2859/1679 1649/2860/1678 1753/2861/1748 1752/2862/1749
-f 1649/2863/1678 1646/2864/1637 1751/2865/1747 1753/2866/1748
-f 1631/2867/1750 1642/2868/1679 1752/2869/1749 1756/2870/1751
-f 1635/2871/1752 1632/2872/1753 1754/2873/1754 1755/2874/1755
-f 1632/2875/1753 1631/2876/1750 1756/2877/1751 1754/2878/1754
-usemtl Shell
-f 1754/2879/1754 1756/2880/1751 1757/2881/1756 1758/2882/1757
-f 1755/2883/1755 1754/2884/1754 1758/2885/1757 1759/2886/1758
-f 1756/2887/1751 1752/2888/1749 1760/2889/1759 1757/2890/1756
-f 1753/2891/1748 1751/2892/1747 1761/2893/1760 1762/2894/1761
-f 1752/2895/1749 1753/2896/1748 1762/2897/1761 1760/2898/1759
-f 1751/2899/1747 1750/2900/1746 1763/2901/1762 1761/2902/1760
-f 1750/2903/1746 1749/2904/1745 1764/2905/1763 1763/2906/1762
-f 1761/2907/1760 1763/2908/1762 1765/2909/1764 1762/2910/1761
-f 1763/2911/1762 1764/2912/1763 1766/2913/1765 1765/2914/1764
-f 1758/2915/1757 1765/2916/1764 1766/2917/1765 1759/2918/1758
-f 1757/2919/1756 1765/2920/1764 1758/2921/1757
-f 1757/2922/1756 1760/2923/1759 1762/2924/1761 1765/2925/1764
-f 1634/2926/1766 1767/2927/1736 1736/2928/1727 1633/2929/1767
-f 1768/2930/1768 1631/2931/1750 1632/2932/1753 1769/2933/1769
-f 1771/2934/1770 1634/2935/1766 1633/2936/1767 1770/2937/1771
-f 1769/2938/1769 1632/2939/1753 1635/2940/1752 1772/2941/1772
-f 1773/2942/1773 1636/2943/1651 1637/2944/1661 1774/2945/1774
-f 1774/2946/1774 1637/2947/1661 1638/2948/1659 1775/2949/1775
-f 1775/2950/1775 1638/2951/1659 1639/2952/1656 1776/2953/1776
-f 1776/2954/1776 1639/2955/1656 1640/2956/1688 1777/2957/1777
-f 1777/2958/1777 1640/2959/1688 1641/2960/1685 1778/2961/1778
-f 1779/2962/1779 1629/2963/1684 1642/2964/1679 1780/2965/1780
-f 1629/2966/1684 1779/2967/1779 1778/2968/1778 1641/2969/1685
-f 1780/2970/1780 1642/2971/1679 1631/2972/1750 1768/2973/1768
-f 1781/2974/1781 1768/2975/1768 1769/2976/1769 1782/2977/1782
-f 1784/2978/1783 1771/2979/1770 1770/2980/1771 1783/2981/1784
-f 1782/2977/1782 1769/2982/1769 1772/2983/1772 1785/2984/1785
-f 1786/2985/1786 1773/2986/1773 1774/2987/1774 1787/2988/1787
-f 1787/2989/1787 1774/2990/1774 1775/2991/1775 1788/2992/1788
-f 1788/2993/1788 1775/2994/1775 1776/2995/1776 1789/2996/1789
-f 1789/2997/1789 1776/2998/1776 1777/2999/1777 1790/3000/1790
-f 1790/3001/1790 1777/3002/1777 1778/3003/1778 1791/3004/1791
-f 1792/3005/1792 1779/3006/1779 1780/3007/1780 1793/3008/1793
-f 1791/3009/1791 1778/3010/1778 1779/3011/1779 1792/3012/1792
-f 1793/3013/1793 1780/3014/1780 1768/3015/1768 1781/3016/1781
-usemtl Belly
-f 1805/3017/1794 1804/3018/1795 1812/3019/1796 1814/3020/1797
-f 1804/3021/1795 1802/3022/1798 1809/3023/1799 1812/3024/1796
-f 1810/3025/1800 1821/3026/1801 1801/3027/1802 1794/3028/1803
-f 1811/3029/1804 1822/3030/1805 1821/3031/1801 1810/3032/1800
-f 1803/3033/1806 1808/3034/1807 1822/3035/1805 1811/3036/1804
-usemtl Shell
-f 1824/3037/1808 1823/3038/1809 1781/3039/1781 1782/3040/1782
-f 1795/3041/1810 1794/3042/1803 1823/3043/1809 1824/3044/1808
-f 1825/3045/1811 1826/3046/1812 1784/3047/1783 1783/3048/1784
-usemtl Belly
-f 1837/3049/1813 1836/3050/1814 1826/3051/1812 1825/3052/1811
-usemtl Shell
-f 1827/3053/1815 1824/3054/1808 1782/3055/1782 1785/3056/1785
-f 1796/3057/1816 1795/3058/1810 1824/3059/1808 1827/3060/1815
-f 1829/3061/1817 1828/3062/1818 1786/3063/1786 1787/3064/1787
-f 1830/3065/1819 1829/3066/1817 1787/3067/1787 1788/3068/1788
-usemtl Belly
-f 1798/3069/1820 1797/3070/1821 1829/3071/1817 1830/3072/1819
-usemtl Shell
-f 1831/3073/1822 1830/3074/1819 1788/3075/1788 1789/3076/1789
-usemtl Belly
-f 1799/3077/1823 1798/3078/1820 1830/3079/1819 1831/3080/1822
-usemtl Shell
-f 1832/3081/1824 1831/3082/1822 1789/3083/1789 1790/3084/1790
-usemtl Belly
-f 1800/3085/1825 1799/3086/1823 1831/3087/1822 1832/3088/1824
-usemtl Shell
-f 1833/3089/1826 1832/3090/1824 1790/3091/1790 1791/3092/1791
-f 1835/3093/1827 1834/3094/1828 1792/3095/1792 1793/3096/1793
-f 1834/3097/1828 1833/3098/1826 1791/3099/1791 1792/3100/1792
-f 1823/3101/1809 1835/3102/1827 1793/3103/1793 1781/3104/1781
-usemtl Belly
-f 1794/3105/1803 1801/3106/1802 1835/3107/1827 1823/3108/1809
-f 1802/3109/1798 1836/3110/1814 1837/3111/1813 1809/3112/1799
-f 1797/3113/1821 1838/3114/1829 1828/3115/1818 1829/3116/1817
-usemtl Shell
-f 1630/3117/1730 1636/3118/1651 1842/3119/1830 1839/3120/1831
-f 1633/3121/1767 1736/3122/1727 1840/3123/1832 1841/3124/1833
-f 1770/3125/1771 1633/3126/1767 1841/3127/1833 1843/3128/1834
-f 1636/3129/1651 1773/3130/1773 1844/3131/1835 1842/3132/1830
-f 1783/3133/1784 1770/3134/1771 1843/3135/1834 1845/3136/1836
-f 1773/3137/1773 1786/3138/1786 1846/3139/1837 1844/3140/1835
-usemtl Belly
-f 1813/3141/1838 1814/3142/1797 1848/3143/1839 1847/3144/1840
-f 1812/3145/1796 1809/3146/1799 1849/3147/1841 1850/3148/1842
-usemtl Shell
-f 1825/3149/1811 1783/3150/1784 1845/3151/1836 1851/3152/1843
-f 1786/3153/1786 1828/3154/1818 1852/3155/1844 1846/3156/1837
-usemtl Belly
-f 1837/3157/1813 1825/3158/1811 1851/3159/1843 1853/3160/1845
-f 1838/3161/1829 1813/3162/1838 1847/3163/1840 1854/3164/1846
-f 1828/3165/1818 1838/3166/1829 1854/3167/1846 1852/3168/1844
-usemtl Shell
-f 1643/3169/1726 1630/3170/1730 1839/3171/1831 1855/3172/1847
-f 1736/3173/1727 1643/3174/1726 1855/3175/1847 1840/3176/1832
-usemtl Belly
-f 1814/3177/1797 1812/3178/1796 1850/3179/1842 1848/3180/1839
-f 1809/3181/1799 1837/3182/1813 1853/3183/1845 1849/3184/1841
-usemtl Shell
-f 1839/3185/1831 1842/3186/1830 1859/3187/1848 1856/3188/1849
-f 1841/3189/1833 1840/3190/1832 1858/3191/1850 1857/3192/1851
-f 1843/3193/1834 1841/3194/1833 1857/3195/1851 1860/3196/1852
-f 1842/3197/1830 1844/3198/1835 1861/3199/1853 1859/3200/1848
-f 1845/3201/1836 1843/3202/1834 1860/3203/1852 1862/3204/1854
-f 1844/3205/1835 1846/3206/1837 1863/3207/1855 1861/3208/1853
-usemtl Belly
-f 1847/3209/1840 1848/3210/1839 1864/3211/1856 1865/3212/1857
-f 1850/3213/1842 1849/3214/1841 1867/3215/1858 1866/3216/1859
-usemtl Shell
-f 1851/3217/1843 1845/3218/1836 1862/3219/1854 1868/3220/1860
-f 1846/3221/1837 1852/3222/1844 1869/3223/1861 1863/3224/1855
-usemtl Belly
-f 1853/3225/1845 1851/3226/1843 1868/3227/1860 1870/3228/1862
-f 1854/3229/1846 1847/3230/1840 1865/3231/1857 1871/3232/1863
-f 1852/3233/1844 1854/3234/1846 1871/3235/1863 1869/3236/1861
-usemtl Shell
-f 1855/3237/1847 1839/3238/1831 1856/3239/1849 1872/3240/1864
-f 1840/3241/1832 1855/3242/1847 1872/3243/1864 1858/3244/1850
-usemtl Belly
-f 1848/3245/1839 1850/3246/1842 1866/3247/1859 1864/3248/1856
-f 1849/3249/1841 1853/3250/1845 1870/3251/1862 1867/3252/1858
-usemtl Shell
-f 1856/3253/1849 1859/3254/1848 1876/3255/1865 1873/3256/1866
-f 1857/3257/1851 1858/3258/1850 1874/3259/1867 1875/3260/1868
-f 1860/3261/1852 1857/3262/1851 1875/3263/1868 1877/3264/1869
-f 1859/3265/1848 1861/3266/1853 1878/3267/1870 1876/3268/1865
-f 1862/3269/1854 1860/3270/1852 1877/3271/1869 1879/3272/1871
-f 1861/3273/1853 1863/3274/1855 1880/3275/1872 1878/3276/1870
-usemtl Belly
-f 1865/3277/1857 1864/3278/1856 1882/3279/1873 1881/3280/1874
-f 1866/3281/1859 1867/3282/1858 1883/3283/1875 1884/3284/1876
-usemtl Shell
-f 1868/3285/1860 1862/3286/1854 1879/3287/1871 1885/3288/1877
-f 1863/3289/1855 1869/3290/1861 1886/3291/1878 1880/3292/1872
-usemtl Belly
-f 1870/3293/1862 1868/3294/1860 1885/3295/1877 1887/3296/1879
-f 1871/3297/1863 1865/3298/1857 1881/3299/1874 1888/3300/1880
-f 1869/3301/1861 1871/3302/1863 1888/3303/1880 1886/3304/1878
-usemtl Shell
-f 1872/3305/1864 1856/3306/1849 1873/3307/1866 1889/3308/1881
-f 1858/3309/1850 1872/3310/1864 1889/3311/1881 1874/3312/1867
-usemtl Belly
-f 1864/3313/1856 1866/3314/1859 1884/3315/1876 1882/3316/1873
-f 1867/3317/1858 1870/3318/1862 1887/3319/1879 1883/3320/1875
-f 1833/3321/1826 1834/3322/1828 1891/3323/1882 1890/3324/1883
-f 1834/3325/1828 1835/3326/1827 1892/3327/1884 1891/3328/1882
-f 1832/3329/1824 1833/3330/1826 1890/3331/1883 1893/3332/1885
-f 1835/3333/1827 1801/3334/1802 1894/3335/1886 1892/3336/1884
-f 1800/3337/1825 1832/3338/1824 1893/3339/1885 1895/3340/1887
-f 1820/3341/1888 1819/3342/1889 1896/3343/1890 1897/3344/1891
-f 1821/3345/1801 1820/3346/1888 1897/3347/1891 1898/3348/1892
-f 1819/3349/1889 1817/3350/1893 1899/3351/1894 1896/3352/1890
-f 1801/3353/1802 1821/3354/1801 1898/3355/1892 1894/3356/1886
-f 1817/3357/1893 1800/3358/1825 1895/3359/1887 1899/3360/1894
-f 1890/3361/1883 1891/3362/1882 1901/3363/1895 1900/3364/1896
-f 1891/3365/1882 1892/3366/1884 1902/3367/1897 1901/3368/1895
-f 1893/3369/1885 1890/3370/1883 1900/3371/1896 1903/3372/1898
-f 1892/3373/1884 1894/3374/1886 1904/3375/1899 1902/3376/1897
-f 1895/3377/1887 1893/3378/1885 1903/3379/1898 1905/3380/1900
-f 1897/3381/1891 1896/3382/1890 1906/3383/1901 1907/3384/1902
-f 1898/3385/1892 1897/3386/1891 1907/3387/1902 1908/3388/1903
-f 1896/3389/1890 1899/3390/1894 1909/3391/1904 1906/3392/1901
-f 1894/3393/1886 1898/3394/1892 1908/3395/1903 1904/3396/1899
-f 1899/3397/1894 1895/3398/1887 1905/3399/1900 1909/3400/1904
-f 1900/3401/1896 1901/3402/1895 1910/3403/1905 1911/3404/1906
-f 1901/3405/1895 1902/3406/1897 1912/3407/1907 1910/3408/1905
-f 1903/3409/1898 1900/3410/1896 1911/3411/1906 1913/3412/1908
-f 1902/3413/1897 1904/3414/1899 1914/3415/1909 1912/3416/1907
-f 1905/3417/1900 1903/3418/1898 1913/3419/1908 1915/3420/1910
-f 1907/3421/1902 1906/3422/1901 1917/3423/1911 1916/3424/1912
-f 1908/3425/1903 1907/3426/1902 1916/3427/1912 1918/3428/1913
-f 1906/3429/1901 1909/3430/1904 1919/3431/1914 1917/3432/1911
-f 1904/3433/1899 1908/3434/1903 1918/3435/1913 1914/3436/1909
-f 1909/3391/1904 1905/3437/1900 1915/3438/1910 1919/3439/1914
-f 1838/3440/1829 1797/3441/1821 1920/3442/1915 1921/3443/1916
-f 1813/3444/1838 1838/3445/1829 1921/3446/1916 1922/3447/1917
-f 1816/3448/1918 1806/3449/1919 1923/3450/1920 1924/3451/1921
-f 1815/3452/1922 1816/3453/1918 1924/3454/1921 1925/3455/1923
-f 1798/3456/1820 1815/3457/1922 1925/3458/1923 1926/3459/1924
-f 1805/3460/1794 1814/3461/1797 1928/3462/1925 1927/3463/1926
-f 1814/3464/1797 1813/3465/1838 1922/3466/1917 1928/3467/1925
-f 1797/3468/1821 1798/3469/1820 1926/3470/1924 1920/3471/1915
-f 1922/3472/1917 1921/3473/1916 1920/3474/1915 1929/3475/1927
-f 1923/3476/1920 1930/3477/1928 1931/3478/1929 1924/3479/1921
-f 1924/3480/1921 1931/3481/1929 1929/3482/1927 1925/3483/1923
-f 1925/3484/1923 1929/3485/1927 1920/3486/1915 1926/3487/1924
-f 1930/3488/1928 1927/3489/1926 1928/3490/1925 1931/3491/1929
-f 1931/3492/1929 1928/3493/1925 1922/3494/1917 1929/3495/1927
-f 1818/3496/1930 1807/3497/1931 1932/3498/1932 1933/3499/1933
-f 1817/3500/1893 1818/3501/1930 1933/3502/1933 1934/3503/1934
-f 1800/3504/1825 1817/3505/1893 1934/3506/1934 1935/3507/1935
-f 1806/3508/1919 1816/3509/1918 1937/3510/1936 1936/3511/1937
-f 1816/3512/1918 1815/3513/1922 1938/3514/1938 1937/3515/1936
-f 1815/3516/1922 1798/3517/1820 1939/3518/1939 1938/3519/1938
-f 1799/3520/1823 1800/3521/1825 1935/3522/1935 1940/3523/1940
-f 1798/3524/1820 1799/3525/1823 1940/3526/1940 1939/3527/1939
-f 1932/3528/1932 1941/3529/1941 1942/3530/1942 1933/3531/1933
-f 1933/3532/1933 1942/3533/1942 1943/3534/1943 1934/3535/1934
-f 1934/3536/1934 1943/3537/1943 1940/3538/1940 1935/3539/1935
-f 1941/3540/1941 1936/3541/1937 1937/3542/1936 1942/3543/1942
-f 1942/3544/1942 1937/3545/1936 1938/3546/1938 1943/3547/1943
-f 1943/3548/1943 1938/3549/1938 1939/3550/1939 1940/3551/1940
-f 1819/3552/1889 1820/3553/1888 1945/3554/1944 1944/3555/1945
-f 1820/3556/1888 1821/3557/1801 1946/3558/1946 1945/3559/1944
-f 1817/3560/1893 1819/3561/1889 1944/3562/1945 1947/3563/1947
-f 1822/3564/1805 1808/3565/1807 1948/3566/1948 1949/3567/1949
-f 1821/3568/1801 1822/3569/1805 1949/3570/1949 1946/3571/1946
-f 1807/3572/1931 1818/3573/1930 1951/3574/1950 1950/3575/1951
-f 1818/3576/1930 1817/3577/1893 1947/3578/1947 1951/3579/1950
-f 1952/3580/1952 1953/3581/1953 1954/3582/1954 1955/3583/1955
-f 1955/3584/1955 1954/3585/1954 1944/3586/1945 1945/3587/1944
-f 1948/3588/1948 1952/3589/1952 1955/3590/1955 1949/3591/1949
-f 1949/3592/1949 1955/3593/1955 1945/3594/1944 1946/3595/1946
-f 1953/3596/1953 1950/3597/1951 1951/3598/1950 1954/3599/1954
-f 1954/3600/1954 1951/3601/1950 1947/3602/1947 1944/3603/1945
-usemtl Shell
-f 1795/3604/1810 1796/3605/1816 1957/3606/1956 1956/3607/1957
-usemtl Belly
-f 1810/3608/1800 1794/3609/1803 1958/3610/1958 1959/3611/1959
-f 1811/3612/1804 1810/3613/1800 1959/3614/1959 1960/3615/1960
-f 1803/3616/1806 1811/3617/1804 1960/3618/1960 1961/3619/1961
-usemtl Shell
-f 1794/3620/1803 1795/3621/1810 1956/3622/1957 1958/3623/1958
diff --git a/examples/turtle/run_turtle b/examples/turtle/run_turtle
old mode 100755
new mode 100644
index d06ed9d5785672cbf72a14f148c35bc33b5652c8..a24eeef17af86d2c3d85d6d28cbd24320d061970
--- a/examples/turtle/run_turtle
+++ b/examples/turtle/run_turtle
@@ -1 +1 @@
-../../build/bin/deformsim turtle.config
+../../utilities/bin/interactiveDeformableSimulator turtle.config
diff --git a/examples/turtle/run_turtle_massspring b/examples/turtle/run_turtle_massspring
old mode 100755
new mode 100644
diff --git a/examples/turtle/run_turtle_ortho b/examples/turtle/run_turtle_ortho
new file mode 100644
index 0000000000000000000000000000000000000000..5642f82e8d5789495c84fba7dc1c1ca54ec2ab41
--- /dev/null
+++ b/examples/turtle/run_turtle_ortho
@@ -0,0 +1 @@
+../../utilities/bin/interactiveDeformableSimulator turtle.ortho.config
diff --git a/examples/turtle/turtle.config b/examples/turtle/turtle.config
index 70ce9eab0ffff091ab7e3a82159340106252f026..5a00f5bad5aebb99bbcbea25ebab47c19ed5f294 100644
--- a/examples/turtle/turtle.config
+++ b/examples/turtle/turtle.config
@@ -1,6 +1,6 @@
 *volumetricMeshFilename
-#model/turtle-volumetric-homogeneous.veg
-model/turtle-volumetric-heterogeneous.veg
+#../../models/turtle/turtle-volumetric-homogeneous.veg
+../../models/turtle/turtle-volumetric-heterogeneous.veg
 
 *solver
 implicitBackwardEuler
@@ -19,13 +19,13 @@ StVK
 #neoHookean
 
 *renderingMeshFilename
-model/turtle-volumetric.obj
+../../models/turtle/turtle-volumetric.obj
 
 *secondaryRenderingMeshFilename
-model/turtle.obj
+../../models/turtle/turtle.obj
 
 *secondaryRenderingMeshInterpolationFilename
-model/turtle.interp
+../../models/turtle/turtle.interp
 
 *timestep
 0.01
@@ -47,8 +47,8 @@ model/turtle.interp
 *deformableObjectCompliance
 30.0
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *enableCompressionResistance
 1
@@ -62,9 +62,6 @@ model/turtle.interp
 *lightingConfigFilename
 turtle.lighting
 
-*massMatrixFilename
-model/turtle-volumetric.mass
-
 *cameraRadius
 12.6
 
@@ -81,7 +78,7 @@ model/turtle-volumetric.mass
 0
 
 *fixedVerticesFilename
-model/turtle-volumetric.bou
+../../models/turtle/turtle-volumetric.bou
 
 *numInternalForceThreads
 4
diff --git a/examples/turtle/turtle.massspring b/examples/turtle/turtle.massspring
index d4b99b4a6df683f7370a88d6b5a2701e4e7a316a..c439c7fd3baa9c83c0a7cc5ef800be4460d01b39 100644
--- a/examples/turtle/turtle.massspring
+++ b/examples/turtle/turtle.massspring
@@ -1,14 +1,14 @@
 *tetMeshFilename
-model/turtle-volumetric-heterogeneous.veg
+../../models/turtle/turtle-volumetric-heterogeneous.veg
 
 *surfaceMeshFilename
-model/turtle-volumetric.obj
+../../models/turtle/turtle-volumetric.obj
 
 *density
 1.0
 
 *tensileStiffness
-100000.0
+25000.0
 
 *damping
 0.01
diff --git a/examples/turtle/turtle.ortho.config b/examples/turtle/turtle.ortho.config
new file mode 100644
index 0000000000000000000000000000000000000000..f461ce7442aa84ceedbe03d6658fc3dfde285b99
--- /dev/null
+++ b/examples/turtle/turtle.ortho.config
@@ -0,0 +1,89 @@
+*volumetricMeshFilename
+#../../models/turtle/turtle-volumetric-homogeneous.ortho.veg
+../../models/turtle/turtle-volumetric-heterogeneous.ortho.veg
+
+*solver
+implicitBackwardEuler
+#implicitNewmark
+#symplecticEuler
+#Euler
+
+*deformableObjectMethod
+#StVK
+#InvertibleFEM
+CLFEM
+#LinearFEM
+
+*invertibleMaterial
+StVK
+#neoHookean
+
+*renderingMeshFilename
+../../models/turtle/turtle-volumetric.obj
+
+*secondaryRenderingMeshFilename
+../../models/turtle/turtle.obj
+
+*secondaryRenderingMeshInterpolationFilename
+../../models/turtle/turtle.interp
+
+*timestep
+0.01
+
+*dampingMassCoef
+1.0
+#10.0
+
+*dampingStiffnessCoef
+0.01
+#0.001
+
+#*addGravity
+#1
+
+#*g
+#-10000
+
+*deformableObjectCompliance
+100.0
+
+*frequencyScaling
+1.0
+
+*enableCompressionResistance
+1
+
+*compressionResistance
+500
+
+*inversionThreshold
+0.1
+
+*lightingConfigFilename
+turtle.lighting
+
+*cameraRadius
+12.6
+
+*cameraLongitude
+-103
+
+*cameraLattitude
+8
+
+*focusPositionY
+3.0
+
+*syncTimestepWithGraphics
+0
+
+*fixedVerticesFilename
+../../models/turtle/turtle-volumetric.bou
+
+*numInternalForceThreads
+4
+
+*numSolverThreads
+2
+
+
diff --git a/examples/turtle/turtle_massspring.config b/examples/turtle/turtle_massspring.config
index ead99d262de5ea21b575167786b986b982516929..94a6ed24e72648221066cce147621322df5bd370 100644
--- a/examples/turtle/turtle_massspring.config
+++ b/examples/turtle/turtle_massspring.config
@@ -1,6 +1,6 @@
 *volumetricMeshFilename
-#model/turtle-volumetric-homogeneous.veg
-model/turtle-volumetric-heterogeneous.veg
+#../../models/turtle/turtle-volumetric-homogeneous.veg
+../../models/turtle/turtle-volumetric-heterogeneous.veg
 
 *solver
 implicitBackwardEuler
@@ -12,16 +12,16 @@ implicitBackwardEuler
 turtle.massspring
 
 *renderingMeshFilename
-model/turtle-volumetric.obj
+../../models/turtle/turtle-volumetric.obj
 
 *secondaryRenderingMeshFilename
-model/turtle.obj
+../../models/turtle/turtle.obj
 
 *secondaryRenderingMeshInterpolationFilename
-model/turtle.interp
+../../models/turtle/turtle.interp
 
 *fixedVerticesFilename
-model/turtle-volumetric.bou
+../../models/turtle/turtle-volumetric.bou
 
 *timestep
 0.001
@@ -37,15 +37,12 @@ model/turtle-volumetric.bou
 *deformableObjectCompliance
 30.0
 
-*baseFrequency
-0.5
+*frequencyScaling
+1.0
 
 *lightingConfigFilename
 turtle.lighting
 
-*massMatrixFilename
-model/turtle-volumetric.mass
-
 *cameraRadius
 12.6
 
diff --git a/examples/virtualTets/comb-arap.config b/examples/virtualTets/comb-arap.config
new file mode 100644
index 0000000000000000000000000000000000000000..01b98a855fb321942aa44e909a135e1da486aa0d
--- /dev/null
+++ b/examples/virtualTets/comb-arap.config
@@ -0,0 +1,20 @@
+*volumetricMeshFilename
+comb-vt.veg
+
+*embeddedMeshFilename
+../../models/comb/comb.obj
+
+*embeddedMeshInterpolationFilename
+comb-vt.interp
+
+*numSolverThreads
+3
+
+*cameraRadius
+106
+
+*cameraLongitude
+-90
+
+*cameraLattitude
+5
diff --git a/examples/virtualTets/dragon-arap.config b/examples/virtualTets/dragon-arap.config
new file mode 100644
index 0000000000000000000000000000000000000000..73b77f702947919267caa2caf7b430d0c11dabe6
--- /dev/null
+++ b/examples/virtualTets/dragon-arap.config
@@ -0,0 +1,20 @@
+*volumetricMeshFilename
+dragon-vt.veg
+
+*embeddedMeshFilename
+../../models/dragon-77k/dragon-77k.obj
+
+*embeddedMeshInterpolationFilename
+dragon-vt.interp
+
+*numSolverThreads
+3
+
+*cameraRadius
+14.5
+
+*cameraLongitude
+-90
+
+*cameraLattitude
+0
diff --git a/examples/virtualTets/edit_comb b/examples/virtualTets/edit_comb
new file mode 100644
index 0000000000000000000000000000000000000000..77ab41a0234b3672e892f151bb3c59417b90d8e0
--- /dev/null
+++ b/examples/virtualTets/edit_comb
@@ -0,0 +1 @@
+../../utilities/bin/editShapeARAP comb-arap.config
diff --git a/examples/virtualTets/edit_dragon b/examples/virtualTets/edit_dragon
new file mode 100644
index 0000000000000000000000000000000000000000..dc9ce3c7672428804eb3993d69669c8c4e3c4794
--- /dev/null
+++ b/examples/virtualTets/edit_dragon
@@ -0,0 +1 @@
+../../utilities/bin/editShapeARAP dragon-arap.config
diff --git a/examples/virtualTets/runMesher_comb b/examples/virtualTets/runMesher_comb
new file mode 100644
index 0000000000000000000000000000000000000000..17ac9f7c634c58050eaf2665cd6a5d2c18221864
--- /dev/null
+++ b/examples/virtualTets/runMesher_comb
@@ -0,0 +1 @@
+../../utilities/bin/virtualTetsDriver ../../models/comb/comb.tet.veg ../../models/comb/comb.obj comb-vt.veg -w comb-vt.interp
diff --git a/examples/virtualTets/runMesher_dragon b/examples/virtualTets/runMesher_dragon
new file mode 100644
index 0000000000000000000000000000000000000000..ca57801570d1c3af0174e33a6e30acc1a8ef9548
--- /dev/null
+++ b/examples/virtualTets/runMesher_dragon
@@ -0,0 +1 @@
+../../utilities/bin/virtualTetsDriver ../../models/dragon-77k/dragon-coarse.tet.veg ../../models/dragon-77k/dragon-77k.obj dragon-vt.veg -w dragon-vt.interp
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
deleted file mode 100644
index 8db0c7c27ca78f5a31904a8f01095a8a28dfe816..0000000000000000000000000000000000000000
--- a/include/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-install(
-  FILES
-    lapack-headers.h
-    macros.h
-    matrixMultiplyMacros.h
-    openGL-headers.h
-    triple.h
-  DESTINATION include/vega
-  COMPONENT Development
-)
diff --git a/include/lapack-headers.h b/include/lapack-headers.h
deleted file mode 100644
index 2ba66ec7ef0725b2c59c6a5268da6bafb7934451..0000000000000000000000000000000000000000
--- a/include/lapack-headers.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#if defined(__INTEL_MKL__)
-  #include "mkl_cblas.h"
-  #include "mkl_types.h"
-  #include "mkl_lapack.h"
-  #include "mkl_blas.h"
-#elif defined(__APPLE__)
-  #include <Accelerate/Accelerate.h>
-#else // Requires Lapacke & cblas
-  #include<lapacke.h>
-  #include<cblas.h>
-  #define DGEEV LAPACK_dgeev
-  #define DGELSY LAPACK_dgelsy
-  #define DGESVD LAPACK_dgesvd
-  #define DGESVD LAPACK_dgesvd
-  #define DGESV LAPACK_dgesv
-  #define DGETRF LAPACK_dgetrf
-  #define DGETRI LAPACK_dgetri
-  #define DGETRS LAPACK_dgetrs
-  #define DPOSV LAPACK_dposv
-  #define DSYEV LAPACK_dsyev
-  #define DSYGV LAPACK_dsygv
-  #define DSYSV LAPACK_dsysv
-  #define SGEEV LAPACK_sgeev
-  #define SGELSY LAPACK_sgelsy
-  #define SGESVD LAPACK_sgesvd
-  #define SGESV LAPACK_sgesv
-  #define SGETRF LAPACK_sgetrf
-  #define SGETRI LAPACK_sgetri
-  #define SSYEV LAPACK_ssyev
-  #define SSYGV LAPACK_ssygv
-#endif
\ No newline at end of file
diff --git a/include/openGL-headers.h b/include/openGL-headers.h
deleted file mode 100755
index 53d9180da0c8a415928eaf2000d2acba39cbb980..0000000000000000000000000000000000000000
--- a/include/openGL-headers.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#if defined(WIN32) || defined(__linux__)
-  #include <GL/gl.h>
-  #include <GL/glu.h> 
-  #include <GL/glut.h>
-#elif defined(__APPLE__)
-  #include <OpenGL/gl.h>
-  #include <OpenGL/glu.h>
-  #include <GLUT/glut.h>
-#endif
-
diff --git a/libraries/animationHelper/handleControl.cpp b/libraries/animationHelper/handleControl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..428264b92ed3de109432219db5e3a292f2821670
--- /dev/null
+++ b/libraries/animationHelper/handleControl.cpp
@@ -0,0 +1,121 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "animationHelper" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "handleControl.h"
+#include "openGLHelper.h"
+#include <iostream>
+using namespace std;
+
+void HandleControl::setMouseButtonActivity(bool mouseDown, bool mouseOnObject, bool buildNewHandle, Vec3d worldPos, float zValue,
+    function<int()> getClosestExistingHandle, function<pair<int,bool>()> addOrRemoveHandle)
+{
+  if (mouseDown && mouseOnObject == false) {
+    selectedHandleVertex = -1;
+    handleRender.resetMouseLocation();
+    return;
+  }
+
+  if (mouseDown) {
+    inDrag = true;
+    if (buildNewHandle == false) {
+      // we search the closest existing handle
+      oldMouseWorldPos = worldPos;
+      handleRender.dragHandle();
+      IKZValue = zValue;
+      if (handleRender.isMouseOnHandle() == false) // if the mouse is not on an existing handle
+        selectedHandleVertex = getClosestExistingHandle();
+      printf("Selected handle at: %d\n", selectedHandleVertex);
+    }
+    else { // build a new handle or delete an existing one
+      auto p = addOrRemoveHandle();
+      int clickedVertex = p.first;
+      if (p.second) { // build a new handle
+        selectedHandleVertex = clickedVertex;
+      }
+      else {
+        if (selectedHandleVertex == clickedVertex)
+        {
+          selectedHandleVertex = -1;
+          handleRender.resetMouseLocation();
+        }
+      }
+    } // end buildNewHandle
+  }
+  else { // release button
+    handleRender.releaseHandle();
+    inDrag = false;
+  }
+}
+
+void HandleControl::clearHandleSelection() {
+  selectedHandleVertex = -1;
+  inDrag = false;
+  handleRender.resetMouseLocation();
+}
+
+void HandleControl::processHandleMovement(int x,int y, bool detailMotion, function<void(int vertex, Vec3d diff)> processDrag) {
+  if (isInDrag()) {
+    Vec3d worldPos(0.0);
+    //      unprojectPointFromScreen(g_vMousePos[0], g_vMousePos[1], &worldPos[0]);
+    unprojectPointFromScreen(x, y, IKZValue, &worldPos[0]);
+
+    Vec3d posDiff = worldPos - oldMouseWorldPos;
+    oldMouseWorldPos = worldPos;
+
+    for(int i = 0; i < 3; i++)
+      posDiff[i] = handleRender.isMouseOverAxis(i) ? posDiff[i] : (0.0);
+
+    if (detailMotion)
+      posDiff *= 0.1;
+
+    if (len2(posDiff) > 0)
+    {
+//      cout << "draging..." << endl;
+      // the user is dragging an IK vertex
+      processDrag(selectedHandleVertex, posDiff);
+    }
+  }
+}
+
+void HandleControl::renderHandle(SphericalCamera * camera, const Vec3d & handlePosition, bool reverseHandle) {
+  double lineWidth = 3.0*(16.0/camera->GetRadius());
+  lineWidth = ((lineWidth > 4.0) ? 4.0 : (lineWidth < 3.0 ? 3.0 : lineWidth));
+  double axisLength = 1.0/(16.0/camera->GetRadius());
+  handleRender.setLineWidth(lineWidth);
+  handleRender.setAxisLength(axisLength);
+  handleRender.setHandleReversed(reverseHandle);
+
+  double scale = 2.0/(16/camera->GetRadius());
+  handleRender.setArrowHeadScale(scale);
+
+  handleRender.render(handlePosition);
+}
diff --git a/libraries/animationHelper/handleControl.h b/libraries/animationHelper/handleControl.h
new file mode 100644
index 0000000000000000000000000000000000000000..0ef800c9d4feb717da7a90a56dbfca068ceba9e0
--- /dev/null
+++ b/libraries/animationHelper/handleControl.h
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "animationHelper" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef HANDLECONTROL_H
+#define HANDLECONTROL_H
+
+#include "handleRender.h"
+#include "minivector.h"
+#include "camera.h"
+
+#include <functional>
+
+class HandleControl
+{
+public:
+  // called when button activity happened
+  // mouseDown: true, mouse pressed; false, mouse released
+  // updateHandle: true, call addOrRemoveHandle() to create/remove handles
+  //               false, call getClosestExistingHandle() to select a handle
+  void setMouseButtonActivity(bool mouseDown, bool mouseOnObject, bool updateHandle, Vec3d worldPos, float zValue,
+      std::function<int()> getClosestExistingHandle, std::function<std::pair<int,bool>()> addOrRemoveHandle);
+
+  // respond to handle movement by calling processDrag()
+  void processHandleMovement(int x,int y, bool detailMotion, std::function<void(int vertex, Vec3d diff)> processDrag);
+
+  void setMousePosition(const Vec3d & worldPos) { handleRender.setMouseLocation(worldPos); }
+
+  int getSelectedHandle() const { return selectedHandleVertex; }
+
+  bool isHandleSelected() const { return selectedHandleVertex >= 0; }
+
+  bool isInDrag() const { return inDrag && selectedHandleVertex >= 0; }
+
+  void clearHandleSelection();
+
+  void renderHandle(SphericalCamera * camera, const Vec3d & handlePosition, bool reverseHandle);
+
+protected:
+  int selectedHandleVertex = -1;
+  HandleRender handleRender;
+  Vec3d oldMouseWorldPos{0.0};
+  bool inDrag = false;
+  float IKZValue = 0.0f;
+};
+
+
+#endif
diff --git a/libraries/animationHelper/handleScript.cpp b/libraries/animationHelper/handleScript.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..559ded1f9e1faf8f48a170eb7c05c811071902f5
--- /dev/null
+++ b/libraries/animationHelper/handleScript.cpp
@@ -0,0 +1,185 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "animationHelper" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "handleScript.h"
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <cassert>
+using namespace std;
+
+const string HandleScript::commandName[HandleScript::NUM_COMMANDS] = {
+  "VOID",
+  "ADD",
+  "REMOVE",
+  "MOVE",
+  "SPECIAL",
+  "END"
+};
+
+
+const HandleScript::Command HandleScript::endCommand(END);
+
+#define CHECK_FAILURE(cond) \
+  if (cond) \
+  { \
+    cerr << "Error HandleScript file format at line " << lineCount << endl; \
+    throw 2; \
+  }
+
+HandleScript::HandleScript() : nextCommand(0)
+{
+}
+
+HandleScript::HandleScript(const char * filename) : nextCommand(0)
+{
+  ifstream fin(filename);
+  if (!fin)
+  {
+    cerr << "Cannot open HandleScript file " << filename << endl;
+    throw 1;
+  }
+
+  string line;
+  fin >> ws;
+
+  string typeName;
+  int lineCount = 0;
+  while(!fin.eof())
+  {
+    lineCount++;
+    getline(fin, line);
+    if (line.size() == 0)
+      continue;
+    if (line[0] == '#')
+      continue;
+
+    Command c;
+    istringstream is(line);
+    typeName.clear();
+    is >> typeName;
+    CHECK_FAILURE(is.fail());
+
+    if (typeName == "VOID")
+    {
+      // do nothing
+    }
+    else if (typeName == "END")
+    {
+      c.type = END;
+    }
+    else if (typeName == "SPECIAL")
+    {
+      c.type = SPECIAL;
+      is >> c.vtx;
+      if (is.fail()) 
+        c.vtx = 0;
+    }
+    else if (typeName == "ADD")
+    {
+      c.type = ADD;
+      is >> c.vtx;
+      CHECK_FAILURE(is.fail());
+      CHECK_FAILURE(c.vtx < 0);
+    }
+    else if (typeName == "REMOVE")
+    {
+      c.type = REMOVE;
+      is >> c.vtx;
+      CHECK_FAILURE(is.fail());
+      CHECK_FAILURE(c.vtx < 0);
+    }
+    else if (typeName == "MOVE")
+    {
+      c.type = MOVE;
+      is >> c.vtx;
+      CHECK_FAILURE(is.fail());
+      CHECK_FAILURE(c.vtx < 0);
+      is >> ws;
+      for(int i = 0; i < 3; i++)
+      {
+        char s = is.peek();
+        if (s == ',')
+          is >> s;
+        is >> c.vec[i];
+        CHECK_FAILURE(is.fail());
+      }
+    }
+
+    commands.push_back(c);
+
+    fin >> ws;
+  }
+}
+
+const HandleScript::Command & HandleScript::getNextCommand()
+{
+  if (nextCommand >= commands.size())
+    return endCommand;
+
+  return commands[nextCommand++];
+}
+
+void HandleScript::addCommand(const Command & c)
+{
+  commands.push_back(c);
+}
+
+bool HandleScript::save(const char * filename)
+{
+  ofstream fout(filename);
+  if(!fout)
+    return false;
+
+  for(size_t i = 0; i < commands.size(); i++)
+  {
+    Command & c = commands[i];
+    CommandType type = c.type;
+    assert(type < NUM_COMMANDS);
+    fout << commandName[(int)type]; //output command name
+    if (type == ADD || type == REMOVE || type == SPECIAL)
+    {
+      fout << " " << c.vtx;
+    }
+    else if (type == MOVE)
+    {
+      fout << " " << c.vtx << " " << c.vec[0] << ", " << c.vec[1] << ", " << c.vec[2];
+    }
+    fout << endl;
+    if (fout.fail())
+      return false;
+  }
+
+  fout.close();
+  return true;
+}
diff --git a/libraries/animationHelper/handleScript.h b/libraries/animationHelper/handleScript.h
new file mode 100644
index 0000000000000000000000000000000000000000..fab80c6db1026ff5fd52706e00cfc26c0bf44eeb
--- /dev/null
+++ b/libraries/animationHelper/handleScript.h
@@ -0,0 +1,77 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "animationHelper" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef HANDLESCRIPT_H
+#define HANDLESCRIPT_H
+
+#include "vec3d.h"
+#include <vector>
+#include <string>
+
+// a simple class to load/save a script of handle creation/deletion/movement
+
+class HandleScript
+{
+public:
+
+  enum CommandType
+  {
+    VOID, ADD, REMOVE, MOVE, SPECIAL, END, NUM_COMMANDS
+  };
+
+  struct Command
+  {
+    CommandType type;
+    int vtx;
+    Vec3d vec;
+    Command(CommandType t=VOID) : type(t), vtx(0), vec(0.0) {}
+  };
+
+  HandleScript();
+  HandleScript(const char * filename);
+  virtual ~HandleScript() {}
+
+  const Command & getNextCommand();
+
+  void addCommand(const Command & c);
+  bool save(const char * filename);
+
+  static const std::string commandName[NUM_COMMANDS];
+
+protected:
+  std::vector<Command> commands;
+  size_t nextCommand;
+  const static Command endCommand;
+};
+
+
+#endif
diff --git a/libraries/animationHelper/simulationRecorder.cpp b/libraries/animationHelper/simulationRecorder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1afba632b328a2ae5739b08bf251e08a6b04bfe5
--- /dev/null
+++ b/libraries/animationHelper/simulationRecorder.cpp
@@ -0,0 +1,112 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "animationHelper" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "simulationRecorder.h"
+#include <iostream>
+#include <cstdio>
+#include "matrixIO.h"
+using namespace std;
+
+FrameRecorder::FrameRecorder(int size, const std::string & fn, int mf, int numSkippedIntervalFrames) :
+  r(size), numFrames(0), filename(fn), maxFrames(mf), saved(false), numSkipIter(numSkippedIntervalFrames+1), skipCounter(numSkipIter) {}
+
+void FrameRecorder::addFrame(const double * frame)
+{
+  if (skipCounter >= numSkipIter)
+  {
+    if (numFrames < maxFrames)
+    {
+      //memcpy(u_record + timestepCounter * 3 * n, integratorBase->Getq(), sizeof(double) * 3 * n);
+      for(int i = 0; i < r; i++)
+        u.push_back(frame[i]);
+      numFrames++;
+    }
+
+    if (!saved && numFrames >= maxFrames)
+      save();
+
+    skipCounter = 0;
+  }
+  skipCounter++;
+}
+
+bool FrameRecorder::save()
+{
+  printf("Writing %d frames into %s... ", numFrames, filename.c_str());
+  int ret = WriteMatrixToDisk(filename.c_str(), r, numFrames, &u[0]);
+  if(ret == 0) {
+    cout << "Done." << endl;
+    saved = true;
+    return true;
+  }
+  cout << "Failed." << endl;
+  return false;
+}
+
+void FrameRecorder::clear()
+{
+  saved = false;
+  u.clear();
+  numFrames = 0;
+  skipCounter = numSkipIter;
+}
+
+////////////////////////////////////////////////
+
+SimulationRecorder::SimulationRecorder(size_t numVtx, std::string & fileBaseName, size_t maxFrames) :
+uFrames(3*numVtx, fileBaseName + "d.u", maxFrames),
+vFrames(3*numVtx, fileBaseName + "v.u", maxFrames),
+fFrames(3*numVtx, fileBaseName + "f.u", maxFrames) {}
+
+SimulationRecorder::~SimulationRecorder() {}
+
+void SimulationRecorder::addFrame(const double * u, const double * v, const double * f)
+{
+  uFrames.addFrame(u);
+  vFrames.addFrame(v);
+  fFrames.addFrame(f);
+}
+
+bool SimulationRecorder::save()
+{
+  bool uret = uFrames.save();
+  bool vret = vFrames.save();
+  bool fret = fFrames.save();
+  return uret && vret && fret;
+}
+
+void SimulationRecorder::clear()
+{
+  uFrames.clear();
+  vFrames.clear();
+  fFrames.clear();
+}
diff --git a/libraries/animationHelper/simulationRecorder.h b/libraries/animationHelper/simulationRecorder.h
new file mode 100644
index 0000000000000000000000000000000000000000..71bf5be0d7a45951ae21b86617e048c50e9161a9
--- /dev/null
+++ b/libraries/animationHelper/simulationRecorder.h
@@ -0,0 +1,102 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "animationHelper" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef SIMULATIONRECORDER_H
+#define SIMULATIONRECORDER_H
+#include <climits>
+#include <string>
+#include <vector>
+
+// record frames of data and save to disk
+class FrameRecorder
+{
+public:
+  // r: frame size, 
+  // If numSkippedIntervalFrames == 0, every frame passed to addFrame() is stored.
+  // otherwise, the recorder skips numSkippedIntervalFrames before accepts a frame in addFrame()
+  FrameRecorder(int r, const std::string & filename, int maxFrames_ = INT_MAX, int numSkippedIntervalFrames = 0);
+
+  // add one frame and save all the frames to disk if maxFrames is reached
+  // it will not accept more frames after it has saved the frames to disk
+  // u may not be stored depending on numSkippedIntervalFrames and maxFrames
+  void addFrame(const double * u);
+
+  // save the current frames to disk
+  // return false if it cannot be saved
+  bool save();
+
+  void setFilename(const std::string & newName) { filename = newName; }
+
+  void setMaxFrames(int f) { maxFrames = f; }
+
+  // return true if it has been saved 
+  bool hasSaved() const { return saved;}
+
+  // restart the recorder. Clear all the stored frames and reset saved flag
+  void clear();
+
+  // get # frames stored so far
+  int getNumFrames() const { return numFrames; }
+
+protected:
+  int r; // size of each frame
+  int numFrames;
+  std::string filename;
+  int maxFrames;
+  std::vector<double> u;
+  bool saved;
+  int numSkipIter;
+  int skipCounter;
+};
+
+// save displacement (u), velocity (v) and force (f) to disk and name them as <basename>d.u, <basename>v.u and <basename>f.u
+
+class SimulationRecorder
+{
+public:
+  SimulationRecorder(size_t numVtx, std::string & basename, size_t maxFrames = INT_MAX);
+  virtual ~SimulationRecorder();
+
+  void addFrame(const double * u, const double * v, const double * f);
+  size_t getNumFrames() const { return uFrames.getNumFrames(); }
+
+  bool save();
+  bool hasSaved() const { return uFrames.hasSaved(); }
+
+  void clear();
+
+protected:
+
+  FrameRecorder uFrames, vFrames, fFrames;
+};
+
+#endif
diff --git a/libraries/basicAlgorithms/averagingBuffer.cpp b/libraries/basicAlgorithms/averagingBuffer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3b574cc2d763ee9eb48e7718c03bf39d414327ec
--- /dev/null
+++ b/libraries/basicAlgorithms/averagingBuffer.cpp
@@ -0,0 +1,96 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cassert>
+#include <cmath>
+#include <algorithm>
+#include "averagingBuffer.h"
+using namespace std;
+
+AveragingBuffer::AveragingBuffer(int size_) : size(size_), index(0)
+{
+  assert(size_ > 0);
+  buffer.resize(size);
+  average = 0.0;
+  numValuesAdded = 0;
+}
+
+AveragingBuffer::~AveragingBuffer() {}
+
+void AveragingBuffer::addValue(double value)
+{
+  if (numValuesAdded < size)
+  {
+    average = (average * numValuesAdded + value) / (numValuesAdded + 1);
+    numValuesAdded++;
+  }
+  else
+  {
+    average -= buffer[index] / size;
+    average += value / size;
+  }
+
+  buffer[index] = value;
+  index = (index + 1) % size;
+}
+
+
+void AveragingBuffer::setBufferSize(int newSize)
+{
+  assert(newSize > 0);
+  if (newSize == size)
+    return;
+
+  // create a new buffer, copy enough values from old buffer to the new buffer
+  vector<double> newBuffer(newSize);
+  int numToCopy = min(newSize, numValuesAdded); // max number of values that can be copied
+  for(int i = 0; i < numToCopy; i++)
+    index = (index + (size-1)) % size; // move index backward #numToCopy times
+
+  average = 0.0; // recompute average value
+  for(int i = 0; i < numToCopy; i++) // move index forward, copy data from old buffer to new buffer
+  {
+    newBuffer[i] = buffer[index];
+    average += buffer[index];
+    index = (index + 1) % size;
+  }
+
+  // replace old vars with new vars
+  buffer = newBuffer;
+  index = numToCopy % newSize;
+  size = newSize;
+  numValuesAdded = numToCopy;
+  average /= numValuesAdded;
+}
diff --git a/src/libcorotationalLinearFEM/corotationalLinearFEMMT.h b/libraries/basicAlgorithms/averagingBuffer.h
similarity index 58%
rename from src/libcorotationalLinearFEM/corotationalLinearFEMMT.h
rename to libraries/basicAlgorithms/averagingBuffer.h
index 8b92420bef0b460212d1b6ec0746987b4eabf9c3..5b3d9dbc58eb8790644f2cee4517633163ff633f 100644
--- a/src/libcorotationalLinearFEM/corotationalLinearFEMMT.h
+++ b/libraries/basicAlgorithms/averagingBuffer.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "corotational linear FEM" library , Copyright (C) 2013 USC            *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,41 +30,34 @@
  *                                                                       *
  *************************************************************************/
 
-#ifndef _COROTATIONALLINEARFEMMT_H_
-#define _COROTATIONALLINEARFEMMT_H_
+#ifndef _AVERAGING_BUFFER_H_
+#define _AVERAGING_BUFFER_H_
 
-#include "corotationalLinearFEM.h"
+#include <vector>
+// FIFO averaging buffer
 
-namespace vega
-{
-/*
-   Multi-threaded version of the CorotationalLinearFEM class. 
-   It uses the POSIX threads ("pthreads").
-   See also corotationalLinearFEM.h
-*/
-
-class CorotationalLinearFEMMT : public CorotationalLinearFEM
+class AveragingBuffer
 {
 public:
+  AveragingBuffer(int size);
+  virtual ~AveragingBuffer();
 
-  CorotationalLinearFEMMT(TetMesh * tetMesh, int numThreads=1);
-  virtual ~CorotationalLinearFEMMT();
-
-  virtual void ComputeForceAndStiffnessMatrix(double * vertexDisplacements, double * internalForces, SparseMatrix * stiffnessMatrix, int warp=1);
+  void addValue(double value);
+  double getAverage() const { return average; }
 
-  int GetStartElement(int rank);
-  int GetEndElement(int rank);
+  int getNumValuesAdded() const { return numValuesAdded; }
 
-protected:
-  int numThreads;
-  int * startElement, * endElement;
-  double * internalForceBuffer;
-  SparseMatrix ** stiffnessMatrixBuffer;
+  int getBufferSize() const { return size; }
 
-  void Initialize();
-  void ComputeHelper(double * u, double * uSecondary, void * target, bool addQuantity);
+  void setBufferSize(int newSize);
 
+protected:
+  int size;
+  int index;
+  std::vector<double> buffer;
+  double average;
+  int numValuesAdded; // #values stored in the buffer
 };
-}
+
 #endif
 
diff --git a/libraries/basicAlgorithms/basicAlgorithms.h b/libraries/basicAlgorithms/basicAlgorithms.h
new file mode 100644
index 0000000000000000000000000000000000000000..37f7e0711725fd978796e6ac3b7f1b7b602d3078
--- /dev/null
+++ b/libraries/basicAlgorithms/basicAlgorithms.h
@@ -0,0 +1,228 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef ALGORITHM_H
+#define ALGORITHM_H
+
+#include "range.h"
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <vector>
+#include <cmath>
+
+template<class Container>
+int sizei(const Container & c) { return (int)c.size(); }
+
+// return true if all elements in [first, last] == value
+template<class InputIt, class T>
+bool allOf(InputIt first, InputIt last, const T & value)
+{
+  auto l = [&](const typename std::iterator_traits<InputIt>::value_type & it)
+  {
+    return it == value;
+  };
+  return std::find_if_not(first, last, l) == last;
+}
+
+template<class Container, class Range>
+void insertRange(Container & a, const Range & b) { a.insert(b.begin(), b.end()); }
+
+template<class Container, class Range>
+void insertRangeToEnd(Container & a, const Range & b) { a.insert(a.end(), b.begin(), b.end()); }
+
+// remove duplicate elements in vec and sort them as a side effect
+// elements of type T need a default constructor
+template<class T>
+void sortAndDeduplicate(std::vector<T> & vec)
+{
+  std::sort(vec.begin(), vec.end());
+  auto newEnd = std::unique(vec.begin(), vec.end());
+  vec.resize(std::distance(vec.begin(), newEnd));
+}
+
+// remove duplicate elements in vec and sort them as a side effect
+// use vector::erase to remove duplicate elements so that they don't need a default constructor
+template<class T>
+void sortAndDeduplicateWithErase(std::vector<T> & vec)
+{
+  std::sort(vec.begin(), vec.end());
+  auto newEnd = std::unique(vec.begin(), vec.end());
+  vec.erase(newEnd, vec.end());
+}
+
+// run binary search on a range to find whehter a val is in the range
+template<class RamdomAccessRange, class T>
+bool binarySearchFound(const RamdomAccessRange & range, const T & val)
+{
+  return binary_search(range.begin(), range.end(), val);
+}
+
+template<class T>
+T clamp(const T & a, const T & low, const T & high)
+{
+  if (a < low) { return low; }
+  if (a > high) { return high; }
+  return a;
+}
+
+template<class T>
+void clampSelf(T & a, const T & low, const T & high)
+{
+  if (a < low) { a = low; }
+  else if (a > high) { a = high; }
+}
+
+// return 1 if v > 0, -1 if v < 0, 0 if v == 0
+template<class T>
+int sign(const T & v)
+{
+  if (v > 0) return 1;
+  if (v < 0) return -1;
+  return 0;
+}
+
+// return 0.0 if v < 0.0
+// useful when you compute a value which should be >= 0.0 but might gives negative values due to numerical errors
+inline double sqrtSafe(double v)
+{
+  if (v < 0.0) { return 0.0; }
+  return std::sqrt(v);
+}
+
+// for a range of elements [first, last) and a unary func on the element,
+// find the element that gives max func(element)
+template<class InputIt, class UnaryFunction>
+InputIt maxFunctionValue(InputIt first, InputIt last, UnaryFunction func)
+{
+  if (first == last) { return last; }
+  InputIt largest = first;
+  auto value = func(*first);
+  first++;
+  for (; first != last; first++)
+  {
+    auto newValue = func(*first);
+    if (value < newValue)
+    {
+      largest = first;
+      value = newValue;
+    }
+  }
+  return largest;
+}
+
+// compute median for [first, last)
+// if first == last, return default value: value_type()
+// computing median involves averaging two values, this is done here by dividing by value_type(2)
+template<class RandomIt>
+typename std::iterator_traits<RandomIt>::value_type median(RandomIt first, RandomIt last)
+{
+  typedef typename std::iterator_traits<RandomIt>::value_type value;
+  if (first == last) { return value(); }
+  auto size = distance(first, last);
+  auto nth = first + size/2;
+  nth_element(first, nth, last);
+  if (size % 2 == 1) { return *nth; }
+
+  // size % 2 == 0
+  auto v0 = *nth;
+  nth_element(first, nth-1, last);
+  return (v0 + *(nth-1)) / value(2);
+}
+
+// remove elements from vector "inputVector" by given indices stored in "indices"
+// indices are sorted
+template<class InputVector, class IndexRange>
+void removeByIndices(InputVector & inputVector, const IndexRange & indices)
+{
+  int vecSize = inputVector.size();
+//  int indSize = distance(indices.begin(), indices.end());
+  auto ID = indices.begin();
+  int newEnd = 0;
+  for(int i = 0; i < vecSize; i++)
+  {
+    if (ID != indices.end() && i == *ID)
+    {
+      ID++;
+      continue;
+    }
+    if (i != newEnd)
+    {
+      inputVector[newEnd] = std::move(inputVector[i]);
+    }
+    newEnd++;
+  }
+  inputVector.resize(newEnd);
+}
+
+
+// The template function must be run on a sorted range from first to last.
+// It functions like std::unique, which operates on a sorted range and moves duplicated elements to the back of the range,
+// and finally returns the iterator pointing to the end of the deduplicated elements.
+// However, reduceDuplicates not only moves duplicate elements to the back, but also calls a reduce operator on
+// those duplicated elements.
+// reduce is a binary function: void reduce(T & entryA, T & entryB), reduces data in entryA and entryB and store the result into entryA
+// e.g. input buffer is [0, 1, 2.1, 2.2, 3, 4.1, 4.2, 4.3], and we only compare with the digits before decimal point
+// then the result is a buffer: [0, 1, 2.2, 3, 4.3, x, x, x], the function returns the iterator on the second 4,
+// and reduce is called on (2.1,2.2) and (4.1,4.2), (4.1,4.3)
+template<class ForwardIt, class BinaryReduce, class BinaryPredicate>
+ForwardIt reduceDuplicates(ForwardIt first, ForwardIt last, BinaryReduce reduce, BinaryPredicate p)
+{
+  if (first == last)
+    return last;
+
+  ForwardIt result = first;
+  while (++first != last)
+  {
+    if (p(*result, *first) == false) // if *result and *first are not equal
+    {
+      ++result; // move result forward
+      if (result != first)           // if result and first does not point to the same location, then
+        *result = std::move(*first); // move *first to *result
+    }
+    else // *result and *first are equal
+    {
+      reduce(*result, *first);
+    }
+  }
+  return ++result;
+}
+
+template<class ForwardIt, class BinaryReduce>
+ForwardIt reduceDuplicates(ForwardIt first, ForwardIt last, BinaryReduce reduce)
+{
+  using T = decltype(*first);
+  return reduceDuplicates(first, last ,reduce, std::equal_to<const T &>());
+}
+
+#endif
diff --git a/libraries/basicAlgorithms/containerHelper.cpp b/libraries/basicAlgorithms/containerHelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b9b28ba3679e62e31d16ac57cc94c86665bdb17
--- /dev/null
+++ b/libraries/basicAlgorithms/containerHelper.cpp
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "containerHelper.h"
+using namespace std;
+
+template<class T>
+bool saveToAscii(const std::vector<T> & v, std::ostream & out)
+{
+  out << v.size() << " ";
+  if (out.fail())
+    return false;
+  for(size_t i = 0; i < v.size(); i++)
+  {
+    out << v[i] << " ";
+    if (out.fail())
+      return false;
+  }
+  return true;
+}
+
+template<class T>
+bool loadFromAscii(std::vector<T> & v, std::istream & in)
+{
+  size_t num = 0;
+  in >> num;
+  if (in.fail())
+    return false;
+  v.reserve(v.size() + num);
+  for(size_t i = 0; i < num; i++)
+  {
+    T k = T();
+    in >> k;
+    if (in.fail())
+      return false;
+    v.push_back(k);
+  }
+  return true;
+}
+
+
+template bool saveToAscii<int>(const std::vector<int> & v, std::ostream & out);
+template bool saveToAscii<double>(const std::vector<double> & v, std::ostream & out);
+
+template bool loadFromAscii<int>(std::vector<int> & v, std::istream & in);
+template bool loadFromAscii<double>(std::vector<double> & v, std::istream & in);
+
diff --git a/libraries/basicAlgorithms/containerHelper.h b/libraries/basicAlgorithms/containerHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ba331636d4925ff4279c38cc13913b1b70c9358
--- /dev/null
+++ b/libraries/basicAlgorithms/containerHelper.h
@@ -0,0 +1,434 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef CONTAINERHELPER_H
+#define CONTAINERHELPER_H
+
+#include <vector>
+#include <set>
+#include <map>
+#include <iostream>
+#include <cstring>
+#include <cmath>
+#include <algorithm>
+#include <numeric>
+#include <unordered_set>
+
+/////////////////////////////////////////////////////
+//            Check element in set/map             //
+/////////////////////////////////////////////////////
+
+template <class T, class C, class A>
+bool setFind(const std::set<T,C,A> & s, const T & x) { return s.find(x) != s.end(); }
+template <class T, class C, class A>
+bool setNotFind(const std::set<T,C,A> & s, const T & x) { return s.find(x) == s.end(); }
+
+template <class T, class U, class C, class A>
+bool mapFind(const std::map<T, U, C, A> & s, const T & x) { return s.find(x) != s.end(); }
+template <class T, class U, class C, class A>
+bool mapNotFind(const std::map<T, U, C, A> & s, const T & x) { return s.find(x) == s.end(); }
+
+template <class T, class H, class P, class A>
+bool usetFind(const std::unordered_set<T,H,P,A> & s, const T & x) { return s.find(x) != s.end(); }
+
+template <class T, class H, class P, class A>
+bool usetNotFind(const std::unordered_set<T,H,P,A> & s, const T & x) { return s.find(x) == s.end(); }
+
+/////////////////////////////////////////////////////
+//            insert Data in Container             //
+/////////////////////////////////////////////////////
+
+template<class T, class A, class Container>
+void vectorInsertRangeBack(std::vector<T,A> & v, const Container & c) { v.insert(v.end(), c.begin(), c.end()); }
+
+/////////////////////////////////////////////////////
+//                Memory operations                //
+/////////////////////////////////////////////////////
+
+template<class T, class A>
+inline void memset(std::vector<T, A> & v, unsigned char value = 0)
+{
+  memset(v.data(), value, sizeof(T) * v.size());
+}
+
+template<class T, class A>
+inline void memcpy(std::vector<T, A> & v, const T * src)
+{
+  memcpy(v.data(), src, sizeof(T) * v.size());
+}
+
+// free any memory allocated in the vector
+template<class T, class A>
+inline void freeMemory(std::vector<T, A> & v)
+{
+  std::vector<T> tmp;
+  v.swap(tmp);
+}
+
+template<class T, class A>
+inline bool inVectorRange(int index, const std::vector<T,A> & vec)
+{
+  return index >= 0 && index < (int)vec.size();
+}
+
+/////////////////////////////////////////////////////
+//               Mapping operations                //
+/////////////////////////////////////////////////////
+
+// create a reverse mapping from each value of vec to its index
+// assuming the values in vec are unique
+template<class T, class A>
+std::map<int, int> getReverseMapping(const std::vector<T,A> & vec)
+{
+  std::map<int, int> ret;
+  for(size_t i = 0; i < vec.size(); i++)
+    ret[vec[i]] = i;
+  return ret;
+}
+
+// return a vector containing unique elements
+// inpID2unqID: optionally return the mapping: input ID -> output unique ID
+// unqID2inpID: optionally return the mapping: output unique ID -> input ID
+template<class T, class A>
+std::vector<T,A> findUniqueElements(const std::vector<T,A> vec, std::vector<int> * inpID2unqID = nullptr, std::vector<int> * unqID2inpID = nullptr);
+
+/////////////////////////////////////////////////////
+//           Euclidean norm / distance             //
+/////////////////////////////////////////////////////
+
+// the squared Euclidean norm of the vector
+template<class T>
+T squaredEuclideanNorm(const std::vector<T> & v);
+template<class T>
+T squaredEuclideanNorm(size_t r, const T * v);
+
+// the squared Euclidean distance between the two vectors of the same size
+template<class T>
+T squaredEuclideanDistance(const std::vector<T> & v1, const std::vector<T> & v2);
+template<class T>
+T squaredEuclideanDistance(size_t r, const T * v1, const T * v2);
+
+
+// compute Euclidean norm / distance of double arrays
+inline double EuclideanNorm(const std::vector<double> & v)
+{
+  return sqrt(squaredEuclideanNorm(v));
+}
+
+inline double EuclideanNorm(size_t r, const double * v)
+{
+  return sqrt(squaredEuclideanNorm(r, v));
+}
+
+inline double EuclideanDistance(const std::vector<double> & v1, const std::vector<double> & v2)
+{
+  return sqrt(squaredEuclideanDistance(v1,v2));
+}
+
+inline double EuclideanDistance(size_t r, const double * v1, const double * v2)
+{
+  return sqrt(squaredEuclideanDistance(r, v1, v2));
+}
+
+/////////////////////////////////////////////////////
+//                  Set operation                  //
+/////////////////////////////////////////////////////
+
+// return whether two sets intersect
+template<class T, class C, class A>
+bool intersect(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2);
+
+// compute the intersection of two vectors and store the result in out (previous data in out is removed)
+template<class T>
+void intersect(const std::vector<T> & v1, const std::vector<T> & v2, std::vector<T> & out);
+
+// return whether two sorted vectors intersect
+template<class T>
+bool intersectSorted(const std::vector<T> & v1, const std::vector<T> & v2);
+
+template<class T,class C, class A>
+int getIntersectionSize(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2);
+
+// return true if A is a subset of B
+template<class T,class C, class A>
+bool isSubset(const std::set<T,C,A> & setA, const std::set<T,C,A> & setB);
+
+// compute s1 / s2
+template<class T, class C, class A>
+std::set<T,C,A> setMinus(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2);
+
+
+/////////////////////////////////////////////////////
+//                 Print elements                  //
+/////////////////////////////////////////////////////
+
+// print (a->x, b->y, c->z, ...) to ostream
+template <class T, class U>
+void printMap(std::ostream & os, const std::map<T, U> & s);
+
+/////////////////////////////////////////////////////
+//         SAVE & LOAD IMPLEMENTATIONS             //
+/////////////////////////////////////////////////////
+// for use in saving/loading a class which includes vector as member var.
+// Same save/load file format as used in vega mayaPlugin
+template<class T>
+bool saveToAscii(const std::vector<T> & v, std::ostream & out);
+
+template<class T>
+bool loadFromAscii(std::vector<T> & v, std::istream & in);
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Below are template implementation
+
+template<class T,class C, class A>
+bool intersect(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2)
+{
+  if (s1.size() == 0 || s2.size() == 0)
+    return false;
+  const std::set<T> * ps1 = &s1, * ps2 = &s2;
+  if(s1.size() > s2.size())
+  {
+    ps1 = &s2;
+    ps2 = &s1;
+  }
+  for(typename std::set<T,C,A>::const_iterator it = ps1->begin(); it != ps1->end(); it++)
+    if (ps2->find(*it) != ps2->end())
+      return true;
+  return false;
+}
+
+template<class T>
+void intersect(const std::vector<T> & v1, const std::vector<T> & v2, std::vector<T> & out)
+{
+  out.clear();
+  if (v1.size() == 0 || v2.size() == 0)
+    return;
+  const std::vector<T> * pv1 = &v1, * pv2 = &v2;
+  if (v1.size() > v2.size())
+  {
+    pv1 = &v2; // pv1 points to the smaller set
+    pv2 = &v1;
+  }
+  std::set<T> s2;
+  s2.insert(pv2->begin(), pv2->end());
+  for(size_t i = 0; i < pv1->size(); i++)
+  {
+    const T & x = (*pv1)[i];
+    if (s2.find(x) != s2.end())
+      out.push_back(x);
+  }
+}
+
+template<class T>
+bool intersectSorted(const std::vector<T> & v1, const std::vector<T> & v2)
+{
+  if (v1.size() == 0 || v2.size() == 0)
+    return false;
+  const std::vector<T> * pv1 = &v1, * pv2 = &v2;
+  if (v1.size() > v2.size())
+  {
+    pv1 = &v2; // pv1 points to the smaller set
+    pv2 = &v1;
+  }
+
+  for(size_t i = 0; i < pv1->size(); i++)
+    if (binary_search(pv2->begin(), pv2->end(),(*pv1)[i]))
+      return true;
+
+  return false;
+}
+
+template<class T,class C, class A>
+int getIntersectionSize(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2)
+{
+  if (s1.size() == 0 || s2.size() == 0)
+    return 0;
+  const std::set<T,C,A> * ps1 = &s1, * ps2 = &s2;
+  if(s1.size() > s2.size()) // make ps1->size() <= ps2->size()
+  {
+    ps1 = &s2;
+    ps2 = &s1;
+  }
+  int count = 0;
+  for(typename std::set<T,C,A>::const_iterator it = ps1->begin(); it != ps1->end(); it++)
+    if (ps2->find(*it) != ps2->end())
+      count++;
+  return count;
+}
+
+template<class T,class C, class A>
+bool isSubset(const std::set<T,C,A> & setA, const std::set<T,C,A> & setB)
+{
+  if (setA.size() > setB.size())
+    return false;
+  for(typename std::set<T,C,A>::const_iterator it = setA.begin(); it != setA.end(); it++)
+    if (setB.find(*it) == setB.end())
+      return false;
+  return true;
+}
+
+template<class T, class C, class A>
+std::set<T,C,A> setMinus(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2)
+{
+  std::set<T,C,A> ret;
+  for(const auto & e : s1)
+  {
+    if (setNotFind(s2, e))
+      ret.insert(e);
+  }
+  return ret;
+}
+
+template<class T>
+T squaredEuclideanNorm(const std::vector<T> & v)
+{
+  T sum = 0;
+  for(size_t i = 0; i < v.size(); i++)
+    sum += v[i] * v[i];
+  return sum;
+}
+
+template<class T>
+T squaredEuclideanNorm(size_t r, const T * v)
+{
+  T sum = 0;
+  for(size_t i = 0; i < r; i++)
+    sum += v[i] * v[i];
+  return sum;
+}
+
+template<class T>
+T squaredEuclideanDistance(const std::vector<T> & v0, const std::vector<T> & v1)
+{
+  T sum = 0;
+  for(size_t i = 0; i < v0.size(); i++)
+  {
+    T value = (v0[i] - v1[i]);
+    sum += value * value;
+  }
+  return sum;
+}
+
+template<class T>
+T squaredEuclideanDistance(size_t r, const T * v1, const T * v2)
+{
+  T sum = 0;
+  for(size_t i = 0; i < r; i++)
+  {
+    T value = (v1[i] - v2[i]);
+    sum += value * value;
+  }
+  return sum;
+}
+
+// helper template class to stream a range with begin(), end() implemented
+template<class Range> class RangeStreamer
+{
+public:
+  RangeStreamer(const Range & input) : range(input) {}
+
+  friend std::ostream & operator << (std::ostream & os, const RangeStreamer & r)
+  {
+    os << "{";
+    bool first = true;
+    for(const auto & v : r.range)
+    {
+      if (first) { first = false; }
+      else { os << ", "; }
+      os << v;
+    }
+    os << "}";
+    return os;
+  }
+protected:
+  const Range & range;
+};
+
+// use this function to create RangeStreamer so that you can use << to output a range (e.g. a container)
+// usage:
+// vector<int> a = {1,2,3};
+// cout << streamRange(a) << endl; // print {1, 2, 3}
+template<class Range>
+RangeStreamer<Range> streamRange(const Range & input) { return RangeStreamer<Range>(input); }
+
+template <class T, class U>
+void printMap(std::ostream & os, const std::map<T, U> & s)
+{
+  os << "(";
+  for(typename std::map<T, U>::const_iterator it = s.begin(); it != s.end(); it++)
+  {
+    if (it != s.begin()) os << ", ";
+    os << "<" << it->first << " -> " << it->second << ">";
+  }
+  os << ")";
+}
+
+template<class T, class A>
+std::vector<T,A> findUniqueElements(const std::vector<T,A> vec, std::vector<int> * inpID2unqID, std::vector<int> * unqID2inpID)
+{
+  std::vector<T,A> ret;
+  if (inpID2unqID) inpID2unqID->resize(vec.size());
+  if (unqID2inpID) unqID2inpID->clear();
+
+  if (vec.size() == 0) return ret;
+
+  std::vector<int> buffer(vec.size());
+  std::iota(buffer.begin(), buffer.end(), 0);
+
+  auto cmp = [&](const int & p0, const int & p1)
+  {
+    return vec[p0] < vec[p1];
+  };
+  std::sort(buffer.begin(), buffer.end(), cmp);
+
+  ret.push_back(vec[buffer[0]]);
+  if (unqID2inpID) unqID2inpID->push_back(buffer[0]);
+  if (inpID2unqID) inpID2unqID->at(buffer[0]) = 0;
+  for(std::size_t i = 1; i < vec.size(); i++)
+  {
+    int vecID = buffer[i];
+    if ((ret.back() != vec[vecID]))
+    {
+      ret.push_back(vec[vecID]);
+      if (unqID2inpID) unqID2inpID->push_back(vecID);
+    }
+    if (inpID2unqID) inpID2unqID->at(vecID) = ret.size()-1;
+  }
+
+  return ret;
+}
+#endif
diff --git a/libraries/basicAlgorithms/disjointSet.cpp b/libraries/basicAlgorithms/disjointSet.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3f131e490461e6f4c9bd41c7515bbd2a4b235378
--- /dev/null
+++ b/libraries/basicAlgorithms/disjointSet.cpp
@@ -0,0 +1,183 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC*
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yili Zhao, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#include <cassert>
+#include "disjointSet.h"
+using namespace::std;
+
+void DisjointSet::makeSet(void)
+{
+  for(size_t i = 0; i < parent.size(); i++)
+  {
+    parent[i] = NO_SUCCESSOR;  // each object is initially a root of a tree
+    depth[i] = 0;
+  }
+}
+
+void DisjointSet::extendSizeTo(int num)
+{
+  assert(num >= (int)parent.size());
+
+  parent.resize(num, NO_SUCCESSOR);
+  depth.resize(num, 0);
+}
+
+int DisjointSet::findSet(int x) const
+{
+  if (x < 0 || x >= (int)(parent.size()))
+  {
+    cout << "Error in DisjointSet::findSet(int x): the x value is illegal." << endl;
+    exit(0);
+  }
+  int ancestor = x;
+  while(parent[ancestor] >= 0)
+    ancestor = parent[ancestor];  // backtrack the ancestor
+  //path compression
+  while(x != ancestor)
+  {
+    int temp = parent[x];
+    parent[x] = ancestor;
+    x = temp;
+  }
+  return ancestor;  // here ancestor should be >=0
+}
+
+void DisjointSet::unionSet(int x, int y)
+{
+  int size = parent.size();
+  if (x < 0 || x >= size)
+  {
+    cout << "Error in DisjointSet::UnionSet(int x, int y): the x value is illegal." << endl;
+    exit(0);
+  }
+  if (y < 0 || y >= size)
+  {
+    cout << "Error in DisjointSet::UnionSet(int x, int y): the y value is illegal." << endl;
+    exit(0);
+  }
+  int ancestor1 = findSet(x);
+  int ancestor2 = findSet(y);
+  if (ancestor1 == ancestor2)
+    return; // they are already in the same set
+
+  if (depth[ancestor1] < depth[ancestor2]) // set 2 is deeper
+  {
+    parent[ancestor1] = ancestor2;
+  }
+  else  if (depth[ancestor1] > depth[ancestor2]) // set 1 is deeper
+  {
+    parent[ancestor2] = ancestor1;
+  }
+  else // set 1 and 2 have same depth
+  {
+    parent[ancestor2] = ancestor1;
+    depth[ancestor1]++; // we have to increase the depth
+  }
+}
+
+std::vector<int> DisjointSet::createOldToNewIDMapping()
+{
+  vector<int> old2new(size());
+  int numNewIDs = 0;
+  vector<int> setRepID2newID(size(), -1);
+  for(int i = 0; i < size(); i++)
+  {
+    int repID = findSet(i); // first, store set representive IDs to old2new
+    if (setRepID2newID[repID] < 0)
+    {
+      setRepID2newID[repID] = (numNewIDs++);
+    }
+    old2new[i] = setRepID2newID[repID];
+  }
+  return old2new;
+}
+
+////////////////////////////////////////////////////////////////
+//               DisjointSetDynamic
+////////////////////////////////////////////////////////////////
+
+int DisjointSetDynamic::getSetID(int elementID)
+{
+  auto it = setID.find(elementID);
+  if (it == setID.end())
+  {
+    int newSetID = set.size();
+    setID.emplace(elementID, newSetID);
+    set.extendSizeTo(newSetID + 1);
+    return newSetID;
+  }
+  return it->second;
+}
+
+int DisjointSetDynamic::findSet(int elementID)
+{
+  auto it = setID.find(elementID);
+  if (it == setID.end())
+  {
+    int newSetID = set.size();
+    setID.emplace(elementID, newSetID);
+    set.extendSizeTo(newSetID + 1);
+    return newSetID;
+  }
+  else
+  {
+    return set.findSet(it->second);
+  }
+}
+
+void DisjointSetDynamic::unionSet(int x, int y)
+{
+  int setIDx = getSetID(x);
+  int setIDy = getSetID(y);
+  set.unionSet(setIDx, setIDy);
+}
+
+vector<vector<int>> DisjointSetDynamic::getAllSets() const
+{
+  map<int, vector<int>> sets; // rep setID -> elementIDs
+  for(const auto & p : setID)
+  {
+    int eleID = p.first;
+    int setID = p.second;
+    sets[set.findSet(setID)].push_back(eleID);
+  }
+  vector<vector<int>> ret;
+  for(auto & p : sets)
+  {
+    ret.emplace_back(move(p.second));
+  }
+
+  return ret;
+}
diff --git a/libraries/basicAlgorithms/disjointSet.h b/libraries/basicAlgorithms/disjointSet.h
new file mode 100644
index 0000000000000000000000000000000000000000..b6290be78fe84562beddfbc6a4fbd14cfb327a62
--- /dev/null
+++ b/libraries/basicAlgorithms/disjointSet.h
@@ -0,0 +1,131 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC*
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yili Zhao, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  "Disjoint set", a union-find data structure
+  that keeps track of a set of elements partitioned into a number of disjoint (non-overlapping) subsets.
+
+  Operations:
+  1) MakeSet: Make each set containing only a given element.
+  2) Find: Determine which set a particular element is in. Also useful for determining if two elements are in the same set.
+  3) Union: Combine or merge two sets into a single set.
+
+  Heuristics such as path compression etc. have been implemented.
+*/
+
+#ifndef _DISJOINT_SET_H_
+#define _DISJOINT_SET_H_
+
+#include <vector>
+#include <map>
+
+// implementation of a disjoint set on a fixed array of elements with continuous IDs
+class DisjointSet
+{
+public:
+  enum{ NO_SUCCESSOR = -1 };
+
+  // constructor
+  DisjointSet() {}
+  // initialize num elements and each elemeent is in its own set
+  // 'num' is the total number of elements
+  DisjointSet(int num) { extendSizeTo(num); }
+
+  void clear() { parent.clear(); depth.clear(); }
+
+  int size() const { return parent.size(); }
+
+  // extend the data structure to have size 'num'
+  // previous set relationship is maintained
+  void extendSizeTo(int num);
+
+  // makes each element be its own set (already done in the constructor)
+  void makeSet();
+
+  // returns the representative of the set that x belongs to (path compression is implemented in this function as well)
+  int findSet(int x) const;
+
+  // merge two sets (the smaller one will be absorbed by the larger one)
+  // x and y can be arbitrary elements, they need not be representatives
+  void unionSet(int x, int y);
+  template<class IntIterator>
+  void unionRange(IntIterator itBegin, IntIterator itEnd);
+  template<class IntRange>
+  void unionRange(IntRange range) { unionRange(range.begin(), range.end()); }
+
+  // create a mapping from elementID to new continuous IDs for each set
+  // returned NewIDMapping is: [0, size()) -> [0, #set)
+  std::vector<int> createOldToNewIDMapping();
+
+protected:
+  mutable std::vector<int> parent;
+  std::vector<int> depth;
+};
+
+// implementation of a disjoint set on a dynamic set of elements with possibly incontinuous IDs
+class DisjointSetDynamic
+{
+public:
+  DisjointSetDynamic() {}
+
+  void addElement(int elementID);
+
+  void clear() { set.clear(); setID.clear(); }
+
+  // return the setID of the set this element belongs to
+  // if this element has not been added before, it will be added and form an individual set
+  int findSet(int elementID);
+
+  // union the sets the two input elements (x, y) belong to
+  // if any one of the input element is not added before, it will be added and form an individual set
+  void unionSet(int elementIDx, int elementIDy);
+
+  // return all remaining sets with their elementIDs inside
+  std::vector<std::vector<int>> getAllSets() const;
+
+protected:
+  int getSetID(int elementID);
+
+  DisjointSet set;
+  std::map<int, int> setID; // elementID -> setID
+};
+
+template<class IntIterator>
+void DisjointSet::unionRange(IntIterator itBegin, IntIterator itEnd)
+{
+  auto jt = itBegin;
+  jt++;
+  for(; jt != itEnd; jt++)
+    unionSet(*itBegin, *jt);
+}
+
+#endif
diff --git a/src/libsparseMatrix/sparseMatrixMT.cpp b/libraries/basicAlgorithms/fileIO.cpp
similarity index 52%
rename from src/libsparseMatrix/sparseMatrixMT.cpp
rename to libraries/basicAlgorithms/fileIO.cpp
index 387557bc9b39369bd354cc42d421b40c8971f751..3b4dfc4ef8e69826cd2b1d0c6a08365704fd43af 100644
--- a/src/libsparseMatrix/sparseMatrixMT.cpp
+++ b/libraries/basicAlgorithms/fileIO.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseMatrixMT" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,48 +30,34 @@
  *                                                                       *
  *************************************************************************/
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <set>
-#include <algorithm>
-#include "sparseMatrixMT.h"
+#include "fileIO.h"
+#include "stringHelper.h"
+#include <cstring>
+#include <iostream>
 using namespace std;
 
-// to use the parallel version, enable this flag, and compile code with the flag -fopenmp
-//#define USE_OPENMP
-
-#ifdef USE_OPENMP
-  #include <omp.h>
-#endif
-
-namespace vega
+int readEachLine(const std::string & filename, std::function<int(std::string & line)> processLine, const char * comment)
 {
-void SparseMatrixMT::MultiplyVector(const SparseMatrix * A, const double * input, double * result, int numThreads)
-{
-  int ** indices = A->GetColumnIndices();
-  double ** entries = A->GetEntries();
-  int * rowLengths = A->GetRowLengths();
-  int n = A->GetNumRows();
-  if (numThreads < 0)
-  #ifdef USE_OPENMP
-    numThreads = omp_get_num_threads();
-  #else
-    numThreads = 0;
-  #endif
+  fstream fin(filename.c_str());
+  if (!fin) return 1;
+  return readEachLine(fin, processLine, comment);
+}
 
-  #ifdef USE_OPENMP
-    #pragma omp parallel for num_threads(numThreads)
-  #endif
-  for(int i=0; i<n; i++)
+int readEachLine(std::istream & fin, std::function<int(std::string & line)> processLine, const char * comment)
+{
+  fin >> std::ws;
+  std::string line;
+  int commentStrLen = (comment ? strlen(comment) : 0);
+  while(fin.eof() == false)
   {
-    result[i] = 0;
-    for(int j=0; j < rowLengths[i]; j++)
-    {
-      result[i] += input[indices[i][j]] * entries[i][j];
-    }
+    line.clear();
+    std::getline(fin, line);
+    fin >> std::ws;
+    line = strip(line);
+    if (line.size() == 0) continue;
+    if (comment && strncmp(line.c_str(), comment, commentStrLen) == 0) continue;
+    int ret =processLine(line);
+    if (ret != 0) return ret;
   }
-}
-
+  return 0;
 }
diff --git a/libraries/basicAlgorithms/fileIO.h b/libraries/basicAlgorithms/fileIO.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f92bdb24d40eb54ec95eb58eb447dd89825f6a4
--- /dev/null
+++ b/libraries/basicAlgorithms/fileIO.h
@@ -0,0 +1,49 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef FILEIO_H
+#define FILEIO_H
+
+#include <fstream>
+#include <functional>
+#include <string>
+
+// read each line from fin
+// empty line will be skipped, so input parameter line to processLine is always not empty
+// the input function processLine should return 0 when success
+// return 0 when success, return the error code of processLine otherwise
+// Note: when using filename as input parameter, the function returns 1 when it fails to open the file
+int readEachLine(const std::string & filename, std::function<int(std::string & line)> processLine, const char * comment = "#");
+int readEachLine(std::istream & fin, std::function<int(std::string & line)> processLine, const char * comment = "#");
+
+
+#endif
diff --git a/libraries/basicAlgorithms/filterIterator.h b/libraries/basicAlgorithms/filterIterator.h
new file mode 100644
index 0000000000000000000000000000000000000000..a39a6a6e4a9ce703fe7689cf0b83e6b0d7f24adc
--- /dev/null
+++ b/libraries/basicAlgorithms/filterIterator.h
@@ -0,0 +1,95 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef FILTERITERATOR_H
+#define FILTERITERATOR_H
+
+#include <iterator>
+#include <functional>
+
+template <class BaseIterator>
+struct FilterIterator
+{
+  typedef typename std::iterator_traits<BaseIterator>::value_type value_type;
+  typedef std::function<bool (const value_type &)> Filter;
+
+  FilterIterator() = default;
+  FilterIterator(Filter filter, BaseIterator base, BaseIterator end = {})
+      : iter_(base), end_(end), filter_(filter)
+  {
+    while (iter_ != end_ && !filter_(*iter_))
+    {
+      iter_++;
+    }
+  }
+
+  FilterIterator & operator++()
+  {
+    do
+    {
+      iter_++;
+    } while (iter_ != end_ && !filter_(*iter_));
+    return *this;
+  }
+
+  FilterIterator operator++(int)
+  {
+    FilterIterator copy = *this;
+    ++(*this);
+    return copy;
+  }
+
+  value_type & operator *() { return *iter_; }
+  const value_type & operator *() const { return *iter_; }
+
+  bool operator == (const FilterIterator & it2) const { return iter_ == it2.iter_; }
+  bool operator != (const FilterIterator & it2) const { return !(*this == it2); }
+
+  bool operator == (const BaseIterator & it2) const { return iter_ == it2; }
+  bool operator != (const BaseIterator & it2) const { return !(*this == it2); }
+
+  inline friend bool operator == (const BaseIterator & it, const FilterIterator & it2) { return it == it2.iter_; }
+  inline friend bool operator != (const BaseIterator & it, const FilterIterator & it2) { return it != it2.iter_; }
+
+private:
+  BaseIterator iter_, end_;
+  Filter filter_;
+};
+
+template <class BaseIterator>
+FilterIterator<BaseIterator> makeFilterIterator(typename FilterIterator<BaseIterator>::Filter f,
+    BaseIterator base, BaseIterator end = {})
+{
+  return { f, base, end };
+}
+
+#endif
diff --git a/libraries/basicAlgorithms/hashHelper.h b/libraries/basicAlgorithms/hashHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..0e427f66c7d73d42c13dd4cdd258c5d22f8ce4b0
--- /dev/null
+++ b/libraries/basicAlgorithms/hashHelper.h
@@ -0,0 +1,17 @@
+// The following code is from Boost library.
+// Boost has the following license text:
+// Distributed under the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE_1_0.txt or copy at
+//          https://www.boost.org/LICENSE_1_0.txt)
+#ifndef HASHHELPER_H
+#define HASHHELPER_H
+#include <cstddef>
+
+inline std::size_t hashCombine(std::size_t seed, std::size_t newValue)
+{
+    seed ^= newValue + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+      return seed;
+}
+
+
+#endif
diff --git a/libraries/basicAlgorithms/range.h b/libraries/basicAlgorithms/range.h
new file mode 100644
index 0000000000000000000000000000000000000000..692611a67ea1207ee5502058c89d72d5a472d78b
--- /dev/null
+++ b/libraries/basicAlgorithms/range.h
@@ -0,0 +1,52 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef RANGE_H_
+#define RANGE_H_
+
+// used when you don't have a container but begin(), end() are needed for a range for loop
+template<class InputIt>
+class Range
+{
+public:
+  Range(InputIt a, InputIt b) : a(a), b(b) {}
+  const InputIt & begin() const { return a; }
+  const InputIt & end() const { return b; }
+
+protected:
+  InputIt a, b;
+};
+
+template<class InputIt>
+Range<InputIt> makeRange(InputIt a, InputIt b) { return Range<InputIt>(a, b); }
+
+#endif /* RANGE_H_ */
diff --git a/libraries/basicAlgorithms/reversibleHeap.h b/libraries/basicAlgorithms/reversibleHeap.h
new file mode 100644
index 0000000000000000000000000000000000000000..65183c2ac28700f8523c69b4ae196e6bf8e2c48a
--- /dev/null
+++ b/libraries/basicAlgorithms/reversibleHeap.h
@@ -0,0 +1,151 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef REVERSIBLEHEAP_H
+#define REVERSIBLEHEAP_H
+
+#include <map>
+#include <set>
+#include <cassert>
+#include <cstddef>
+
+// a heap that finds the Key with the smallest Value
+// the input Key must be unique
+// has the ability to find the Key inside the heap (therefore named Reversible)
+template<class Value, class Key>
+class ReversibleHeap
+{
+public:
+  ReversibleHeap() {}
+  virtual ~ReversibleHeap() {}
+
+  typedef std::pair<Value, Key> ValueKeyPair;
+
+  // add (value, key) into the heap, each key must be unique
+  void push(const Value & value, const Key & key);
+
+  void pop();
+
+  // get the top (value, key) pair
+  const ValueKeyPair & top() const { return *setQueue.begin(); }
+
+  void erase(const Key & key);
+
+  // add or update (value, key)
+  void update(const Value & value, const Key & key);
+
+  void clear();
+
+  // get the value associated with key; return NULL if not found
+  const Value * find(const Key & key) const;
+
+  size_t size() const { return setQueue.size(); }
+
+protected:
+
+  typedef std::set<ValueKeyPair> ValueKeySet;
+  typedef typename ValueKeySet::iterator ValueKeySetIter;
+  typedef std::map<Key, ValueKeySetIter> ReverseMap;
+  typedef typename ReverseMap::iterator ReverseMapIter;
+
+  ValueKeySet setQueue;
+  ReverseMap reverseMap;
+};
+
+
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::push(const Value & value, const Key & key)
+{
+  std::pair<ValueKeySetIter, bool> p = setQueue.insert(ValueKeyPair(value, key));
+  assert(p.second == true); // assert no same ValueKeyPair stored before
+  ReverseMapIter it =  reverseMap.find(key);
+  assert(it == reverseMap.end());
+  reverseMap.insert(std::pair<Key, ValueKeySetIter>(key, p.first));
+}
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::update(const Value & value, const Key & key)
+{
+  ReverseMapIter it =  reverseMap.find(key);
+  if (it != reverseMap.end()) // this key has been added before
+  {
+    setQueue.erase(it->second);
+    std::pair<ValueKeySetIter, bool> p = setQueue.insert(ValueKeyPair(value, key));
+    assert(p.second == true); // assert no same ValueKeyPair stored before
+    it->second = p.first; // update the ValueKeySetIter
+  }
+  else
+  {
+    std::pair<ValueKeySetIter, bool> p = setQueue.insert(ValueKeyPair(value, key));
+    assert(p.second == true); // assert no same ValueKeyPair stored before
+    reverseMap.insert(std::pair<Key, ValueKeySetIter>(key, p.first));
+  }
+}
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::pop()
+{
+  ValueKeySetIter it = setQueue.begin();
+  reverseMap.erase(it->second);
+  setQueue.erase(it);
+}
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::erase(const Key & key)
+{
+  ReverseMapIter it =  reverseMap.find(key);
+  if (it != reverseMap.end())
+  {
+    setQueue.erase(it->second);
+    reverseMap.erase(it);
+  }
+}
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::clear()
+{
+  reverseMap.clear();
+  setQueue.clear();
+}
+
+template<class Value, class Key>
+const Value * ReversibleHeap<Value, Key>::find(const Key & key) const
+{
+  typename std::map<Key, ValueKeySetIter>::const_iterator it =  reverseMap.find(key);
+  if (it != reverseMap.end())
+    return &it->second->first; // it->second is a ValueKeySetIter, it->second->first is the value pointed by the ValueKeySetIter
+  return NULL;
+}
+
+
+#endif /* REVERSIBLEHEAP_H_ */
diff --git a/libraries/basicAlgorithms/stringHelper.cpp b/libraries/basicAlgorithms/stringHelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3802cf32559c7aa4398cd91ebcb5a451125fa404
--- /dev/null
+++ b/libraries/basicAlgorithms/stringHelper.cpp
@@ -0,0 +1,129 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "stringHelper.h"
+#include <string>
+#include <algorithm>
+#include <cstring>
+using namespace std;
+
+bool endWith(const string & str, const string & substr)
+{
+  if (str.size() < substr.size())
+    return false;
+  return std::equal(str.begin() + (str.size() - substr.size()), str.end(), substr.begin());
+}
+
+bool iendWith(const string & str, const string & substr)
+{
+  if (str.size() < substr.size())
+    return false;
+  for(auto iter = str.begin() + (str.size() - substr.size()), iter2 = substr.begin(); iter != str.end(); iter++, iter2++)
+  {
+    if (toupper(*iter) != toupper(*iter2))
+      return false;
+  }
+  return true;
+}
+
+bool iendWith(const string & str, const char * substr)
+{
+  size_t sublen = strlen(substr);
+  if (str.size() < sublen)
+    return false;
+  for(auto iter = str.begin() + (str.size() - sublen); iter != str.end(); iter++, substr++)
+  {
+    if (toupper(*iter) != toupper(*substr))
+      return false;
+  }
+  return true;
+}
+
+void stripSelf(string & s)
+{
+  if (s.size() == 0)
+    return;
+  size_t start = 0; // the index for the first non-space character in s
+  for(; start < s.size() && isspace(s[start]); start++) ;
+  if (start == s.size()) // the entire string contains only white-space character
+  {
+    s.clear();
+    return;
+  }
+  size_t end = s.size() - 1;
+  for(; isspace(s[end]); end--) ; // end will not be < start because there must be a non-space character in s
+  // for(; end >= 0 && isspace(s[end]); end--) ;
+  // assert(start <= end);
+  s = s.substr(start, end - start + 1);
+}
+
+string strip(const string & s)
+{
+  if (s.size() == 0)
+    return s;
+  size_t start = 0; // the index for the first non-space character in s
+  for(; start < s.size() && isspace(s[start]); start++) ;
+  if (start == s.size()) // the entire string contains only white-space character
+    return string();
+
+  size_t end = s.size() - 1;
+  for(; isspace(s[end]); end--) ; // end will not be < start because there must be a non-space character in s
+  // for(; end >= 0 && isspace(s[end]); end--) ;
+  // assert(start <= end);
+  return s.substr(start, end - start + 1);
+}
+
+char * stripLight(char * s)
+{
+  size_t start = 0;
+  for(; s[start] != '\0' && isspace(s[start]); start++) ;
+  if (s[start] == '\0')  // empty string
+    return s+start;
+
+  size_t end = strlen(s) - 1;
+  for(; isspace(s[end]); end--) ;
+  s[end+1] = '\0';
+  return s+start;
+}
+
+// convert string to uppercase
+void upperCase(char * s)
+{
+  for(size_t i = 0, num = strlen(s); i < num; i++)
+    s[i] = toupper(s[i]);
+}
+
+void upperCase(string & s)
+{
+  for(size_t i = 0, num = s.size(); i < num; i++)
+    s[i] = toupper(s[i]);
+}
diff --git a/libraries/basicAlgorithms/stringHelper.h b/libraries/basicAlgorithms/stringHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..8694a332be58c6d509834f5fc94766eb366702cf
--- /dev/null
+++ b/libraries/basicAlgorithms/stringHelper.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef STRINGHELPER_H
+#define STRINGHELPER_H
+
+#include <string>
+
+// check with str ends with substr
+bool endWith(const std::string & str, const std::string & substr);
+// version with case-insensitive comparison
+bool iendWith(const std::string & str, const std::string & substr);
+bool iendWith(const std::string & str, const char * substr);
+
+// strip a string, removing white-space characters at the beginning and end of the string
+// if the string contains only white-space characters, return an empty string
+void stripSelf(std::string & s);
+std::string strip(const std::string & s);
+
+// strip a c-string s with minimal modification to the memory
+// remove the end white-space and return the beginning of the stripped s
+char * stripLight(char * s);
+
+inline void skipSpace(char * & s)
+{
+  while(isspace(*s))
+    s++;
+}
+
+// converts a string to upper case
+void upperCase(char * s);
+void upperCase(std::string & s);
+
+#endif
diff --git a/libraries/basicAlgorithms/uniqueIntegerID.cpp b/libraries/basicAlgorithms/uniqueIntegerID.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f2549cb92f0d856f1754aa72fc50c752475fa161
--- /dev/null
+++ b/libraries/basicAlgorithms/uniqueIntegerID.cpp
@@ -0,0 +1,114 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+   Generates unique integer IDs. Makes it possible to release IDs.
+*/
+
+#include "uniqueIntegerID.h"
+using namespace std;
+
+UniqueIntegerID::UniqueIntegerID(unsigned int startID)
+{
+  maxID = startID;
+}
+
+unsigned int UniqueIntegerID::Get()
+{
+  if (deletedIDs.size() > 0)
+  {
+    set<unsigned int> :: iterator iter = deletedIDs.begin();
+    unsigned int value = *iter;
+    deletedIDs.erase(iter);
+    activeIDs.insert(value);
+    return value;
+  }
+  else
+  {
+    maxID++;
+    activeIDs.insert(maxID - 1);
+    return maxID - 1;
+  }
+}
+
+int UniqueIntegerID::Release(unsigned int ID)
+{
+  set<unsigned int> :: iterator iter = activeIDs.find(ID);
+  if (iter == activeIDs.end())
+    return 1;
+
+  unsigned int value = *iter;
+  deletedIDs.insert(value);
+  activeIDs.erase(iter);
+
+  return 0;
+}
+
+void UniqueIntegerID::GetIDs(set<unsigned int> & IDs)
+{
+  IDs = activeIDs;
+}
+
+void UniqueIntegerID::Register(unsigned int ID)
+{
+  // if ID is already active, do nothing
+  if (activeIDs.find(ID) != activeIDs.end())
+    return;
+
+  // if ID is in the deleted set, remove it from deleted set, and add it to active set
+  if (deletedIDs.find(ID) != deletedIDs.end())
+  {
+    deletedIDs.erase(ID);
+    activeIDs.insert(ID);
+    return;
+  }
+
+  // now, we know ID >= maxID (unless the user used startID and then passed a small value as ID, which can be handled by ignoring it)
+  // put ID into activeIDs
+  // move the range from maxID to ID-1 into deleteIDs
+  // set maxID to ID + 1
+  if (ID >= maxID)
+  {
+    activeIDs.insert(ID);
+    for(unsigned int i=maxID; i < ID; i++)
+      deletedIDs.insert(i);
+    maxID = ID + 1;
+  }
+}
+
+void UniqueIntegerID::Clear(unsigned int startID)
+{
+  activeIDs.clear();
+  deletedIDs.clear();
+  maxID = startID;
+}
+
diff --git a/libraries/basicAlgorithms/uniqueIntegerID.h b/libraries/basicAlgorithms/uniqueIntegerID.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4123e0e6d7f1bef3f32ec33996b73b4b983f102
--- /dev/null
+++ b/libraries/basicAlgorithms/uniqueIntegerID.h
@@ -0,0 +1,67 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+   Generates unique integer IDs. Makes it possible to release IDs.
+*/
+
+#ifndef _UNIQUEINTEGERID_
+#define _UNIQUEINTEGERID_
+
+#include <set>
+
+class UniqueIntegerID
+{
+public:
+  UniqueIntegerID(unsigned int startID=0);
+
+  // get a unique ID
+  unsigned int Get();
+
+  // registers an already existing ID
+  void Register(unsigned int ID);
+
+  // returns 0 on success, 1 if ID does not exist
+  int Release(unsigned int ID);
+
+  void GetIDs(std::set<unsigned int> & IDs);
+
+  void Clear(unsigned int startID=0); // clears all IDs
+
+protected:
+  unsigned int maxID;
+  std::set<unsigned int> activeIDs;
+  std::set<unsigned int> deletedIDs;
+};
+
+#endif
+
diff --git a/libraries/basicAlgorithms/uniqueRecentQueue.h b/libraries/basicAlgorithms/uniqueRecentQueue.h
new file mode 100644
index 0000000000000000000000000000000000000000..4bfc7d373eba4d20b3aae1fbaaaf8b5be2cecdda
--- /dev/null
+++ b/libraries/basicAlgorithms/uniqueRecentQueue.h
@@ -0,0 +1,106 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef UNIQUERECENTQUEUE_H_
+#define UNIQUERECENTQUEUE_H_
+
+#include "range.h"
+#include <list>
+
+template<class T>
+class UniqueRecentQueue
+{
+public:
+  UniqueRecentQueue(int maxSize) : maxSize(maxSize) {}
+
+  void push(const T & v);
+  void push(T && v);
+  template<typename... _Args>
+  void emplace(_Args &&... __args);
+
+  typename std::list<T>::const_iterator begin() const { return l.begin(); }
+  typename std::list<T>::const_iterator end() const { return l.end(); }
+
+  std::size_t size() const { return l.size(); }
+  Range<typename std::list<T>::const_iterator> last(int numElements) const;
+
+protected:
+  std::list<T> l;
+  int maxSize = 0;
+};
+
+template<class T>
+void UniqueRecentQueue<T>::push(const T & v)
+{
+  if (l.empty())
+  {
+    l.push_back(v);
+  }
+  else if (l.back() != v)
+  {
+    l.push_back(v);
+    if (l.size() > size_t(maxSize))
+      l.pop_front();
+  }
+}
+
+template<class T>
+void UniqueRecentQueue<T>::push(T && v)
+{
+  if (l.empty())
+  {
+    l.push_back(std::move(v));
+  }
+  else if (l.back() != v)
+  {
+    l.push_back(std::move(v));
+    if (l.size() > maxSize)
+      l.pop_front();
+  }
+}
+
+template<class T> template<typename... _Args>
+void UniqueRecentQueue<T>::emplace(_Args &&... __args)
+{
+  l.emplace_back(std::forward<_Args>(__args)...);
+}
+
+template<class T>
+Range<typename std::list<T>::const_iterator> UniqueRecentQueue<T>::last(int numElements) const
+{
+  int n = l.size() - numElements;
+  typename std::list<T>::const_iterator it = l.begin();
+  for(int i = 0; i < n; i++, it++) {}
+  return { it, l.end() };
+}
+
+#endif /* UNIQUERECENTQUEUE_H_ */
diff --git a/libraries/basicAlgorithms/vectorStack.h b/libraries/basicAlgorithms/vectorStack.h
new file mode 100644
index 0000000000000000000000000000000000000000..647c67e80d026c3a93c7934f03fde69995e3f8e4
--- /dev/null
+++ b/libraries/basicAlgorithms/vectorStack.h
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef VECTORSTACK_H
+#define VECTORSTACK_H
+
+#include <vector>
+
+// a stack/vector combination that holds multiple items in order and has an index
+// that points to the current item
+// useful for displaying history of an editing process
+template <class T>
+class VectorStack
+{
+public:
+  VectorStack() : i(-1) {}
+  void clear() { vec.clear(); i = -1; }
+
+  void push(const T & newItem) { i = vec.size(); vec.push_back(newItem); }
+  void pop() { vec.pop_back(); i = int(vec.size()) - 1; }
+
+  void toBegin() { i = 0; }
+  void toEnd() { i = int(vec.size()) - 1; }
+
+  int size() const { return vec.size(); }
+  bool empty() const { return vec.empty(); }
+
+  const T & current() const { assert(i >= 0 && i < (int)vec.size()); return vec[i]; }
+  T & current() { assert(i >= 0 && i < (int)vec.size()); return vec[i]; }
+  int index() const { return this->i; }
+
+  const T & top() const { assert(vec.size() > 0); return vec.back(); }
+  T & top() { assert(vec.size() > 0); return vec.back(); }
+
+  // ++index
+  int & operator++() { if (i+1 == (int)vec.size()) return i; return ++i; }
+  // --index
+  int & operator--() { if (i <= 0) return i; return --i; }
+  // index++
+  int operator++(int) { if (i+1 == (int)vec.size()) return i; return i++; }
+  // index--
+  int operator--(int) { if (i <= 0) return i; return i--; }
+
+protected:
+  std::vector<T> vec;
+  int i;
+};
+
+
+#endif
diff --git a/src/libcamera/camera.cpp b/libraries/camera/camera.cpp
similarity index 69%
rename from src/libcamera/camera.cpp
rename to libraries/camera/camera.cpp
index 3f05a811508a1468c015d8c1bb076e20a2e441f3..c362357e435c9d3d9ba956b538ea6e4987418d0a 100644
--- a/src/libcamera/camera.cpp
+++ b/libraries/camera/camera.cpp
@@ -43,6 +43,10 @@
 #include <stdlib.h>
 #include <string.h>
 #include <float.h>
+#ifdef WIN32
+  #include <windows.h>
+#endif
+
 #include "camera.h"
 #include "openGL-headers.h"
 #include "macros.h"
@@ -55,9 +59,6 @@ SphericalCamera::SphericalCamera(double R, double Phi, double Theta, double * fo
   up[0] = up_[0]; 
   up[1] = up_[1]; 
   up[2] = up_[2];
-  origin[0] = 0.0; 
-  origin[1] = 0.0; 
-  origin[2] = 0.0;
   Reset();
 }
 
@@ -120,7 +121,7 @@ void SphericalCamera::Reset() // resets position to defaults
   ComputeLocalCoordinateSystem();
 }
 
-void SphericalCamera::SetFocusPosition(double focusPosition_[3])
+void SphericalCamera::SetFocusPosition(const double focusPosition_[3])
 { 
   focusPosition[0] = focusPosition_[0];
   focusPosition[1] = focusPosition_[1];
@@ -129,6 +130,13 @@ void SphericalCamera::SetFocusPosition(double focusPosition_[3])
   ComputeLocalCoordinateSystem();
 }
 
+void SphericalCamera::SetRadius(double r)
+{
+  R = r;
+  ComputeCameraPosition();
+  ComputeLocalCoordinateSystem();
+}
+
 void SphericalCamera::SetPosition(double r, double phi, double theta) 
 { 
   R = r; 
@@ -140,9 +148,22 @@ void SphericalCamera::SetPosition(double r, double phi, double theta)
 
 void SphericalCamera::Look()
 {
-  gluLookAt( cameraPosition[0], cameraPosition[1], cameraPosition[2], 
-    focusPosition[0], focusPosition[1], focusPosition[2], 
-    up[0], up[1], up[2]);
+  if (R >= 0)
+  {
+    gluLookAt( cameraPosition[0], cameraPosition[1], cameraPosition[2], 
+      focusPosition[0], focusPosition[1], focusPosition[2], 
+      up[0], up[1], up[2]);
+  }
+  else
+  {
+    double newFocus[3];
+    for(int i = 0; i < 3; i++)
+    {
+      newFocus[i] = 2 * cameraPosition[i] - focusPosition[i];
+    }
+    gluLookAt( cameraPosition[0], cameraPosition[1], cameraPosition[2], 
+      newFocus[0], newFocus[1], newFocus[2], up[0], up[1], up[2]);
+  }
 }
 
 void SphericalCamera::ComputeCameraPosition()
@@ -182,55 +203,30 @@ void SphericalCamera::GetStereoPosition(double earSeparation,
 
 void SphericalCamera::ComputeLocalCoordinateSystem()
 {
-  xAxis[0] = -R*sin(Phi)*cos(Theta);
+  // cameraPosition = focusPosition + 
+  //  [ R * cos(Phi) * cos (Theta), R * sin(Theta), -R * sin(Phi) * cos (Theta) ]
+  xAxis[0] = -sin(Phi);
   xAxis[1] = 0;
-  xAxis[2] = -R*cos(Phi)*cos(Theta);
-
-  yAxis[0] = -R*cos(Phi)*sin(Theta);
-  yAxis[1] = R*cos(Theta);
-  yAxis[2] = R*sin(Phi)*sin(Theta);
-
-  zAxis[0] = cameraPosition[0] - focusPosition[0];
-  zAxis[1] = cameraPosition[1] - focusPosition[1];
-  zAxis[2] = cameraPosition[2] - focusPosition[2];
-
-  double length;
-
-  length = sqrt(xAxis[0] * xAxis[0] + xAxis[1] * xAxis[1] + xAxis[2] * xAxis[2]);
-  xAxis[0] /= length;
-  xAxis[1] /= length;
-  xAxis[2] /= length;
+  xAxis[2] = -cos(Phi);
 
-  length = sqrt(yAxis[0] * yAxis[0] + yAxis[1] * yAxis[1] + yAxis[2] * yAxis[2]);
-  yAxis[0] /= length;
-  yAxis[1] /= length;
-  yAxis[2] /= length;
+  yAxis[0] = -cos(Phi)*sin(Theta);
+  yAxis[1] = cos(Theta);
+  yAxis[2] = sin(Phi)*sin(Theta);
 
-  length = sqrt(zAxis[0] * zAxis[0] + zAxis[1] * zAxis[1] + zAxis[2] * zAxis[2]);
-  zAxis[0] /= length;
-  zAxis[1] /= length;
-  zAxis[2] /= length;
+  zAxis[0] = cos(Phi) * cos (Theta);
+  zAxis[1] = sin(Theta);
+  zAxis[2] = - sin(Phi) * cos (Theta);
 
-  xAxis2D[0] = -R*sin(Phi);
-  xAxis2D[1] = -R*cos(Phi);
+  xAxis2D[0] = xAxis[0];
+  xAxis2D[1] = xAxis[2];
   xAxis2D[2] = 0;
 
-  yAxis2D[0] = R*cos(Phi);
-  yAxis2D[1] = -R*sin(Phi);
+  yAxis2D[0] = cos(Phi);
+  yAxis2D[1] = -sin(Phi);
   yAxis2D[2] = 0;
-
-  length = sqrt(xAxis2D[0] * xAxis2D[0] + xAxis2D[1] * xAxis2D[1] + xAxis2D[2] * xAxis2D[2]);
-  xAxis2D[0] /= length;
-  xAxis2D[1] /= length;
-  xAxis2D[2] /= length;
-
-  length = sqrt(yAxis2D[0] * yAxis2D[0] + yAxis2D[1] * yAxis2D[1] + yAxis2D[2] * yAxis2D[2]);
-  yAxis2D[0] /= length;
-  yAxis2D[1] /= length;
-  yAxis2D[2] /= length;
 }
 
-void SphericalCamera::Get2DAxes(double xAxis2D[2], double yAxis2D[2])
+void SphericalCamera::Get2DAxes(double xAxis2D[2], double yAxis2D[2]) const
 {
   xAxis2D[0] = this->xAxis2D[0];
   xAxis2D[1] = this->xAxis2D[1];
@@ -239,85 +235,73 @@ void SphericalCamera::Get2DAxes(double xAxis2D[2], double yAxis2D[2])
   yAxis2D[1] = this->yAxis2D[1];
 }
 
-void SphericalCamera::Get3DAxes(double xAxis3D[3], double yAxis3D[3], double zAxis3D[3])
+void SphericalCamera::Get3DAxes(double xAxis3D[3], double yAxis3D[3], double zAxis3D[3]) const
 {
-  ComputeLocalCoordinateSystem();
   memcpy(xAxis3D, this->xAxis, sizeof(double) * 3);
   memcpy(yAxis3D, this->yAxis, sizeof(double) * 3);
   memcpy(zAxis3D, this->zAxis, sizeof(double) * 3);
 }
 
-void SphericalCamera::CameraRotation2WorldRotation2D(double * c, double * w)
-{
-  // c is in row-major format
-  // 0  1  2
-  // 3  4  5
-  // 6  7  8 
-
-  // w = cameraMatrix * c
-  w[0] = c[0] * xAxis2D[0] + c[6] * yAxis2D[0];
-  w[3] = c[3];
-  w[6] = c[0] * xAxis2D[1] + c[6] * yAxis2D[1];
-
-  w[1] = c[1] * xAxis2D[0] + c[7] * yAxis2D[0];
-  w[4] = c[4];
-  w[7] = c[1] * xAxis2D[1] + c[7] * yAxis2D[1];
+void SphericalCamera::CameraVector2WorldVector2D(const double c[3], double w[3]) const
+{ 
+  CameraVector2WorldVector2D(c[0], c[1], c[2], w);
+}
 
-  w[2] = c[2] * xAxis2D[0] + c[8] * yAxis2D[0];
-  w[5] = c[5];
-  w[8] = c[2] * xAxis2D[1] + c[8] * yAxis2D[1];
+void SphericalCamera::CameraVector2WorldVector2D(double c0, double c1, double c2, double w[3]) const
+{ 
+  w[0] = cameraPosition[0] + ( c0 * xAxis2D[0] + c2 * yAxis2D[0] ) * camera2WorldScalingFactor;
+  w[1] = cameraPosition[1] + c1 * camera2WorldScalingFactor;
+  w[2] = cameraPosition[2] + ( c0 * xAxis2D[1] + c2 * yAxis2D[1] ) * camera2WorldScalingFactor;
 }
 
-void SphericalCamera::CameraRotation2WorldRotation2D(float * c, float * w)
+void SphericalCamera::WorldVector2CameraVector2D(const float w[3], float c[3]) const
 {
-  // c is in row-major format
-  // 0  1  2
-  // 3  4  5
-  // 6  7  8 
-
-  // w = cameraMatrix * c
-  w[0] = c[0] * xAxis2D[0] + c[6] * yAxis2D[0];
-  w[3] = c[3];
-  w[6] = c[0] * xAxis2D[1] + c[6] * yAxis2D[1];
-
-  w[1] = c[1] * xAxis2D[0] + c[7] * yAxis2D[0];
-  w[4] = c[4];
-  w[7] = c[1] * xAxis2D[1] + c[7] * yAxis2D[1];
-
-  w[2] = c[2] * xAxis2D[0] + c[8] * yAxis2D[0];
-  w[5] = c[5];
-  w[8] = c[2] * xAxis2D[1] + c[8] * yAxis2D[1];
+  c[0] = ((yAxis2D[1] * (w[0] - cameraPosition[0]) - yAxis2D[0] * (w[2] - cameraPosition[2]))/camera2WorldScalingFactor)/(xAxis2D[0] * yAxis2D[1] - xAxis2D[1] * yAxis2D[0]);
+  c[1] = (w[1] - cameraPosition[1])/camera2WorldScalingFactor;
+  c[2] = ((xAxis2D[1] * (w[0] - cameraPosition[0]) - xAxis2D[0] * (w[2] - cameraPosition[2]))/camera2WorldScalingFactor)/(yAxis2D[0] * xAxis2D[1] - yAxis2D[1] * xAxis2D[0]);
 }
 
-void SphericalCamera::CameraVector2WorldVector2D(double * c, double * w)
-{ 
-  w[0] = origin[0] + ( c[0] * xAxis2D[0] + c[2] * yAxis2D[0] ) * camera2WorldScalingFactor;
-  w[1] = origin[1] + c[1] * camera2WorldScalingFactor;
-  w[2] = origin[2] + ( c[0] * xAxis2D[1] + c[2] * yAxis2D[1] ) * camera2WorldScalingFactor;
+void SphericalCamera::WorldVector2CameraVector2D(const double w[3], double c[3]) const
+{
+  c[0] = ((yAxis2D[1] * (w[0] - cameraPosition[0]) - yAxis2D[0] * (w[2] - cameraPosition[2]))/camera2WorldScalingFactor)/(xAxis2D[0] * yAxis2D[1] - xAxis2D[1] * yAxis2D[0]);
+  c[1] = (w[1] - cameraPosition[1])/camera2WorldScalingFactor;
+  c[2] = ((xAxis2D[1] * (w[0] - cameraPosition[0]) - xAxis2D[0] * (w[2] - cameraPosition[2]))/camera2WorldScalingFactor)/(yAxis2D[0] * xAxis2D[1] - yAxis2D[1] * xAxis2D[0]);
 }
 
-void SphericalCamera::CameraVector2WorldVector2D(double c0, double c1, double c2, double * w)
+void SphericalCamera::CameraVector2WorldVector_OrientationOnly2D(const double c[3], double w[3]) const
 { 
-  w[0] = origin[0] + ( c0 * xAxis2D[0] + c2 * yAxis2D[0] ) * camera2WorldScalingFactor;
-  w[1] = origin[1] + c1 * camera2WorldScalingFactor;
-  w[2] = origin[2] + ( c0 * xAxis2D[1] + c2 * yAxis2D[1] ) * camera2WorldScalingFactor;
+  CameraVector2WorldVector_OrientationOnly2D(c[0], c[1], c[2], w);
 }
 
-void SphericalCamera::CameraVector2WorldVector_OrientationOnly2D(double * c, double * w)
+void SphericalCamera::CameraVector2WorldVector_OrientationOnly2D(const float c[3], float w[3]) const
 { 
   w[0] = ( c[0] * xAxis2D[0] + c[2] * yAxis2D[0] ) * camera2WorldScalingFactor;
   w[1] = c[1] * camera2WorldScalingFactor;
   w[2] = ( c[0] * xAxis2D[1] + c[2] * yAxis2D[1] ) * camera2WorldScalingFactor;
 }
 
-void SphericalCamera::CameraVector2WorldVector_OrientationOnly2D(double c0, double c1, double c2, double * w)
+void SphericalCamera::CameraVector2WorldVector_OrientationOnly2D(double c0, double c1, double c2, double w[3]) const
 { 
   w[0] = ( c0 * xAxis2D[0] + c2 * yAxis2D[0] ) * camera2WorldScalingFactor;
   w[1] = c1 * camera2WorldScalingFactor;
   w[2] = ( c0 * xAxis2D[1] + c2 * yAxis2D[1] ) * camera2WorldScalingFactor;
 }
 
-void SphericalCamera::CameraVector2WorldVector_OrientationOnly3D(double c0, double c1, double c2, double * w)
+void SphericalCamera::CameraVector2WorldVector_NoScaling_OrientationOnly2D(const double c[3], double w[3]) const
+{ 
+  w[0] = ( c[0] * xAxis2D[0] + c[2] * yAxis2D[0] ); 
+  w[1] = c[1];
+  w[2] = ( c[0] * xAxis2D[1] + c[2] * yAxis2D[1] );
+}
+
+void SphericalCamera::CameraVector2WorldVector_NoScaling_OrientationOnly2D(const float c[3], float w[3]) const
+{ 
+  w[0] = ( c[0] * xAxis2D[0] + c[2] * yAxis2D[0] );
+  w[1] = c[1];
+  w[2] = ( c[0] * xAxis2D[1] + c[2] * yAxis2D[1] );
+}
+
+void SphericalCamera::CameraVector2WorldVector_OrientationOnly3D(double c0, double c1, double c2, double w[3]) const
 { 
   // cameraPosition = focusPosition + 
   //   (R * cos(Phi) * cos (Theta), R * sin(Theta), -R * sin(Phi) * cos (Theta));
@@ -351,7 +335,93 @@ void SphericalCamera::WorldVector2CameraVector_OrientationOnly2D(double w0, doub
   c[2] = w0 * yAxis2D[0] + w2 * yAxis2D[1];
 }
 
-void SphericalCamera::SavePosition(char * filename)
+void SphericalCamera::WorldVector2CameraVector_Scaling_OrientationOnly2D(float * w, float * c)
+{
+  c[0] = (w[0] * xAxis2D[0] + w[2] * xAxis2D[1]) / camera2WorldScalingFactor;
+  c[1] = w[1] / camera2WorldScalingFactor;
+  c[2] = (w[0] * yAxis2D[0] + w[2] * yAxis2D[1]) / camera2WorldScalingFactor;
+}
+
+void SphericalCamera::WorldVector2CameraVector_Scaling_OrientationOnly2D(double * w, double * c)
+{
+  c[0] = (w[0] * xAxis2D[0] + w[2] * xAxis2D[1]) / camera2WorldScalingFactor;
+  c[1] = w[1] / camera2WorldScalingFactor;
+  c[2] = (w[0] * yAxis2D[0] + w[2] * yAxis2D[1]) / camera2WorldScalingFactor;
+}
+
+void SphericalCamera::CameraRotation2WorldRotation2D(const double c[9], double w[9]) const
+{
+  // c is in row-major format
+  // 0  1  2
+  // 3  4  5
+  // 6  7  8 
+
+  // w = cameraMatrix * c
+  w[0] = c[0] * xAxis2D[0] + c[6] * yAxis2D[0];
+  w[3] = c[3];
+  w[6] = c[0] * xAxis2D[1] + c[6] * yAxis2D[1];
+
+  w[1] = c[1] * xAxis2D[0] + c[7] * yAxis2D[0];
+  w[4] = c[4];
+  w[7] = c[1] * xAxis2D[1] + c[7] * yAxis2D[1];
+
+  w[2] = c[2] * xAxis2D[0] + c[8] * yAxis2D[0];
+  w[5] = c[5];
+  w[8] = c[2] * xAxis2D[1] + c[8] * yAxis2D[1];
+}
+
+void SphericalCamera::CameraRotation2WorldRotation2D(const float c[9], float w[9]) const
+{
+  // c is in row-major format
+  // 0  1  2
+  // 3  4  5
+  // 6  7  8 
+
+  // w = cameraMatrix * c
+  w[0] = c[0] * xAxis2D[0] + c[6] * yAxis2D[0];
+  w[3] = c[3];
+  w[6] = c[0] * xAxis2D[1] + c[6] * yAxis2D[1];
+
+  w[1] = c[1] * xAxis2D[0] + c[7] * yAxis2D[0];
+  w[4] = c[4];
+  w[7] = c[1] * xAxis2D[1] + c[7] * yAxis2D[1];
+
+  w[2] = c[2] * xAxis2D[0] + c[8] * yAxis2D[0];
+  w[5] = c[5];
+  w[8] = c[2] * xAxis2D[1] + c[8] * yAxis2D[1];
+}
+
+void SphericalCamera::WorldRotation2CameraRotation2D(const double w[9], double c[9]) const
+{
+  c[0] = w[0] * xAxis2D[0] + w[6] * xAxis2D[1];
+  c[3] = w[3];
+  c[6] = w[0] * yAxis2D[0] + w[6] * yAxis2D[1];
+
+  c[1] = w[1] * xAxis2D[0] + w[7] * xAxis2D[1];
+  c[4] = w[4];
+  c[7] = w[1] * yAxis2D[0] + w[7] * yAxis2D[1];
+
+  c[2] = w[2] * xAxis2D[0] + w[8] * xAxis2D[1];
+  c[5] = w[5];
+  c[8] = w[2] * yAxis2D[0] + w[8] * yAxis2D[1];
+}
+
+void SphericalCamera::WorldRotation2CameraRotation2D(const float w[9], float c[9]) const
+{
+  c[0] = w[0] * xAxis2D[0] + w[6] * xAxis2D[1];
+  c[3] = w[3];
+  c[6] = w[0] * yAxis2D[0] + w[6] * yAxis2D[1];
+
+  c[1] = w[1] * xAxis2D[0] + w[7] * xAxis2D[1];
+  c[4] = w[4];
+  c[7] = w[1] * yAxis2D[0] + w[7] * yAxis2D[1];
+
+  c[2] = w[2] * xAxis2D[0] + w[8] * xAxis2D[1];
+  c[5] = w[5];
+  c[8] = w[2] * yAxis2D[0] + w[8] * yAxis2D[1];
+}
+
+void SphericalCamera::SavePosition(const char * filename)
 {
   FILE * fout;
   fout = fopen(filename, "w");
@@ -360,7 +430,7 @@ void SphericalCamera::SavePosition(char * filename)
   fclose(fout);
 }
 
-void SphericalCamera::LoadPosition(char * filename)
+void SphericalCamera::LoadPosition(const char * filename)
 {
   FILE * fin;
   fin = fopen(filename,"r");
@@ -443,9 +513,9 @@ void SphericalCamera::CameraTransform2WorldTransform2D(double * c, double * w)
   w[10] = c[2] * xAxis2D[1] + c[10] * yAxis2D[1];
   w[14] = 0;
 
-  w[3] = origin[0] + ( c[3] * xAxis2D[0] + c[11] * yAxis2D[0] ) * camera2WorldScalingFactor;
-  w[7] = origin[1] + c[7] * camera2WorldScalingFactor;
-  w[11] = origin[2] + ( c[3] * xAxis2D[1] + c[11] * yAxis2D[1] ) * camera2WorldScalingFactor;
+  w[3] = cameraPosition[0] + ( c[3] * xAxis2D[0] + c[11] * yAxis2D[0] ) * camera2WorldScalingFactor;
+  w[7] = cameraPosition[1] + c[7] * camera2WorldScalingFactor;
+  w[11] = cameraPosition[2] + ( c[3] * xAxis2D[1] + c[11] * yAxis2D[1] ) * camera2WorldScalingFactor;
   w[15] = 1;
 }
 
@@ -475,9 +545,9 @@ void SphericalCamera::CameraTransform2WorldTransform2D(float * c, float * w)
   w[10] = c[2] * xAxis2D[1] + c[10] * yAxis2D[1];
   w[14] = 0;
 
-  w[3] = origin[0] + ( c[3] * xAxis2D[0] + c[11] * yAxis2D[0] ) * camera2WorldScalingFactor;
-  w[7] = origin[1] + c[7] * camera2WorldScalingFactor;
-  w[11] = origin[2] + ( c[3] * xAxis2D[1] + c[11] * yAxis2D[1] ) * camera2WorldScalingFactor;
+  w[3] = cameraPosition[0] + ( c[3] * xAxis2D[0] + c[11] * yAxis2D[0] ) * camera2WorldScalingFactor;
+  w[7] = cameraPosition[1] + c[7] * camera2WorldScalingFactor;
+  w[11] = cameraPosition[2] + ( c[3] * xAxis2D[1] + c[11] * yAxis2D[1] ) * camera2WorldScalingFactor;
   w[15] = 1;
 }
 
@@ -507,9 +577,9 @@ void SphericalCamera::CameraTransform2WorldTransform2D_ColumnMajor(float * c, fl
   w[10] = c[8] * xAxis2D[1] + c[10] * yAxis2D[1];
   w[11] = 0;
 
-  w[12] = origin[0] + ( c[12] * xAxis2D[0] + c[14] * yAxis2D[0] ) * camera2WorldScalingFactor;
-  w[13] = origin[1] + c[13] * camera2WorldScalingFactor;
-  w[14] = origin[2] + ( c[12] * xAxis2D[1] + c[14] * yAxis2D[1] ) * camera2WorldScalingFactor;
+  w[12] = cameraPosition[0] + ( c[12] * xAxis2D[0] + c[14] * yAxis2D[0] ) * camera2WorldScalingFactor;
+  w[13] = cameraPosition[1] + c[13] * camera2WorldScalingFactor;
+  w[14] = cameraPosition[2] + ( c[12] * xAxis2D[1] + c[14] * yAxis2D[1] ) * camera2WorldScalingFactor;
   w[15] = 1;
 }
 
@@ -540,19 +610,24 @@ void SphericalCamera::CameraTransform2WorldTransform2D_NoScaling_ColumnMajor(flo
   w[10] = c[8] * xAxis2D[1] + c[10] * yAxis2D[1];
   w[11] = 0;
 
-  w[12] = origin[0] + ( c[12] * xAxis2D[0] + c[14] * yAxis2D[0] );
-  w[13] = origin[1] + c[13];
-  w[14] = origin[2] + ( c[12] * xAxis2D[1] + c[14] * yAxis2D[1] );
+  w[12] = cameraPosition[0] + ( c[12] * xAxis2D[0] + c[14] * yAxis2D[0] );
+  w[13] = cameraPosition[1] + c[13];
+  w[14] = cameraPosition[2] + ( c[12] * xAxis2D[1] + c[14] * yAxis2D[1] );
   w[15] = 1;
 }
 
-void SphericalCamera::GetFocusPosition(double focusPosition_[3])
+void SphericalCamera::GetFocusPosition(double focusPosition_[3]) const
 { 
   focusPosition_[0] = focusPosition[0];
   focusPosition_[1] = focusPosition[1];
   focusPosition_[2] = focusPosition[2];
 }
 
+void SphericalCamera::GetCameraPosition(double cameraPosition_[3]) const
+{ 
+  memcpy(cameraPosition_, cameraPosition, sizeof(double) * 3);
+}
+
 void SphericalCamera::MoveFocusRight(double amount)
 {
   double focusPos[3];
diff --git a/src/libcamera/camera.h b/libraries/camera/camera.h
similarity index 59%
rename from src/libcamera/camera.h
rename to libraries/camera/camera.h
index 841107d807249f38d74bedacd18739d1ce1ba1be..1de1540fdfac501b1d3f679754db654ca501eb16 100644
--- a/src/libcamera/camera.h
+++ b/libraries/camera/camera.h
@@ -31,8 +31,12 @@
 
   A "spherical" OpenGL camera class, with the ability to set the OpenGL modelview transformation matrix.
 
-  A "spherical" camera is a camera that is located at location (R, Phi, Theta), in spherical coordinates, away from some focus position. It is pointed towards the focus position. It is useful for orbiting a fixed location (focus position) in interactive applications.
-
+  A "spherical" camera is a camera that is located at location (R, Phi, Theta), in spherical coordinates, away from some focus position. 
+  R: radius of the sphere (-inf, +inf)
+  Phi: longitudinal angle value [0, 2PI), Phi goes counter-clockwise if viewed in opposite y direction
+  Theta: latitudinal angle value (-PI/2.0, PI/2.0), 0 at equator, PI/2.0 at north pole, -P/2.0 at south pole
+  It is pointed towards the focus position. It is useful for orbiting a fixed location (focus position) in interactive applications.
+  
   cameraPosition = focusPosition + 
    [ R * cos(Phi) * cos (Theta), R * sin(Theta), -R * sin(Phi) * cos (Theta) ]
 
@@ -41,39 +45,42 @@
 #ifndef _CAMERA_H_
 #define _CAMERA_H_
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 class SphericalCamera
 {
 public:
 
   // R is the camera distance from the focus position 
   // Phi, Theta are camera longitude and lattitude (in radians)
+  // cameraPosition = focusPosition + [ R * cos(Phi) * cos (Theta), R * sin(Theta), -R * sin(Phi) * cos (Theta) ]
   // focusPosition is the 3D camera focus position (e.g., [0, 0, 0])
   // up is the 3D up vector (e.g., [0, 1, 0])
   // movementSensitivity controls how much camera moves during the camera move commands
   // camera2WorldScalingFactor controls the scaling between the "camera" coordinate systems and "world" coordinate systems (only needed with the camera<-->world conversion routines)
-  SphericalCamera(double R, double Phi, double Theta, double * focusPosition, double * up, double movementSensitivity=1.0, double camera2WorldScalingFactor=1.0);
+  SphericalCamera(double R, double Phi, double Theta, double focusPosition[3], double up[3], 
+    double movementSensitivity=1.0, double camera2WorldScalingFactor=1.0);
 
   // commands to move the camera
-  void MoveRight(double);
-  void MoveUp(double);
-  void ZoomIn(double); // negative value of parameter means zoom out
-  void MoveIn(double);
-  void MoveFocusRight(double); // move the camera focus, relative to the current focus
-  void MoveFocusUp(double);
-
-  void Look(); // calls gluLookAt to set the OpenGL modelview matrix, corresponding to the current camera position, focus, and up vector
-
-  double GetRadius() { return R; }
-  double GetPhi() { return Phi; }
-  double GetTheta() { return Theta; }
-
+  void MoveRight(double amount); // adds Phi by amount * movementSensitivity
+  void MoveUp(double amount);    // adds Theta by amount * movementSensitivity
+  void ZoomIn(double amount);    // negative value of parameter means zoom out; reduces R by amount * movementSensitivity;
+  void MoveIn(double amount);    // adds focusPosition by amount * zAxis
+  void MoveFocusRight(double amount); // move the camera focus, relative to the current focus
+  void MoveFocusUp(double amount);
+
+  // calls gluLookAt to set the OpenGL modelview matrix, corresponding to the current camera position, focus, and up vector
+  // if R > 0, the camera looks at the focus position
+  // if R < 0, the camera is behind the focus position, it looks beyond the focus position
+  void Look(); 
+
+  double GetRadius() const { return R; }
+  double GetPhi() const { return Phi; }
+  double GetTheta() const { return Theta; }
+  void GetFocusPosition(double focusPosition_[3]) const;
+  void GetCameraPosition(double cameraPosition_[3]) const;
+
+  void SetRadius(double r);
+  void SetFocusPosition(const double focusPosition_[3]);
   void SetPosition(double r, double phi, double theta);
-  void SetFocusPosition(double focusPosition_[3]);
-  void GetFocusPosition(double focusPosition_[3]);
 
   // get the camera position in the world coordinate system
   void GetAbsWorldPosition(double & x, double & y, double & z);
@@ -82,8 +89,8 @@ public:
   void Reset(); // reset camera position to the default position
   
   // save/load current camera position to/from a file
-  void SavePosition(char * filename);
-  void LoadPosition(char * filename);
+  void SavePosition(const char * filename);
+  void LoadPosition(const char * filename);
 
   // push/pop camera position onto an internal stack
   void PushPosition();
@@ -99,25 +106,32 @@ public:
 
   // === routines below this point are advanced ===
 
-  double GetCamera2WorldScalingFactor() { return camera2WorldScalingFactor; }
+  double GetCamera2WorldScalingFactor() const { return camera2WorldScalingFactor; }
   void SetCamera2WorldScalingFactor(double factor) { camera2WorldScalingFactor = factor; }
 
-  // sets the world-coordinate position corresponding to zero position in the "camera" coordinate system
-  inline void SetOrigin(double * origin_) { origin[0] = origin_[0]; origin[1] = origin_[1]; origin[2] = origin_[2]; }
-
   // transform a location specified in the camera coordinate system into the world coordinate system
-  void CameraVector2WorldVector2D(double * c, double * w);
-  void CameraVector2WorldVector2D(double cx, double cy, double cz, double * w);
+  void CameraVector2WorldVector2D(const double c[3], double w[3]) const;
+  void CameraVector2WorldVector2D(double cx, double cy, double cz, double w[3]) const;
+  void WorldVector2CameraVector2D(const float w[3], float c[3]) const;
+  void WorldVector2CameraVector2D(const double w[3], double c[3]) const;
 
   // same as above, except that it doesn't take into account the position of the camera
   // useful for transforming velocities
-  void CameraVector2WorldVector_OrientationOnly2D(double * c, double * w);
-  void CameraVector2WorldVector_OrientationOnly2D(double cx, double cy, double cz, double * w);
-  void CameraVector2WorldVector_OrientationOnly3D(double cx, double cy, double cz, double * w); // uses a coordinate system where z-axis is normal to the camera sphere
+  void CameraVector2WorldVector_OrientationOnly2D(const double c[3], double w[3]) const;
+  void CameraVector2WorldVector_OrientationOnly2D(const float c[3], float w[3]) const;
+  void CameraVector2WorldVector_NoScaling_OrientationOnly2D(const double c[3], double w[3]) const;
+  void CameraVector2WorldVector_NoScaling_OrientationOnly2D(const float c[3], float w[3]) const;
+  void CameraVector2WorldVector_OrientationOnly2D(double cx, double cy, double cz, double w[3]) const;
+  // uses a coordinate system where z-axis is the normal of the camera sphere
+  void CameraVector2WorldVector_OrientationOnly3D(double cx, double cy, double cz, double w[3]) const;
 
   // converts a rotation in camera system to the corresponding rotation in the world coordinate system
-  void CameraRotation2WorldRotation2D(float * c, float * w);
-  void CameraRotation2WorldRotation2D(double * c, double * w);
+  // input is a 3x3 row-major matrix
+  void CameraRotation2WorldRotation2D(const float c[9], float w[9]) const;
+  void CameraRotation2WorldRotation2D(const double c[9], double w[9]) const;
+
+  void WorldRotation2CameraRotation2D(const float c[9], float w[9]) const;
+  void WorldRotation2CameraRotation2D(const double c[9], double w[9]) const;
 
   // converts 4x4 row-major camera-space transform matrix to 4x4 row-major world-space transform matrix
   void CameraTransform2WorldTransform2D(double * c, double * w); 
@@ -130,15 +144,16 @@ public:
   // useful for transforming forces and torques from world coordinate system back to the user(=camera) coordinate system
   void WorldVector2CameraVector_OrientationOnly2D(double * w, double * c);
   void WorldVector2CameraVector_OrientationOnly2D(float * w, float * c);
+  void WorldVector2CameraVector_Scaling_OrientationOnly2D(double * w, double * c);
+  void WorldVector2CameraVector_Scaling_OrientationOnly2D(float * w, float * c);
   void WorldVector2CameraVector_OrientationOnly2D(double w0, double w1, double w2, double * c);
 
-  void Get2DAxes(double xAxis2D[2], double yAxis2D[2]);
-  void Get3DAxes(double xAxis3D[3], double yAxis3D[3], double zAxis3D[3]);
-
-  void ComputeCameraPosition(); // the user normally never needs to call this routine
-  void ComputeLocalCoordinateSystem(); // the user normally never needs to call this routine
+  void Get2DAxes(double xAxis2D[2], double yAxis2D[2]) const;
+  void Get3DAxes(double xAxis3D[3], double yAxis3D[3], double zAxis3D[3]) const;
 
 protected:
+  void ComputeCameraPosition();
+  void ComputeLocalCoordinateSystem();
 
   // the default values
   double R0, Phi0, Theta0;
@@ -155,16 +170,22 @@ protected:
 
   double buf[9];
 
-  double xAxis[3], yAxis[3], zAxis[3]; // the camera coordinate system axes; xAxis is parallel to xz-plane, zAxis points towards focus position
-
-  double xAxis2D[3], yAxis2D[3]; // the simplified view axes (for 2D viewing transformation only)
-     // these are actually 2d vectors, third components is only used for easy normalizing via NORMALIZE macro
+  // the camera coordinate system axes
+  // when viewed in camera space, xAxis points to right, yAxis is up, zAxis goes out of the screen
+  // xAxis, yAxis and zAxis are directions of the derivatives of the equation for computing the camera position
+  double xAxis[3], yAxis[3], zAxis[3];
+  
+  // the simplified view axes (for 2D viewing transformation only)
+  // these are actually 2d vectors, third components is only used for easy normalizing via NORMALIZE macro
+  // the simplified view assumes the camera is alwarys on the equator, i.e. theta is zero
+  // in this case, the y direction always points to world negative y, 
+  // xAxis2D stores xAxis projected to xz plane, and yAxis2D stores zAxis projected to xz plane
+  double xAxis2D[3], yAxis2D[3]; 
+ 
 
   double movementSensitivity;
   double camera2WorldScalingFactor; // dx(world) = dx(camera) * camera2WorldScalingFactor
 
-  double origin[3]; // only used to transform from camera to world coordinate system
-
   struct {
     double R,Phi,Theta;
     double focusPosition[3];
diff --git a/libraries/camera/cameraChangeLoad.cpp b/libraries/camera/cameraChangeLoad.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..65a754465a5fd1187a1d34273d02ed836ff37a2a
--- /dev/null
+++ b/libraries/camera/cameraChangeLoad.cpp
@@ -0,0 +1,112 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "camera" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "cameraChangeLoad.h"
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <cmath>
+using namespace std;
+
+#define CHECK_FAILURE(cond) \
+  if (cond) \
+  { \
+    cerr << "Error CameraChangeLoad file format at line " << lineCount << endl; \
+    return 2; \
+  }
+
+CameraChangeLoad::CameraChangeLoad(const std::string & filename)
+{
+  if (load(filename) != 0) throw 1;
+}
+
+int CameraChangeLoad::load(const std::string & filename)
+{
+  ifstream fin(filename);
+  if (!fin)
+  {
+    cerr << "Cannot open HandleScript file " << filename << endl;
+    return 1;
+  }
+
+  string line;
+  fin >> ws;
+
+  int lastTime = 0;
+  int lineCount = 0;
+  while(!fin.eof())
+  {
+    lineCount++;
+    getline(fin, line);
+    if (line.size() == 0)
+      continue;
+    if (line[0] == '#')
+      continue;
+
+    Entry c;
+    istringstream is(line);
+    is >> c.frameID;
+    CHECK_FAILURE(is.fail());
+    if (c.frameID < lastTime)
+    {
+      cerr << "Error CameraChangeLoad file format at line " << lineCount << ", time becomes smaller" << endl;
+      return 2;
+    }
+    is >> c.dr >> c.dphi >> c.dtheta >> ws;
+    CHECK_FAILURE(is.fail());
+    if (!is.eof()) is >> c.duration;
+    CHECK_FAILURE(is.fail() || c.duration <= 0);
+
+    lastTime = c.frameID+c.duration;
+    c.dphi *= M_PI / 180.0;
+    c.dtheta *= M_PI / 180.0;
+
+    entries.push_back(c);
+
+    fin >> ws;
+  }
+  return 0;
+}
+
+void CameraChangeLoad::controlCamera(int timestepCount, SphericalCamera & camera) const
+{
+  int curIndex = 0;
+  for(; curIndex < (int)entries.size(); curIndex++) {
+    const auto & e = entries[curIndex];
+    if (e.frameID <= timestepCount && timestepCount < e.frameID+e.duration) {
+      if (e.dphi != 0.0) camera.MoveRight(e.dphi);
+      if (e.dr != 0.0) camera.ZoomIn(-e.dr);
+      if (e.dtheta != 0.0) camera.MoveUp(e.dtheta);
+    }
+  }
+}
+
diff --git a/libraries/camera/cameraChangeLoad.h b/libraries/camera/cameraChangeLoad.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1e8bec7e10a614413e18fe892b0243e52e0c18e
--- /dev/null
+++ b/libraries/camera/cameraChangeLoad.h
@@ -0,0 +1,73 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "camera" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef CAMERACHANGELOAD_H
+#define CAMERACHANGELOAD_H
+
+#include "camera.h"
+#include <string>
+#include <vector>
+
+// load a script to control camera from disk
+
+// file format
+// each line is:
+// <frameID> <dr> <dphi> <dtheta> (<duration>)
+// comment start with '#'
+// dr: change of spherical camera radius
+// dphi: change of spherical camera phi angle, the longitude angle
+// dtheta: change of spherical camera theta angle, the lattitude angle
+// duration: the length of the change, default is 1 frame
+
+class CameraChangeLoad
+{
+public:
+  CameraChangeLoad() {}
+  
+  CameraChangeLoad(const std::string & filename);
+
+  int load(const std::string & filename);
+
+  void controlCamera(int frameID, SphericalCamera & camera) const;
+
+protected:
+  struct Entry
+  {
+    Entry() {}
+    int frameID = 0;
+    double dr = 0.0, dphi = 0.0, dtheta = 0.0;
+    int duration = 1;
+  };
+  std::vector<Entry> entries;
+};
+
+#endif
diff --git a/libraries/clothBW/clothBW.cpp b/libraries/clothBW/clothBW.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b026c76a8df3fda02a8506ef13fdee9563e1d997
--- /dev/null
+++ b/libraries/clothBW/clothBW.cpp
@@ -0,0 +1,867 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "clothBW" library , Copyright (C) 2018 USC                            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#include "clothBW.h"
+#include "macros.h"
+#include "matrixMultiplyMacros.h"
+#include "minivector.h"
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <map>
+#include <set>
+#include <algorithm>
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cmath>
+#include <cassert>
+
+
+using namespace std;
+
+// constructor without triangleUVs
+ClothBW::ClothBW(int numVertices_, const double * restPositions_, const double * masses_,
+  int numTriangles_, const int * triangles_, const int * triangleGroups_,
+  int numMaterialGroups_, const MaterialGroup * materialGroups_, int addGravity_)
+  : addGravity(addGravity_), g(9.81)
+{
+  // build triangle UVs
+  vector<double> triangleUVs(3 * 2 * numTriangles_);
+  for (int i = 0; i < numTriangles_; i++)
+  {
+    int vertexA = triangles_[3*i+0];
+    int vertexB = triangles_[3*i+1];
+    int vertexC = triangles_[3*i+2];
+
+    triangleUVs[6*i+0] = 0.0; // vertex A, u coordinate
+    triangleUVs[6*i+1] = 0.0; // vertex A, v coordinate
+
+    Vec3d x0; // vector from A to B
+    x0[0] = restPositions_[3*vertexA+0] - restPositions_[3*vertexB+0];
+    x0[1] = restPositions_[3*vertexA+1] - restPositions_[3*vertexB+1]; 
+    x0[2] = restPositions_[3*vertexA+2] - restPositions_[3*vertexB+2];
+
+    double lengthAB = len(x0);
+    triangleUVs[6*i+2] = lengthAB;	// vertex B, u coordinate
+    triangleUVs[6*i+3] = 0.0; // vertex B, v coordinate
+
+    Vec3d x1; // vector from A to C
+    x1[0] = restPositions_[3*vertexA+0] - restPositions_[3*vertexC+0];
+    x1[1] = restPositions_[3*vertexA+1] - restPositions_[3*vertexC+1]; 
+    x1[2] = restPositions_[3*vertexA+2] - restPositions_[3*vertexC+2];    
+
+    Vec3d xn0 = norm(x0); // normalized vector from A to B
+    double lengthAC = len(x1);
+    triangleUVs[6*i+4] = dot(xn0, x1); // vertex C, u coordinate
+    triangleUVs[6*i+5] = sqrt(lengthAC * lengthAC - triangleUVs[6*i+4] * triangleUVs[6*i+4]);	// vertex C, v coordinate
+  }
+
+  GenerateBW(numVertices_, restPositions_, masses_, numTriangles_, triangles_, triangleUVs.data(), 
+    triangleGroups_, numMaterialGroups_, materialGroups_, addGravity_);
+}
+
+// constructor with trianglesUVs
+ClothBW::ClothBW(int numVertices_, const double * restPositions_, const double * masses_,
+  int numTriangles_, const int * triangles_, const double * triangleUVs_, const int * triangleGroups_,
+  int numMaterialGroups_, const MaterialGroup * materialGroups_, int addGravity_) : addGravity(addGravity_), g(9.81)
+{
+  GenerateBW(numVertices_, restPositions_, masses_, numTriangles_, triangles_, triangleUVs_,
+    triangleGroups_, numMaterialGroups_, materialGroups_, addGravity_);
+}
+
+ClothBW::WuvInfo ClothBW::ComputeWuvInfo(const double triangleUV[6])
+{
+  // distance of neighboring vertices in planar coordinates. 
+  // (delta_u1, delta_v1): planar vector from A to B,
+  // (delta_u2, delta_v2): planar vector from B to A.
+  double du1, du2, dv1, dv2;
+  du1 = triangleUV[2] - triangleUV[0];
+  dv1 = triangleUV[3] - triangleUV[1];
+  du2 = triangleUV[4] - triangleUV[0];
+  dv2 = triangleUV[5] - triangleUV[1];
+  double Delta = 1.0/(du1*dv2-du2*dv1);
+  WuvInfo info; // compute derivatives of wu and wv with respect to vtx position x
+  // 3x1 vector: wu = ( (x1-x0) dv2 - (x2-x0) dv1 ) / (du1 dv2 - du2 dv1), xi is a 3x1 vector for vtx i on a triangle<x0,x1,x2>
+  // 3x1 vector: wv = (-(x1-x0) du2 + (x2-x0) du1 ) / (du1 dv2 - du2 dv1)
+
+  info.pwupx[0] = (dv1 - dv2) * Delta;
+  info.pwupx[1] = dv2 * Delta;
+  info.pwupx[2] = -dv1 * Delta;
+  info.pwvpx[0] = (du2 - du1) * Delta;
+  info.pwvpx[1] = -du2 * Delta;
+  info.pwvpx[2] = du1 * Delta;
+  return info;
+}
+
+static int getVtxIndexInTriangle(int * tri, int vtx)
+{
+  for (int i = 0; i < 3; i++)
+    if (tri[i] == vtx) 
+      return i;
+  assert(0); // this should never be reached
+  return 0;
+}
+
+double ClothBW::ComputeBendingStiffness(const BendInfo & info, const double * triangleUVs)
+{
+  // find the bend edge vtx
+  int v[2] = { info.v[1], info.v[2] };
+  int triA = info.tri[0], triB = info.tri[1];
+  int g1 = triangleGroups[triA], g2 = triangleGroups[triB];  // material group index for triangle A, B
+
+  Vec2d vtxUVA[2], vtxUVB[2]; // vtxUVA[2]: the UV coordinate of the two edge vtx (v1/v2)
+  vtxUVA[0] = triangleUVs + 6*triA + 2*getVtxIndexInTriangle(&triangles[3*triA], v[0]);
+  vtxUVA[1] = triangleUVs + 6*triA + 2*getVtxIndexInTriangle(&triangles[3*triA], v[1]);
+  vtxUVB[0] = triangleUVs + 6*triB + 2*getVtxIndexInTriangle(&triangles[3*triB], v[0]);
+  vtxUVB[1] = triangleUVs + 6*triB + 2*getVtxIndexInTriangle(&triangles[3*triB], v[1]);
+
+  Vec2d duvA = vtxUVA[0] - vtxUVA[1]; // uv coordinate difference from edge vtx v1 to edge vtx v2, on triangle A
+  Vec2d duvB = vtxUVB[0] - vtxUVB[1]; // uv coordinate difference from edge vtx v1 to edge vtx v2, on triangle B
+  // on one triangle: k = (ku du^2 + kv dv^2) / (du^2 + dv^2)
+  double stiffnessA = (materialGroups[g1].bendStiffnessU * duvA[0] * duvA[0] +  materialGroups[g1].bendStiffnessV * duvA[1] * duvA[1])
+      / (duvA[0] * duvA[0] +  duvA[1] * duvA[1]);
+  double stiffnessB = (materialGroups[g2].bendStiffnessU * duvB[0] * duvB[0] +  materialGroups[g2].bendStiffnessV * duvB[1] * duvB[1])
+      / (duvB[0] * duvB[0] +  duvB[1] * duvB[1]);
+  return (stiffnessA + stiffnessB) / 2.; // average bend stiffness from the two triangles
+}
+
+void ClothBW::GenerateBW(int numVertices_, const double * restPositions_, const double * masses_,
+  int numTriangles_, const int * triangles_, const double * triangleUVs_, const int * triangleGroups_,
+  int numMaterialGroups_, const MaterialGroup * materialGroups_, int addGravity_)
+{
+  bu = 1.0;
+  bv = 1.0;
+  // default to computing everything:
+  cond[0] = true; // stretch&shear force
+  cond[1] = true; // bend force
+  cond[2] = true; // stretch&shear bend stiffness
+  cond[3] = true; // bend stiffness matrix
+
+  // default to taking rest angles into account
+  useRestAnglesForBendingForces = 1;
+
+  numVertices = numVertices_;
+  restPositions.resize(numVertices);
+  for(int i = 0; i < numVertices; i++)
+    restPositions[i] = Vec3d(&restPositions_[3*i]);
+  numTriangles = numTriangles_;
+  triangles.resize(3 * numTriangles);
+  memcpy(triangles.data(), triangles_, sizeof(int) * 3 * numTriangles);
+
+  triangleGroups.resize(numTriangles);
+  memcpy(triangleGroups.data(), triangleGroups_, sizeof(int) * numTriangles);
+
+  masses.resize(numVertices);
+  memcpy(masses.data(), masses_, sizeof(double) * numVertices);
+
+  numMaterialGroups = numMaterialGroups_;
+  materialGroups.resize(numMaterialGroups);
+  memcpy(materialGroups.data(), materialGroups_, sizeof(MaterialGroup) * numMaterialGroups);
+
+  // index all edges that can bend
+
+  /* New ordering below
+           B         D
+           1---------3
+          / \       /
+         /   \  5  /
+        /  4  \   /
+       /       \ /
+      0---------2
+      A         C
+   */
+  // SORTED ensures that edges are paired in ascending order
+  #define SORTED(i,j) ( (i) <= (j) ? make_pair((i),(j)) : make_pair((j),(i)) )
+  typedef pair<int,int> Edge; // store sorted edge vtx index
+  typedef pair<int, int> TriOpVtx; // store the triangle data (triangle index, the non opposite vtx on the triangle) for an edge
+  map<Edge, TriOpVtx> edgeInfo; // map the edge to the <triangle index, opposite vtx> data
+  map<Edge, TriOpVtx >::iterator iter;
+
+  alphas.resize(numTriangles);
+  wuvInfos.resize(numTriangles);
+  for (int tri = 0; tri < numTriangles; tri++)
+  {
+    const int * trivtx = &triangles[tri*3];
+    assert(trivtx[0] >= 0 && trivtx[1] >= 0 && trivtx[2] >= 0);
+    assert(trivtx[0] < numVertices && trivtx[1] < numVertices && trivtx[2] < numVertices);
+
+    // compute the triangle surface area in UV coordinates (called "alpha" in [Baraff and Witkin 1998])
+    double surfaceArea = GetTriangleSurfaceArea(restPositions[trivtx[0]], restPositions[trivtx[1]], restPositions[trivtx[2]]);
+
+    // In [Baraff and Witkin 1998], alpha is set to be the surface area in UV coordinates
+    //alphas[tri] = surfaceArea;
+    // In David Pritchard's report (2012), it is reported that setting alpha = area^(3/4) is better if one wishes for cloth to have resolution-invariance
+    alphas[tri] = pow(surfaceArea, 3.0 / 4.0);
+
+    // compute wuv info
+    wuvInfos[tri] = ComputeWuvInfo(&triangleUVs_[6*tri]);
+
+    for (int j = 0; j < 3; j++)
+    {
+      Edge edge = SORTED(trivtx[j], trivtx[(j+1)%3]);
+      int oppositeVtx = trivtx[(j+2)%3];
+
+      if ((iter = edgeInfo.find(edge)) == edgeInfo.end()) // edge is not in edgeInfo, we insert it
+        edgeInfo.insert(make_pair(edge, TriOpVtx(tri, oppositeVtx)));
+      else // find one edge. We can form a bend edge with the new one and the old one
+      {
+        BendInfo info;
+        TriOpVtx & previousTri = iter->second;
+        // If we already visited this bend edge before and created a BendInfo,
+        // there's an edge in the mesh that has three neighboring triangles.
+        if (previousTri.first < 0)
+          throw 1;
+
+        info.v[0] = previousTri.second; // index of the first triangle's vtx that is opposite to the edge
+        info.v[1] = edge.first;         // index of the first edge vtx
+        info.v[2] = edge.second;        // index of the second edge vtx
+        info.v[3] = oppositeVtx;        // index of the second triangle's vtx that is opposite to the edge
+        info.tri[0] = previousTri.first; // index of the first triangle
+        info.tri[1] = tri;               // index of the second triangle
+        quadComponentIndices.push_back(info);
+        previousTri.first = -1; // mark this edge as visited
+      }
+    }
+  }
+
+  numQuads = quadComponentIndices.size();
+  restAngles.resize(numQuads);
+  bendStiffnesses.resize(numQuads);
+
+  iter = edgeInfo.begin();
+
+  // build the locations of non-zero entries in the stiffness matrix of size n x n
+  SparseMatrixOutline skeletonOutline(numVertices);
+
+  for (int i = 0; i < numVertices; i++) // protection for isolated vertices
+    skeletonOutline.AddEntry(i, i);
+
+  for (int i = 0; i < numTriangles; i++)
+  {
+    int * trivtx = &triangles[3 * i];
+    for (int j = 0; j < 3; j++)
+      for (int k = 0; k < 3; k++)
+        skeletonOutline.AddEntry(trivtx[j], trivtx[k]);
+  }
+
+  for (int i = 0; i < numQuads; i++)
+  {
+    int vertexA = quadComponentIndices[i].v[0];
+    int vertexB = quadComponentIndices[i].v[1];
+    int vertexC = quadComponentIndices[i].v[2];
+    int vertexD = quadComponentIndices[i].v[3];
+
+    skeletonOutline.AddEntry(vertexA, vertexD);
+    skeletonOutline.AddEntry(vertexD, vertexA);
+
+    // --- compute bend stiffness ---
+    bendStiffnesses[i] = ComputeBendingStiffness(quadComponentIndices[i], triangleUVs_);
+
+    // --- compute Resting Angles ---
+    Vec3d restPositionA = restPositions[vertexA];
+    Vec3d restPositionB = restPositions[vertexB];
+    Vec3d restPositionC = restPositions[vertexC];
+    Vec3d restPositionD = restPositions[vertexD];
+
+    Vec3d vecBA = restPositionB - restPositionA;
+    Vec3d vecCA = restPositionC - restPositionA;
+    Vec3d vecBD = restPositionB - restPositionD;
+    Vec3d vecCD = restPositionC - restPositionD;
+    Vec3d vecBC = restPositionB - restPositionC;
+
+    // new math ordering below
+    Vec3d NA = cross(vecCA, vecBA); // normal for the first triangle
+    Vec3d NB = cross(vecBD, vecCD);	// normal for the second triangle
+    Vec3d E = vecBC; // edge vector
+
+    // normalized normals and edge	
+    Vec3d NAn = norm(NA);
+    Vec3d NBn = norm(NB);
+    Vec3d En = norm(E);
+
+    double cosTheta = dot(NAn, NBn);
+    double sinTheta;
+
+    Vec3d NANB = cross(NAn, NBn); // storing the cross product of NAn and NBn
+
+    sinTheta = dot(NANB, En);
+
+    restAngles[i] = atan2(sinTheta, cosTheta);
+  }
+
+  // build inverse indices for stiffness matrix access
+  SparseMatrix skeleton(&skeletonOutline);
+
+  inverseIndicesStretchAndShear.resize(9 * numTriangles);
+  for (int i = 0; i < numTriangles; i++)
+  {
+    int * trivtx = &triangles[3 * i];
+    for (int j = 0, l = 0; j < 3; j++)
+      for (int k = 0; k < 3; k++)
+        inverseIndicesStretchAndShear[9 * i + (l++)] = skeleton.GetInverseIndex(trivtx[j], trivtx[k]);
+  }
+
+  inverseIndicesQuad.resize(16 * numQuads);
+  for (int i = 0; i < numQuads; i++)
+  {
+    const int * quadvtx = quadComponentIndices[i].v;
+    for (int j = 0, l = 0; j < 4; j++)
+      for (int k = 0; k < 4; k++)
+        inverseIndicesQuad[16 * i + (l++)] = skeleton.GetInverseIndex(quadvtx[j], quadvtx[k]);
+  }
+}
+
+ClothBW::~ClothBW()
+{
+}
+
+void ClothBW::GenerateMassMatrix(SparseMatrix **M, int expanded) const
+{
+  SparseMatrixOutline outline(expanded * numVertices);
+  for(int i=0; i<numVertices; i++)
+    for(int j=0; j<expanded; j++)
+      outline.AddEntry(expanded*i+j, expanded*i+j, masses[i]); 
+  *M = new SparseMatrix(&outline);	
+}
+
+double ClothBW::GetTriangleSurfaceArea(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2)
+{
+  Vec3d s0 = p1 - p0;
+  Vec3d s1 = p2 - p0;
+  return 0.5 * len(cross(s0, s1));
+}
+
+void ClothBW::SetComputationMode(const bool conditions[4])
+{
+  memcpy(cond, conditions, sizeof(bool) * 4);
+}
+
+void ClothBW::ComputeGravity(double * f)
+{
+  for(int i=0; i<numVertices; i++)
+    f[3*i+1] += g * masses[i];
+}
+
+void ClothBW::ComputeVertexGravity(int vid, double g[3])
+{
+  g[0] = 0;
+  g[1] = this->g * masses[vid];
+  g[2] = 0;
+}
+
+void ClothBW::ComputeStretchAndShear(const double * u, int triangleID, double * energy, double f[9], double K[81])
+{
+  int tri = triangleID;
+  int group = triangleGroups[tri]; // group index
+
+  int p0 = triangles[3 * tri + 0]; // triangle vtx indices
+  int p1 = triangles[3 * tri + 1];
+  int p2 = triangles[3 * tri + 2];
+
+  Vec3d pos0 = restPositions[p0] + Vec3d(&u[3 * p0]);
+  Vec3d pos1 = restPositions[p1] + Vec3d(&u[3 * p1]);
+  Vec3d pos2 = restPositions[p2] + Vec3d(&u[3 * p2]);
+
+  Vec3d vec01 = pos1 - pos0;
+  Vec3d vec02 = pos2 - pos0;
+
+  const double * dwudx = wuvInfos[tri].pwupx;
+  const double * dwvdx = wuvInfos[tri].pwvpx;
+  Vec3d wu = dwudx[1] * vec01 + dwudx[2] * vec02; // = (dv2 * vec01 - dv1 * vec02) * Delta;
+  Vec3d wv = dwvdx[1] * vec01 + dwvdx[2] * vec02; // = ( -du2 * vec01 + du1 * vec02) * Delta;
+  double length_wu = len(wu);
+  double length_wv = len(wv);
+  Vec3d wun = wu / length_wu; //wu normalized
+  Vec3d wvn = wv / length_wv; // wv normalized
+
+  double alpha = alphas[tri];
+
+  // --- compute stretch and shear energy ---
+  // stretch energy Es = 0.5 Cs^T Cs
+  // Cs = [Csu Csv]^T = alpha [ |wu|-bu |wv| -bv ]^T
+  // shear energy E_h = 0.5 Ch^T Ch
+  // Ch = alpha wu^T wv
+  double Cstru = alpha * (length_wu - bu); // stretch energy in u: Csu
+  double Cstrv = alpha * (length_wv - bv); // stretch energy in v: Csv
+  double Cshear = alpha * dot(wu, wv);     // shear energy       : Ch
+
+  if (energy)
+  {
+    *energy = 0.5 * materialGroups[group].tensileStiffness * (Cstru*Cstru + Cstrv * Cstrv);
+    *energy += 0.5 * materialGroups[group].shearStiffness * Cshear * Cshear;
+  }
+
+  // --- compute the derivatives of Csu, Csv, Ch ---
+  // For stretch:
+  // 3x1 vector: pCsu/px = alpha pwu/px \hat{wu} ,  pCsv/px = alpha pwv/px \hat{wv} , \hat{W} means the normalized W
+
+  // Define pwu/pxi = wuxi * I, pwv/pxi = wvxi * I, where wuxi, wvxi are both scalars
+  // So,
+  // pCsu/px0 = alpha * wux0 * \hat{Wu}
+  // pCsu/px1 = alpha * wux1 * \hat{Wu}
+  // pCsu/px2 = alpha * wux2 * \hat{Wu}
+  // pCsv/px0 = alpha * wvx0 * \hat{Wv}
+  // pCsv/px1 = alpha * wvx1 * \hat{Wv}
+  // pCsv/px2 = alpha * wvx2 * \hat{Wv}
+
+  // For shear:
+  // 3x1 vector: pCh/pxi = alpha ( pwv/pxi wu + pwu/pxi wv) = alpha ( wvxi wu + wuxi wv )
+  Vec3d pCsupx[3]; // stretch energy derivative on u: pCsu/px0, pCsu/px1, pCsupx2, <x0, x1, x2> vtx pos./disp. on one triangle
+  Vec3d pCsvpx[3]; // stretch energy derivative on u: pCsv/px0, pCsv/px1, pCsvpx2
+  Vec3d pChpx[3];  // shear energy derivative: pChpx0, pChpx1, pChpx2
+  for (int vtx = 0; vtx < 3; vtx++)
+  {
+    pCsupx[vtx] = (alpha * dwudx[vtx]) * wun;
+    pCsvpx[vtx] = (alpha * dwvdx[vtx]) * wvn;
+    pChpx[vtx] = alpha * (dwudx[vtx] * wv + dwvdx[vtx] * wu);
+  }
+
+  // --- compute force ---
+  // stretch energy Es = 0.5 [Csu Csv] [Csu Csv]^T
+  // 3x1 vector: pEs/px = Csu pCsu/px + Csv pCsv/px
+  // shear energy Eh = 0.5 Ch^2
+  // 3x1 vector pEh/pxi = Ch pCh/pxi
+  // force = pEspx + pEhpx
+  if (f)
+  {
+    for (int vtx = 0; vtx < 3; vtx++)
+    {
+      Vec3d force(0.0);
+      force += materialGroups[group].tensileStiffness * (Cstru * pCsupx[vtx] + Cstrv * pCsvpx[vtx]);
+      force += materialGroups[group].shearStiffness * Cshear * pChpx[vtx];
+      force.convertToArray(f + 3 * vtx);
+    }
+  }
+
+  // --- compute stiffness matrix ---
+  if (K)
+  {
+    for (int i = 0; i < 3; i++) // force vtx i
+      for (int j = 0; j < 3; j++) // disp. vtx j
+      {
+        // second derivative of stretch Csu, Csv
+        // p(pCsu/pxi)/pxj = (alpha / |Wu|) wuxi wuxj (I - \hat{Wu} \hat{Wu}^T)
+        Mat3d p2Csupxij = ((alpha / length_wu) * dwudx[i] * dwudx[j]) * (Mat3d(1.) - tensorProduct(wun, wun));
+        Mat3d p2Csvpxij = ((alpha / length_wv) * dwvdx[i] * dwvdx[j]) * (Mat3d(1.) - tensorProduct(wvn, wvn));
+        // p2Es/pxipxj = p(pEs/pxi)/pxj = pCsu/pxi pCsu/pxj^T + Csu p(pCsu/pxi)/pxj + pCsv/pxi pCsv/pxj^T + Csv p(pCsv/pxi)/pxj
+        Mat3d Kstrij = tensorProduct(pCsupx[i], pCsupx[j]) + Cstru * p2Csupxij
+          + tensorProduct(pCsvpx[i], pCsvpx[j]) + Cstrv * p2Csvpxij;
+        Kstrij *= materialGroups[group].tensileStiffness;
+
+        // second derivative of shear Ch:
+        // p2Eh/pxipxj = pCh/pxi pCh/pxj^T + Ch p2Ch/pxipxj
+        // p2Ch/pxipxj = alpha ( wvxi wuxj + wuxi wvxj )
+        Mat3d Kshij = tensorProduct(pChpx[i], pChpx[j]) +
+          (Cshear * alpha * (dwvdx[i] * dwudx[j] + dwudx[i] * dwvdx[j])) * Mat3d(1.);
+        Kshij *= materialGroups[group].shearStiffness;
+
+        for (int m = 0; m < 3; m++)     // loop inside force vtx i component
+          for (int n = 0; n < 3; n++)   // loop inside disp vtx j component
+          {
+            // Adding it to K topology
+            int entry_row = 3 * i + m;
+            int entry_col = 3 * j + n;
+
+            K[entry_col * 9 + entry_row] = Kstrij[m][n] + Kshij[m][n];
+          }
+      } // end i,j
+  } // end if (K)
+}
+
+void ClothBW::ComputeBending(const double * u, int quadId, double * energy, double f[12], double K[144])
+{
+  int qua = quadId;
+
+  int p0 = quadComponentIndices[qua].v[0];
+  int p1 = quadComponentIndices[qua].v[1];
+  int p2 = quadComponentIndices[qua].v[2];
+  int p3 = quadComponentIndices[qua].v[3];
+
+  double kBend = bendStiffnesses[qua];
+
+  // vtx Indices defined below
+  //
+  //       1---------3
+  //      / \       /
+  //     /   \  B  /
+  //    /  A  \   /
+  //   /       \ /
+  //  0---------2
+
+  Vec3d pos0 = restPositions[p0] + Vec3d(&u[3 * p0]);
+  Vec3d pos1 = restPositions[p1] + Vec3d(&u[3 * p1]);
+  Vec3d pos2 = restPositions[p2] + Vec3d(&u[3 * p2]);
+  Vec3d pos3 = restPositions[p3] + Vec3d(&u[3 * p3]);
+
+  Vec3d v01 = pos1 - pos0;
+  Vec3d v02 = pos2 - pos0;
+  Vec3d v31 = pos1 - pos3;
+  Vec3d v32 = pos2 - pos3;
+  Vec3d v21 = pos1 - pos2;
+
+  Vec3d v12 = -v21;
+  Vec3d v20 = -v02;
+  Vec3d v13 = -v31;
+
+  // normals for triangles A and B
+  // nA = cross(x2-x0, x1-x0)
+  // nB = cross(x1-x3, x2-x3) // force vtx m
+  // e = x1 - x2
+  Vec3d nA = cross(v02, v01);
+  Vec3d nB = cross(v31, v32);
+  Vec3d edge = v21;
+
+  double nAL = len(nA);
+  double nBL = len(nB);
+  double eL = len(edge);
+  double invnAL = 1.0 / nAL;
+  double invnBL = 1.0 / nBL;
+  assert(eL != 0.0);
+  double inveL = 1.0 / eL;
+
+  // now normalize the... normals
+  Vec3d nAN = nA * invnAL;
+  Vec3d nBN = nB * invnBL;
+  Vec3d eN = edge * inveL;
+
+  // --- compute bend energy Cb ---
+  // bend energy E_b = 0.5 Cb^T Cb
+  // Cb = theta - theta0
+  // pEb/px = Cb pCb/pxi
+
+  // calculate sin theta and cos theta
+  double cosTheta = dot(nAN, nBN);
+  double sinTheta = dot(cross(nAN, nBN), eN);
+  double cbend = atan2(sinTheta, cosTheta); // store theta into cbend
+
+                                            // account for the rest angles?
+  if (useRestAnglesForBendingForces)
+    cbend -= restAngles[qua];
+  // now cbend stores Cb = theta - theta0
+
+  if (energy)
+    *energy = 0.5 * kBend * cbend * cbend;
+
+  if (f || K)
+  {
+    Vec3d qA[4] = { v12, v20, v01, Vec3d(0.0) };
+    Vec3d qB[4] = { Vec3d(0.0), v32, v13, v21 };
+    const double qe[4] = { 0, 1, -1, 0 };
+    Mat3d phnApx[4], phnBpx[4], phepx[4];
+
+    for (int vtx = 0; vtx < 4; vtx++)
+    {
+      // --- compute derivatives of triangle A normal \hat{nA}, triangle B normal \hat{nB} and edge direction \hat{e} ---
+      // Here we compute the derivatives based on approximation that the normal and edge vectors have constant magnitude
+      // phnA/pxms = 1.0 / |nA| * S_s(qAm)
+      phnApx[vtx] = invnAL * skewSymmetricMatrix(qA[vtx]); // now phnA/pxms = phnApx[m][s]
+      phnBpx[vtx] = invnBL * skewSymmetricMatrix(qB[vtx]);
+      phepx[vtx] = inveL * qe[vtx] * Mat3d(1.0);
+    }
+
+    // --- compute derivatives of sin(theta), cos(theta) and theta ---
+    Vec3d pSinpx[4], pCospx[4], pThetapx[4];
+    for (int m = 0; m < 4; m++) // over 4 vtx
+    {
+      pCospx[m] = phnApx[m] * nBN + phnBpx[m] * nAN;
+      for (int s = 0; s < 3; s++)
+      {
+        pSinpx[m][s] = dot(cross(phnApx[m][s], nBN) + cross(nAN, phnBpx[m][s]), eN) +
+          dot(cross(nAN, nBN), phepx[m][s]);
+        pThetapx[m][s] = cosTheta * pSinpx[m][s] - sinTheta * pCospx[m][s];
+      }
+
+      // --- compute force ---
+      if (f)
+      {
+        // Cb = theta - theta0
+        // 3x1 vector: force = pEb/pxm = Cb pCb/pxm
+        Vec3d force = kBend * cbend * pThetapx[m];
+        force.convertToArray(f + 3 * m);
+      }
+    }
+
+    // --- compute stiffness matrix ---
+    if (K)
+    {
+      // qA is used to compute pqAm/pxnt
+      const double qA[4][4] = { { 0,-1,1,0 },{ 1,0,-1,0 },{ -1,1,0,0 },{ 0,0,0,0 } };
+      const double qB[4][4] = { { 0,0,0,0 },{ 0,0,1,-1 },{ 0,-1,0,1 },{ 0,1,-1,0 } };
+      for (int m = 0; m < 4; m++)  // force vtx m
+        for (int n = 0; n < 4; n++) // disp. vtx n
+        {
+          // compute K_mn,  3x3 matrix in K at rows 3*m -> 3*m+2, columns: 3*n -> 3*n+2
+
+          // compute second derivatives of \hat{nA} and \hat{nB} given vtx index m and n fixed
+          Mat3d p2hnApt[3], p2hnBpt[3];
+          for (int t = 0; t < 3; t++) // loop over the component of disp. vtx n
+          {
+            Vec3d pqAmpxnt(0.0), pqBmpxnt(0.0);
+            pqAmpxnt[t] = qA[m][n];              //pqAmpxnt = partial qAm / partial xnt
+            pqBmpxnt[t] = qB[m][n];
+            p2hnApt[t] = invnAL * skewSymmetricMatrix(pqAmpxnt); // p2hnApt[t][s] stores p2 hnA / pxms pxnt
+            p2hnBpt[t] = invnBL * skewSymmetricMatrix(pqBmpxnt);
+          }
+
+          // compute second derivatives of cos(theta), sin(theta) and theta
+          Mat3d p2Cospst, p2Sinpst, p2Thetapst;
+          for (int s = 0; s < 3; s++)
+            for (int t = 0; t < 3; t++)
+            {
+              p2Cospst[s][t] = dot(p2hnApt[t][s], nBN) + dot(phnBpx[n][t], phnApx[m][s])
+                + dot(phnApx[n][t], phnBpx[m][s]) + dot(nAN, p2hnBpt[t][s]);
+
+              p2Sinpst[s][t] = dot(cross(p2hnApt[t][s], nBN) + cross(phnApx[m][s], phnBpx[n][t]) +
+                cross(phnApx[n][t], phnBpx[m][s]) + cross(nAN, p2hnBpt[t][s]), eN) +
+                dot(cross(phnApx[m][s], nBN) + cross(nAN, phnBpx[m][s]), phepx[n][t]) +
+                dot(cross(phnApx[n][t], nBN) + cross(nAN, phnBpx[n][t]), phepx[m][s]);
+
+              p2Thetapst[s][t] = cosTheta * p2Sinpst[s][t] - sinTheta * p2Cospst[s][t] +
+                (sinTheta * sinTheta - cosTheta * cosTheta) * (pSinpx[m][s] * pCospx[n][t] + pCospx[m][s] * pSinpx[n][t])
+                + 2 * sinTheta * cosTheta * (pCospx[m][s] * pCospx[n][t] - pSinpx[m][s] * pSinpx[n][t]);
+            }
+
+          // Cb = theta - theta0
+          // 3x1 vector: pCb/px
+          // 3x1 vector: pEb/pxm = Cb pCb/pxm
+          // p2Eb/pxmxn = pCb/pxm pCb/pxn^T + Cb p2Cb/pxmxn
+          Mat3d p2Eb = kBend * (tensorProduct(pThetapx[m], pThetapx[n]) + cbend * p2Thetapst);
+
+          for (int s = 0; s < 3; s++)
+            for (int t = 0; t < 3; t++)
+            {
+              int entry_row = 3 * m + s;
+              int entry_col = 3 * n + t;
+              K[entry_col * 12 + entry_row] = p2Eb[s][t];
+            }
+        } // end n 0 -> 4, // end m 0 -> 4
+    } // end if K
+  }
+}
+
+
+void ClothBW::AddStretchAndShear(const double * u, int startTriangle, int endTriangle, double * energy, double * f, SparseMatrix * K)
+{
+  for (int tri = startTriangle; tri < endTriangle; tri++)
+  {
+    double triEnergy = 0.0;
+    double trif[9];
+    double triK[81];
+
+    ComputeStretchAndShear(u, tri,
+      energy ? &triEnergy : nullptr,
+      f ? trif : nullptr,
+      K ? triK : nullptr);
+
+    if (energy)
+      *energy += triEnergy;
+
+    if (f)
+    {
+      for (int vtx = 0; vtx < 3; vtx++)
+      {
+        int vtxId = triangles[tri * 3 + vtx];
+        double *targetv = f + vtxId * 3;
+        const double *srcv = trif + vtx * 3;
+        VECTOR_ADDEQUAL3(targetv, srcv);
+      }
+    }
+
+    if (K)
+    {
+      for (int i = 0; i < 3; i++) // force vtx i
+        for (int j = 0; j < 3; j++) // disp. vtx j
+        {
+          for (int m = 0; m < 3; m++)     // loop inside force vtx i component
+            for (int n = 0; n < 3; n++)   // loop inside disp vtx j component
+            {
+              // Adding it to K topology
+              int entry_row = 3 * triangles[3 * tri + i] + m;
+              int entry_col = 3 * inverseIndicesStretchAndShear[9 * tri + i * 3 + j] + n;
+
+              int local_row = i * 3 + m;
+              int local_col = j * 3 + n;
+              double entry = triK[local_col * 9 + local_row];
+
+              K->AddEntry(entry_row, entry_col, entry);
+            }
+        } // end i,j
+    } // end if (K)
+  }
+}
+
+void ClothBW::AddBend(const double * u, int startQuad, int endQuad, double * energy, double * f, SparseMatrix * K)
+{
+  for (int qua = startQuad; qua < endQuad; qua++)
+  {
+    double quadEnergy = 0.0;
+    double quadf[12];
+    double quadK[144];
+
+    ComputeBending(u, qua,
+      energy ? &quadEnergy : nullptr,
+      f ? quadf : nullptr,
+      K ? quadK : nullptr);
+
+    if (energy)
+      *energy += quadEnergy;
+
+    if (f) {
+      for (int m = 0; m < 4; m++) // over 4 vtx
+      {
+        int vtxidx = quadComponentIndices[qua].v[m];
+        const double *srcf = quadf + m * 3;
+        double *targetf = f + vtxidx * 3;
+        VECTOR_ADDEQUAL3(targetf, srcf);
+      }
+    }
+
+    // --- compute stiffness matrix ---
+    if (K)
+    {
+      for (int m = 0; m < 4; m++)  // force vtx m
+        for (int n = 0; n < 4; n++) // disp. vtx n
+        {
+          for (int s = 0; s < 3; s++)
+            for (int t = 0; t < 3; t++)
+            {
+              int entry_row = 3 * quadComponentIndices[qua].v[m] + s;
+              int entry_col = 3 * inverseIndicesQuad[qua * 16 + m * 4 + n] + t;
+
+              int local_row = 3 * m + s;
+              int local_col = 3 * n + t;
+              double val = quadK[local_col * 12 + local_row];
+
+              K->AddEntry(entry_row, entry_col, val);
+            }
+        } // end n 0 -> 4, // end m 0 -> 4
+    } // end if K
+  } // end quad
+}
+
+//void ClothBW::ComputeDampingForce(const double *u, double *uvel, double *f, bool addForce)
+//{
+//  // unimplemented
+//}
+
+//void ClothBW::AddDampingForce(double *uvel, double *f, int startTriangle, int endTriangle)
+//{
+//  // unimplemented
+//}
+
+void ClothBW::GenerateStiffnessMatrixTopology(SparseMatrix **K) const
+{
+  SparseMatrixOutline KOutline(3 * numVertices);
+  for (int vtx = 0; vtx < numVertices; vtx++) // for all vtx. in case there's an isolated vtx
+    KOutline.AddBlock3x3Entry(vtx, vtx);
+
+  // for per triangle
+  for (int i = 0; i < numTriangles; i++)
+  {
+    const int * trivtx = &triangles[3 * i];
+    for (int j = 0; j < 3; j++)
+      for (int k = 0; k < 3; k++)
+        KOutline.AddBlock3x3Entry(trivtx[j], trivtx[k]);
+  }
+
+  // for per quad, in addition to per triangle topology
+  for (int i = 0; i < numQuads; i++)
+  {
+    int vertexA = quadComponentIndices[i].v[0];
+    int vertexD = quadComponentIndices[i].v[3];
+
+    KOutline.AddBlock3x3Entry(vertexA, vertexD);
+    KOutline.AddBlock3x3Entry(vertexD, vertexA);
+  }
+
+  *K = new SparseMatrix(&KOutline);
+}
+
+double ClothBW::ComputeEnergy(const double * u)
+{
+  double energy = 0.0;
+  if (cond[0])
+    AddStretchAndShear(u, 0, numTriangles, &energy, NULL, NULL);
+  if (cond[1])
+    AddBend(u, 0, numQuads, &energy, NULL, NULL);
+  return energy;
+}
+
+void ClothBW::ComputeForce(const double * u, double * f, bool addForce)
+{
+  if (!addForce)
+    memset(f, 0, sizeof(double) * 3 * numVertices);
+
+  if (cond[0])
+    AddStretchAndShear(u, 0, numTriangles, NULL, f, NULL);
+
+  if (cond[1])
+    AddBend(u, 0, numQuads, NULL, f, NULL);
+
+  if (addGravity)
+    ComputeGravity(f);
+}
+
+void ClothBW::ComputeStiffnessMatrix(const double * u, SparseMatrix * K, bool addMatrix)
+{
+  if (!addMatrix)
+    K->ResetToZero();
+
+  if (cond[2])
+    AddStretchAndShear(u, 0, numTriangles, NULL, NULL, K);
+
+  if (cond[3])
+    AddBend(u, 0, numQuads, NULL, NULL, K);
+}
+
+void ClothBW::ComputeForceAndMatrix(const double * u, double * f, SparseMatrix * K, bool addQuantity)
+{
+  if (addQuantity == false)
+  {
+    memset(f, 0, sizeof(double) * 3 * numVertices);
+    K->ResetToZero();
+  }
+
+  if (cond[0] || cond[2])
+    AddStretchAndShear(u, 0, numTriangles, NULL, (cond[0] ? f : NULL), (cond[2] ? K : NULL));
+
+  if (cond[1] || cond[3])
+    AddBend(u, 0, numQuads, NULL, (cond[1] ? f : NULL), (cond[3] ? K : NULL));
+
+  if (addGravity)
+    ComputeGravity(f);
+}
+
diff --git a/libraries/clothBW/clothBW.h b/libraries/clothBW/clothBW.h
new file mode 100644
index 0000000000000000000000000000000000000000..a874711ded3c92259b0cead6edd17fc2187e49f3
--- /dev/null
+++ b/libraries/clothBW/clothBW.h
@@ -0,0 +1,233 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "clothBW" library , Copyright (C) 2018 USC                            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+ This class implements the cloth model presented in:
+
+ Baraff and Witkin: Large Steps in Cloth Simulation, SIGGRAPH 1998. 
+ 
+ It can compute the elastic energy, the internal elastic forces, and the tangent stiffness matrix,
+ for stretch/shear and bend terms.
+ Damping is not implemented, but you can use the damping in the Vega integrator class.
+
+ Our implementation follows the original Baraff and Witkin paper, with the insights
+ presented in the following report:
+
+ David Pritchard: Implementing Baraff & Witkin's Cloth Simulation, May 2003, with minor updates April 2012
+*/
+
+#ifndef _CLOTHBW_H_
+#define _CLOTHBW_H_
+
+#include "sparseMatrix.h"
+#include "vec3d.h"
+
+class ClothBW
+{
+public:
+
+  // cloth material parameters
+  struct MaterialGroup
+  {
+    double tensileStiffness;
+    double shearStiffness;
+    double bendStiffnessU;
+    double bendStiffnessV;
+    double damping;
+  };
+
+  // creates the cloth elastic model, from a given triangle mesh
+  // "masses" is an array of length "numVertices"
+  // "restPositions" is an array of length 3x"numVertices"
+  // "triangles" is an integer array of length 3x"numTriangles" (giving integer indices of the three vertices forming a triangle)
+  // "triangleGroups" is an integer array of length "numTriangles" (giving the integer index of the material group to which each triangle belongs)
+  // "triangleUVs" is an double array of length 3x2x"numTriangles", indicating the uv for every vertex; this array can be user-provided, or the constructor can compute it automatically
+  // all indices in this class are 0-indexed
+  // constructor that does not require triangleUVs input (it computes UVs automatically; note: the UVs are continuous only within each triangle; the UV map is not global (which is fine, provided one does not want to simulate anisotropic effects) )
+  ClothBW(int numVertices, const double * restPositions, const double * masses,
+      int numTriangles, const int * triangles, const int * triangleGroups,
+      int numMaterialGroups, const MaterialGroup * materialGroups, int addGravity=0);
+  
+  // constructor with triangleUVs input
+  ClothBW(int numVertices, const double * restPositions, const double * masses,
+      int numTriangles, const int * triangles, const double * triangleUVs, const int * triangleGroups,
+      int numMaterialGroups, const MaterialGroup * materialGroups, int addGravity=0);
+    
+  virtual ~ClothBW();
+
+  int GetNumVertices() const { return numVertices; }
+  int GetNumTriangles() const { return numTriangles; }
+  int GetNumQuads() const { return numQuads; }
+  int GetNumMaterialGroups() const { return (int)materialGroups.size(); }
+
+  const int * GetTriangles() const { return triangles.data(); }
+  const Vec3d * GetRestPositions() const { return restPositions.data(); }
+
+  const int *GetTriangleVertexIndices(int tri) const { return triangles.data() + tri * 3; }
+  const int *GetQuadVertexIndices(int quad) const { return quadComponentIndices[quad].v; }
+  const int *GetQuadTriangleIndices(int quad) const { return quadComponentIndices[quad].tri; }
+
+  void SetRestUVStretchValues(double bu, double bv) { this->bu = bu; this->bv = bv; } // these are the b_u and b_v stretch values from Equation (10) in the BW paper (default values are 1.0)
+
+  // computes the gravitational force (result goes into f)
+  void SetGravity(bool addGravity, double g=9.81) { this->addGravity = addGravity; this->g = g; } // if addGravity is enabled, ComputeForces will add the gravity force to the elastic forces
+
+  // this allows users to toggle on/off computation of the stretch/shear forces, bend forces,
+  // stretch/shear stiffness matrix, and bend stiffness matrix
+  // the function ComputeEnergy uses mode[0] and mode[1] flags
+  // mode[0] = computeStretchAndShearForce
+  // mode[1] = computeBendForce
+  // mode[2] = computeStretchAndShearStiffnessMatrices
+  // mode[3] = computeBendStiffnessMatrices
+  void SetComputationMode(const bool mode[4]);
+
+  // allows user to toggle on/off the use of rest angles in bend force/stiffness matrix
+  // calculations; if set to 1, bend force/matrix will be computed in relation to the quad's rest
+  // angle. if set to 0, bend force/matrix will be computed in relation to a flat angle of 0.0
+  // default is 1.
+  void UseRestAnglesForBendingForces(bool useRestAnglesForBend) { useRestAnglesForBendingForces = useRestAnglesForBend; }
+
+  // creates the mass matrix (which is diagonal); each diagonal entry is expanded into a diagonal submatrix of size 'expanded' (typically, for 3D simulations, expanded should be 3)
+  void GenerateMassMatrix(SparseMatrix ** M, int expanded=3) const;
+
+  // === compute elastic energy, force and stiffness matrix ===
+
+  // compute the elastic energy, under deformation u
+  virtual double ComputeEnergy(const double * u);
+
+  // compute the internal elastic force, under deformation u
+  // note: the force has the sign of the left side of the dynamic equation, Mu'' + Du' + f_int(u) = f_ext(t), i.e., f_int(u), that is, **opposite** to an external force f_ext(t) acting on the body 
+  virtual void ComputeForce(const double * u, double * f, bool addForce=false); // if addForce is "true", f will be not be reset to zero prior to adding the forces
+
+  // compute the tangent stiffness matrix of the elastic force
+  // call once to establish the location of sparse entries of the stiffness matrix
+  void GenerateStiffnessMatrixTopology(SparseMatrix ** K) const;
+
+  virtual void ComputeStiffnessMatrix(const double * u, SparseMatrix * K, bool addMatrix=false);
+
+  virtual void ComputeForceAndMatrix(const double * u, double * f, SparseMatrix * K, bool addQuantity = false);
+
+  // compute the damping force
+  // unimplemented
+  // (use damping provided in the integrator class)
+  // virtual void ComputeDampingForce(const double * u, double * uvel, double * f, bool addForce=false);
+
+  // compute the energy E, internal forces f and stiffness matrix K of a single triangle.
+  // u is the input displacement with dimention (# vtx x 3)
+  // E is a pointer to a double
+  // f is a pointer to a array of 9 doubles (3x3)
+  // K is a pointer to a array of 81 doubles ((3x3) x (3x3)). The matrix is store in column major.
+  // E, f, K can be nullptr. If it is, the function will not compute it.
+  void ComputeTriangleElement(int tri, const double *u, double *E, double f[9], double K[81]) { ComputeStretchAndShear(u, tri, E, f, K); }
+  // compute the bending energy E, internal forces f and stiffness matrix K of a edge that involves two triangles.
+  // function arguments are the same as the previous one.
+  void ComputeQuadElement(int quad, const double *u, double *E, double f[12], double K[144]) { ComputeBending(u, quad, E, f, K); }
+
+  // compute the gravity of a single vertex
+  void ComputeVertexGravity(int vid, double g[3]);
+
+protected:
+
+  struct WuvInfo // derivatives of wu, wv with regard to vtx displacements
+  {
+    double pwupx[3];
+    double pwvpx[3];
+  };
+
+  struct BendInfo // store vtx and triangle indices for computing bend force
+  {
+    int v[4];
+    int tri[2];
+  };
+
+  static double GetTriangleSurfaceArea(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2);
+
+  static WuvInfo ComputeWuvInfo(const double triangleUV[6]);
+
+  double ComputeBendingStiffness(const BendInfo & bendInfo, const double * triangleUVs);
+
+  void GenerateBW(int numVertices, const double * restPositions, const double * masses,
+    int numTriangles, const int * triangles, const double * triangleUVs, const int * triangleGroups,
+    int numMaterialGroups, const MaterialGroup * materialGroups, int addGravity=0);
+
+  void ComputeStretchAndShear(const double * u, int triangleID, double * energy, double f[9], double K[81]);
+
+  void ComputeBending(const double * u, int quadId, double * energy, double f[12], double K[144]);
+
+  void AddStretchAndShear(const double * u, int startTriangle, int endTriangle, double * energy, double * f, SparseMatrix * K);
+
+  void AddBend(const double * u, int startQuad, int endQuad, double * energy, double * f, SparseMatrix * K);
+
+  // unimplemented
+  // (use damping provided in the integrator class)
+  //void AddDampingForce(double * uvel, double * f, int startTriangle, int endTriangle);
+
+  void ComputeGravity(double * f);
+
+  int numVertices;
+  std::vector<double> masses;
+  std::vector<Vec3d> restPositions;
+  int numTriangles;
+  std::vector<int> triangles;
+
+  std::vector<WuvInfo> wuvInfos;
+  std::vector<int> inverseIndicesStretchAndShear;
+  std::vector<int> triangleGroups;
+  std::vector<double> alphas; // a scaling factor used in stretch and shear energy
+
+  // Internal variables for bending computation:
+  // "numQuads" is number of 'quads' made up of two adjacent triangles. Important because each quad contains a bendable edge (the edge shared by the two triangles).
+  // "restAngles" is array of size numQuads containing the rest angles of edges that can bend (indexed by numQuads)
+  // "inverseIndicesQuad" is numQuads x 16 integer array
+  // "quadComponentIndices" is numQuads x 6 integer array
+  int numQuads;
+  std::vector<double> restAngles;       // rest angle for each quad
+  std::vector<double> bendStiffnesses;  // bend stiffness for each quad
+  std::vector<int> inverseIndicesQuad;
+  std::vector<BendInfo> quadComponentIndices;
+
+  int numMaterialGroups;
+
+  std::vector<MaterialGroup> materialGroups;
+
+  double bu; // stretch constraint in u (default = 1.0)
+  double bv; // stretch constraint in v (default = 1.0)
+
+  int addGravity;
+  double g;
+
+  bool cond[4];
+  bool useRestAnglesForBendingForces;
+};
+
+#endif
+
diff --git a/libraries/clothBW/clothBWFromObjMesh.cpp b/libraries/clothBW/clothBWFromObjMesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1632044fdc28df6f153a463be7641a22658526c4
--- /dev/null
+++ b/libraries/clothBW/clothBWFromObjMesh.cpp
@@ -0,0 +1,87 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "clothBW" library , Copyright (C) 2018 USC                            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "macros.h"
+#include "clothBWFromObjMesh.h"
+
+using namespace std;
+
+ClothBW * ClothBWFromObjMesh::GenerateClothBW(const ObjMesh * mesh, double surfaceDensity, const ClothBW::MaterialGroup & material, int addGravity)
+{
+  int numMaterialGroups = mesh->getNumGroups();
+  
+  // create arrays of density & stiffness values
+  vector<ClothBW::MaterialGroup> materialGroups(numMaterialGroups);
+  vector<double> densities(numMaterialGroups);
+  
+  // fill arrays all with the same value
+  for (int i=0; i<numMaterialGroups; i++) 
+  {
+    materialGroups[i] = material;
+    densities[i] = surfaceDensity;
+  }
+  
+  // construct clothBW
+  return GenerateClothBW(mesh, numMaterialGroups, densities.data(), materialGroups.data(), addGravity);
+}
+
+ClothBW * ClothBWFromObjMesh::GenerateClothBW(const ObjMesh * mesh, int numMaterialGroups, const double * groupSurfaceDensities,
+    const ClothBW::MaterialGroup * material, int addGravity)
+{
+  int numVertices = 0, numTriangles = 0, numGroups = 0;
+  double * restPositions = NULL;
+  int * triangles = NULL, * triangleGroups = NULL;
+  
+  //exportGeometry returns triangulated results
+  mesh->exportGeometry(&numVertices, &restPositions, &numTriangles, &triangles, &numGroups, &triangleGroups);
+
+  if (numGroups != numMaterialGroups)
+  {
+    printf("Mismatch in the number of groups. Mesh has %d groups.\n", numGroups);
+    free(restPositions);
+    free(triangles);
+    free(triangleGroups);
+    return NULL;
+  }
+  
+  vector<double> groupSurfaceMassDensity(numGroups), masses;
+  memcpy(groupSurfaceMassDensity.data(), groupSurfaceDensities, sizeof(double) * numGroups);
+  mesh->computeMassPerVertex(groupSurfaceMassDensity, masses);
+
+  ClothBW * clothBW = new ClothBW(numVertices,  restPositions, masses.data(), numTriangles, triangles, triangleGroups,
+      numGroups, material, addGravity);
+  
+  free(restPositions);
+  free(triangles);
+  free(triangleGroups);
+  return clothBW;
+}
diff --git a/src/libclothBW/clothBWFromObjMesh.h b/libraries/clothBW/clothBWFromObjMesh.h
similarity index 72%
rename from src/libclothBW/clothBWFromObjMesh.h
rename to libraries/clothBW/clothBWFromObjMesh.h
index e8e21c46618e3918eedd533286be9c6ca619dd67..25857d0934ea974e9abb2a64f8ea1c94a5979fa8 100644
--- a/src/libclothBW/clothBWFromObjMesh.h
+++ b/libraries/clothBW/clothBWFromObjMesh.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "clothBW" library , Copyright (C) 2013 USC                            *
+ * "clothBW" library , Copyright (C) 2018 USC                            *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Andy Pierce, Yu Yu Xu, Jernej Barbic                     *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,16 +40,14 @@
 #include "clothBW.h"
 #include "objMesh.h"
 
-namespace vega
-{
 class ClothBWFromObjMesh
 {
 public:
   
   // generate a cloth model from the given mesh (builds tensile, shear, and bending springs)
   // surface density and stiffnesses are the same for every triangle
-  static int GenerateClothBW(ObjMesh * mesh, ClothBW ** clothBW, double surfaceDensity, double tensileStiffness, double shearStiffness, double bendStiffnessU, double bendStiffnessV, double damping, int bu = 1.0, int bv = 1.0, int addGravity=0);
-  
+  static ClothBW * GenerateClothBW(const ObjMesh * mesh, double surfaceDensity, const ClothBW::MaterialGroup & material,int addGravity=0);
+
   // NOTE: materialGroup 0 is hard-coded as "default" in ObjMesh.cpp. So if a .mtl file 
   // specifies 2 materials (with 2 'usemtl' calls), there will actually be 3 material groups.
   // As a result, the density/stiffness arrays that specify a value for each material group
@@ -54,10 +56,10 @@ public:
   
   // generate a cloth model from the given mesh (builds tensile, shear, and bending springs)
   // user passes array of doubles to specify surface densities and stiffness values for each material group
-  static int GenerateClothBW(ObjMesh * mesh, ClothBW ** clothBW, int numMaterialGroups, double * surfaceDensity, double * tensileStiffness, double * shearStiffness, double * bendStiffnessU, double * bendStiffnessV, double * damping, int * bu = NULL, int * bv = NULL, int addGravity=0);
+  static ClothBW * GenerateClothBW(const ObjMesh * mesh, int numMaterialGroups, const double * groupSurfaceDensities, const ClothBW::MaterialGroup * materials, int addGravity=0);
   
 protected:
 };
 
-}
 #endif
+
diff --git a/libraries/configFile/configFile.cpp b/libraries/configFile/configFile.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..13f8d1de1dfa9a918c3e3e612207839ee1405df1
--- /dev/null
+++ b/libraries/configFile/configFile.cpp
@@ -0,0 +1,659 @@
+/*
+  * Copyright (c) 2007, Carnegie Mellon University
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions are met:
+  *     * Redistributions of source code must retain the above copyright
+  *       notice, this list of conditions and the following disclaimer.
+  *     * Redistributions in binary form must reproduce the above copyright
+  *       notice, this list of conditions and the following disclaimer in the
+  *       documentation and/or other materials provided with the distribution.
+  *     * Neither the name of Carnegie Mellon University, nor the
+  *       names of its contributors may be used to endorse or promote products
+  *       derived from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  * DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <string>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <iostream>
+#include <fstream>
+#include <cassert>
+#include <sstream>
+#include "configFile.h"
+using namespace std;
+
+#define OPT_INT 0
+#define OPT_BOOL 1
+#define OPT_FLOAT 2
+#define OPT_DOUBLE 3
+#define OPT_VEC3D 4
+#define OPT_CSTR 10
+#define OPT_STRING 11
+#define OPT_STRARR 20
+
+ConfigFile::ConfigFile() : suppressWarnings_(0) 
+{
+  strcpy(stoppingString, "**EOF");
+}
+
+ConfigFile::~ConfigFile() {}
+
+// finds a particular option name among all specified options
+int ConfigFile::seekOption(const char * optionName) const
+{
+  string upperOptionName = optionName;
+
+  upperCase(&upperOptionName[0]);
+  for(unsigned int i=0; i<optionNames.size(); i++)
+  {
+    if (upperOptionName == optionNames[i])
+      return (int)i;
+  }
+  return -1;
+}
+
+void ConfigFile::printOptions() const
+{
+  for(unsigned int i=0; i<optionNames.size(); i++)
+  {
+    switch(optionTypes[i])
+    {
+      case OPT_INT:
+        printf("%s: %d\n",optionNames[i].c_str(), *(int*)(destLocations[i]));
+        break;
+
+      case OPT_BOOL:
+        printf("%s: %d\n",optionNames[i].c_str(), *(bool*)(destLocations[i]));
+        break;
+
+      case OPT_FLOAT:
+        printf("%s: %G\n",optionNames[i].c_str(), *(float*)(destLocations[i]));
+        break;
+
+      case OPT_DOUBLE:
+        printf("%s: %G\n",optionNames[i].c_str(), *(double*)(destLocations[i]));
+        break;
+
+      case OPT_VEC3D:
+        printf("%s: %G, %G, %G\n", optionNames[i].c_str(), (*(Vec3d*)(destLocations[i]))[0], (*(Vec3d*)(destLocations[i]))[1], (*(Vec3d*)(destLocations[i]))[2]);
+        break;
+
+      case OPT_CSTR:
+        printf("%s: %s\n",optionNames[i].c_str(), (char*)(destLocations[i]));
+        break;
+
+      case OPT_STRING:
+	      printf("%s: %s\n",optionNames[i].c_str(), ((string*)(destLocations[i]))->c_str());
+	       break;
+
+      case OPT_STRARR:
+        {
+          vector<string> & vct = *(vector<string> *)(destLocations[i]);
+          printf("%s: ", optionNames[i].c_str());
+          for(size_t i = 0; i < vct.size(); i++) 
+          {
+            printf("%s", vct[i].c_str());
+            if (i + 1 != vct.size())
+              printf(", ");
+          }
+          printf("\n");
+        }
+        break;
+
+      default:
+        printf("Error: invalid type requested (1)\n");
+    }
+  }
+}
+
+// a generic routine to add a new option entry to the list of all options
+template<class T>
+int ConfigFile::addOptionHelper(const char * optionName, T * destLocation)
+{
+  if (seekOption(optionName) != -1)
+  {
+    if (!suppressWarnings_)
+      printf("Warning: option %s already exists. Ignoring request to re-add it.\n",optionName);
+    return 1;
+  }
+  std::string optionName_(optionName);
+
+  // convert to uppercase
+  for(unsigned int i=0; i< optionName_.size(); i++)
+    optionName_[i] = toupper(optionName_[i]);
+
+  optionNames.push_back(optionName_);
+  destLocations.push_back((void*)destLocation);
+  optionSet.push_back(false);
+  optionLoaded.push_back(false);
+  return 0;
+}
+
+#define ADD_OPTION_METHOD(type, OPT_TYPE) \
+  int ConfigFile::addOption(const char * optionName, type * destLocation) \
+  { \
+    int code; \
+    if ((code = addOptionHelper(optionName, destLocation)) != 0) \
+      return code; \
+    optionTypes.push_back(OPT_TYPE); \
+    return 0; \
+  }
+
+ADD_OPTION_METHOD(int, OPT_INT)
+
+ADD_OPTION_METHOD(bool, OPT_BOOL)
+
+ADD_OPTION_METHOD(float, OPT_FLOAT)
+
+ADD_OPTION_METHOD(double, OPT_DOUBLE)
+
+ADD_OPTION_METHOD(Vec3d, OPT_VEC3D)
+
+ADD_OPTION_METHOD(char, OPT_CSTR)
+
+ADD_OPTION_METHOD(string, OPT_STRING)
+
+ADD_OPTION_METHOD(vector<std::string>, OPT_STRARR)
+
+template<class T>
+int ConfigFile::addOptionOptional(const char * optionName, T * destLocation, const T & defaultValue)
+{
+  int code = addOption(optionName,destLocation);
+  *destLocation = defaultValue;
+  optionSet[optionSet.size()-1] = true;
+  return code;
+}
+
+int ConfigFile::addOptionOptional(const char * optionName, char * destLocation, const char * defaultValue)
+{
+  int code = addOption(optionName,destLocation);
+  // must use memmove because strings may overlap
+  memmove(destLocation, defaultValue, strlen(defaultValue) + 1);
+  optionSet[optionSet.size()-1] = true;
+  return code;
+}
+
+#define CHECK_FORMAT(cond, printStatement, error_no) \
+  do \
+  { \
+    if (cond) \
+    { \
+      if (verbose) \
+        printStatement; \
+      return error_no; \
+    } \
+  } while(0)
+
+#define CHECK_EMPTY_OPTION(cond) \
+  CHECK_FORMAT(cond, printf("Error: empty option %d: %s\n", count, &line[0]), 2)
+
+#define CHECK_DATA_WITHOUT_OPTION(cond) \
+  CHECK_FORMAT(cond, printf("Error: dataline with no option %d: %s\n", count, &line[0]), 3)
+
+#define CHECK_NO_DATALINE(cond) \
+  CHECK_FORMAT(cond, printf("Error: No dataline for option %s\n", optionNames[optionIndex].c_str()), 4)
+
+#define CHECK_EOF_REACHED(cond) \
+  CHECK_FORMAT(cond, printf("Error: EOF reached without specifying option value.\n"), 5)
+
+#define CHECK_INVALID_DATA(cond) \
+  CHECK_FORMAT(cond, printf("Error: invalid dataline for option %s: %s.\n", optionNames[optionIndex].c_str(), line), 6)
+
+#define CHECK_INVALID_BOOL(cond) \
+  CHECK_FORMAT(cond, printf("Error: invalid boolean specification for option %s: %s.\n", optionNames[optionIndex].c_str(), line), 7)
+
+#define CHECK_OPTION_SET \
+  for(size_t i=0; i<optionNames.size(); i++) \
+  { \
+    if (!optionSet[i]) \
+    { \
+      if (verbose) \
+        printf("Error: option %s didn't have an entry in the config file.\n",optionNames[i].c_str()); \
+      return 1; \
+    } \
+  }
+
+// eat a character from an istream
+#define EAT_CH(i) \
+  do \
+  { \
+    char c = 0; \
+    i >> c; \
+  } while(0)
+
+int ConfigFile::parseNumber(int type, const char * line, void * dest, int optionIndex, int verbose)
+{
+  if (type == OPT_INT)
+    CHECK_INVALID_DATA(sscanf(line, "%d", (int*)dest) == 0);
+  else if (type == OPT_FLOAT)
+    CHECK_INVALID_DATA(sscanf(line, "%f", (float*)dest) == 0);
+  else if (type == OPT_DOUBLE)
+    CHECK_INVALID_DATA(sscanf(line, "%lf", (double*)dest) == 0);
+  else if (type == OPT_BOOL)
+  {
+    if (strncmp(line,"true",4) == 0)
+      *((bool *)dest) = true;
+    else if (strncmp(line,"false",5) == 0)
+      *((bool *)dest) = false;
+    else 
+    {
+      int number = 0;
+      CHECK_INVALID_BOOL(sscanf(line, "%d", &number) == 0);
+      *((bool *)dest) = (number == 0 ? false : true);
+    }
+  }
+  else if (type == OPT_VEC3D) 
+  {
+    istringstream ss(line);
+    ss >> ws;
+    int leftMarker = ss.peek();
+    CHECK_INVALID_DATA(leftMarker == EOF);
+    bool hasBracket = false, hasParentheses = false;
+    if (leftMarker == '[') 
+    {
+      hasBracket = true;
+      EAT_CH(ss);
+    }
+    else if (leftMarker == '(')
+    {
+      hasParentheses = true;
+      EAT_CH(ss);
+    }
+    Vec3d vec(0.);
+    ss >> vec[0];
+    CHECK_INVALID_DATA(ss.fail());
+    for(int i = 1; i < 3; i++) 
+    {
+      ss >> ws;
+      int separator = ss.peek(); // peek next character
+      CHECK_INVALID_DATA(separator == EOF);
+      if (separator == ',')
+      {
+        EAT_CH(ss);
+      }
+      ss >> vec[i];
+      CHECK_INVALID_DATA(ss.fail());
+    }
+
+    if (hasBracket || hasParentheses)
+    {
+      char c = 0;
+      ss >> ws >> c;
+      CHECK_INVALID_DATA(ss.fail());
+      CHECK_INVALID_DATA((hasBracket && c!=']') || (hasParentheses && c!=')'));
+    }
+    *((Vec3d*)dest) = vec;
+  }
+  return 0;
+}
+
+int ConfigFile::parseOptions(const Entries & entries, Entries * remainingEntries, int verbose) 
+{
+  fill(optionLoaded.begin(), optionLoaded.end(), false);
+  assert(optionLoaded.size() == optionSet.size());
+  for(size_t i = 0; i < entries.size(); i++) 
+  {
+    const Entry & entry = entries[i];
+    const string & option = entry.option;
+    const string & value = entry.value;
+
+    int optionIndex = seekOption(option.c_str());
+    //printf("Read entry: %s . Option index: %d .\n", &line[1], index);
+    if (optionIndex == -1) 
+    {  
+      if (remainingEntries != NULL) 
+        remainingEntries->push_back(entry);
+      else if ((verbose) && (!suppressWarnings_))
+        printf("Warning: unknown option on line %d: %s\n",entry.lineCount,option.c_str());
+      continue;
+    }
+
+    int type = optionTypes[optionIndex];
+    void * dest = destLocations[optionIndex];
+    const char * line = value.c_str();
+
+    CHECK_NO_DATALINE(type != OPT_STRARR && value.size() == 0);
+
+    if (type == OPT_CSTR) 
+    {
+      size_t length = 0;
+      if ((length = value.find('\n')) == value.npos)
+        length = value.size();
+      memcpy(dest, value.c_str(), length);
+      ((char*)dest)[length] = '\0';
+    }
+    else if (type == OPT_STRING) 
+    {
+      size_t length = 0;
+      if ((length = value.find('\n')) == value.npos)
+        length = value.size();
+      *((string *)dest) = value.substr(0, length);
+    }
+    else if (type == OPT_STRARR) 
+    {
+      if (value.size() > 0) 
+      {
+        size_t start = 0, pos = 0;
+        while((pos = value.find('\n', start)) != value.npos) 
+        {
+          ((vector<string> *)dest)->push_back(value.substr(start, pos - start));
+          start = pos+1;
+        }
+        ((vector<string> *)dest)->push_back(value.substr(start));
+      }
+    }
+    else
+    {
+      int ret = 0;
+      if ((ret = parseNumber(type, line ,dest, optionIndex, verbose)) != 0)
+        return ret;
+    }
+    optionSet[optionIndex] = true;
+    optionLoaded[optionIndex] = true;
+  } // end for entries
+
+  CHECK_OPTION_SET;
+  return 0;
+}
+
+static char * stripc(char * s) 
+{
+  size_t start = 0;
+  for(; s[start] != '\0' && isspace(s[start]); start++) ;
+  if (s[start] == '\0')  // empty string
+    return s+start;
+
+  size_t end = strlen(s) - 1;
+  for(; isspace(s[end]); end--) ;
+  s[end+1] = '\0';
+  return s+start;
+}
+
+int ConfigFile::parseOptions(FILE * fin, int verbose)
+{
+  int count = 0;
+  char lineBuffer[4096];
+
+  int optionIndex = -1;
+  bool dataRead = false; // whether data entry has been read
+  int type = 0;          // option type: INT, BOOL, ...
+  void * dest = NULL;
+  bool skipUnknownOption = false;
+  fill(optionLoaded.begin(), optionLoaded.end(), false);
+  assert(optionLoaded.size() == optionSet.size());
+
+  while (fgets(lineBuffer,4096,fin) != NULL)
+  {
+    count++;
+    const char * line = stripc(lineBuffer);
+    
+    // ignore blank lines and comments
+    if ((line[0] == '#') || (line[0] == '\0'))
+      continue;
+    if (strcmp(line, stoppingString) == 0)
+      break;
+
+    if (line[0] == '*') // option line
+    {
+      CHECK_NO_DATALINE(optionIndex >= 0 && dataRead == false); // check no data for the last option
+      CHECK_EMPTY_OPTION(line[1] == '\0'); // option line with empty option
+
+      optionIndex = seekOption(&line[1]);
+      dataRead = false; // reset dataRead because a new option is found
+      skipUnknownOption = false;
+      if (optionIndex == -1) // cannot find the option
+      {
+        if (verbose && (!suppressWarnings_))
+          printf("Warning: unknown option on line %d: %s\n", count, &line[1]);
+        skipUnknownOption = true;
+        continue;
+      }
+      type = optionTypes[optionIndex];
+      dest = destLocations[optionIndex];
+      if (type == OPT_STRARR)
+      {
+        dataRead = true;       //for STRARR, 0 to multiple lines of string are allowed
+        optionSet[optionIndex] = true;
+        optionLoaded[optionIndex] = true;
+      }
+    }
+    else // found data entry
+    {
+      if (skipUnknownOption)
+        continue;
+      CHECK_DATA_WITHOUT_OPTION(optionIndex == -1); // check data entry with no corresponding option
+
+      if (type == OPT_CSTR)
+        strcpy((char*)dest, line);
+      else if (type == OPT_STRING)
+        *((string *)dest) = line;
+      else if (type == OPT_STRARR)
+        ((vector<string> *)dest)->push_back(line);
+      else
+      {
+        int ret = 0;
+        if ((ret = parseNumber(type, line ,dest, optionIndex, verbose)) != 0)
+          return ret;
+      }
+      optionSet[optionIndex] = true;
+      optionLoaded[optionIndex] = true;
+      dataRead = true;
+    } // end if found data entry
+  } // end while
+  CHECK_EOF_REACHED(optionIndex >= 0 && dataRead == false);
+
+  CHECK_OPTION_SET;
+  return 0;
+}
+
+// parse a file and return its config entries
+int ConfigFile::parse(const char * filename, Entries & entries, const char * stoppingString, int verbose)
+{
+  ifstream fin(filename, ios::binary);
+  if (!fin)
+  {
+    printf("Error: could not open option file %s\n",filename);
+    return 1;
+  }
+  return parse(fin, entries, stoppingString, verbose);
+}
+
+// parse a file and return its config entries
+int ConfigFile::parse(std::istream & fin, Entries & entries, const char * stoppingString, int verbose)
+{
+  if (!fin)
+    return 1;
+
+  string line;
+  int count = 0;
+  // bool dataRead = false;
+  Entry entry;
+  while(!fin.eof()) 
+  {
+    line.clear();
+    getline(fin, line);
+    count++;
+
+    strip(line); // remove blank characters at the beginning and end
+    
+    // ignore blank lines and comments
+    if (line.size() == 0 || line[0] == '#')
+      continue;
+    if (strcmp(line.c_str(), stoppingString) == 0)
+      break;
+
+    // a new entry found
+    if (line[0] == '*') 
+    {
+      // we only push an entry to entries when a new option has found
+      // in this way we can handle option with multiple lines
+      if (entry.option.size() > 0)
+        entries.push_back(entry);
+
+      entry.option.clear();
+      entry.value.clear();
+
+      entry.option = &line[1];
+      entry.lineCount = count;
+      CHECK_EMPTY_OPTION(entry.option.size() == 0);
+    }
+    else // add entry value. Append data to the end of existing data, separated by \n
+    {
+      // check if data entry with no corresponding option
+      CHECK_DATA_WITHOUT_OPTION(entry.option.size() == 0);
+      if (entry.value.size() > 0)
+        entry.value.append("\n");
+      entry.value.append(line);
+    }
+  } // end while (!fin.eof())
+  if (entry.option.size() > 0) // if there is a remaining entry
+    entries.push_back(entry);
+  return 0;
+}
+
+int ConfigFile::parseOptions(const char * filename, int verbose)
+{
+  FILE * fin = fopen(filename,"r");
+  if (!fin)
+  {
+    printf("Error: could not open option file %s\n",filename);
+    return 1;
+  }
+  int code = parseOptions(fin, verbose);
+  fclose(fin);
+  return code;
+}
+
+int ConfigFile::parseOptions(std::istream & in, int verbose)
+{
+  vector<Entry> entries;
+  if (parse(in, entries, stoppingString) != 0)
+    return 1; 
+  return parseOptions(entries, NULL, verbose);
+}
+
+bool ConfigFile::isOptionLoaded(const char * optionName) const
+{
+  int optionID = seekOption(optionName); // returns -1 if not found
+  if (optionID < 0) return false;
+  assert(optionID < (int)optionLoaded.size());
+  return optionLoaded[optionID];
+}
+
+void ConfigFile::strip(string & s) 
+{
+  if (s.size() == 0) 
+    return;
+  size_t start = 0;
+  for(; start < s.size() && isspace(s[start]); start++) ;
+  if (start == s.size()) 
+  {
+    s.clear();
+    return;
+  }
+  size_t end = s.size() - 1;
+  for(; isspace(s[end]); end--) ;
+  assert(start <= end);
+  s = s.substr(start, end - start + 1);
+}
+
+// convert string to uppercase
+void ConfigFile::upperCase(char * s)
+{
+  for(size_t i=0; i<strlen(s); i++)
+    s[i] = toupper(s[i]);
+}
+
+void ConfigFile::setStoppingString(const char * stoppingString_)
+{
+  if (strlen(stoppingString_) >= 31)
+  {
+    printf("Warning: proposed stopping string %s is too long. Stopping string not modifed.\n", stoppingString_);
+    return;
+  }
+  strcpy(stoppingString, stoppingString_);
+}
+
+int ConfigFile::saveOptions(const char * filename) const
+{
+  ofstream fout(filename);
+  if (!fout)
+  {
+    cout << "Failed to save configs to " << filename << endl;
+    return 1;
+  }
+
+  for(size_t i = 0; i < optionNames.size(); i++)
+  {
+    fout << "*" << optionNames[i] << '\n';
+
+    switch(optionTypes[i])
+    {
+      case OPT_INT:
+        fout << *(int*)destLocations[i] << '\n' << endl;
+        break;
+
+      case OPT_BOOL:
+        fout << (*(bool*)destLocations[i] ? "true" : "false" ) << '\n' << endl;
+        break;
+
+      case OPT_FLOAT:
+        fout << *(float*)destLocations[i] << '\n' << endl;
+        break;
+
+      case OPT_DOUBLE:
+        fout << *(double*)destLocations[i] << '\n' << endl;
+        break;
+
+      case OPT_VEC3D:
+        fout << *(Vec3d*)destLocations[i] << '\n' << endl;
+        break;
+
+      case OPT_CSTR:
+        fout << (char*)destLocations[i] << '\n' << endl;
+        break;
+
+      case OPT_STRING:
+        fout << *(string*)destLocations[i] << '\n' << endl;
+        break;
+
+      case OPT_STRARR:
+      {
+        const auto & strs = *(vector<string>*)destLocations[i];
+        for(size_t j = 0; j < strs.size(); j++)
+          fout << strs[j] << '\n';
+        fout << endl;
+        break;
+      }
+
+      default:
+        printf("Error: invalid type requested (2)\n");
+    }
+  }
+  fout.close();
+  cout << "Saved configs to " << filename << endl;
+  return 0;
+}
+
+template int ConfigFile::addOptionOptional<bool>(const char * optionName, bool * destLocation, const bool & defaultValue);
+template int ConfigFile::addOptionOptional<int>(const char * optionName, int * destLocation, const int & defaultValue);
+template int ConfigFile::addOptionOptional<float>(const char * optionName, float * destLocation, const float & defaultValue);
+template int ConfigFile::addOptionOptional<double>(const char * optionName, double * destLocation, const double & defaultValue);
+template int ConfigFile::addOptionOptional<Vec3d>(const char * optionName, Vec3d * destLocation, const Vec3d & defaultValue);
+template int ConfigFile::addOptionOptional<string>(const char * optionName, string * destLocation, const string & defaultValue);
+template int ConfigFile::addOptionOptional<vector<string> >(const char * optionName, vector<string> * destLocation, const vector<string> & defaultValue);
+
diff --git a/src/libconfigFile/configFile.h b/libraries/configFile/configFile.h
similarity index 51%
rename from src/libconfigFile/configFile.h
rename to libraries/configFile/configFile.h
index a1c6aa810563df34fada2c63a56341cdcbbd2136..7f8427d0f6e5d8cca4fee90f7dafd340ef7e1c46 100644
--- a/src/libconfigFile/configFile.h
+++ b/libraries/configFile/configFile.h
@@ -32,8 +32,8 @@
   CMU, 2005-2007
   Version 1.0
 
-  A class to parse text configuration files.
-  See configFile-example.cpp and configFile-example.config for examples.
+  A class to parse text configuration files. 
+  See configFile-example.cpp and configFile-example.config for examples. 
   It is by far easier to look at the example first, than to proceed
   straight to reading the class interface.
 
@@ -43,6 +43,9 @@
   float (single precision floating point)
   double (double precision floating point)
   char* (a C-style string)
+  Vec3d (vector of three double values)
+  string (std::string)
+  vector<string> (0 to multiple lines of string)
 
   Configuration files are text files. The format for each option entry is:
   *<option name>
@@ -52,24 +55,44 @@
   *temperature
   36.6
 
-  Blank lines are ignored.
+  *rainy
+  # boolean option can accept both integer (0/1) or text (true/false) input
+  false
+
+  *color
+  # Vec3d option can accept numbers separated by either comma "," or space " ", or surrounded by "()" or "[]"
+  # 0.4, 0.8, 0.5
+  # [0.4 0.8 0.5]
+  # ( 0.4 0.8 0.5 )
+  0.4 0.8 0.5
+
+  *street
+  # string/char* option accepts one line of characters. Memory preallocation
+  # is required for char* option
+  Hoover
+
+  *toDoList
+  # vector<string> option can accept 0 to multiple lines of text
+  wash car
+  buy food
+  repair chair
+
+  Blank lines are ignored. 
   A line is a comment if it begins with a "#".
-  The options can appear in arbirary order. They need not follow the order
-  in which they were created via addOption/addOptionOptional .
+  The options can appear in arbitrary order. They need not follow the order
+  in which they were created via addOption/addOptionOptional.
 
 */
 
 #include <vector>
 #include <string>
+#include "vec3d.h"
 
-namespace vega
-{
 class ConfigFile
 {
 public:
-
   ConfigFile();
-  ~ConfigFile();
+  virtual ~ConfigFile();
 
   // === routines to define the valid option entries for your configuration file ===
   // each option entry is either mandatory, or optional (in which case you need to provide a default value)
@@ -82,20 +105,65 @@ public:
   int addOption(const char * optionName, bool * destLocation);
   int addOption(const char * optionName, float * destLocation);
   int addOption(const char * optionName, double * destLocation);
+  int addOption(const char * optionName, Vec3d * destLocation);
   int addOption(const char * optionName, char * destLocation); // for strings, you must pass a pointer to a pre-allocated string buffer (and not a pointer to a (char*) pointer)
+  int addOption(const char * optionName, std::string * destLocation);
+  int addOption(const char * optionName, std::vector<std::string> * destLocation); // allow arbitrary lines of strings
 
   // routines to specify option entries that are optional
   // if not specified in a particular config file, the value will default to the given default value
   template<class T>
-  int addOptionOptional(const char * optionName, T * destLocation, T defaultValue);
+  int addOptionOptional(const char * optionName, T * destLocation, const T & defaultValue);
   int addOptionOptional(const char * optionName, char * destLocation, const char * defaultValue);
 
-  // === after you have specified your option entries with addOption and/or addOptionOptional, call "parseOptions" to open a particular configuration file and load the option values to their destination locations.
-
-  int parseOptions(const char * filename); // returns 0 on success, and a non-zero value on failure
+  // === routines to parse config files ===
+  // after you have specified your option entries with addOption and/or addOptionOptional, 
+  // call "parseOptions" to open a particular configuration file and load the option values to their destination locations.
+  int parseOptions(const char * filename, int verbose = 1); // returns 0 on success, and a non-zero value on failure
+  int parseOptions(std::istream & in, int verbose = 1);     // makes it possible to parse from std::istream
+  int parseOptions(FILE * fin, int verbose=1);              // makes it possible to parse from a file stream
 
   // after calling "parseOptions", you can print out the values of all options, to see the values that were read from the configuration file
-  void printOptions();
+  void printOptions() const;
+
+  // whether an option is loaded
+  // this data is reset everytime before calling parseOptions()
+  // return false if no such option name
+  // note: take O(n) to find the option
+  bool isOptionLoaded(const char * optionName) const;
+
+  // save option to disk
+  // Option values are stored as pointers. So it will save the values stored at the variables passed in addOption(Optional)
+  // when saveOptions() is called.
+  // return 0 if succeeds
+  int saveOptions(const char * filename) const;
+
+  // === recursive parsing ===
+
+  // following routines allow recursive parsing. first convert config file to a vector of entries (<option name>, <option value>)
+  // then in each level, a ConfigFile object parses the entry vector, assigns the correct options that the object knows, 
+  // and passes the unrecognized entries to the next level
+  struct Entry 
+  {
+    std::string option;
+    std::string value;
+    int lineCount;
+    Entry() : lineCount(-1) {}
+  };
+  typedef std::vector<Entry> Entries;
+
+  // parse a file and return its config entries
+  static int parse(const char * filename, Entries & entries, const char * stoppingString = "**EOF", int verbose = 1);
+  static int parse(std::istream & in, Entries & entries, const char * stoppingString = "**EOF", int verbose = 1); // makes it possible to parse from a file stream
+ 
+  // parse option from entries. If remainingEntries is provided, those entries that are not recognized are pushed into remainingEntries
+  int parseOptions(const Entries & entries, Entries * remainingEntries = NULL, int verbose = 1);
+
+  // === additional settings ===
+
+  // (optional) set a stopping string (when it is encountered in a file, parsing will stop); default: **EOF
+  // if the stopping string does not appear in a file, it will be parsed to the end
+  void setStoppingString(const char * stoppingString);
 
   // you can disable printing out warnings (default: enabled)
   void suppressWarnings(int suppressWarnings_) { this->suppressWarnings_ = suppressWarnings_; }
@@ -105,21 +173,23 @@ protected:
   std::vector<int> optionTypes;
   std::vector<void*> destLocations;
 
-  std::vector<bool> optionSet;
-
-  int seekOption(const char * optionName); // returns -1 if not found
+  std::vector<bool> optionSet;     // whether an option is set or not as default in addOptionOptional() or by the loaded configFile
+  std::vector<bool> optionLoaded;  // whether an option is loaded from file
+  
+  int seekOption(const char * optionName) const; // returns -1 if not found
 
   template<class T>
   int addOptionHelper(const char * optionName, T * destLocation);
 
-  int getTypeSize(int type);
-  void getTypeFormatSpecifier(int type, char * fsp);
+  int parseNumber(int type, const char * line, void * dest, int optionIndex, int verbose);
 
-  void upperCase(char * s); // converts a string to upper case
-  void removeTrailingCharacters(char * s, char ch); // removes (one or more) characters 'ch' from the end of the string
+  static void strip(std::string & s);
+  static void upperCase(char * s); // converts a string to upper case
 
   int suppressWarnings_;
+
+  char stoppingString[32];
 };
-}
+
 #endif
 
diff --git a/src/libinsertRows/insertRows.cpp b/libraries/constrainedDOFs/constrainedDOFs.cpp
similarity index 50%
rename from src/libinsertRows/insertRows.cpp
rename to libraries/constrainedDOFs/constrainedDOFs.cpp
index 35700e429758f568683ebde11ecd43f0f56e246e..94258650de8e4e710dd5474a492969c150e57edc 100644
--- a/src/libinsertRows/insertRows.cpp
+++ b/libraries/constrainedDOFs/constrainedDOFs.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "insertRows" library , Copyright (C) 2007 CMU, 2009 MIT               *
+ * "constrainedDOFs" library , Copyright (C) 2007 CMU, 2009 MIT          *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * Code author: Jernej Barbic, Yijing Li                                 *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -21,13 +30,12 @@
  *                                                                       *
  *************************************************************************/
 
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "stdio.h"
 #include "stdlib.h"
+#include <cassert>
 
-namespace vega
-{
-void InsertRows(int mFull, double * xConstrained, double * x, int numFixedRows, int * fixedRows, int oneIndexed)
+void ConstrainedDOFs::InsertDOFs(int mFull, const double * xConstrained, double * x, int numFixedRows, const int * fixedRows, int oneIndexed)
 {
   int destRow = 0; // current row in the dest matrix
   int sourceRow = 0; // in source
@@ -35,11 +43,8 @@ void InsertRows(int mFull, double * xConstrained, double * x, int numFixedRows,
   for(int i=0; i<numFixedRows; i++)
   {
     int index = fixedRows[i] + 1 - oneIndexed;
-    if ((index > mFull) || (index < 1))
-    {
-      printf("Error: invalid index %d specified.\n",index);
-      exit(1);
-    }
+    assert((index <= mFull) && (index >= 1));
+    //if ((index > mFull) || (index < 1))
     index--;
 
     while (destRow < index)
@@ -63,7 +68,7 @@ void InsertRows(int mFull, double * xConstrained, double * x, int numFixedRows,
   }
 }
 
-void RemoveRows(int mFull, double * xConstrained, double * x, int numFixedRows, int * fixedRows, int oneIndexed)
+void ConstrainedDOFs::RemoveDOFs(int mFull, double * xConstrained, const double * x, int numFixedRows, const int * fixedRows, int oneIndexed)
 {
   int numrows = 0;
   int row = 0;
@@ -71,11 +76,8 @@ void RemoveRows(int mFull, double * xConstrained, double * x, int numFixedRows,
   for(int i=0; i<numFixedRows; i++)
   {
     int index = fixedRows[i] + 1 - oneIndexed;
-    if ((index > mFull) || (index < 1))
-    {
-      printf("Error: invalid index %d specified.\n",index);
-      exit(1);
-    }
+    //if ((index > mFull) || (index < 1))
+    assert((index <= mFull) && (index >= 1));
     index--;
 
     while (row<index)
@@ -109,7 +111,7 @@ void RemoveRows(int mFull, double * xConstrained, double * x, int numFixedRows,
   }
 }
 
-void FullDOFsToConstrainedDOFs(int mFull, int numDOFs, int * DOFsConstrained, int * DOFs, int numFixedRows, int * fixedRows, int oneIndexed)
+void ConstrainedDOFs::FullDOFsToConstrainedDOFs(int mFull, int numDOFs, int * DOFsConstrained, const int * DOFs, int numFixedRows, const int * fixedRows, int oneIndexed)
 {
   if (numDOFs == 0)
     return;
@@ -122,11 +124,8 @@ void FullDOFsToConstrainedDOFs(int mFull, int numDOFs, int * DOFsConstrained, in
 
     // correct for (optional) 1-indexing
     int index = fixedRows[i] + 1 - oneIndexed;
-    if ((index > mFull) || (index < 1))
-    {
-      printf("Error: invalid index %d specified.\n",index);
-      exit(1);
-    }
+    assert((index <= mFull) && (index >= 1));
+    //if ((index > mFull) || (index < 1))
     index--;
 
     while (DOFs[dof] < index)
@@ -152,4 +151,49 @@ void FullDOFsToConstrainedDOFs(int mFull, int numDOFs, int * DOFsConstrained, in
   }
 }
 
+void ConstrainedDOFs::FindFreeDOFs(int mFull, int * freeDOFs, int numFixedDOFs, const int * fixedDOFs, int oneIndexed)
+{
+  assert(mFull >= numFixedDOFs);
+  int mFree = mFull - numFixedDOFs;
+  int constrainedDOF = 0;
+  int superDOF = 0;
+  for(int i=0; i<numFixedDOFs; i++)
+  { 
+    int nextFixedDOF = fixedDOFs[i];
+    nextFixedDOF -= oneIndexed;
+    assert((nextFixedDOF < mFull) && (nextFixedDOF >= 0));
+
+    while (superDOF < nextFixedDOF)
+    {
+      assert(constrainedDOF < mFree); // check if too many DOFs specified
+      freeDOFs[constrainedDOF] = superDOF;
+      constrainedDOF++;
+      superDOF++;
+    }
+
+    superDOF++; // skip the deselected DOF
+  }
+  while (superDOF < mFull)
+  { 
+    assert(constrainedDOF < mFree);
+    freeDOFs[constrainedDOF] = superDOF; // check if too many DOFs specified
+
+    constrainedDOF++;
+    superDOF++;
+  }
+}
+
+bool ConstrainedDOFs::CheckValidSortedDOFs(int mFull, int numDOFs, const int * DOFs, int oneIndexed)
+{
+  int lowBound = 0 + oneIndexed;
+  int highBound = mFull + oneIndexed;
+  for(int i = 0; i < numDOFs; i++)
+  {
+    if (DOFs[i] < lowBound || DOFs[i] >= highBound)
+      return false;
+    if (i > 0 && DOFs[i] <= DOFs[i-1])
+      return false;
+  }
+  return true;
 }
+
diff --git a/libraries/constrainedDOFs/constrainedDOFs.h b/libraries/constrainedDOFs/constrainedDOFs.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef54835b6c48526284e0cd30d733212f7cbd4aad
--- /dev/null
+++ b/libraries/constrainedDOFs/constrainedDOFs.h
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "constrainedDOFs" library , Copyright (C) 2007 CMU, 2009 MIT          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic, Yijing Li                                 *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _CONSTRAINED_DOFS_H_
+#define _CONSTRAINED_DOFS_H_
+
+/*
+  Insert or remove given components from a linear array.
+  The dynamic solver uses these routines to fix the specified vertices and remove
+  rows from mass and stiffness matrices, as necessary.
+*/
+class ConstrainedDOFs
+{
+public:
+  // The input fixedRows array must be pre-sorted
+
+  // inserts zero entries into an array, at the specified locations
+  // the locations must be given with respect to the full array 
+  // input: xConstrained
+  // output: x
+  static void InsertDOFs(int mFull, const double * xConstrained, double * x, int numFixedRows, const int * fixedRows, int oneIndexed=0);   
+
+  // removes entries at the specified locations from an array
+  // the locations must be given with respect to the full array 
+  // input: x
+  // output: xConstrained
+  static void RemoveDOFs(int mFull, double * xConstrained, const double * x, int numFixedRows, const int * fixedRows, int oneIndexed=0);   
+
+  // translates the array indices from original indices to indices after removal of the specified entries
+  // input: DOFs (must be sorted) (0-indexed)
+  // output: DOFsConstrained (0-indexed)
+  // oneIndexed applies only to fixedRows array, NOT to DOFsConstrained or DOFs
+  static void FullDOFsToConstrainedDOFs(int mFull, int numDOFs, int * DOFsConstrained, const int * DOFs, int numFixedRows, const int * fixedRows, int oneIndexed=0);   
+
+  // find the free DOFs that do not belong to fixedDOFs
+  // input: fixedDOFs, pre-sorted
+  // output freeDOFs, sorted
+  // oneIndexed applies only to fixedDOFs array, not freeDOFs
+  // example: mFull=7, fixedDOFs=(2, 3, 5), oneIndexed=0 => freeDOFs=(0,1,4,6)
+  static void FindFreeDOFs(int mFull, int * freeDOFs, int numFixedDOFs, const int * fixedDOFs, int oneIndexed=0);  
+
+  // return true if DOFs is sorted (from small to large) and in range of [0, mFull) if (oneIndexed == 0) or [1, mFull] if (oneIndex == 1)
+  static bool CheckValidSortedDOFs(int mFull, int numDOFs, const int * DOFs, int oneIndexed=0);
+
+};
+#endif
+
diff --git a/libraries/corotationalLinearFEM/corotationalLinearFEM.cpp b/libraries/corotationalLinearFEM/corotationalLinearFEM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5b06eeca21fda50b2ae11ef358251a312ca2954
--- /dev/null
+++ b/libraries/corotationalLinearFEM/corotationalLinearFEM.cpp
@@ -0,0 +1,1210 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "corotational linear FEM" library , Copyright (C) 2018 USC            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "corotationalLinearFEM.h"
+#include "polarDecomposition.h"
+#include "matrixMultiplyMacros.h"
+#include "mat3d.h"
+#include "volumetricMeshENuMaterial.h"
+#include "volumetricMeshOrthotropicMaterial.h"
+#include "cubicMesh.h"
+
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <cassert>
+#include <iostream>
+using namespace std;
+
+// Corotational linear FEM for tet meshes is implemented by following:
+// 
+// M. Mueller, M. Gross: Interactive Virtual Materials.
+// In Proc. of Graphics Interface 2004 (2004), pp. 239–246.
+//
+// Details can be found in the above paper.
+
+// Corotational linear FEM for cubic meshes is implemented by following:
+// Jesus Perez, Alvaro G. Perez and Miguel A. Otaduy:
+// Simulation of Hyperelastic Materials Using Energy Constraints
+// Proc. of Congreso Espanol de Informatica Grafica, 2013
+//
+// Details can be found in the above paper. Some additional notes here:
+//
+// We use natural coordinates s = (s_1, s_2, s_3)^T , where s_i belongs to [-1, 1].
+// The natural coordinates for eight cubic nodes are: sn = ((-1)^i, (-1)^j, (-1)^k)^T, n = 4(i-1) + 2(j-1) + k, for i,j,k = {1,2}.
+// So the shape functions are: N_n(s) = (1+sn_1 s_1) (1+sn_2 s_2) (1+sn_3 s_3) / 8 .
+// The interpolation scheme based on shape functions is: y(s) = \sum_{n=1 to 8} yn N_n(s) .
+// The element stiffness matrix at rest pose is:
+// Ke = \integrate_X B'(X)^T E B'(X) dV_X , where X is the point in undeformed space, E is the 6x6 elasticity stiffness tensor
+//    = \integratr_s B(s)^T E B(s) w dV_s, and s are the natural coordinates, and
+// W = \partial{X} / \partial{s} is the scaling matrix from natural to material space.
+// For cubic meshes, W can be just a scaled identity, W = w I.
+// In the above, B(s) is a 6x24 matrix: B(s) = (diag(\partial{N_1}/\partial{s}), ..., diag(\partial{N_}/\partial{s}) ...).
+// For a pair of corresponding X and s, B'(x) = B(s) * w^(-1).
+// Due to the scaling from natural to material space, we have dV_X = w^3 dV_s .
+// We approximate the integration of the elastic energy with second-order Gaussian quadrature:
+// s_q = ((-1)^i, (-1)^j, (-1)^k)^T / \sqrt(3), q = 4(i-1) + 2(j-1) + k, for i,j,k = {1,2}
+// weights w_q = 1
+// So, Ke = \sum_q w_q B(s_q)^T E B(s_q) w .
+//
+// Shape functions are:
+// n=1, i,j,k = 1,1,1, N_1 = (1-s1)(1-s2)(1-s3) / 8
+// n=2, i,j,k = 1,1,2, N_2 = (1-s1)(1-s2)(1+s3) / 8
+// n=3, i,j,k = 1,2,1, N_3 = (1-s1)(1+s2)(1-s3) / 8
+// n=4, i,j,k = 1,2,2, N_4 = (1-s1)(1+s2)(1+s3) / 8
+// n=5, i,j,k = 2,1,1, N_5 = (1+s1)(1-s2)(1-s3) / 8
+// n=6, i,j,k = 2,1,2, N_6 = (1+s1)(1-s2)(1+s3) / 8
+// n=7, i,j,k = 2,2,1, N_7 = (1+s1)(1+s2)(1-s3) / 8
+// n=8, i,j,k = 2,2,2, N_8 = (1+s1)(1+s2)(1+s3) / 8
+//
+// B(s) = [-(1-s2)(1-s3) 0 0  ...  (1+s2)(1+s3) 0 0] / 8
+//        [0 -(1-s1)(1-s3) 0  ... 0  (1+s1)(1+s3) 0]
+//        [0 0 -(1-s1)(1-s2)  ... 0 0  (1+s1)(1+s2)]
+//        [-(1-s1)(1-s3) -(1-s2)(1-s3)             ]
+//        [...                                     ]
+//        [...                                     ]
+//
+// Vertex order for cubic meshes (same as in the CubicMesh class) :
+//
+//     3 - - - 2
+//    /|      /|
+//   7 - - - 6 |       y
+//   | |     | |       |
+//   | 0 - - | 1       |_ _ _x
+//   |/      |/       /
+//   4 - - - 5       z
+//
+// We choose the tet (3,4,1,6) to compute the rotation component of the cube deformation:
+static int cubeRotationTetIndex[4] = {3, 4, 1, 6};
+
+// MInverse is pre-allocated
+void CorotationalLinearFEM::buildMInverse(double * MInverse, VolumetricMesh * volumetricMesh) {
+
+  int numElements = volumetricMesh->getNumElements();
+  VolumetricMesh::elementType type = volumetricMesh->getElementType();
+  int vtxIndex[4] = {0};
+  for(int el = 0; el < numElements; el++)
+  {
+    // get the integer indices of the tet vertices
+    if(type == VolumetricMesh::TET)
+    {
+      for(int vtx=0; vtx<4; vtx++)
+        vtxIndex[vtx] = volumetricMesh->getVertexIndex(el, vtx);
+    }
+    else if(type == VolumetricMesh::CUBIC)
+    {
+      for(int i = 0; i < 4; i++)
+        vtxIndex[i] = volumetricMesh->getVertexIndex(el, cubeRotationTetIndex[i]);
+    }
+
+    //  Form matrix:
+    //  M = [ v0   v1   v2   v3 ]
+    //      [  1    1    1    1 ]
+    double M[16]; // row-major
+    for(int vtx=0; vtx<4; vtx++)
+      for(int dim=0; dim<3; dim++)
+        M[4 * dim + vtx] = (volumetricMesh->getVertex(vtxIndex[vtx]))[dim]; //undeformedPositions[3 * vtxIndex[vtx] + dim];
+    M[12] = M[13] = M[14] = M[15] = 1.0;
+
+    inverse4x4(M, MInverse + el * 16);
+  }
+}
+
+CorotationalLinearFEM::CorotationalLinearFEM(VolumetricMesh * volumetricMesh_) : volumetricMesh(volumetricMesh_)
+{
+  VolumetricMesh::elementType type = volumetricMesh->getElementType();
+  if(type != VolumetricMesh::TET && type != VolumetricMesh::CUBIC)
+  {
+    printf("Error: CorotationalLinearFEM: unknown element type for the volumetric mesh.\n");
+    throw 1;
+  }
+
+  numVertices = volumetricMesh->getNumVertices();
+  int numElements = volumetricMesh->getNumElements();
+
+  // store the undeformed positions
+  undeformedPositions = (double*) malloc (sizeof(double) * 3 * numVertices);
+  for(int i=0; i < numVertices; i++)
+  {
+    const Vec3d & v = volumetricMesh->getVertex(i);
+    for(int j=0; j<3; j++)
+      undeformedPositions[3*i+j] = v[j];
+  }
+
+  MInverse = (double**) malloc (sizeof(double*) * numElements);
+  for(int el = 0; el < numElements; el++)
+  {
+    // get the integer indices of the tet vertices
+    int vtxIndex[4];
+    if(type == VolumetricMesh::TET)
+    {
+      for(int vtx=0; vtx<4; vtx++)
+        vtxIndex[vtx] = volumetricMesh->getVertexIndex(el, vtx);
+    }
+    else if(type == VolumetricMesh::CUBIC)
+    {
+      for(int i = 0; i < 4; i++)
+        vtxIndex[i] = volumetricMesh->getVertexIndex(el, cubeRotationTetIndex[i]);
+    }
+
+    /*
+       Form matrix:
+       M = [ v0   v1   v2   v3 ]
+           [  1    1    1    1 ]
+    */
+    double M[16]; // row-major
+    for(int vtx=0; vtx<4; vtx++)
+      for(int dim=0; dim<3; dim++)
+        M[4 * dim + vtx] = undeformedPositions[3 * vtxIndex[vtx] + dim];
+    M[12] = M[13] = M[14] = M[15] = 1.0;
+
+    // invert M and cache inverse (see [Mueller 2004])
+    MInverse[el] = (double*) malloc (sizeof(double) * 16);
+    inverse4x4(M, MInverse[el]);
+  }
+
+  // build acceleration indices for fast writing to the global stiffness matrix
+  SparseMatrix * sparseMatrix = NULL;
+  GetStiffnessMatrixTopology(&sparseMatrix);
+  BuildRowColumnIndices(sparseMatrix);
+  delete(sparseMatrix);
+
+  // compute stiffness matrices for all the elements in the undeformed configuration
+  KElementUndeformed = (double**) calloc (numElements, sizeof(double*));
+
+  if(type == VolumetricMesh::TET)
+  {
+    for (int el = 0; el < numElements; el++)
+    {
+      double * MInv = MInverse[el];
+
+      // Form stiffness matrix of the element in the undeformed configuration.
+      // The procedure below is standard in FEM solid mechanics.
+      // This code implements the equations given in Ahmed A. Shabana: Theory of Vibration, Volume II: Discrete and Continuous Systems, Springer--Verlag, New York, NY, 1990.
+      // compute elasticity stiffness tensor
+      double E[36]; // 6 x 6 matrix, stored row-major (symmetric, so row vs column-major storage makes no difference anyway)
+      // theta = E * epsilon
+      // epsilon = {e11, e22, e33, 2*e12, 2*e23, 2*e31}
+      // theta = {t11, t22, t33, t12, t23, t31}
+      if(computeElasticityStiffnessTensor(E, el) == false) // failure in constructing E
+      {
+        clear();  //clean data allocated so far
+        throw 2;
+      }
+
+      double B[72] =
+        { MInv[0], 0, 0,       MInv[4], 0, 0,       MInv[8], 0, 0,        MInv[12], 0, 0,
+          0, MInv[1], 0,       0, MInv[5], 0,       0, MInv[9], 0,        0, MInv[13], 0,
+          0, 0, MInv[2],       0, 0, MInv[6],       0, 0, MInv[10], 0,    0, MInv[14],
+          MInv[1], MInv[0], 0, MInv[5], MInv[4], 0, MInv[9], MInv[8], 0,  MInv[13], MInv[12], 0,
+          0, MInv[2], MInv[1], 0, MInv[6], MInv[5], 0, MInv[10], MInv[9], 0, MInv[14], MInv[13],
+          MInv[2], 0, MInv[0], MInv[6], 0, MInv[4], MInv[10], 0, MInv[8], MInv[14], 0, MInv[12] };
+
+      // EB = E * B
+      double EB[72];
+      memset(EB, 0, sizeof(double) * 72);
+      for (int i=0; i<6; i++)
+        for (int j=0; j<12; j++)
+          for (int k=0; k<6; k++)
+            EB[12 * i + j] += E[6 * i + k] * B[12 * k + j];
+
+      // KElementUndeformed[el] = B^T * EB
+      KElementUndeformed[el] = (double*) calloc (144, sizeof(double)); // element stiffness matrix
+      for (int i=0; i<12; i++)
+        for (int j=0; j<12; j++)
+          for (int k=0; k<6; k++)
+            KElementUndeformed[el][12 * i + j] += B[12 * k + i] * EB[12 * k + j];
+
+      // KElementUndeformed[el] *= volume
+      double volume = volumetricMesh->getElementVolume(el);
+
+      for(int i=0; i<144; i++)
+        KElementUndeformed[el][i] *= volume;
+    }
+  }
+  else if(type == VolumetricMesh::CUBIC)
+  {
+    CubicMesh * cubicMesh = dynamic_cast<CubicMesh *>(volumetricMesh);
+    double cubicSize = cubicMesh->getCubeSize();
+    // w is the scale from nature to material coord.
+    double w = cubicSize / 2;
+
+    // create sampled B(s)
+    double Bq[8][6*24];
+    memset(Bq, 0, sizeof(Bq));
+    assert(sizeof(Bq) == sizeof(double) * 8 * 6 * 24);
+
+    const double sqrt3 = sqrt(3.);
+
+    // the order of x[8],y[8],z[8] is the same as the vtx order in CUBIC:
+    int x[8] = {-1, 1, 1,-1,-1, 1, 1,-1};
+    int y[8] = {-1,-1, 1, 1,-1,-1, 1, 1};
+    int z[8] = {-1,-1,-1,-1, 1, 1, 1, 1};
+    for(int q = 0; q < 8; q++)
+    {
+      // s_q = ((-1)^i, (-1)^j, (-1)^k)^T / \sqrt(3), q = 4(i-1) + 2(j-1) + k, for i,j,k = {1,2}
+      double s[3] = {x[q]/sqrt3, y[q]/sqrt3, z[q]/sqrt3};
+      double s1 = s[0], s2 = s[1], s3 = s[2];
+      double ms1 = 1-s1, ps1 = 1+s1, ms2 = 1-s2, ps2 = 1+s2, ms3 = 1-s3, ps3 = 1+s3;
+      double s1c[2] = {ms1, ps1}, s2c[2] = {ms2, ps2}, s3c[2] = {ms3, ps3};
+      // double N1x = -ms2*ms3, N1y = -ms1*ms3, N1z = -ms1*ms2;
+      double dNds[8][3];
+      for(int k = 0; k < 8; k++)
+      {
+        dNds[k][0] = x[k] * s2c[(y[k]+1)/2] * s3c[(z[k]+1)/2] / 8;
+        dNds[k][1] = s1c[(x[k]+1)/2] * y[k] * s3c[(z[k]+1)/2] / 8;
+        dNds[k][2] = s1c[(x[k]+1)/2] * s2c[(y[k]+1)/2] * z[k] / 8;
+      }
+
+      // n=1, i,j,k = 1,1,1, N_1 = (1-s1)(1-s2)(1-s3) / 8
+      // n=2, i,j,k = 1,1,2, N_2 = (1-s1)(1-s2)(1+s3) / 8
+      // ...
+
+      // B(s) = [-(1-s2)(1-s3) 0 0  ...  (1+s2)(1+s3) 0 0] / 8
+      //        [0 -(1-s1)(1-s3) 0  ... 0  (1+s1)(1+s3) 0]
+      //        [0 0 -(1-s1)(1-s2)  ... 0 0  (1+s1)(1+s2)]
+      //        [-(1-s1)(1-s3) -(1-s2)(1-s3)             ]
+      //        [...                                     ]
+      //        [...                                     ]
+
+      // compute B(sq)
+      for(int k = 0; k < 8; k++)
+      {
+        Bq[q][         k*3] = dNds[k][0];
+        Bq[q][24   + 1+k*3] = dNds[k][1];
+        Bq[q][24*2 + 2+k*3] = dNds[k][2];
+        Bq[q][24*3 +   k*3] = dNds[k][1]; Bq[q][24*3 + 1+k*3] = dNds[k][0];
+        Bq[q][24*4 + 1+k*3] = dNds[k][2]; Bq[q][24*4 + 2+k*3] = dNds[k][1];
+        Bq[q][24*5 +   k*3] = dNds[k][2]; Bq[q][24*5 + 2+k*3] = dNds[k][0];
+      }
+    }
+
+    for (int el = 0; el < numElements; el++)
+    {
+      // double * MInv = MInverse[el];
+      double E[36]; // 6 x 6 matrix, stored row-major (symmetric, so row vs column-major storage makes no difference anyway)
+      if(computeElasticityStiffnessTensor(E, el) == false) // failure in constructing E
+      {
+        clear();  //clean data allocated so far
+        throw 1;
+      }
+
+      // Ke is of size 24 * 24
+      KElementUndeformed[el] = (double*) calloc(576, sizeof(double));
+      for(int q = 0; q < 8; q++)
+      {
+        double weight_q = 1.0;
+        // Ke = \sum_q weight_q B(s_q)^T E B(s_q) w
+
+        // EB = E * B
+        double EB[6*24];
+        memset(EB, 0, sizeof(double) * 6*24);
+        for (int i = 0; i < 6; i++)
+          for (int j = 0; j < 24; j++)
+            for (int k = 0; k < 6; k++)
+              EB[24 * i + j] += E[6 * i + k] * Bq[q][24 * k + j];
+
+        // KElementUndeformed[el] += weight_q * B^T * EB
+        for (int i = 0; i < 24; i++)
+          for (int j = 0; j < 24; j++)
+            for (int k = 0; k < 6; k++)
+              KElementUndeformed[el][24 * i + j] += weight_q * Bq[q][24 * k + i] * EB[24 * k + j];
+      }
+      for(int i = 0; i < 576; i++)
+        KElementUndeformed[el][i] *= w;
+
+    }
+  }
+}
+
+CorotationalLinearFEM::~CorotationalLinearFEM()
+{
+  clear();
+}
+
+void CorotationalLinearFEM::clear()
+{
+  free(undeformedPositions);
+  undeformedPositions = NULL;
+  for(int el=0; el < volumetricMesh->getNumElements(); el++)
+  {
+    free(KElementUndeformed[el]);
+    free(MInverse[el]);
+  }
+  free(KElementUndeformed);
+  KElementUndeformed = NULL;
+
+  free(MInverse);
+  MInverse = NULL;
+
+  ClearRowColumnIndices();
+}
+
+// compute elasticity stiffness tensor
+// E: 6 x 6 matrix, stored row-major (symmetric, so row vs column-major storage makes no difference anyway)
+bool CorotationalLinearFEM::computeElasticityStiffnessTensor(double E[36], int el)
+{
+  VolumetricMesh::Material * material = volumetricMesh->getElementMaterial(el);
+
+  // check if material is ENuMaterial (i.e., isotropic)
+  VolumetricMesh::ENuMaterial * eNuMaterial = downcastENuMaterial(material);
+  if (eNuMaterial != NULL)
+  {
+    // material is isotropic, specified by E, nu
+    // compute Lame coefficients
+    double lambda = eNuMaterial->getLambda();
+    double mu = eNuMaterial->getMu();
+
+    double Et[36] = { lambda + 2 * mu, lambda, lambda, 0, 0, 0,
+        lambda, lambda + 2 * mu, lambda, 0, 0, 0,
+        lambda, lambda, lambda + 2 * mu, 0, 0, 0,
+        0, 0, 0, mu, 0, 0,
+        0, 0, 0, 0, mu, 0,
+        0, 0, 0, 0, 0, mu };
+
+    memcpy(E, Et, sizeof(double) * 36);
+  }
+  else
+  {
+    // orthotropic material
+    // we follow the following references:
+    // Yijing Li and Jernej Barbic: Stable Orthotropic Materials, Symposium on Computer Animation 2014
+    // http://en.wikipedia.org/wiki/Orthotropic_material
+    // http://www.solidmechanics.org/text/Chapter3_2/Chapter3_2.htm
+
+    // test if material is OrthotropicMaterial (i.e., orthotropic)
+    VolumetricMesh::OrthotropicMaterial * orthotropicMaterial = downcastOrthotropicMaterial(material);
+    if (orthotropicMaterial != NULL)
+    {
+      double E1 = orthotropicMaterial->getE1();
+      double E2 = orthotropicMaterial->getE2();
+      double E3 = orthotropicMaterial->getE3();
+      double nu12 = orthotropicMaterial->getNu12();
+      double nu23 = orthotropicMaterial->getNu23();
+      double nu31 = orthotropicMaterial->getNu31();
+      double G12 = orthotropicMaterial->getG12();
+      double G23 = orthotropicMaterial->getG23();
+      double G31 = orthotropicMaterial->getG31();
+
+      double nu21 = nu12 * E2 / E1;
+      double nu32 = nu23 * E3 / E2;
+      double nu13 = nu31 * E1 / E3;
+
+      double Y = 1.0 / (1.0 - nu12 * nu21 - nu23 * nu32 - nu31 * nu13 - 2.0 * nu21 * nu32 * nu13);
+
+      double ELocal[36] = { E1 * (1.0 - nu23 * nu32) * Y, E1 * (nu21 + nu31 * nu23) * Y, E1 * (nu31 + nu21 * nu32) * Y, 0.0, 0.0, 0.0,
+          E1 * (nu21 + nu31 * nu23) * Y, E2 * (1.0 - nu13 * nu31) * Y, E2 * (nu32 + nu12 * nu31) * Y, 0.0, 0.0, 0.0,
+          E1 * (nu31 + nu21 * nu32) * Y, E2 * (nu32 + nu12 * nu31) * Y, E3 * (1.0 - nu12 * nu21) * Y, 0.0, 0.0, 0.0,
+          0, 0, 0, G12, 0, 0,
+          0, 0, 0, 0, G23, 0,
+          0, 0, 0, 0, 0, G31 };
+
+      //memcpy(E, ELocal, sizeof(double) * 36); // debug
+
+      double R[9]; // row-major
+      orthotropicMaterial->getR(R);
+
+      // rotate Elocal into the basis given by the columns of R
+      #define Relt(i,j) (R[3*(i)+(j)])
+      double rotator[36];
+      for(int i=0; i<3; i++)
+        for(int j=0; j<3; j++)
+        {
+          rotator[6 * i + j] = Relt(i,j) * Relt(i,j);
+          rotator[6 * i + 3 + j] = 2.0 * Relt(i, j) * Relt(i, (j+1) % 3);
+          rotator[6 * (i + 3) + j] = Relt(i, j) * Relt((i+1) % 3, j);
+          rotator[6 * (i + 3) + 3 + j] = Relt(i, j) * Relt((i+1) % 3, (j+1) % 3) + Relt(i, (j+1) % 3) * Relt((i+1) % 3, j);
+        }
+      #undef Relt
+
+      // debug
+      //memset(rotator, 0, sizeof(double) * 36);
+      //for(int i=0; i<6; i++)
+      //rotator[6*i+i] = 1.0;
+
+      // E = rotator * ELocal * rotator^T
+      double buffer[36];
+      memset(buffer, 0, sizeof(double) * 36);
+      // buffer = ELocal * rotator^T
+      for(int i=0; i<6; i++)
+        for(int j=0; j<6; j++)
+          for(int k=0; k<6; k++)
+            buffer[6 * i + j] += ELocal[6 * i + k] * rotator[6 * j + k];
+
+      // E = rotator * buffer
+      memset(E, 0, sizeof(double) * 36);
+      for(int i=0; i<6; i++)
+        for(int j=0; j<6; j++)
+          for(int k=0; k<6; k++)
+            E[6 * i + j] += rotator[6 * i + k] * buffer[6 * k + j];
+
+    }
+    else
+    {
+      printf("Error: CorotationalLinearFEM: unknown material encounterd in the mesh.\n");
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void CorotationalLinearFEM::GetStiffnessMatrixTopology(SparseMatrix ** stiffnessMatrixTopology)
+{
+  GetStiffnessMatrixTopology(volumetricMesh, stiffnessMatrixTopology);
+}
+
+void CorotationalLinearFEM::GetStiffnessMatrixTopology(VolumetricMesh * volumetricMesh, SparseMatrix ** stiffnessMatrixTopology)
+{
+  int numVertices = volumetricMesh->getNumVertices();
+  SparseMatrixOutline * emptyMatrix = new SparseMatrixOutline(3 * numVertices);
+
+  int numElements = volumetricMesh->getNumElements();
+  int numElementVertices = volumetricMesh->getNumElementVertices();
+  vector<int> vtxIndex(numElementVertices);
+  for (int el = 0; el < numElements; el++)
+  {
+    for(int vtx = 0; vtx < numElementVertices; vtx++)
+      vtxIndex[vtx] = volumetricMesh->getVertexIndex(el, vtx);
+
+    for (int i = 0; i < numElementVertices; i++)
+      for (int j = 0; j < numElementVertices; j++)
+      {
+        // add 3x3 block corresponding to pair of vertices (i,j)
+        for(int k = 0; k < 3; k++)
+          for(int l = 0; l < 3; l++)
+            emptyMatrix->AddEntry(3 * vtxIndex[i] + k, 3 * vtxIndex[j] + l, 0.0);
+      }
+  }
+
+  *stiffnessMatrixTopology = new SparseMatrix(emptyMatrix);
+  delete(emptyMatrix);
+}
+
+// compute RK = R * K and RKRT = R * K * R^T (block-wise)
+// input: K, R
+// output: RK, RKRT
+void CorotationalLinearFEM::WarpMatrix(double * K, double * R, double * RK, double * RKRT)
+{
+  int numElementVertices = volumetricMesh->getNumElementVertices();
+  int Ksize = numElementVertices * 3;
+  memset(RK, 0, sizeof(double) * Ksize * Ksize);
+  memset(RKRT, 0, sizeof(double) * Ksize * Ksize);
+  for(int i=0; i<numElementVertices; i++)
+    for(int j=0; j<numElementVertices; j++)
+    {
+      // RK = R * K
+      for(int k=0; k<3; k++)
+         for(int l=0; l<3; l++)
+           for(int m=0; m<3; m++)
+             RK[Ksize * (3 * i + k) + (3 * j + l)] += R[3 * k + m] * K[Ksize * (3 * i + m) + (3 * j + l)];
+
+      // RKRT = RK * R^T
+      for(int k=0; k<3; k++)
+        for(int l=0; l<3; l++)
+          for(int m=0; m<3; m++)
+            RKRT[Ksize * (3 * i + k) + (3 * j + l)] += RK[Ksize * (3 * i + k) + (3 * j + m)] * R[3 * l + m];
+    }
+}
+
+void CorotationalLinearFEM::ComputeEnergyAndForceAndStiffnessMatrix(const double * u, double * energy, double * f, SparseMatrix * stiffnessMatrix, int warp)
+{
+  if (energy != NULL)
+    *energy = 0.0;
+  if (f != NULL) // clear f to zero
+    memset(f, 0, sizeof(double) * 3 * numVertices);
+  if (stiffnessMatrix != NULL) // clear stiffness matrix to zero
+    stiffnessMatrix->ResetToZero();
+
+  AddEnergyAndForceAndStiffnessMatrixOfSubmesh(u, energy, f, stiffnessMatrix, warp, 0, volumetricMesh->getNumElements());
+}
+
+void CorotationalLinearFEM::ComputeElementEnergyAndForceAndStiffnessMatrix(int el, const double * u, double * elementEnergy, 
+    double * elementInternalForces, double * elementStiffnessMatrix, int warp)
+{
+  const int maxNumElementVertices = 8;
+  const int maxNumElementDOFs = maxNumElementVertices * 3;
+
+  const int numElementVertices = volumetricMesh->getNumElementVertices();
+  const int numElementDOFs = numElementVertices * 3;
+  const int elementStiffnessMatrixSpace = numElementDOFs * numElementDOFs;
+  VolumetricMesh::elementType type = volumetricMesh->getElementType();
+
+  // int vtxIndex[4];
+  const int * vtxIndex = volumetricMesh->getVertexIndices(el);
+
+  Vec3d deformedPos[maxNumElementVertices];
+  for (int i = 0; i < numElementVertices; i++)
+    deformedPos[i] = Vec3d(&undeformedPositions[3 * vtxIndex[i]]) + Vec3d(&u[3 * vtxIndex[i]]);
+
+  if (warp > 0)
+  {
+    double P[16] ={0}; // the current world-coordinate positions (row-major)
+    
+    // P = [ v0   v1   v2   v3 ]
+    //     [  1    1    1    1 ]
+    // rows 1,2,3
+    if(type == VolumetricMesh::TET)
+    {
+      for(int i=0; i<3; i++)
+        for(int j=0; j<4; j++)
+          P[4 * i + j] = deformedPos[j][i];
+    }
+    else if(type == VolumetricMesh::CUBIC)
+    {
+      for(int i=0; i<3; i++)
+        for(int j=0; j<4; j++)
+          P[4 * i + j] = deformedPos[cubeRotationTetIndex[j]][i];
+    }
+    else
+      assert(0);
+    // row 4
+    for(int j=0; j<4; j++)
+      P[12 + j] = 1.0;
+
+    // F = P * Inverse(M)
+    double F[9]; // upper-left 3x3 block
+    for(int i=0; i<3; i++) 
+      for(int j=0; j<3; j++) 
+      {
+        F[3 * i + j] = 0;
+        for(int k=0; k<4; k++)
+          F[3 * i + j] += P[4 * i + k] * MInverse[el][4 * k + j];
+      }
+
+    double R[9]; // rotation (row-major)
+    double S[9]; // symmetric (row-major)
+    double tolerance = 1E-6;
+    int forceRotation = 1;
+    PolarDecomposition::Compute(F, R, S, tolerance, forceRotation);
+
+    // RK = R * K
+    // KElement = R * K * R^T
+    double RK[maxNumElementDOFs * maxNumElementDOFs]; // row-major
+    if (elementStiffnessMatrix != NULL)
+      WarpMatrix(KElementUndeformed[el], R, RK, elementStiffnessMatrix);
+
+    double z[maxNumElementDOFs]; // z = RT x - x0
+    Mat3d Rmat(R);
+    Mat3d RTmat = trans(Rmat);
+    for(int vtx = 0; vtx < numElementVertices; vtx++)
+    {
+      Vec3d x0 = Vec3d(&undeformedPositions[3 * vtxIndex[vtx]]);
+      Vec3d vtxz = RTmat * deformedPos[vtx] - x0;
+      vtxz.convertToArray(z + 3*vtx);
+    }
+    
+    if (elementEnergy != NULL) // energy = 1/2 <Kz, z>, z = RT x - x0
+    {
+      double Kz[maxNumElementDOFs];
+      memset(Kz, 0, sizeof(Kz));
+      for(int i = 0; i < numElementDOFs; i++)
+        for(int j = 0; j < numElementDOFs; j++)
+          Kz[i] += KElementUndeformed[el][numElementDOFs*i+j] * z[j];
+
+      double e = 0.0;
+      for(int i = 0; i < numElementDOFs; i++)
+        e += Kz[i] * z[i];
+      *elementEnergy = 0.5 * e;
+    }
+
+    if (elementInternalForces != NULL) // f = RK (RT x - x0) = RK z
+    {      
+      memset(elementInternalForces, 0, sizeof(double) * numElementDOFs);
+      for(int i = 0; i < numElementDOFs; i++)
+        for(int j = 0; j < numElementDOFs; j++)
+          elementInternalForces[i] += RK[numElementDOFs*i+j] * z[j];
+    }
+
+    // compute exact stiffness matrix
+    if (warp == 2 && type == VolumetricMesh::TET && elementStiffnessMatrix != NULL)
+    {
+      // compute G = (tr(S) I - S) R^T
+      double G[9]; 
+      double tr = S[0] + S[4] + S[8];
+      double temp[9];
+      for(int i=0; i<9; i++)
+        temp[i] = -S[i];
+      temp[0] += tr;
+      temp[4] += tr;
+      temp[8] += tr;
+      // G = temp * R^T
+      MATRIX_MULTIPLY3X3ABT(temp, R, G);
+
+      double invG[9]; // invG = G^{-1}
+      inverse3x3(G, invG);
+
+      double rhs[27]; // 3 x 9 matrix (column-major)
+      for(int i=0; i<3; i++)
+        for(int j=0; j<3; j++)
+        {
+          double temp[9];
+          for(int k=0; k<9; k++)
+            temp[k] = 0.0;
+          // copy i-th row of R into column j of temp      
+          for(int k=0; k<3; k++)
+            temp[3 * k + j] = R[3 * i + k];
+          // extract the skew-symmetric part
+          SKEW_PART_NO_DIV2(temp, &rhs[3 * (3 * i + j)]);
+        }
+
+      // solve G * omega = rhs
+      double omega[27]; // column-major
+      for(int i=0; i<9; i++)
+      {
+        MATRIX_VECTOR_MULTIPLY3X3(invG, &rhs[3 * i], &omega[3 * i]);
+      }
+
+      double dRdF[81]; // each column is skew(omega) * R ; column-major
+      for(int i=0; i<9; i++)
+      {
+        double skew[9];
+        SKEW_MATRIX(&omega[3 * i], skew);
+        MATRIX_MULTIPLY3X3(skew, R, &dRdF[9 * i]);
+      }
+
+      double B[3][3][9];
+      // re-arrange dRdF into B, for easier dRdF * dFdx multiplication (to exploit sparsity of dFdx)
+      for(int i=0; i<3; i++)
+        for(int j=0; j<3; j++)
+          for(int k=0; k<3; k++)
+            for(int l=0; l<3; l++)
+            {
+              int row = 3 * i + k;
+              int column = 3 * j + l;
+              B[i][j][3 * k + l] = dRdF[9 * column + row];
+            }
+
+      // four pointers to a 3-vector
+      double * minv[4] = { &MInverse[el][0], &MInverse[el][4], &MInverse[el][8], &MInverse[el][12] }; // the four rows of MInverse (last column ignored)
+
+      double dRdx[108]; // derivative of the element rotation matrix with respect to the positions of the tet vertices; column-major
+      for(int k=0; k<4; k++)
+        for(int i=0; i<3; i++)
+          for(int j=0; j<3; j++)
+          {
+            double temp[3];
+            MATRIX_VECTOR_MULTIPLY3X3(B[i][j], minv[k], temp);
+            int row = 3 * i;
+            int column = 3 * k + j;
+            VECTOR_SET3(&dRdx[9 * column + row], temp);
+          }
+
+      // add contribution of dRdx to KElement
+
+      // term 1: \hat{dR/dxl} K (R^T x - m)
+
+      // compute K (R^T x - m)
+      double tempVec[12]; // R^T x - m
+      for(int vtx=0; vtx<4; vtx++)
+      {
+        double pos[3];
+        for(int i=0; i<3; i++)
+          pos[i] = P[4 * i + vtx];
+        MATRIX_VECTOR_MULTIPLY3X3T(R, pos, &tempVec[3*vtx]);
+        // subtract m
+        for(int i=0; i<3; i++)
+          tempVec[3*vtx+i] -= undeformedPositions[3 * vtxIndex[vtx] + i];
+      }
+      double a[12]; // a = K * tempVec
+      for (int i=0; i<12; i++)
+      {
+        a[i] = 0.0;
+        for (int j=0; j<12; j++)
+          a[i] += KElementUndeformed[el][12 * i + j] * tempVec[j];
+      }
+
+      // add [\hat{dR/dxl} K R^T x]_l, l=1 to 12
+      for(int column=0; column<12; column++)
+      {
+        double b[12]; // b = \hat{dR/dxl} * a
+        for(int j=0; j<4; j++)
+        {
+          MATRIX_VECTOR_MULTIPLY3X3(&dRdx[9 * column], &a[3*j], &b[3*j]);
+        }
+        // write b into KElement (add b to i-th column)
+        for(int row=0; row<12; row++)
+          elementStiffnessMatrix[12 * row + column] += b[row]; // KElement is row-major
+      }
+
+      // term 2: (R K \hat{dRdxl}^T)x
+
+      // re-write positions into a
+      for(int vtx=0; vtx<4; vtx++)
+      {
+        for(int i=0; i<3; i++)
+          a[3 * vtx + i] = P[4 * i + vtx];
+      }
+
+      // compute [\hat{dRdxl}^T x)]_l, l=1 to 12
+      for(int column=0; column<12; column++)
+      {
+        double b[12]; // b = \hat{dRdxl}^T * a
+        for(int j=0; j<4; j++)
+        {
+          MATRIX_VECTOR_MULTIPLY3X3T(&dRdx[9 * column], &a[3*j], &b[3*j]);
+        }
+
+        // add RK * b to column of KElement
+        int rowStart = 0;
+        for (int row=0; row<12; row++)
+        {
+          double contrib = 0.0;
+          for (int j=0; j<12; j++)
+            contrib += RK[rowStart + j] * b[j];
+          elementStiffnessMatrix[rowStart + column] += contrib;
+          rowStart += 12;
+        }
+      }
+    }
+  }
+  else // no warping, warp == 0
+  {
+    if (elementStiffnessMatrix != NULL)
+      memcpy(elementStiffnessMatrix, KElementUndeformed[el], sizeof(double) * elementStiffnessMatrixSpace);
+    // f = K u
+    if (elementInternalForces != NULL || elementEnergy != NULL)
+    {
+      double fElementBuffer[maxNumElementDOFs];
+      double * fEle = (elementInternalForces ? elementInternalForces : fElementBuffer);
+      for(int i=0; i<numElementDOFs; i++)
+      {
+        fEle[i] = 0.0;
+        for(int j=0; j<numElementVertices; j++)
+        {
+          fEle[i] += 
+            KElementUndeformed[el][numElementDOFs * i + 3 * j + 0] * u[3 * vtxIndex[j] + 0] +
+            KElementUndeformed[el][numElementDOFs * i + 3 * j + 1] * u[3 * vtxIndex[j] + 1] +
+            KElementUndeformed[el][numElementDOFs * i + 3 * j + 2] * u[3 * vtxIndex[j] + 2];
+        }
+      }
+      if (elementEnergy != NULL)
+      {
+        double energy = 0.0;
+        for(int j = 0; j < numElementVertices; j++)
+          energy += fEle[3*j + 0] * u[3*vtxIndex[j] + 0] +
+                    fEle[3*j + 1] * u[3*vtxIndex[j] + 1] +
+                    fEle[3*j + 2] * u[3*vtxIndex[j] + 2];
+        *elementEnergy = 0.5 * energy;
+      }
+    }
+  }
+}
+
+void CorotationalLinearFEM::AddEnergyAndForceAndStiffnessMatrixOfSubmesh(const double * u, double * energy, double * f, SparseMatrix * stiffnessMatrix, int warp, int elementLo, int elementHi)
+{
+  const int maxNumElementVertices = 8;
+  const int maxNumElementDOFs = maxNumElementVertices * 3;
+
+  int numElementVertices = volumetricMesh->getNumElementVertices();
+
+  int numElementDOFs = numElementVertices * 3;
+  int elementStiffnessMatrixSpace = numElementDOFs * numElementDOFs;
+  VolumetricMesh::elementType type = volumetricMesh->getElementType();
+  for (int el=elementLo; el < elementHi; el++)
+  {
+    const int * vtxIndex = volumetricMesh->getVertexIndices(el);
+
+    // element stiffness matrix, to be computed below; row-major
+    double KElement[maxNumElementDOFs * maxNumElementDOFs];
+
+    Vec3d deformedPos[maxNumElementVertices];
+    for (int i = 0; i < numElementVertices; i++)
+      deformedPos[i] = Vec3d(&undeformedPositions[3 * vtxIndex[i]]) + Vec3d(&u[3 * vtxIndex[i]]);
+
+    if (warp > 0)
+    {
+      double P[16] ={0}; // the current world-coordinate positions (row-major)
+      //
+      // P = [ v0   v1   v2   v3 ]
+      //     [  1    1    1    1 ]
+      // rows 1,2,3
+      if(type == VolumetricMesh::TET)
+      {
+        for(int i=0; i<3; i++)
+          for(int j=0; j<4; j++)
+            P[4 * i + j] = deformedPos[j][i];
+      }
+      else if(type == VolumetricMesh::CUBIC)
+      {
+        for(int i=0; i<3; i++)
+          for(int j=0; j<4; j++)
+            P[4 * i + j] = deformedPos[cubeRotationTetIndex[j]][i];
+      }
+      else
+        assert(0);
+      // row 4
+      for(int j=0; j<4; j++)
+        P[12 + j] = 1.0;
+
+      // F = P * Inverse(M)
+      double F[9]; // upper-left 3x3 block
+      for(int i=0; i<3; i++) 
+        for(int j=0; j<3; j++) 
+        {
+          F[3 * i + j] = 0;
+          for(int k=0; k<4; k++)
+            F[3 * i + j] += P[4 * i + k] * MInverse[el][4 * k + j];
+        }
+
+      double R[9]; // rotation (row-major)
+      double S[9]; // symmetric (row-major)
+      double tolerance = 1E-6;
+      int forceRotation = 1;
+      PolarDecomposition::Compute(F, R, S, tolerance, forceRotation);
+
+      // RK = R * K
+      // KElement = R * K * R^T
+      double RK[maxNumElementDOFs * maxNumElementDOFs]; // row-major
+      WarpMatrix(KElementUndeformed[el], R, RK, KElement);
+
+      double z[maxNumElementDOFs]; // z = RT x - x0
+      Mat3d Rmat(R);
+      Mat3d RTmat = trans(Rmat);
+      for(int vtx = 0; vtx < numElementVertices; vtx++)
+      {
+        Vec3d x0 = Vec3d(&undeformedPositions[3 * vtxIndex[vtx]]);
+        Vec3d vtxz = RTmat * deformedPos[vtx] - x0;
+        vtxz.convertToArray(z + 3*vtx);
+      }
+      
+      if (energy != NULL) // energy = 1/2 <Kz, z>, z = RT x - x0
+      {
+        double Kz[maxNumElementDOFs];
+        memset(Kz, 0, sizeof(Kz));
+        for(int i = 0; i < numElementDOFs; i++)
+          for(int j = 0; j < numElementDOFs; j++)
+            Kz[i] += KElementUndeformed[el][numElementDOFs*i+j] * z[j];
+
+        double e = 0.0;
+        for(int i = 0; i < numElementDOFs; i++)
+          e += Kz[i] * z[i];
+        *energy += 0.5 * e;
+      }
+
+      double fElement[maxNumElementDOFs];
+      if (f != NULL) // f = RK (RT x - x0) = RK z
+      {      
+        memset(fElement, 0, sizeof(fElement));
+        for(int i = 0; i < numElementDOFs; i++)
+          for(int j = 0; j < numElementDOFs; j++)
+            fElement[i] += RK[numElementDOFs*i+j] * z[j];
+
+        // write to global matrix
+        for(int j=0; j<numElementVertices; j++)
+          for(int l=0; l<3; l++)
+            f[3 * vtxIndex[j] + l] += fElement[3 * j + l];
+      }
+
+      // compute exact stiffness matrix
+      if (warp == 2 && type == VolumetricMesh::TET)
+      {
+        // compute G = (tr(S) I - S) R^T
+        double G[9]; 
+        double tr = S[0] + S[4] + S[8];
+        double temp[9];
+        for(int i=0; i<9; i++)
+          temp[i] = -S[i];
+        temp[0] += tr;
+        temp[4] += tr;
+        temp[8] += tr;
+        // G = temp * R^T
+        MATRIX_MULTIPLY3X3ABT(temp, R, G);
+
+        double invG[9]; // invG = G^{-1}
+        inverse3x3(G, invG);
+
+        double rhs[27]; // 3 x 9 matrix (column-major)
+        for(int i=0; i<3; i++)
+          for(int j=0; j<3; j++)
+          {
+            double temp[9];
+            for(int k=0; k<9; k++)
+              temp[k] = 0.0;
+            // copy i-th row of R into column j of temp      
+            for(int k=0; k<3; k++)
+              temp[3 * k + j] = R[3 * i + k];
+            // extract the skew-symmetric part
+            SKEW_PART_NO_DIV2(temp, &rhs[3 * (3 * i + j)]);
+          }
+
+        // solve G * omega = rhs
+        double omega[27]; // column-major
+        for(int i=0; i<9; i++)
+        {
+          MATRIX_VECTOR_MULTIPLY3X3(invG, &rhs[3 * i], &omega[3 * i]);
+        }
+
+        double dRdF[81]; // each column is skew(omega) * R ; column-major
+        for(int i=0; i<9; i++)
+        {
+          double skew[9];
+          SKEW_MATRIX(&omega[3 * i], skew);
+          MATRIX_MULTIPLY3X3(skew, R, &dRdF[9 * i]);
+        }
+
+        double B[3][3][9];
+        // re-arrange dRdF into B, for easier dRdF * dFdx multiplication (to exploit sparsity of dFdx)
+        for(int i=0; i<3; i++)
+          for(int j=0; j<3; j++)
+            for(int k=0; k<3; k++)
+              for(int l=0; l<3; l++)
+              {
+                int row = 3 * i + k;
+                int column = 3 * j + l;
+                B[i][j][3 * k + l] = dRdF[9 * column + row];
+              }
+
+        // four pointers to a 3-vector
+        double * minv[4] = { &MInverse[el][0], &MInverse[el][4], &MInverse[el][8], &MInverse[el][12] }; // the four rows of MInverse (last column ignored)
+
+        double dRdx[108]; // derivative of the element rotation matrix with respect to the positions of the tet vertices; column-major
+        for(int k=0; k<4; k++)
+          for(int i=0; i<3; i++)
+            for(int j=0; j<3; j++)
+            {
+              double temp[3];
+              MATRIX_VECTOR_MULTIPLY3X3(B[i][j], minv[k], temp);
+              int row = 3 * i;
+              int column = 3 * k + j;
+              VECTOR_SET3(&dRdx[9 * column + row], temp);
+            }
+
+        // add contribution of dRdx to KElement
+
+        // term 1: \hat{dR/dxl} K (R^T x - m)
+
+        // compute K (R^T x - m)
+        double tempVec[12]; // R^T x - m
+        for(int vtx=0; vtx<4; vtx++)
+        {
+          double pos[3];
+          for(int i=0; i<3; i++)
+            pos[i] = P[4 * i + vtx];
+          MATRIX_VECTOR_MULTIPLY3X3T(R, pos, &tempVec[3*vtx]);
+          // subtract m
+          for(int i=0; i<3; i++)
+            tempVec[3*vtx+i] -= undeformedPositions[3 * vtxIndex[vtx] + i];
+        }
+        double a[12]; // a = K * tempVec
+        for (int i=0; i<12; i++)
+        {
+          a[i] = 0.0;
+          for (int j=0; j<12; j++)
+            a[i] += KElementUndeformed[el][12 * i + j] * tempVec[j];
+        }
+
+        // add [\hat{dR/dxl} K R^T x]_l, l=1 to 12
+        for(int column=0; column<12; column++)
+        {
+          double b[12]; // b = \hat{dR/dxl} * a
+          for(int j=0; j<4; j++)
+          {
+            MATRIX_VECTOR_MULTIPLY3X3(&dRdx[9 * column], &a[3*j], &b[3*j]);
+          }
+          // write b into KElement (add b to i-th column)
+          for(int row=0; row<12; row++)
+            KElement[12 * row + column] += b[row]; // KElement is row-major
+        }
+
+        // term 2: (R K \hat{dRdxl}^T)x
+
+        // re-write positions into a
+        for(int vtx=0; vtx<4; vtx++)
+        {
+          for(int i=0; i<3; i++)
+            a[3 * vtx + i] = P[4 * i + vtx];
+        }
+
+        // compute [\hat{dRdxl}^T x)]_l, l=1 to 12
+        for(int column=0; column<12; column++)
+        {
+          double b[12]; // b = \hat{dRdxl}^T * a
+          for(int j=0; j<4; j++)
+          {
+            MATRIX_VECTOR_MULTIPLY3X3T(&dRdx[9 * column], &a[3*j], &b[3*j]);
+          }
+
+          // add RK * b to column of KElement
+          int rowStart = 0;
+          for (int row=0; row<12; row++)
+          {
+            double contrib = 0.0;
+            for (int j=0; j<12; j++)
+              contrib += RK[rowStart + j] * b[j];
+            KElement[rowStart + column] += contrib;
+            rowStart += 12;
+          }
+        }
+      }
+    }
+    else
+    {
+      // no warp
+      memcpy(KElement, KElementUndeformed[el], sizeof(double) * elementStiffnessMatrixSpace);
+      // f = K u
+      double fElement[maxNumElementDOFs];
+      if (f != NULL || energy != NULL)
+      {
+        for(int i=0; i<numElementDOFs; i++)
+        {
+          fElement[i] = 0.0;
+          for(int j=0; j<numElementVertices; j++)
+          {
+            fElement[i] += 
+              KElement[numElementDOFs * i + 3 * j + 0] * u[3 * vtxIndex[j] + 0] +
+              KElement[numElementDOFs * i + 3 * j + 1] * u[3 * vtxIndex[j] + 1] +
+              KElement[numElementDOFs * i + 3 * j + 2] * u[3 * vtxIndex[j] + 2];
+          }
+        }
+        if (energy != NULL)
+        {
+          double elementEnergy = 0.0;
+          for(int j = 0; j < numElementVertices; j++)
+            elementEnergy += fElement[3*j + 0] * u[3*vtxIndex[j] + 0] +
+                             fElement[3*j + 1] * u[3*vtxIndex[j] + 1] +
+                             fElement[3*j + 2] * u[3*vtxIndex[j] + 2];
+          *energy += 0.5 * elementEnergy;
+        }
+
+        // add fElement into the global f
+        if (f != NULL)
+          for(int j=0; j<numElementVertices; j++)
+          {
+            f[3 * vtxIndex[j] + 0] += fElement[3 * j + 0];
+            f[3 * vtxIndex[j] + 1] += fElement[3 * j + 1];
+            f[3 * vtxIndex[j] + 2] += fElement[3 * j + 2];
+          }
+      }
+    }
+
+    if (stiffnessMatrix != NULL)
+    {
+      int * rowIndex = rowIndices[el];
+      int * columnIndex = columnIndices[el];
+
+      // add KElement to the global stiffness matrix
+      for (int i=0; i<numElementVertices; i++)
+        for (int j=0; j<numElementVertices; j++)
+          for(int k=0; k<3; k++)
+            for(int l=0; l<3; l++)
+              stiffnessMatrix->AddEntry(3 * rowIndex[i] + k, 3 * columnIndex[numElementVertices * i + j] + l, KElement[numElementDOFs * (3 * i + k) + 3 * j + l]);
+    }
+  }
+}
+
+void CorotationalLinearFEM::ClearRowColumnIndices()
+{
+  for (int el=0; el < volumetricMesh->getNumElements(); el++)
+  {
+    free(rowIndices[el]);
+    free(columnIndices[el]);
+  }
+
+  free(rowIndices);
+  free(columnIndices);
+  rowIndices = NULL;
+  columnIndices = NULL;
+}
+
+void CorotationalLinearFEM::BuildRowColumnIndices(SparseMatrix * sparseMatrix)
+{
+  int numElements = volumetricMesh->getNumElements();
+  int numElementVertices = volumetricMesh->getNumElementVertices();
+  rowIndices = (int**) malloc (sizeof(int*) * numElements);
+  columnIndices = (int**) malloc (sizeof(int*) * numElements);
+
+  for (int el=0; el < numElements; el++)
+  {
+    // the rows corresponding to each vertices in the element
+    rowIndices[el] = (int*) malloc (sizeof(int) * numElementVertices);
+    for(int i=0; i<numElementVertices; i++)
+      rowIndices[el][i] = volumetricMesh->getVertexIndex(el, i);
+
+    // the columns corresponding to all element vertices, in row of each vertex
+    columnIndices[el] = (int*) malloc (sizeof(int) * numElementVertices * numElementVertices);
+    // find index of vertex j in row of vertex i, and cache it
+    for(int i=0; i<numElementVertices; i++)
+      for(int j=0; j<numElementVertices; j++)
+        columnIndices[el][numElementVertices * i + j] = sparseMatrix->GetInverseIndex(3*rowIndices[el][i], 3*rowIndices[el][j]) / 3;
+  }
+}
+
+// inverse of a 3x3 matrix
+// row-major format
+void CorotationalLinearFEM::inverse3x3(double * A, double * AInv)
+{
+  // converted to C from Mathematica output   
+  AInv[0] = -A[5] * A[7] + A[4] * A[8]; 
+  AInv[1] = A[2] * A[7] - A[1] * A[8]; 
+  AInv[2] = -A[2] * A[4] + A[1] * A[5];
+  AInv[3] = A[5] * A[6] - A[3] * A[8]; 
+  AInv[4] = -A[2] * A[6] + A[0] * A[8]; 
+  AInv[5] = A[2] * A[3] - A[0] * A[5];
+  AInv[6] = -A[4] * A[6] + A[3] * A[7]; 
+  AInv[7] = A[1] * A[6] - A[0] * A[7];
+  AInv[8] = -A[1] * A[3] + A[0] * A[4];
+
+  double invDet = 1.0 / (A[0] * AInv[0] + A[1] * AInv[3] + A[2] * AInv[6]);
+
+  for(int i=0; i<9; i++)
+    AInv[i] *= invDet;
+}
+
+// inverse of a 4x4 matrix
+// row-major format
+void CorotationalLinearFEM::inverse4x4(double * A, double * AInv)
+{
+  // converted to C from Mathematica output   
+  AInv[0] = -A[11] * A[14] * A[5] + A[10] * A[15] * A[5] + A[11] * A[13] * A[6] - A[10] * A[13] * A[7] - A[15] * A[6] * A[9] + A[14] * A[7] * A[9];
+  AInv[1] = A[1] * A[11] * A[14] - A[1] * A[10] * A[15] - A[11] * A[13] * A[2] + A[10] * A[13] * A[3] + A[15] * A[2] * A[9] - A[14] * A[3] * A[9];
+  AInv[2] = -A[15] * A[2] * A[5] + A[14] * A[3] * A[5] + A[1] * A[15] * A[6] - A[13] * A[3] * A[6] - A[1] * A[14] * A[7] + A[13] * A[2] * A[7];
+  AInv[3] = A[11] * A[2] * A[5] - A[10] * A[3] * A[5] - A[1] * A[11] * A[6] + A[1] * A[10] * A[7] + A[3] * A[6] * A[9] - A[2] * A[7] * A[9];
+  AInv[4] = A[11] * A[14] * A[4] - A[10] * A[15] * A[4] - A[11] * A[12] * A[6] + A[10] * A[12] * A[7] + A[15] * A[6] * A[8] - A[14] * A[7] * A[8];
+  AInv[5] = -A[0] * A[11] * A[14] + A[0] * A[10] * A[15] + A[11] * A[12] * A[2] - A[10] * A[12] * A[3] - A[15] * A[2] * A[8] + A[14] * A[3] * A[8];
+  AInv[6] = A[15] * A[2] * A[4] - A[14] * A[3] * A[4] - A[0] * A[15] * A[6] + A[12] * A[3] * A[6] + A[0] * A[14] * A[7] - A[12] * A[2] * A[7];
+  AInv[7] = -A[11] * A[2] * A[4] + A[10] * A[3] * A[4] + A[0] * A[11] * A[6] - A[0] * A[10] * A[7] - A[3] * A[6] * A[8] + A[2] * A[7] * A[8];
+  AInv[8] = -A[11] * A[13] * A[4] + A[11] * A[12] * A[5] - A[15] * A[5] * A[8] + A[13] * A[7] * A[8] + A[15] * A[4] * A[9] - A[12] * A[7] * A[9];
+  AInv[9] = -A[1] * A[11] * A[12] + A[0] * A[11] * A[13] + A[1] * A[15] * A[8] - A[13] * A[3] * A[8] - A[0] * A[15] * A[9] + A[12] * A[3] * A[9];
+  AInv[10] = -A[1] * A[15] * A[4] + A[13] * A[3] * A[4] + A[0] * A[15] * A[5] - A[12] * A[3] * A[5] + A[1] * A[12] * A[7] - A[0] * A[13] * A[7];
+  AInv[11] = A[1] * A[11] * A[4] - A[0] * A[11] * A[5] + A[3] * A[5] * A[8] - A[1] * A[7] * A[8] - A[3] * A[4] * A[9] + A[0] * A[7] * A[9]; 
+  AInv[12] = A[10] * A[13] * A[4] - A[10] * A[12] * A[5] + A[14] * A[5] * A[8] - A[13] * A[6] * A[8] - A[14] * A[4] * A[9] + A[12] * A[6] * A[9];
+  AInv[13] = A[1] * A[10] * A[12] - A[0] * A[10] * A[13] - A[1] * A[14] * A[8] + A[13] * A[2] * A[8] + A[0] * A[14] * A[9] - A[12] * A[2] * A[9]; 
+  AInv[14] = A[1] * A[14] * A[4] - A[13] * A[2] * A[4] - A[0] * A[14] * A[5] + A[12] * A[2] * A[5] - A[1] * A[12] * A[6] + A[0] * A[13] * A[6];
+  AInv[15] = -A[1] * A[10] * A[4] + A[0] * A[10] * A[5] - A[2] * A[5] * A[8] + A[1] * A[6] * A[8] + A[2] * A[4] *A[9] - A[0] * A[6] * A[9];
+
+  double invDet = 1.0 / (A[0] * AInv[0] + A[1] * AInv[4] + A[2] * AInv[8] + A[3] * AInv[12]);
+
+  for(int i=0; i<16; i++)
+    AInv[i] *= invDet;
+}
+
diff --git a/libraries/corotationalLinearFEM/corotationalLinearFEM.h b/libraries/corotationalLinearFEM/corotationalLinearFEM.h
new file mode 100644
index 0000000000000000000000000000000000000000..9cb79e3c529099343b9a4104c0d31f93e74219e1
--- /dev/null
+++ b/libraries/corotationalLinearFEM/corotationalLinearFEM.h
@@ -0,0 +1,140 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "corotational linear FEM" library , Copyright (C) 2018 USC            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _COROTATIONALLINEARFEM_H_
+#define _COROTATIONALLINEARFEM_H_
+
+/*
+  Corotational linear FEM deformable model for tet meshes and cubic meshes.
+
+  1. For tet meshes, this class implements the deformable model described in the following paper:
+
+    M. Mueller, M. Gross: Interactive Virtual Materials.
+    In Proc. of Graphics Interface 2004 (2004), pp. 239–246.
+
+  In [Mueller 2004], the tangent stiffness matrix is approximate (warp=1). 
+  This class can also compute the exact tangent stiffness matrix (warp=2).
+  The implementation is described in:
+  J. Barbic: Exact Corotational Linear FEM Stiffness Matrix, Technical Report, USC, 2012
+
+  It is also possible to turn warping off (warp=0). This gives fast linear FEM dynamics,
+  but large deformations are not well-represented.
+
+  2. For cubic meshes, the class implements the following paper:
+
+    Jesus Perez, Alvaro G. Perez and Miguel A. Otaduy:
+    Simulation of Hyperelastic Materials Using Energy Constraints
+    Proc. of Congreso Espanol de Informatica Grafica, 2013
+
+  Note that for cubic meshes, only the warp=1 and warp=0 modes are supported.
+  For cubic meshes with warp=2, the code behaves the same as for warp=1.
+
+  3. This class supports both isotropic and orthotropic materials. By default, isotropic material is used.
+  The choice of material is controlled by the material specification in the .veg file.
+  For orthotropic materials, we implement the following:
+
+     Yijing Li and Jernej Barbic: 
+     Stable Orthotropic Materials, 
+     Symposium on Computer Animation 2014.
+     
+  See also:
+     http://en.wikipedia.org/wiki/Orthotropic_material
+     http://www.solidmechanics.org/text/Chapter3_2/Chapter3_2.htm
+*/
+
+#include "tetMesh.h"
+#include "sparseMatrix.h"
+
+class CorotationalLinearFEM
+{
+public:
+
+  // initializes corotational linear FEM
+  // input: tetMesh and cubicMesh
+  CorotationalLinearFEM(VolumetricMesh * volumetricMesh);
+  virtual ~CorotationalLinearFEM();
+
+  void GetStiffnessMatrixTopology(SparseMatrix ** stiffnessMatrixTopology); // returns a zero matrix containing the locations of non-zero elements in the stiffness matrix
+
+  // computes elastic energy, the internal forces and (warped) stiffness matrix for the entire mesh
+  // vertex displacements (input) and internal forces (output) must be (pre-allocated) vectors of length 3 * numVertices
+  // the internal forces are returned with the sign corresponding to f_int(x) on the left side of the equation M * x'' + f_int(x) = f_ext
+  // i.e., the computed internal forces are *negatives* of the actual physical internal forces acting on the material
+  // warp:
+  //   0: no warping (linear FEM)
+  //   1: stiffness warping (corotational linear FEM with approximate stiffness matrix) [Mueller 2004]
+  //   2: corotational linear FEM with exact tangent stiffness matrix (see the technical report [Barbic 2012]), only works on tetMesh
+  // if you do not want to compute the energy, internal forces or stiffness matrix (any combination), pass a NULL pointer for that argument
+  virtual void ComputeEnergyAndForceAndStiffnessMatrix(const double * vertexDisplacements, double * energy, double * internalForces, SparseMatrix * stiffnessMatrix, int warp=1);
+
+  // this routine is same as above, except that (1) it adds to the existing value, (2) only traverses elements from elementLo <= element <= elementHi - 1
+  // if you do not want to compute the energy, internal forces or stiffness matrix (any combination), pass a NULL pointer for that argument
+  void AddEnergyAndForceAndStiffnessMatrixOfSubmesh(const double * vertexDisplacements, double * energy, double * internalForces, SparseMatrix * stiffnessMatrix, int warp, int elementLo, int elementHi);
+
+  // this routine is same as above, except that (1) it only computes a single element. (2) the output forces and the matrix 
+  // are stored in dense format.
+  void ComputeElementEnergyAndForceAndStiffnessMatrix(int elementID, const double * vertexDisplacements, double * elementEnergy, 
+    double * elementInternalForces, double * elementStiffnessMatrix, int warp);
+
+  inline VolumetricMesh * GetVolumetricMesh() { return volumetricMesh; }
+
+  static void inverse3x3(double * A, double * AInv); // inverse of a row-major 3x3 matrix
+  static void inverse4x4(double * A, double * AInv); // inverse of a row-major 4x4 matrix
+
+protected:
+  int numVertices;
+  VolumetricMesh * volumetricMesh;
+  double * undeformedPositions;
+  double ** MInverse;
+  double ** KElementUndeformed;
+
+  void WarpMatrix(double * K, double * R, double * RK, double * RKRT);
+
+  // acceleration indices
+  int ** rowIndices;
+  int ** columnIndices;
+  void ClearRowColumnIndices();
+  void BuildRowColumnIndices(SparseMatrix * sparseMatrix);
+
+  bool computeElasticityStiffnessTensor(double E[36], int el);
+  // build inverse of M = [ v0   v1   v2   v3 ]
+  //                      [  1    1    1    1 ]
+  // volumetricMesh must be TET of CUBIC. If it's CUBIC, M consists of vertices forming a tet in the center of the cube.
+  static void buildMInverse(double * MInverse, VolumetricMesh * volumetricMesh);
+
+  void clear();
+
+  static void GetStiffnessMatrixTopology(VolumetricMesh * mesh, SparseMatrix ** stiffnessMatrixTopology);
+};
+
+#endif
+
diff --git a/libraries/distanceField/closestPointField.cpp b/libraries/distanceField/closestPointField.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..09ddf459670809eefab3be59f7750232518483e7
--- /dev/null
+++ b/libraries/distanceField/closestPointField.cpp
@@ -0,0 +1,302 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  ClosestPointField is similar to a distance field, 
+  except it also stores the nearest point, not just the distance to it.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <fstream>
+#include "closestPointField.h"
+#include "trilinearInterpolation.h"
+using namespace std;
+
+ClosestPointField::ClosestPointField() : DistanceField()
+{
+  closestPointData = NULL;
+}
+
+ClosestPointField::~ClosestPointField()
+{
+  free(closestPointData);
+}
+
+// the routines for signed/unsigned closest point field computation
+#define COMPUTE_CLOSEST_POINT
+  #define COMPUTE_SIGNED_FIELD
+    #include "computeField.cpp"
+  #undef COMPUTE_SIGNED_FIELD
+    #include "computeField.cpp"
+#undef COMPUTE_CLOSEST_POINT
+
+void ClosestPointField::set(int resolutionX, int resolutionY, int resolutionZ, Vec3d bmin_, Vec3d bmax_, float * distanceData, float * closestPointData)
+{
+  DistanceField::set(resolutionX, resolutionY, resolutionZ, bmin_, bmax_, distanceData);
+
+  free(this->closestPointData);
+  this->closestPointData = (float*) malloc (sizeof(float)*3*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+  memcpy(this->closestPointData, closestPointData, sizeof(float)*3*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+}
+
+int ClosestPointField::load(const std::string& filename)
+{
+  ifstream fin(filename.c_str(),ios::binary);
+  if (!fin)
+    return 1;
+
+  fin.read((char*)&resolutionX,4);
+
+  // the type of data (single-precision or double-precision) is encoded  
+  // as the sign of the x-resolution
+  bool floatData = (resolutionX < 0);
+  if (floatData)
+    resolutionX = -resolutionX;
+  
+  fin.read((char*)&resolutionY,4); 
+  if (resolutionY >=0)
+  {
+    return 1; // by convention the second resolution must be negative, so that we can differentiate closest point files from distance files
+  }
+  resolutionY = -resolutionY;
+  fin.read((char*)&resolutionZ,4);
+
+  fin.read((char*)&(bmin_[0]),8);
+  fin.read((char*)&(bmin_[1]),8);
+  fin.read((char*)&(bmin_[2]),8);
+  
+  fin.read((char*)&(bmax_[0]),8);
+  fin.read((char*)&(bmax_[1]),8);
+  fin.read((char*)&(bmax_[2]),8);
+
+  setGridParameters();
+
+  double * buffer = (double*) malloc (sizeof(double) * 3 * (resolutionX+1) * (resolutionY+1)); // place for one slice
+
+  // load distances
+  distanceData = (float*) malloc (sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+
+  float * distanceDataPos = distanceData;
+
+  for(int k=0; k <= resolutionZ; k++)
+  {
+    if (floatData)
+      fin.read((char*)buffer,sizeof(float)*(resolutionX+1)*(resolutionY+1));
+    else
+      fin.read((char*)buffer,sizeof(double)*(resolutionX+1)*(resolutionY+1));
+
+    char * bufferPos = (char*)buffer;
+    int bufferPosInc = floatData ? sizeof(float) : sizeof(double);
+
+    // copy data to internal distance field buffer
+    for(int j = 0; j <= resolutionY; j++)
+      for(int i = 0; i <= resolutionX; i++)
+      {
+        if (floatData)
+          *distanceDataPos = *(float*)bufferPos;
+        else
+          *distanceDataPos = *(double*)bufferPos;
+        distanceDataPos++;
+        bufferPos += bufferPosInc;
+      }
+  }
+
+  // load closest points
+  closestPointData = (float*) malloc (sizeof(float)*3*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+
+  float * closestPosDataPos = closestPointData;
+
+  for(int k=0; k <= resolutionZ; k++)
+  {
+    if (floatData)
+      fin.read((char*)buffer,sizeof(float)*3*(resolutionX+1)*(resolutionY+1));
+    else
+      fin.read((char*)buffer,sizeof(double)*3*(resolutionX+1)*(resolutionY+1));
+
+    char * bufferPos = (char*)buffer;
+    int bufferPosInc = floatData ? sizeof(float) : sizeof(double);
+
+    // copy data to internal closest point field buffer
+    for(int j = 0; j <= resolutionY; j++)
+      for(int i = 0; i <= resolutionX; i++)
+        for(int coord=0; coord<3; coord++)
+        {
+          if (floatData)
+            *closestPosDataPos = *(float*)bufferPos;
+          else
+            *closestPosDataPos = *(double*)bufferPos;
+          closestPosDataPos++;
+          bufferPos += bufferPosInc;
+        }
+  }
+
+  fin.close();
+
+  return 0;
+}
+
+int ClosestPointField::save(const std::string& filename, bool doublePrecision)
+{
+  if (doublePrecision)
+    printf("Error: double precision output not supported. Using float instead.\n");
+  doublePrecision = false;
+
+  ofstream fout(filename.c_str(),ios::binary);
+
+  int data = resolutionX;
+  if (!doublePrecision)
+    data = -data;
+  fout.write((char*)&data,4);
+  data = -resolutionY;
+  fout.write((char*)&data,4);
+  fout.write((char*)&resolutionZ,4);
+
+  fout.write((char*)&(bmin_[0]),8);
+  fout.write((char*)&(bmin_[1]),8);
+  fout.write((char*)&(bmin_[2]),8);
+
+  fout.write((char*)&(bmax_[0]),8);
+  fout.write((char*)&(bmax_[1]),8);
+  fout.write((char*)&(bmax_[2]),8);
+
+/*
+  float * buffer = NULL;
+  if (!doublePrecision)
+    convertToFloat(&buffer);
+*/
+
+  // write out distances
+  // float precision
+  fout.write((char*)distanceData,
+    sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+  
+/*
+  if (doublePrecision)
+    fout.write((char*)distanceData,sizeof(double)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+  else
+  {
+    fout.write((char*)buffer,sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+  }
+*/
+
+  // write out closest points
+  // float precision
+  fout.write((char*)closestPointData, sizeof(float)*3*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+
+/*
+  if (doublePrecision)
+    fout.write((char*)closestPointData,sizeof(double)*3*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+  else
+  {
+    fout.write((char*)(buffer+(resolutionX+1)*(resolutionY+1)*(resolutionZ+1)),
+      sizeof(float)*3*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+    free(buffer);
+  }
+*/
+
+  fout.close();
+
+  return 0;
+}
+
+void ClosestPointField::getClosestPointData(float ** floatBuffer)
+{
+  unsigned int size = 3 * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
+  *floatBuffer = (float*) malloc (sizeof(float) * size);
+  memcpy(*floatBuffer, closestPointData, sizeof(float) * size);
+}
+
+void ClosestPointField::closestPoint(float pos[3], float result[3])
+{
+  // get the indices
+  int i = (int)((pos[0] - bmin_[0]) * invGridX);
+  int j = (int)((pos[1] - bmin_[1]) * invGridY);
+  int k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  if ((i<0) || (i>=resolutionX) || (j<0) || (j>=resolutionY) || (k<0) || (k>=resolutionZ))
+  {
+    printf("Warning: querying the closest point field outside of the bounding box.\n");
+    result[0] = result[1] = result[2] = 0;
+  }
+
+  double wx = ((pos[0]-bmin_[0]) / gridX) - i;
+  double wy = ((pos[1]-bmin_[1]) / gridY) - j;
+  double wz = ((pos[2]-bmin_[2]) / gridZ) - k;
+
+  float c0[3], c1[3], c2[3], c3[3], c4[3], c5[3], c6[3], c7[3];
+
+  closestPoint(i,j,k,c0);
+  closestPoint(i+1,j,k,c1);
+  closestPoint(i+1,j+1,k,c2);
+  closestPoint(i,j+1,k,c3);
+  closestPoint(i,j,k+1,c4);
+  closestPoint(i+1,j,k+1,c5);
+  closestPoint(i+1,j+1,k+1,c6);
+  closestPoint(i,j+1,k+1,c7);
+
+  result[0] = TRILINEAR_INTERPOLATION(wx,wy,wz,
+     c0[0], c1[0], c2[0], c3[0], c4[0], c5[0], c6[0], c7[0]);
+  result[1] = TRILINEAR_INTERPOLATION(wx,wy,wz,
+     c0[1], c1[1], c2[1], c3[1], c4[1], c5[1], c6[1], c7[1]);
+  result[2] = TRILINEAR_INTERPOLATION(wx,wy,wz,
+     c0[2], c1[2], c2[2], c3[2], c4[2], c5[2], c6[2], c7[2]);
+}
+
+bool ClosestPointField::sanityCheck()
+{
+  bool exitCode = DistanceField::sanityCheck();
+
+  bool myExitCode = true;
+  for (int k=0; k <= resolutionZ; k++)
+    for (int j=0; j <= resolutionY; j++)
+      for (int i=0; i <= resolutionX; i++)
+      {
+        float distanceScalar = distance(i,j,k);
+        float closestPoint_[3];
+        closestPoint(i,j,k, closestPoint_);
+        Vec3d gridPos = getGridPosition(i,j,k);
+        float distanceNorm = 
+          sqrt((closestPoint_[0] - gridPos[0])*(closestPoint_[0] - gridPos[0]) +
+               (closestPoint_[1] - gridPos[1])*(closestPoint_[1] - gridPos[1]) +
+               (closestPoint_[2] - gridPos[2])*(closestPoint_[2] - gridPos[2]));
+
+        if (distanceScalar - distanceNorm > 1E-6)
+        {
+          printf("(i,j,k)=(%d,%d,%d): distance=%G | norm of closest vector=%G\n", i,j,k, distanceScalar, distanceNorm);
+          myExitCode = false;
+        }
+      }
+
+  return exitCode && myExitCode;
+}
+
diff --git a/libraries/distanceField/closestPointField.h b/libraries/distanceField/closestPointField.h
new file mode 100644
index 0000000000000000000000000000000000000000..f208a850359098e70cea7b8d882995921fde5de6
--- /dev/null
+++ b/libraries/distanceField/closestPointField.h
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  ClosestPointField is similar to a distance field, 
+  except it also stores the nearest point, not just the distance to it.
+*/
+
+#ifndef _CLOSEST_POINT_FIELD_H_
+#define _CLOSEST_POINT_FIELD_H_
+
+#include "distanceField.h"
+
+class ClosestPointField : public DistanceField
+{
+public:
+  ClosestPointField();
+  ~ClosestPointField();
+
+  int computeUnsignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+  int computeSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+
+  virtual int load(const std::string& filename);
+  virtual int save(const std::string& filename, bool doublePrecision=true);
+  virtual void set(int resolutionX, int resolutionY, int resolutionZ, Vec3d bmin_, Vec3d bmax_, float * distanceData, float * closestPointData);
+
+  void closestPoint(float pos[3], float result[3]);
+
+  inline void closestPoint(int i, int j, int k, float result[3]) { int pos = 3*((k * (resolutionY+1) + j ) * (resolutionX+1) + i); result[0] = closestPointData[pos]; result[1] = closestPointData[pos+1]; result[2] = closestPointData[pos+2]; }
+
+  virtual bool sanityCheck();
+
+  void getClosestPointData(float ** floatBuffer);
+
+  inline void setClosestPoint(int i, int j, int k, float value[3]) { int pos = 3*((k * (resolutionY+1) + j ) * (resolutionX+1) + i); closestPointData[pos] = value[0]; closestPointData[pos+1] = value[1]; closestPointData[pos+2] = value[2]; }
+
+protected:
+  float * closestPointData;
+
+  virtual int ZigZagSigned(void * objMeshOctree, void * meshGraph);
+  virtual int ZigZagUnsigned(void * objMeshOctree, void * meshGraph);
+  virtual int ZigZagSigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+  virtual int ZigZagUnsigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+};
+
+#endif
+
diff --git a/libraries/distanceField/computeField.cpp b/libraries/distanceField/computeField.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..84528711792d4556f0f78709d56d35c80d71b10d
--- /dev/null
+++ b/libraries/distanceField/computeField.cpp
@@ -0,0 +1,493 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  These are the key computational routines to compute the distance field.
+*/
+
+#include <float.h>
+#include "objMeshGraph.h"
+#include "vegalong.h"
+#include "basicAlgorithms.h"
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+
+#ifdef COMPUTE_SIGNED_FIELD
+  #ifdef COMPUTE_FLOOD_FIELD
+    int DistanceField::computeFloodFillSignedField(ObjMesh* objMeshIn, int  resolutionX_, int resolutionY_, int resolutionZ_, int maxTriCount_, int maxDepth_,int zMin_, int zMax_ )
+  #else
+    #ifdef COMPUTE_CLOSEST_POINT
+      int ClosestPointField::computeSignedField(ObjMesh * objMeshIn, int resolutionX_, int resolutionY_, int resolutionZ_, int maxTriCount_, int maxDepth_,int zMin_, int zMax_ )
+    #else
+      int DistanceField::computeSignedField(ObjMesh * objMeshIn, int resolutionX_, int resolutionY_, int resolutionZ_, int maxTriCount_, int maxDepth_, int zMin_, int zMax_)
+    #endif
+  #endif
+#else
+  #ifdef COMPUTE_CLOSEST_POINT
+    int ClosestPointField::computeUnsignedField(ObjMesh * objMeshIn, int resolutionX_, int resolutionY_, int resolutionZ_, int maxTriCount_, int maxDepth_, int zMin_, int zMax_)
+  #else
+    int DistanceField::computeUnsignedField(ObjMesh * objMeshIn, int resolutionX_, int resolutionY_, int resolutionZ_, int maxTriCount_, int maxDepth_, int zMin_, int zMax_)
+  #endif
+#endif
+{
+  ObjMesh objMesh(*objMeshIn);
+
+  maxTriCount = maxTriCount_; 
+  maxDepth = maxDepth_;
+  resolutionX = resolutionX_;
+  resolutionY = resolutionY_;
+  resolutionZ = resolutionZ_;
+
+  #ifdef COMPUTE_SIGNED_FIELD
+    // === check if closed mesh
+    if (!(objMesh.isTriangularMesh()))
+    {
+      printf("Mesh was not triangular. Triangulating..."); fflush(NULL);
+      objMesh.triangulate();
+      printf(" done\n");
+    }
+    ObjMeshOrientable * objMeshOrientable = new ObjMeshOrientable(&objMesh, 0);
+
+    int numOrientationFlips = objMeshOrientable->GenerateHalfEdgeDataStructure();
+    if (numOrientationFlips < 0)
+    {
+      printf("Error: cannot orient the mesh. Geometry is non-manifold.\n");
+      exit(1);
+    }
+
+    cout << "Number of distinct connected components: " << objMeshOrientable->numConnectedComponents() << endl;
+  
+    cout << "Checking if mesh has no boundary..." << endl;
+   
+    // check if mesh has no boundary
+    if (objMeshOrientable->hasBoundary())
+    {
+      cout << "Error: mesh has boundary. Signed distance field is ill-defined." << endl;
+      cout << "  Num boundary edges: " << objMeshOrientable->numBoundaryEdges() << endl;
+      int edge = objMeshOrientable->boundaryEdge(0);
+      cout << "  A boundary edge: " << objMesh.getPosition(objMeshOrientable->halfEdge(edge).startVertex()) << " " <<
+               objMesh.getPosition(objMeshOrientable->halfEdge(edge).endVertex()) << endl;
+
+      return 1;
+    }
+
+    cout << "Mesh has no boundary (i.e. is closed surface)." << endl;
+
+    cout << "Checking if input mesh is oriented consistently..."; 
+    if (numOrientationFlips != 0)
+    {
+      cout << " no." << endl;
+      cout << "Error: triangles in the input mesh are not oriented consistently." << endl;
+      return 2;
+    }
+    else
+      cout << " yes." << endl;
+  
+    delete(objMeshOrientable);
+  #endif
+
+  ObjMeshGraph * meshGraph = NULL;
+  if (computeVoronoiDiagram)
+  {
+    meshGraph = new ObjMeshGraph(&objMesh);
+    printf("Original graph:\n");
+    meshGraph->PrintInfo();
+    //meshGraph->ExpandNeighbors();
+    //printf("Expanded graph:\n");
+    //meshGraph->PrintInfo();
+    if (voronoiDiagram != NULL)
+      free(voronoiDiagram);
+    voronoiDiagram = (int*) malloc (sizeof(int) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1));
+  }
+
+  // === build octree
+
+  printf("Preparing to build the octree. Max triangle count per cell: %d . Max depth: %d .\n", maxTriCount, maxDepth);fflush(NULL);
+
+  #ifdef COMPUTE_SIGNED_FIELD
+    ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals> * objMeshOctree = 
+      new ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>( &objMesh, maxTriCount, maxDepth );
+  #else
+    ObjMeshOctree<TriangleWithCollisionInfo> * objMeshOctree =
+      new ObjMeshOctree<TriangleWithCollisionInfo>( &objMesh, maxTriCount, maxDepth );
+  #endif
+
+
+  #ifndef COMPUTE_FLOOD_FIELD
+    if (useAutomaticBox && !bboxComputed)
+    {
+      computeBoundingBox(&objMesh, resolutionX, resolutionY, resolutionZ);
+    }
+   #endif
+
+  setGridParameters();
+  zMin = (zMin_==-1) ? 0 : zMin_;
+  zMax = (zMax_==-1) ? resolutionZ : zMax_;
+  
+  #ifndef COMPUTE_FLOOD_FIELD
+    //distanceData = (float*) malloc (sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+    vegalong size = sizeof(float)*(resolutionX+1)*(resolutionY+1)*(zMax - zMin +1);
+    distanceData = (float*) realloc (distanceData, size);
+     
+    // initialize to FLT_MAX
+    for(vegalong ii=0; ii<=resolutionX; ii++)
+      for(vegalong jj=0; jj<=resolutionY; jj++)
+        for(vegalong kk=zMin; kk<=zMax; kk++)
+        {
+          vegalong index = (kk-zMin) * (resolutionX+1) * (resolutionY+1) + jj * (resolutionX+1) + ii;
+          distanceData[index] = FLT_MAX;
+        }
+  #else
+    if(zMin == 0)
+    {
+      cout << "computing the flood fill tag..." << endl;
+      computeFloodFillTag(&objMesh);
+    }
+  #endif
+
+  // debug
+  #ifdef GENERATE_DEBUG_DATA
+    pseudoData = (float*) malloc (6*sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+  #endif
+
+  #ifdef COMPUTE_CLOSEST_POINT
+     closestPointData = (float*) malloc (3*sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+  #endif
+
+  cout << "Computing the distance field..." << endl;
+  cout << "Corners of the bounding box are:" << endl;
+  cout << "  " << bmin_ << endl;
+  cout << "  " << bmax_ << endl;
+  cout << "Grid sizes are: " << side[0] / resolutionX << " " << side[1] / resolutionY << " " << side[2] / resolutionZ << endl;
+
+  #ifdef COMPUTE_SIGNED_FIELD
+    #ifdef COMPUTE_FLOOD_FIELD
+      ZigZagFloodFillSigned((void*)objMeshOctree, (void*)meshGraph);
+    #else
+      ZigZagSigned((void*)objMeshOctree, (void*)meshGraph);
+    #endif
+  #else
+    ZigZagUnsigned((void*)objMeshOctree, (void*)meshGraph);
+  #endif
+
+  delete(objMeshOctree);
+  delete(meshGraph);
+
+  if(zMax == resolutionZ)
+  {
+    //free(floodFillTag);
+    #ifdef COMPUTE_SIGNED_FIELD
+      cout << endl << "Signed distance field successfully computed..." << endl;
+    #else
+      cout << endl << "Unsigned distance field successfully computed..." << endl;
+    #endif
+  }
+
+  return 0;
+}
+
+#ifdef COMPUTE_SIGNED_FIELD
+  #ifdef COMPUTE_FLOOD_FIELD
+    int DistanceField::ZigZagFloodFillSigned(void * objMeshOctree_, void * meshGraph_)
+  #else
+    #ifdef COMPUTE_CLOSEST_POINT
+      int ClosestPointField::ZigZagSigned(void * objMeshOctree_, void * meshGraph_)
+    #else
+      int DistanceField::ZigZagSigned(void * objMeshOctree_, void * meshGraph_)
+    #endif
+  #endif
+  #ifdef COMPUTE_FLOOD_FIELD
+     #define ZIGZAGROUTINE ZigZagFloodFillSigned
+  #else
+     #define ZIGZAGROUTINE ZigZagSigned
+  #endif
+#else
+  #ifdef COMPUTE_CLOSEST_POINT
+    int ClosestPointField::ZigZagUnsigned(void * objMeshOctree_, void * meshGraph_)
+  #else
+    int DistanceField::ZigZagUnsigned(void * objMeshOctree_, void * meshGraph_)
+  #endif
+  #define ZIGZAGROUTINE ZigZagUnsigned
+#endif
+{
+#ifdef USE_TBB
+  try
+  {
+    tbb::parallel_for(zMin, zMax+1, [&](int z)
+    {
+      int ret = ZIGZAGROUTINE (objMeshOctree_, meshGraph_, z, z);
+      if (ret != 0) throw ret;
+    });
+  }
+  catch(int e)
+  { 
+    assert(e != 0);
+    return e;
+  }
+  catch(tbb::captured_exception & ex) // in case tbb failed to propogate our thrown ret
+  {
+    return 1;
+  }
+  return 0;
+#else
+  return ZIGZAGROUTINE (objMeshOctree_, meshGraph_, zMin, zMax);
+#endif
+}
+#undef ZIGZAGROUTINE
+
+#ifdef COMPUTE_SIGNED_FIELD
+  #ifdef COMPUTE_FLOOD_FIELD
+    int DistanceField::ZigZagFloodFillSigned(void * objMeshOctree_, void * meshGraph_, int zLo, int zHi, int asterisk)
+  #else
+    #ifdef COMPUTE_CLOSEST_POINT
+      int ClosestPointField::ZigZagSigned(void * objMeshOctree_, void * meshGraph_, int zLo, int zHi, int asterisk)
+    #else
+      int DistanceField::ZigZagSigned(void * objMeshOctree_, void * meshGraph_, int zLo, int zHi, int asterisk)
+    #endif
+  #endif
+#else
+  #ifdef COMPUTE_CLOSEST_POINT
+    int ClosestPointField::ZigZagUnsigned(void * objMeshOctree_, void * meshGraph_, int zLo, int zHi, int asterisk)
+  #else
+    int DistanceField::ZigZagUnsigned(void * objMeshOctree_, void * meshGraph_, int zLo, int zHi, int asterisk)
+  #endif
+#endif
+{
+  ObjMeshGraph * meshGraph = (ObjMeshGraph*) meshGraph_;
+
+  // triangleList will hold the triangles retrieved by range query
+  #ifdef COMPUTE_SIGNED_FIELD
+    vector< TriangleWithCollisionInfoAndPseudoNormals* > triangleList;
+    ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals> * objMeshOctree = (ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals> *) objMeshOctree_;
+  #else
+    vector< TriangleWithCollisionInfo* > triangleList;
+    ObjMeshOctree<TriangleWithCollisionInfo> * objMeshOctree = (ObjMeshOctree<TriangleWithCollisionInfo> *) objMeshOctree_;
+  #endif
+
+  // do the zig-zag
+  vegalong i=0, j=0, k=0;
+  k = zLo;
+  
+  int diri=1;
+  int dirj=1;
+
+  double radius = len(side);
+
+  int needtoCheckFlag = 0;
+  int skipFlag = 0;
+  needtoCheckFlag = needtoCheckFlag; // just for removing warnings
+  do
+  {
+    vegalong index = (k-zMin) * (resolutionX+1) * (resolutionY+1) + j * (resolutionX+1) + i;
+    skipFlag = 0;
+    #ifdef COMPUTE_FLOOD_FIELD
+    if(!needtoCheckFlag && floodFillTag[index] == 0)
+       skipFlag = 1;
+    #endif
+
+    float closestDistance = 0;
+    if(!skipFlag)
+    {
+      Vec3d currentPosition
+                  (bmin_[0] + 1.0 * i * gridX,
+                   bmin_[1] + 1.0 * j * gridY, 
+                   bmin_[2] + 1.0 * k * gridZ);
+
+      //printf("%d %d %d\n",i,j,k);
+      // process grid point i,j,k 
+      triangleList.clear();
+    
+      while (triangleList.size() <= 0) // we should only go through this loop exactly once
+      {
+        //printf("radius: %f pos: %f %f %f\n", radius, currentPosition[0], currentPosition[1], currentPosition[2]);
+        objMeshOctree->rangeQuery(triangleList, SimpleSphere(currentPosition, radius));
+
+        if (triangleList.size() <= 0) 
+        { // should never happen... but to stay robust
+          cout << "Warning: range query didn't find any triangles. Increasing radius by a factor of 2 and re-trying." << endl;
+          radius *= 2;
+        }
+      }
+
+      sortAndDeduplicate(triangleList);
+      // find closest triangle among the retrieved ones
+      // initialization:
+      double closestDistance2 = 1.25 * radius * radius; // there will be somebody within that radius (actually even without the factor "1.25")
+        // (factor "1.25" added to account for numerical round-off)
+
+      int closestFeature = -1;
+      int closestTriangle = -1;
+      closestTriangle = closestTriangle; // to avoid compiler warnings
+      int indexClosestTriangle = -1;
+
+      #ifdef COMPUTE_CLOSEST_POINT
+        Vec3d closestPosition(DBL_MAX, DBL_MAX, DBL_MAX);
+      #endif
+      
+      for (unsigned int l=0; l<triangleList.size(); l++)
+      {
+        int closestLocalFeature = -1;        
+
+        #ifdef COMPUTE_CLOSEST_POINT
+          double alpha, beta, gamma;
+          double d2 = triangleList[l]->distanceToPoint2(currentPosition, &closestLocalFeature, &alpha, &beta, &gamma);        
+          if (d2 < closestDistance2)
+          {          
+            closestDistance2 = d2;
+            closestPosition = triangleList[l]->getBarycentricLocation(alpha, beta, gamma);          
+            closestFeature = closestLocalFeature; 
+            closestTriangle = l;
+            indexClosestTriangle =  triangleList[l]->index();          
+          }
+        #else
+          double d2 = triangleList[l]->distanceToPoint2(currentPosition,&closestLocalFeature);
+          if (d2 < closestDistance2)
+          {
+            closestDistance2 = d2;
+            closestFeature = closestLocalFeature;
+            closestTriangle = l;
+            indexClosestTriangle =  triangleList[l]->index();          
+          }
+        #endif
+      }
+
+      // now, indexClosestTriangle and closestFeature are set to the closest triangle and the closest feature on the triangle
+    
+      if (computeVoronoiDiagram)
+      {
+        int closestSite = meshGraph->graphID(indexClosestTriangle, closestFeature);
+        voronoiDiagram[k * (resolutionX+1) * (resolutionY+1) + j * (resolutionX+1) + i] = closestSite;
+        //printf("cs: %d cf: %d\n", closestSite, closestFeature);
+      }
+
+      if (closestFeature < 0) // should never happen
+      {
+        cout << "Internal error: did not find any triangle within the guaranteed radius:" << radius << endl;
+        return 3;
+      }
+
+      // square root...
+      closestDistance = (float) sqrt(closestDistance2);
+    
+      #ifdef COMPUTE_SIGNED_FIELD
+        // determine sign, as in [Baerentzen 2002]
+        Vec3d pseudoNormal = triangleList[closestTriangle]->pseudoNormal(closestFeature);
+        Vec3d pseudoClosestPosition = triangleList[closestTriangle]->pseudoClosestPosition(closestFeature);
+
+        #ifdef COMPUTE_FLOOD_FIELD
+          if (dot(pseudoNormal,currentPosition-pseudoClosestPosition) <= 0) // interior, must flip sign
+          {
+            closestDistance *= -1;
+            needtoCheckFlag = 1;
+          }
+          else
+            needtoCheckFlag = 0;
+        #else
+          if (dot(pseudoNormal,currentPosition-pseudoClosestPosition) < 0) // interior, must flip sign
+             closestDistance *= -1;
+        #endif
+        //printf("%G ", closestDistance);
+      #endif
+
+      // register result
+      distanceData[index] = closestDistance;
+    
+      #ifdef COMPUTE_CLOSEST_POINT
+        vegalong dataIndex = 3 * (k * (resolutionX+1) * (resolutionY+1) + j * (resolutionX+1) + i);
+        closestPointData[dataIndex+0] = closestPosition[0];
+        closestPointData[dataIndex+1] = closestPosition[1];
+        closestPointData[dataIndex+2] = closestPosition[2];
+      #endif
+    } // end if (!skipFlag)
+
+    closestDistance = distanceData[index];
+   
+    vegalong oldi = i;
+    vegalong oldj = j;
+    vegalong oldk = k;
+
+    // increment i,j,k
+    i += diri; 
+
+    if (i>resolutionX)
+    {
+      diri = -1; // start going to the left
+      i = resolutionX;
+      j += dirj;
+    }
+
+    if (i<0)
+    {
+      diri = +1; // start going to the right
+      i = 0;
+      j += dirj;
+    }
+
+    if (j>resolutionY)
+    {
+      dirj = -1; // start going down
+      j = resolutionY;
+      if (asterisk)
+        cout << "*";
+      cout << k << " " << flush;
+      k += 1;
+    }
+
+    if (j<0)
+    {
+      dirj = +1; // start going  up
+      j = 0;
+      if (asterisk)
+        cout << "*";
+      cout << k << " " << flush;
+      k += 1;
+    }
+
+    // spatial coherence: update radius for next iteration
+    double delta=0.0; // this will be modified below
+    if (i != oldi)
+      delta = 1.01 * gridX;
+
+    if (j != oldj)
+      delta = 1.01 * gridY;
+
+    if (k != oldk)
+      delta = 1.01 * gridZ;
+
+    radius = fabs(closestDistance) + delta;
+  }
+  while (k <= zHi); 
+
+  return 0;
+}
+
diff --git a/libraries/distanceField/computeFieldNarrowBand.cpp b/libraries/distanceField/computeFieldNarrowBand.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..741db1d97441551daa4ee7c1dc8f2dceecd3e8c0
--- /dev/null
+++ b/libraries/distanceField/computeFieldNarrowBand.cpp
@@ -0,0 +1,372 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Main computational routine for narrowband distance field computation.
+*/
+
+#include <float.h>
+#include "performanceCounter.h"
+#include "vegalong.h"
+
+#ifdef COMPUTE_SIGNED_FIELD_NARROWBAND
+   #ifdef COMPUTE_INTERIOR_FIELD_NARROWBAND
+     int DistanceFieldNarrowBand::computeInteriorSignedField(ObjMesh * objMeshIn, int resolutionX_, int resolutionY_, int resolutionZ_, double bandWidth, int maxTriCount_, int maxDepth_)
+   #else
+     int DistanceFieldNarrowBand::computeSignedField(ObjMesh * objMeshIn, int resolutionX_, int resolutionY_, int resolutionZ_, double bandWidth, int maxTriCount_, int maxDepth_)
+   #endif
+#else
+   int DistanceFieldNarrowBand::computeUnsignedField(ObjMesh * objMeshIn, int resolutionX_, int resolutionY_, int resolutionZ_, double bandWidth, int maxTriCount_, int maxDepth_)
+#endif
+{
+  ObjMesh objMesh(*objMeshIn);
+
+  maxTriCount = maxTriCount_; 
+  maxDepth = maxDepth_;
+  resolutionX = resolutionX_;
+  resolutionY = resolutionY_;
+  resolutionZ = resolutionZ_;
+
+  vegalong size = (resolutionX + 1) * (resolutionY + 1) * (resolutionZ + 1);
+  gridPointStatus = (char*) realloc(gridPointStatus, sizeof(char) * size);
+  
+  for(vegalong i = 0; i < size; i++)
+    gridPointStatus[i] = DistanceFieldNarrowBand::EXTERIOR_UNCOMPUTED;
+  
+  #ifdef COMPUTE_SIGNED_FIELD_NARROWBAND
+    // === check if closed mesh
+    if (!(objMesh.isTriangularMesh()))
+    {
+      printf("Mesh was not triangular. Triangulating..."); fflush(NULL);
+      objMesh.triangulate();
+      printf(" done\n");
+    }
+    ObjMeshOrientable * objMeshOrientable = new ObjMeshOrientable(&objMesh, 0);
+
+    int numOrientationFlips = objMeshOrientable->GenerateHalfEdgeDataStructure();
+
+    if (numOrientationFlips==-1)
+      return -1;
+
+    cout << "Number of distinct connected components: " << objMeshOrientable->numConnectedComponents() << endl;
+  
+    cout << "Checking if mesh has no boundary..." << endl;
+   
+    // check if mesh has no boundary
+    if (objMeshOrientable->hasBoundary())
+    {
+      cout << "Error: mesh has boundary. Signed distance field is ill-defined." << endl;
+      cout << "  Num boundary edges: " << objMeshOrientable->numBoundaryEdges() << endl;
+      int edge = objMeshOrientable->boundaryEdge(0);
+      cout << "  A boundary edge: " << objMesh.getPosition(objMeshOrientable->halfEdge(edge).startVertex()) << " " <<
+            objMesh.getPosition(objMeshOrientable->halfEdge(edge).endVertex()) << endl;
+      return 1;
+    }
+
+    cout << "Mesh has no boundary (i.e. is closed surface)." << endl;
+
+    cout << "Checking if input mesh is oriented consistently..."; 
+    if (numOrientationFlips != 0)
+    {
+      cout << " no." << endl;
+      cout << "Error: triangles in the input mesh are not oriented consistently." << endl;
+      return 2;
+    }
+    else
+      cout << " yes." << endl;
+  
+    delete(objMeshOrientable);
+  #endif
+
+  // === build octree
+
+  printf("Preparing to build the octree. Max triangle count per cell: %d . Max depth: %d .\n", maxTriCount, maxDepth);fflush(NULL);
+
+  #ifdef COMPUTE_SIGNED_FIELD_NARROWBAND
+    ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals> * objMeshOctree = 
+      new ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>( &objMesh, maxTriCount, maxDepth );
+  #else
+    ObjMeshOctree<TriangleWithCollisionInfo> * objMeshOctree =
+      new ObjMeshOctree<TriangleWithCollisionInfo>( &objMesh, maxTriCount, maxDepth );
+  #endif
+
+  if (useAutomaticBox && !bboxComputed)
+    computeBoundingBox(&objMesh, resolutionX, resolutionY, resolutionZ);
+
+  setGridParameters();
+ 
+  cout << "Finding all the surface grid points..." << endl;
+  // find all grid points that are in voxels that intersect the input polygonal geometry
+  findSurfaceGridPoints(objMeshIn);
+
+  cout << "Computing the distance field..." << endl;
+  cout << "Corners of the bounding box are:" << endl;
+  cout << "  " << bmin_ << endl;
+  cout << "  " << bmax_ << endl;
+  cout << "Grid sizes are: " << side[0] / resolutionX << " " << side[1] / resolutionY << " " << side[2] / resolutionZ << endl;
+
+  int asterisk = 1;
+  #ifdef COMPUTE_SIGNED_FIELD_NARROWBAND
+    #ifdef COMPUTE_INTERIOR_FIELD_NARROWBAND
+      {
+        // compute the signed field inside the outer sigma-isosurface (assumes unsigned field has already been computed)
+        std::map<gridPoint, float> distanceDataCopy = distanceData;
+        distanceData.clear(); // clear the map
+        breadthFirstTraversalInteriorSigned((void*)objMeshOctree, bandWidth, 0, resolutionZ, asterisk);
+
+        for(std::map<gridPoint, float>::iterator it = distanceDataCopy.begin(); it != distanceDataCopy.end(); it++)
+        {
+          gridPoint v = it->first;
+          vegalong index = (v.third * (resolutionY+1) + v.second) * (resolutionX + 1) + v.first;
+          if (gridPointStatus[index] != DistanceFieldNarrowBand::COMPUTED)
+          {
+            distanceData.insert(*it);
+            gridPointStatus[index] = DistanceFieldNarrowBand::COMPUTED;
+          }
+        }
+      }
+    #else
+      // input geometry is manifold
+      distanceData.clear(); // clear the compressed map
+      breadthFirstTraversalSigned((void*)objMeshOctree, bandWidth, 0, resolutionZ, asterisk);
+    #endif
+  #else
+    distanceData.clear();
+    breadthFirstTraversalUnsigned((void*)objMeshOctree, bandWidth, 0, resolutionZ, asterisk);
+  #endif
+
+  delete(objMeshOctree);
+  
+  finalizeGridPointStatus();
+
+  #ifdef COMPUTE_SIGNED_FIELD_NARROWBAND
+    cout << endl << "Signed distance field successfully computed..." << endl;
+  #else
+    cout << endl << "Unsigned distance field successfully computed..." << endl;
+  #endif
+
+  return 0;
+}
+
+#ifdef COMPUTE_SIGNED_FIELD_NARROWBAND
+  #ifdef COMPUTE_INTERIOR_FIELD_NARROWBAND
+    int DistanceFieldNarrowBand::breadthFirstTraversalInteriorSigned(void * objMeshOctree_, float bandWidth, int zLo, int zHi, int asterisk)
+  #else
+    int DistanceFieldNarrowBand::breadthFirstTraversalSigned(void * objMeshOctree_, float bandWidth, int zLo, int zHi, int asterisk)
+  #endif
+#else
+  int DistanceFieldNarrowBand::breadthFirstTraversalUnsigned(void * objMeshOctree_, float bandWidth, int zLo, int zHi, int asterisk)
+#endif
+{
+  // triangleList will hold the triangles retrieved by range query
+  #ifdef COMPUTE_SIGNED_FIELD_NARROWBAND
+    vector< TriangleWithCollisionInfoAndPseudoNormals* > triangleList;
+    ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals> * objMeshOctree = (ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals> *) objMeshOctree_;
+  #else
+    vector< TriangleWithCollisionInfo* > triangleList;
+    ObjMeshOctree<TriangleWithCollisionInfo> * objMeshOctree = (ObjMeshOctree<TriangleWithCollisionInfo> *) objMeshOctree_;
+  #endif
+
+  double radius = len(side);
+
+  std::set<gridPoint> scheduledGridPoints;
+  
+  vegalong mul = (resolutionX + 1) * (resolutionY + 1);
+  for(unsigned int p = 0; p < surfaceGridPoints.size(); p++)
+  {
+    vegalong index = surfaceGridPoints[p];
+
+    vegalong k = index / mul;
+    if ((k >= zLo) && (k <= zHi))
+    {
+      int i = index % (resolutionX + 1);
+      int j = (index / (resolutionX + 1)) % (resolutionY + 1);
+
+      scheduledGridPoints.insert(gridPoint(i, j, k));
+    }
+  }
+
+  int counter = 0;
+  while(!scheduledGridPoints.empty())
+  {
+    gridPoint v = *(scheduledGridPoints.begin());
+    scheduledGridPoints.erase(scheduledGridPoints.begin());
+
+    int i = v.first;
+    int j = v.second;
+    int k = v.third;
+
+    radius = len(side);
+    
+    float d[6];
+    d[0] = distance((i < resolutionX) ? (i+1) : resolutionX, j, k);
+    d[1] = distance((i > 0) ? (i-1) : 0, j, k);
+    d[2] = distance(i, (j < resolutionY) ? (j+1) : resolutionY, k);
+    d[3] = distance(i, (j > 0) ? (j-1) : 0, k);
+    d[4] = distance(i, j, (k < resolutionZ) ? (k+1) : resolutionZ);
+    d[5] = distance(i, j, (k > 0) ? (k-1) : 0);
+
+    int ll = -1;
+    for(int l = 0; l < 6; l++)
+    {
+      if (d[l] != FLT_MAX)
+      {
+	float fd = fabs(d[l]);
+	if (fd < radius)
+	{
+	  radius = fabs(fd);
+	  ll = l;
+	}
+      }
+    }
+
+    if ((ll == 0) || (ll == 1))
+      radius += 1.01 * gridX;
+    if ((ll == 2) || (ll == 3))
+      radius += 1.01 * gridY;
+    if ((ll == 4) || (ll == 5))
+      radius += 1.01 * gridZ;
+
+    Vec3d currentPosition
+      (bmin_[0] + 1.0 * i * gridX,
+       bmin_[1] + 1.0 * j * gridY, 
+       bmin_[2] + 1.0 * k * gridZ);
+
+    triangleList.clear();
+    
+    while (triangleList.size() <= 0) // we should only go through this loop exactly once
+    {
+      //printf("radius: %f pos: %f %f %f\n", radius, currentPosition[0], currentPosition[1], currentPosition[2]);
+      objMeshOctree->rangeQuery(triangleList, SimpleSphere(currentPosition, radius));
+
+      if (triangleList.size() <= 0) 
+      { // should never happen... but to stay robust
+        cout << "Warning: range query didn't find any triangles. Radius = " << radius << " . Increasing radius by a factor of 2 and re-trying." << endl;
+        radius *= 2;
+      }
+    }    
+
+    // find closest triangle among the retrieved ones
+    // initialization:
+    double closestDistance2 = 1.25 * radius * radius; // there will be somebody within that radius (actually even without the factor "1.25")
+    // (factor "1.25" added to account for numerical round-off)
+
+    int closestFeature = -1;
+    int closestTriangle = -1;
+    closestTriangle = closestTriangle; // to avoid compiler warnings
+  
+    for (unsigned int l=0; l<triangleList.size(); l++)
+    {
+      int closestLocalFeature = -1;        
+      
+      double d2 = triangleList[l]->distanceToPoint2(currentPosition,&closestLocalFeature);
+    
+      if (d2 < closestDistance2)
+      {
+        closestDistance2 = d2;
+        closestFeature = closestLocalFeature;
+        closestTriangle = l;
+      }
+    }
+
+    // now, indexClosestTriangle and closestFeature are set to the closest triangle and the closest feature on the triangle
+   
+    if (closestFeature < 0) // should never happen
+    {
+      cout << "Internal error: did not find any triangle within the guaranteed radius:" << radius << endl;
+      return 3;
+    }
+
+    // square root...
+    float closestDistance = (float) sqrt(closestDistance2);
+
+    #ifdef COMPUTE_SIGNED_FIELD_NARROWBAND    
+    // determine sign, as in [Baerentzen 2002]
+      Vec3d pseudoNormal = triangleList[closestTriangle]->pseudoNormal(closestFeature);
+      Vec3d pseudoClosestPosition = triangleList[closestTriangle]->pseudoClosestPosition(closestFeature);
+        
+      if (dot(pseudoNormal,currentPosition-pseudoClosestPosition) < 0) // interior, must flip sign
+        closestDistance *= -1;
+    #endif
+
+    if (fabs(closestDistance) < bandWidth)
+    {
+      int flag = 1;
+      #ifdef COMPUTE_INTERIOR_FIELD_NARROWBAND
+        if (closestDistance > 0)
+          flag = 0;
+      #endif
+
+      if (flag)
+      {
+        for(int l = 0; l < 6; l++) 
+        {
+          if (fabs(d[l]) == FLT_MAX)
+          {
+            if (l < 2)
+            {
+              int xIndex = i + ((l % 2) ? -1 : 1);
+              if ((xIndex >= 0) && (xIndex <= resolutionX))
+                scheduledGridPoints.insert(gridPoint(xIndex, j, k));
+            }
+            else if ((l >= 2) && (l < 4))
+            {
+              int yIndex = j + ((l % 2) ? -1 : 1);
+              if ((yIndex >= 0) && (yIndex <= resolutionY))
+                scheduledGridPoints.insert(gridPoint(i, yIndex, k));
+            }
+            else
+            {
+              int zIndex = k + ((l % 2) ? -1 : 1);
+              if ((zIndex >= zLo) && (zIndex <= zHi))
+                scheduledGridPoints.insert(gridPoint(i, j, zIndex));
+            }
+          }
+        }
+      }
+    }
+    
+    distanceData.insert(pair<gridPoint, float>(v, closestDistance));
+    vegalong index = (v.third * (resolutionY + 1) + v.second) * (resolutionX + 1)+ v.first;
+    gridPointStatus[index] = DistanceFieldNarrowBand::COMPUTED;
+
+    counter ++;
+    if (asterisk && (counter % (resolutionX * resolutionY) == 0))
+    {
+      counter = 0;
+      printf("*"); fflush(NULL);
+    }
+  }
+
+  return 0;
+}
+
diff --git a/libraries/distanceField/distanceField.cpp b/libraries/distanceField/distanceField.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e1bc4bbd0f60269f9cedba6c556326a846a31579
--- /dev/null
+++ b/libraries/distanceField/distanceField.cpp
@@ -0,0 +1,1060 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This code generates a distance filed, either signed or unsigned, 
+  to the given triangle mesh.
+
+  The distance field can be loaded/saved to a file.
+  You can also lookup the field (once computed or loaded) at arbitrary 
+  locations inside the field, using trilinear interpolation.
+
+  Input mesh need not have triangle faces (e.g., can have quads; 
+  they will be triangulated).
+
+  For signed field generation, the input mesh must be a closed manifold mesh.
+
+  Input mesh must be given in the .obj format:
+  http://www.royriggs.com/obj.html
+
+  By default, the bounding box will be a cube, obtained by fitting the  
+  smallest cube to the geometry, and then expanded (scaled) from its 
+  center by a factor of 1.5. You can provide your own bounding boxes. 
+  However, note: (1) the provided bounding box must cover all the geometry, 
+  and (2) bounding boxes that are not cubes were not (yet) tested.
+
+  The bounding box will be divided into a "resolution" number of 
+  cubes ("voxels") along each axis. The distance field will be computed at the 
+  vertices of these voxels. So, if resolution is 256, the bounding box 
+  will get divided into 256 x 256 x 256 voxels, and the distance field 
+  will be computed on the resulting 257 x 257 x 257 grid of voxel vertices. 
+  Note that when indexing voxels, the indices (i,j,k) will run from 0 to 255 
+  inclusive, whereas when indexing voxel vertices (also called "grid vertices"), 
+  they will run from 0 to 256 inclusive.
+
+  Distance field data is stored at voxel vertices. 
+  In memory, distance field value at voxel vertex (i,j,k) is stored 
+  at location k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i .
+
+  Internally, the code builds an octree on top of the triangle mesh. 
+  There are two parameters that control this process (you can keep them 
+  at default values, which worked well in practice for us) :
+  the max depth of the octree is "maxDepth", and
+  the max number of triangles intersecting an octree cell is "maxTriCount".
+  Note: once max depth level is reached, the maxTriCount bound is not imposed any more.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <float.h>
+#include <fstream>
+#include <set>
+#include "triangle.h"
+#include "triple.h"
+#include "boundingBox.h"
+#include "distanceField.h"
+#include "trilinearInterpolation.h"
+#include "vegalong.h"
+using namespace std;
+
+//#define GENERATE_DEBUG_DATA
+
+vegalong DistanceField::GetFilesize(const char *filename)
+{
+  FILE * f = fopen(filename, "rb");  /* open the file in read only */
+
+  vegalong size = 0;
+  if (fseek(f,0,SEEK_END)==0) /* seek was successful */
+      size = ftell(f);
+  fclose(f);
+  return size;
+}
+
+DistanceField::DistanceField() : DistanceFieldBase()
+{ 
+  distanceData = NULL;
+  pseudoData = NULL;
+  computeVoronoiDiagram = false;
+  voronoiDiagram = NULL;
+  minBoundaryDistance = 0;
+  floodFillTag = NULL;
+}
+
+DistanceField::DistanceField(int resolutionX_, int resolutionY_, int resolutionZ_): DistanceFieldBase(resolutionX_, resolutionY_, resolutionZ_)
+{
+  distanceData = NULL;
+  pseudoData = NULL;
+  computeVoronoiDiagram = false;
+  voronoiDiagram = NULL;
+  minBoundaryDistance = 0;
+  floodFillTag = NULL;
+  
+  distanceData = (float*) malloc (sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+}
+
+DistanceField::~DistanceField()
+{
+  free(distanceData); 
+  free(pseudoData); 
+  free(floodFillTag);
+  free(voronoiDiagram);
+}
+
+void DistanceField::enableVoronoiDiagramComputation(bool computeVoronoiDiagram)
+{
+  this->computeVoronoiDiagram = computeVoronoiDiagram;
+}
+
+// the routines for signed and unsigned distance field computation
+#define COMPUTE_SIGNED_FIELD
+  #define COMPUTE_FLOOD_FIELD
+    #include "computeField.cpp"
+  #undef COMPUTE_FLOOD_FIELD
+    #include "computeField.cpp"
+#undef COMPUTE_SIGNED_FIELD
+  #include "computeField.cpp"
+
+int DistanceField::load(const std::string& filename)
+{
+  ifstream fin(filename.c_str(),ios::binary);
+  if (!fin)
+    return 1;
+
+  fin.read((char*)&resolutionX, sizeof(int));
+
+  // the type of data (single-precision or double-precision) is encoded
+  //   as the sign of the x-resolution
+  bool floatData = (resolutionX < 0);
+  if (floatData)
+    resolutionX = -resolutionX;
+
+  fin.read((char*)&resolutionY, sizeof(int));
+
+  if (resolutionY < 0) // negative second resolution implies closest point data
+    return 1;
+
+  fin.read((char*)&resolutionZ, sizeof(int));
+
+  fin.read((char*)&(bmin_[0]), sizeof(double));
+  fin.read((char*)&(bmin_[1]), sizeof(double));
+  fin.read((char*)&(bmin_[2]), sizeof(double));
+
+  fin.read((char*)&(bmax_[0]), sizeof(double));
+  fin.read((char*)&(bmax_[1]), sizeof(double));
+  fin.read((char*)&(bmax_[2]), sizeof(double));
+
+  setGridParameters();
+
+  distanceData = (float*) realloc (distanceData, sizeof(float) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1));
+
+  float * distanceDataPos = distanceData;
+  int sliceResolution = (resolutionX+1) * (resolutionY+1);
+  int unitSize = floatData ? sizeof(float) : sizeof(double);
+  for(int k=0; k <= resolutionZ; k++, distanceDataPos += sliceResolution)
+    fin.read((char*)distanceDataPos, unitSize * sliceResolution);
+  //printf("\n");
+  fin.close();
+
+  return 0;
+}
+
+int DistanceField::openStreamDistanceField(const std::string& filename, Vec3d * bmin, Vec3d * bmax, int * resolutionX, int * resolutionY, int * resolutionZ, bool * floatData, ifstream & fin)
+{
+  fin.open(filename.c_str(),ios::binary);
+  if (!fin)
+    return 1;
+
+  fin.read((char*)resolutionX, 4);
+
+  // the type of data (single-precision or double-precision) is encoded
+  //   as the sign of the x-resolution
+  *floatData = (*resolutionX < 0);
+  if (*floatData)
+    *resolutionX = - *resolutionX;
+   
+  fin.read((char*)resolutionY, 4); 
+
+  if (*resolutionY < 0) // negative second resolution implies closest point data
+    return 1;
+
+  fin.read((char*)resolutionZ, 4);
+
+  double bminx, bminy, bminz;
+  fin.read((char*)&bminx, 8);
+  fin.read((char*)&bminy, 8);
+  fin.read((char*)&bminz, 8);
+
+  double bmaxx, bmaxy, bmaxz;
+  fin.read((char*)&bmaxx, 8);
+  fin.read((char*)&bmaxy, 8);
+  fin.read((char*)&bmaxz, 8);
+
+  (*bmin)[0] = bminx;
+  (*bmin)[1] = bminy;
+  (*bmin)[2] = bminz;
+
+  (*bmax)[0] = bmaxx;
+  (*bmax)[1] = bmaxy;
+  (*bmax)[2] = bmaxz;
+
+  return 0;
+}
+
+void DistanceField::retrieveZSlice(ifstream & fin, bool floatData, int resolutionX, int resolutionY, int resolutionZ, float * slice)
+{
+  double * buffer = (double*) malloc (sizeof(double) * (resolutionX+1) * (resolutionY+1)); // place for one slice
+
+  float * distanceDataPos = slice;
+
+  if (floatData)
+    fin.read((char*)buffer, sizeof(float) * (resolutionX+1) * (resolutionY+1));
+  else
+    fin.read((char*)buffer, sizeof(double) * (resolutionX+1) * (resolutionY+1));
+
+  char * bufferPos = (char*)buffer;
+  int bufferPosInc = floatData ? sizeof(float) : sizeof(double);
+
+  // copy data to the slice
+  for(int j = 0; j <= resolutionY; j++)
+    for(int i = 0; i <= resolutionX; i++)
+    {
+      if (floatData)
+        *distanceDataPos = *(float*)bufferPos;
+      else
+        *distanceDataPos = *(double*)bufferPos;
+      distanceDataPos++;
+      bufferPos += bufferPosInc;
+    }
+}
+
+void DistanceField::set(int resolutionX, int resolutionY, int resolutionZ, Vec3d bmin_, Vec3d bmax_, float * distanceData)
+{
+  this->resolutionX = resolutionX;
+  this->resolutionY = resolutionY;
+  this->resolutionZ = resolutionZ;
+  this->bmin_ = bmin_; 
+  this->bmax_ = bmax_; 
+
+  setGridParameters();
+
+  free(this->distanceData);
+  this->distanceData = (float*) malloc (sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+  memcpy(this->distanceData, distanceData, sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1));
+}
+
+int DistanceField::isDoublePrecision(const std::string & filename, bool & doublePrecision)
+{
+  ifstream fin(filename.c_str(),ios::binary);
+  if (!fin)
+    return 1;
+
+  int res;
+  fin.read((char*)&res,4);
+
+  // the type of data (single-precision or double-precision) is encoded
+  //   as the sign of the x-resolution
+  doublePrecision = (res >= 0);
+
+  fin.close();
+
+  return 0;
+}
+
+int DistanceField::saveToText(const std::string& filename)
+{
+  FILE * fout = fopen((char*)filename.c_str(), "w");
+  if (!fout)
+    return 1;
+
+  fprintf(fout, "%d\n", resolutionX);
+  fprintf(fout, "%d\n", resolutionY);
+  fprintf(fout, "%d\n", resolutionZ);
+
+  for(int i=0; i<=resolutionX; i++)
+    for(int j=0; j<=resolutionY; j++)
+      for(int k=0; k<=resolutionZ; k++)
+      {
+        fprintf(fout,"%G\n", distance(i,j,k));
+      }
+
+  fclose(fout);
+
+  return 0;
+}
+
+int DistanceField::save(const std::string& filename, bool doublePrecision)
+{
+  if (doublePrecision)
+    printf("Error: double precision output is not supported. Using float instead.\n");
+  doublePrecision = false;
+
+  ofstream fout(filename.c_str(),ios::binary);
+
+  int data = resolutionX;
+  if (!doublePrecision)
+    data = -data;
+
+  fout.write((char*)&data,sizeof(int));
+  fout.write((char*)&resolutionY,sizeof(int));
+  fout.write((char*)&resolutionZ,sizeof(int));
+
+  fout.write((char*)&(bmin_[0]),sizeof(double));
+  fout.write((char*)&(bmin_[1]),sizeof(double));
+  fout.write((char*)&(bmin_[2]),sizeof(double));
+
+  fout.write((char*)&(bmax_[0]),sizeof(double));
+  fout.write((char*)&(bmax_[1]),sizeof(double));
+  fout.write((char*)&(bmax_[2]),sizeof(double));
+
+  // float precision
+  //fout.write((char*)distanceData,sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1)); // this version sometimes resulted in the field incorrectly written to file (unclear why)
+
+  for(int k=0; k<=resolutionZ; k++)
+    for(int j=0; j<=resolutionY; j++)
+      for(int i=0; i<=resolutionX; i++)
+      {
+        fout.write((char*)&distanceData[k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i], sizeof(float));
+      }
+
+/*
+  if (doublePrecision)
+    fout.write((char*)distanceData,sizeof(double)*(resolution+1)*(resolution+1)*(resolution+1));
+  else
+  {
+    float * buffer;
+    convertToFloat(&buffer);
+    fout.write((char*)buffer,sizeof(float)*(resolution+1)*(resolution+1)*(resolution+1));
+    free(buffer);
+  }
+*/
+  fout.close();
+
+  #ifdef GENERATE_DEBUG_DATA
+    ofstream fout1("debugPseudo", ios::binary);
+    fout1.write((char*)pseudoData, 6*sizeof(float)*(resolution+1)*(resolution+1)*(resolution+1));
+    fout1.close();
+  #endif
+
+  return 0;
+}
+
+int DistanceField::saveVoronoiDiagram(const std::string& filename)
+{
+  if (voronoiDiagram == NULL)
+  {
+    printf("Error in saveVoronoiDiagram: Voronoi diagram has not been computed.\n");
+    return 1;
+  }
+
+  ofstream fout(filename.c_str(), ios::binary);
+
+  int header = 0;
+
+  fout.write((char*)&header,sizeof(int));
+  fout.write((char*)&resolutionX,sizeof(int));
+  fout.write((char*)&resolutionY,sizeof(int));
+  fout.write((char*)&resolutionZ,sizeof(int));
+
+  fout.write((char*)&(bmin_[0]),sizeof(double));
+  fout.write((char*)&(bmin_[1]),sizeof(double));
+  fout.write((char*)&(bmin_[2]),sizeof(double));
+
+  fout.write((char*)&(bmax_[0]),sizeof(double));
+  fout.write((char*)&(bmax_[1]),sizeof(double));
+  fout.write((char*)&(bmax_[2]),sizeof(double));
+
+  fout.write((char*)voronoiDiagram, sizeof(int) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1));
+
+  fout.close();
+
+  return 0;
+}
+
+typedef struct
+{
+  int i,j,k;
+  int di,dj,dk;
+  double fieldDist, gridDist, relError; 
+} errorData;
+
+struct more_errorData : public std::binary_function< errorData, errorData, bool > {
+  bool operator()(const errorData& x, const errorData& y) {
+    return((x.relError) > (y.relError)); 
+  }
+};
+
+bool DistanceField::sanityCheck()
+{
+  //double diagonal = sqrt(gridX*gridX + gridY*gridY + gridZ*gridZ);
+
+  bool result = true;
+  int count = 0;
+
+  const int numErrorsPrinted = 3;
+
+  vector<errorData> relErrors;
+  errorData emptyEntry = {0,0,0,0,0,0,0.0,0.0,-1.0};
+  for(int i=0; i<numErrorsPrinted; i++)
+   relErrors.push_back(emptyEntry);
+
+  for (int k=0; k <= resolutionZ; k++)
+  {
+    for (int j=0; j <= resolutionY; j++)
+      for (int i=0; i <= resolutionX; i++)
+      {
+        float d = distance(i,j,k);
+        float d1;
+        float sanity;
+        float relError;
+        float fieldDist;
+        float gridDist;
+
+        #define PROCESS(di,dj,dk)\
+        if ((i+(di) <= resolutionX) && (i+(di) >= 0) &&\
+            (j+(dj) <= resolutionY) && (j+(dj) >= 0) &&\
+            (k+(dk) <= resolutionZ) && (k+(dk) >= 0))\
+        {\
+          d1 = distance(i+(di),j+(dj),k+(dk));\
+          gridDist = (float) len(Vec3d((di)*gridX,(dj)*gridY,(dk)*gridZ));\
+          fieldDist = fabs(d-d1);\
+          sanity = fieldDist - gridDist;\
+          if (sanity > 1E-6)\
+          {\
+            relError = sanity/gridDist;\
+            if (relError > relErrors[numErrorsPrinted-1].relError)\
+            {\
+              errorData errorEntry = {i,j,k,di,dj,dk,fieldDist,gridDist,relError};\
+              relErrors[numErrorsPrinted-1] = errorEntry;\
+              sort(relErrors.begin(),relErrors.end(),more_errorData());\
+            }\
+            result = false;\
+            count++;\
+          }\
+        }
+       
+        PROCESS(1,0,0);
+        PROCESS(1,1,0);
+        PROCESS(0,1,0);
+        PROCESS(-1,1,0);
+        PROCESS(-1,0,0);
+        PROCESS(-1,-1,0);
+        PROCESS(0,-1,0);
+        PROCESS(1,-1,0);
+
+        PROCESS(0,0,1);
+        PROCESS(1,0,1);
+        PROCESS(1,1,1);
+        PROCESS(0,1,1);
+        PROCESS(-1,1,1);
+        PROCESS(-1,0,1);
+        PROCESS(-1,-1,1);
+        PROCESS(0,-1,1);
+        PROCESS(1,-1,1);
+
+        PROCESS(0,0,-1);
+        PROCESS(1,0,-1);
+        PROCESS(1,1,-1);
+        PROCESS(0,1,-1);
+        PROCESS(-1,1,-1);
+        PROCESS(-1,0,-1);
+        PROCESS(-1,-1,-1);
+        PROCESS(0,-1,-1);
+        PROCESS(1,-1,-1);
+
+      }
+    cout << "." << flush;
+  }
+
+  cout << endl;
+
+  if (count == 0)
+    cout << "Success: sanity check passed." << endl;
+  else
+  {
+    cout << "Encountered " << count << " possible errors. Largest top " << numErrorsPrinted << " errors (or all errors if fewer):" << endl;
+    for(int i=0; i< (count < numErrorsPrinted ? count : numErrorsPrinted); i++)
+    {
+      errorData * errorEntry = &relErrors[i]; 
+      float d1 = distance(errorEntry->i,errorEntry->j,errorEntry->k);
+      float d2 = distance(errorEntry->i + errorEntry->di ,errorEntry->j + errorEntry->dj, errorEntry->k + errorEntry->dk);
+      cout << "Distance field change too large. [" << errorEntry->i << "," << errorEntry->j << "," << errorEntry->k << "] to [" << errorEntry->i + (errorEntry->di) << "," << errorEntry->j + (errorEntry->dj) << "," << errorEntry->k + (errorEntry->dk) << "]" << " Dist 1: " << d1 << " Dist 2: " << d2 << " Reported change in distance field: " << errorEntry->fieldDist << " Grid distance: " << errorEntry->gridDist << " Relative error: " << errorEntry->relError << endl;
+    }
+  }
+  return result;
+}
+
+float DistanceField::distance(Vec3d pos, int constrainToBox) const
+{
+  // get the index coordinate of the lower-right-bottom corner of the voxel containing 'pos'
+  int i = (int)((pos[0] - bmin_[0]) * invGridX);
+  int j = (int)((pos[1] - bmin_[1]) * invGridY);
+  int k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  if (((i<0) || (i>=resolutionX) || (j<0) || (j>=resolutionY) || (k<0) || (k>=resolutionZ)) && (!constrainToBox))
+  {
+    printf("Warning: querying the distance field outside of the bounding box: (i,j,k)=(%d,%d,%d), (x, y, z)=(%lf,%lf,%lf), resolution=(%d,%d,%d)\n",i, j, k, pos[0], pos[1], pos[2], resolutionX, resolutionY, resolutionZ);
+    return FLT_MAX;
+  }
+
+  if (constrainToBox)
+  {
+    if (i >= resolutionX)
+    {
+      i = resolutionX - 1;
+      pos[0] = bmax_[0];
+    }
+
+    if (i < 0)
+    {
+      i = 0;
+      pos[0] = bmin_[0];
+    }
+
+    if (j >= resolutionY)
+    {
+      j = resolutionY - 1;
+      pos[1] = bmax_[1];
+    }
+
+    if (j < 0)
+    {
+      j = 0;
+      pos[1] = bmin_[1];
+    }
+
+    if (k >= resolutionZ)
+    {
+      k = resolutionZ - 1;
+      pos[2] = bmax_[2];
+    }
+
+    if (k < 0)
+    {
+      k = 0;
+      pos[2] = bmin_[2];
+    }
+  }
+  
+  //printf("I=%d J=%d K=%d\n", i,j,k);
+  //printf("remx=%f remy=%f remz=%f\n", pos[0] - bmin_[0] - gridX * i,
+  //        pos[1] - bmin_[1] - gridY * j, pos[2] - bmin_[2] - gridZ * k);
+
+  double wx,wy,wz;
+  wx = ((pos[0]-bmin_[0]) / gridX) - i;
+  wy = ((pos[1]-bmin_[1]) / gridY) - j;
+  wz = ((pos[2]-bmin_[2]) / gridZ) - k;
+
+  return (float) (TRILINEAR_INTERPOLATION(wx,wy,wz,distance(i,j,k),distance(i+1,j,k),distance(i+1,j+1,k),distance(i,j+1,k),
+                                distance(i,j,k+1),distance(i+1,j,k+1),distance(i+1,j+1,k+1),distance(i,j+1,k+1)));
+}
+
+float DistanceField::maxValue()
+{
+  float maxValue=-FLT_MAX;
+  for (int k=0; k <= resolutionZ; k++)
+    for (int j=0; j <= resolutionY; j++)
+      for (int i=0; i <= resolutionX; i++)
+      {
+        float dist = distance(i,j,k);
+        if (dist > maxValue)
+          maxValue = dist;
+      }
+  return maxValue;
+}
+
+float DistanceField::minValue()
+{
+  float minValue=FLT_MAX;
+  for (int k=0; k <= resolutionZ; k++)
+    for (int j=0; j <= resolutionY; j++)
+      for (int i=0; i <= resolutionX; i++)
+      {
+        float dist = distance(i,j,k);
+        if (dist < minValue)
+          minValue = dist;
+      }
+  return minValue;
+}
+
+void DistanceField::maxMinValue(float* maxValue, float* minValue)
+{
+  *minValue=FLT_MAX;
+  *maxValue=-FLT_MAX;
+  for (int k=0; k <= resolutionZ; k++)
+    for (int j=0; j <= resolutionY; j++)
+      for (int i=0; i <= resolutionX; i++)
+      {
+        float dist = distance(i,j,k);
+        if (dist < *minValue)
+          *minValue = dist;
+        if (dist > *maxValue)
+          *maxValue = dist;
+      }
+}
+
+float DistanceField::maxAbsValue()
+{
+  float maxValue=0;
+  for (int k=0; k <= resolutionZ; k++)
+    for (int j=0; j <= resolutionY; j++)
+      for (int i=0; i <= resolutionX; i++)
+      {
+        float dist = fabs(distance(i,j,k));
+        if (dist > maxValue)
+          maxValue = dist;
+      }
+  return maxValue;
+}
+
+float DistanceField::maxNonInftyAbsValue()
+{
+  float maxValue=0;
+  for (int k=0; k <= resolutionZ; k++)
+    for (int j=0; j <= resolutionY; j++)
+      for (int i=0; i <= resolutionX; i++)
+      {
+        float dist = fabs(distance(i,j,k));
+        if ((dist > maxValue) && (dist != FLT_MAX))
+          maxValue = dist;
+      }
+  return maxValue;
+}
+
+float DistanceField::maxAbsValue(float threshold)
+{
+  float maxValue=0;
+  for (int k=0; k <= resolutionZ; k++)
+    for (int j=0; j <= resolutionY; j++)
+      for (int i=0; i <= resolutionX; i++)
+      {
+        float dist = fabs(distance(i,j,k));
+        if ((dist > maxValue) && (dist < threshold))
+          maxValue = dist;
+      }
+  return maxValue;
+}
+
+Vec3d DistanceField::gradient(const Vec3d& pos)
+{
+  int i,j,k;
+
+  // get the indices
+  i = (int)((pos[0] - bmin_[0]) * invGridX);
+  j = (int)((pos[1] - bmin_[1]) * invGridY);
+  k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  if ((i<=0) || (i>=resolutionX) || (j<=0) || (j>=resolutionY) || (k<=0) || (k>=resolutionZ))
+  {
+    return Vec3d(0,0,0);
+  }
+                                                                                                                                                             
+  double wx,wy,wz;
+  wx = ((pos[0]-bmin_[0]) / gridX) - i;
+  wy = ((pos[1]-bmin_[1]) / gridY) - j;
+  wz = ((pos[2]-bmin_[2]) / gridZ) - k;
+
+  // gradient with respect to trilinear interpolation
+
+  float v000 = distance(i,j,k);
+  float v100 = distance(i+1,j,k);
+  float v110 = distance(i+1,j+1,k);
+  float v010 = distance(i,j+1,k);
+  float v001 = distance(i,j,k+1);
+  float v101 = distance(i+1,j,k+1);
+  float v111 = distance(i+1,j+1,k+1);
+  float v011 = distance(i,j+1,k+1);
+
+  return Vec3d(
+    GRADIENT_COMPONENT_X(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011),
+    GRADIENT_COMPONENT_Y(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011),
+    GRADIENT_COMPONENT_Z(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) );
+                              
+}
+
+void DistanceField::getDistanceData(float ** floatBuffer)
+{
+  unsigned int size = (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
+  *floatBuffer = (float*) malloc (sizeof(float) * size);
+  memcpy(*floatBuffer, distanceData, sizeof(float) * size);
+  //for(unsigned int i=0; i< size; i++)
+    //(*floatBuffer)[i] = (float) (distanceData[i]);
+}
+
+
+/*
+void DistanceField::convertToFloat(float ** floatBuffer)
+{
+  unsigned int size = (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
+  *floatBuffer = (float*) malloc (sizeof(float) * size);
+  for(unsigned int i=0; i< size; i++)
+    (*floatBuffer)[i] = (float) (distanceData[i]);
+}
+*/
+
+/*
+void DistanceField::convertToDouble(double ** doubleBuffer)
+{
+  unsigned int size = (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
+  *doubleBuffer = (double*) malloc (sizeof(double) * size);
+  memcpy(*doubleBuffer, distanceData, sizeof(double) * size);
+}
+*/
+
+bool DistanceField::isSurfaceVoxel(int i, int j, int k)
+{
+  float v[8];
+  v[0] = distance(i,j,k);
+  v[1] = distance(i+1,j,k);
+  v[2] = distance(i+1,j+1,k);
+  v[3] = distance(i,j+1,k);
+  v[4] = distance(i,j,k+1);
+  v[5] = distance(i+1,j,k+1);
+  v[6] = distance(i+1,j+1,k+1);
+  v[7] = distance(i,j+1,k+1);
+
+  bool allPositive = true;
+  for(int l=0; l<8; l++)
+    if (v[l] < 0)
+    {
+      allPositive = false;
+      break;
+    }
+
+  bool allNegative = true;
+  for(int l=0; l<8; l++)
+    if (v[l] >= 0)
+    {
+      allNegative = false;
+      break;
+    }
+
+  return (!allNegative && !allPositive);
+}
+
+bool DistanceField::isSurfaceVoxel(int customResolutionX, int customResolutionY, int customResolutionZ, int i, int j, int k, float levelSetValue)
+{
+  //printf("i: %d j:%d k:%d\n",i,j,k);
+  Vec3d customGrid;
+  customGrid[0] = 1.0 * side[0] / customResolutionX;
+  customGrid[1] = 1.0 * side[1] / customResolutionY;
+  customGrid[2] = 1.0 * side[2] / customResolutionZ;
+  
+  Vec3d basePos = bmin_ + Vec3d( 1.0 * i * customGrid[0], 1.0 * j * customGrid[1], 1.0 * k * customGrid[2] );
+
+  if (((i < 0) || (i >= customResolutionX)) && ((j < 0) || (j >= customResolutionY)) && ((k < 0) || (k >= customResolutionZ)))
+  {
+    printf("Warning (inside isSurfaceVoxel): voxel insides specified outside of the bounding box: (i,j,k)=(%d,%d,%d), customResolution=(%d,%d,%d)\n", i, j, k, customResolutionX, customResolutionY, customResolutionZ);
+  }
+
+  // pass parameter 1 to constrain to box
+  float v[8];
+  v[0] = distance(basePos, 1) - levelSetValue;
+  v[1] = distance(basePos + Vec3d(customGrid[0], 0, 0), 1) - levelSetValue;
+  v[2] = distance(basePos + Vec3d(customGrid[0], customGrid[1], 0), 1) - levelSetValue;
+  v[3] = distance(basePos + Vec3d(0, customGrid[1], 0), 1) - levelSetValue;
+  v[4] = distance(basePos + Vec3d(0, 0, customGrid[2]), 1) - levelSetValue;
+  v[5] = distance(basePos + Vec3d(customGrid[0], 0, customGrid[2]), 1) - levelSetValue;
+  v[6] = distance(basePos + Vec3d(customGrid[0], customGrid[1], customGrid[2]), 1) - levelSetValue;
+  v[7] = distance(basePos + Vec3d(0, customGrid[1], customGrid[2]), 1) - levelSetValue;
+
+/*
+  double v[8];
+  v[0] = distance(i,j,k);
+  v[1] = distance(i+1,j,k);
+  v[2] = distance(i+1,j+1,k);
+  v[3] = distance(i,j+1,k);
+  v[4] = distance(i,j,k+1);
+  v[5] = distance(i+1,j,k+1);
+  v[6] = distance(i+1,j+1,k+1);
+  v[7] = distance(i,j+1,k+1);
+*/
+
+  bool allPositive = true;
+  for(int l=0; l<8; l++)
+    if (v[l] < 0)
+    {
+      allPositive = false;
+      break;
+    }
+
+  bool allNegative = true;
+  for(int l=0; l<8; l++)
+    if (v[l] >= 0)
+    {
+      allNegative = false;
+      break;
+    }
+
+  return (!allNegative && !allPositive);
+}
+
+int DistanceField::numSurfaceVoxels(float levelSetValue)
+{
+  return numSurfaceVoxels(resolutionX, resolutionY, resolutionZ, levelSetValue);
+}
+
+int DistanceField::numSurfaceVoxels(int customResolutionX, int customResolutionY, int customResolutionZ, float levelSetValue)
+{
+  printf("num surface voxels... custom res: (%d,%d,%d)\n", customResolutionX, customResolutionY, customResolutionZ);
+  int count = 0;
+
+  for(int k=0; k<customResolutionZ; k++)
+    for(int j=0; j<customResolutionY; j++)
+      for(int i=0; i<customResolutionX; i++)
+        if (isSurfaceVoxel(customResolutionX, customResolutionY, customResolutionZ, i, j, k, levelSetValue))
+          count++;
+
+  return count;
+}
+
+float DistanceField::computeMinBoundaryValue()
+{
+  float minDistance = FLT_MAX;
+  for(int k=0; k <= resolutionZ; k++)
+    for(int j=0; j <= resolutionY; j++)
+      for(int i=0; i <= resolutionX; i += resolutionX)
+      {
+        float value = distance(i,j,k);
+        if (value < minDistance)
+          minDistance = value;
+      }
+
+  for(int k=0; k <= resolutionZ; k++)
+    for(int j=0; j <= resolutionY; j += resolutionY)
+      for(int i=0; i <= resolutionX; i++)
+      {
+        float value = distance(i,j,k);
+        if (value < minDistance)
+          minDistance = value;
+      }
+
+  for(int k=0; k <= resolutionZ; k += resolutionZ)
+    for(int j=0; j <= resolutionY; j++)
+      for(int i=0; i <= resolutionX; i++)
+      {
+        float value = distance(i,j,k);
+        if (value < minDistance)
+          minDistance = value;
+      }
+
+  minBoundaryDistance = minDistance;
+  return minDistance;
+}
+
+void DistanceField::print()
+{
+  for(int k=0; k <= resolutionX; k++)
+    for(int j=0; j <= resolutionY; j++)
+      for(int i=0; i <= resolutionZ; i++)
+      {
+        printf("i=%d j=%d k=%d d=%G\n", i, j, k, distance(i,j,k));
+      }
+}
+
+void DistanceField::computeFloodFillTag(ObjMesh* objMeshIn)
+{
+  ObjMesh objMesh(*objMeshIn);
+  vegalong size = sizeof(char) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
+  if (floodFillTag != NULL)
+    free(floodFillTag);
+  floodFillTag = (char*) malloc(size);  
+  memset(floodFillTag, 0, sizeof(char) * (resolutionX+1) * (resolutionY+1) * (resolutionZ+1));
+
+  typedef triple<int,int,int> voxel;
+
+  std::set<voxel> checkedVoxels;
+  std::vector<voxel> scheduledVoxels;
+
+  for(unsigned int i=0; i < objMesh.getNumGroups(); ++i)
+  {
+    const ObjMesh::Group * group = objMesh.getGroupHandle(i);
+
+    for (unsigned int j=0; j<group->getNumFaces(); ++j)
+    {
+      Vec3d p0 = objMesh.getPosition(group->getFace(j).getVertex(0).getPositionIndex());
+      Vec3d p1 = objMesh.getPosition(group->getFace(j).getVertex(1).getPositionIndex());
+      Vec3d p2 = objMesh.getPosition(group->getFace(j).getVertex(2).getPositionIndex());
+
+      TriangleBasic triangle(p0,p1,p2);
+      
+      Vec3d center = 1.0/3 * (p0 + p1 + p2);
+      Vec3d recCenter = center - bmin_;
+
+      int vi,vj,vk;
+
+      vi = (int)(recCenter[0] / gridX);
+      vj = (int)(recCenter[1] / gridY);
+      vk = (int)(recCenter[2] / gridZ);
+
+      checkedVoxels.clear();
+      checkedVoxels.insert(voxel(vi, vj, vk));
+
+      scheduledVoxels.clear();
+      scheduledVoxels.push_back(voxel(vi,vj,vk));
+
+      while (!scheduledVoxels.empty())
+      {
+        voxel v = scheduledVoxels.back();
+        scheduledVoxels.pop_back();
+
+        Vec3d bbmin = bmin_ + Vec3d(v.first * gridX, v.second * gridY, v.third * gridZ);
+        Vec3d bbmax = bbmin + Vec3d(gridX, gridY, gridZ);
+
+        BoundingBox bbox(bbmin, bbmax);
+
+        if (triangle.doesIntersectBox(bbox))
+        {
+          //surfaceVoxels.insert(v);
+          //floodFillTag[(v.third * (resolutionY+1) + v.second) * (resolutionX + 1) + v.first] = 1;
+
+          voxel neighbor;
+          vegalong index;
+          #define TAGNEIGHBOR(ii,jj,kk)\
+          neighbor = voxel(v.first+(ii), v.second+(jj), v.third+(kk));\
+          if ((neighbor.first <= resolutionX) &&\
+              (neighbor.second <= resolutionY) &&\
+              (neighbor.third <= resolutionZ))\
+          {\
+            index = (neighbor.third * (resolutionY+1) + neighbor.second) * (resolutionX + 1) + neighbor.first;\
+            floodFillTag[index] = 1;\
+          }
+
+          for (int kkk=0; kkk<=1; ++kkk)
+            for (int jjj=0; jjj<=1; ++jjj)
+              for (int iii=0; iii<=1; ++iii)
+              {
+                TAGNEIGHBOR(iii, jjj, kkk);
+              }
+
+
+          #define CHECKNEIGHBOR(ii,jj,kk)\
+          neighbor = voxel(v.first+(ii), v.second+(jj), v.third+(kk));\
+          if ((neighbor.first >= 0) && (neighbor.first <= resolutionX) &&\
+              (neighbor.second >= 0) && (neighbor.second <= resolutionY) &&\
+              (neighbor.third >= 0) && (neighbor.third <= resolutionZ))\
+              {\
+            if (checkedVoxels.find(neighbor) == checkedVoxels.end())\
+            {\
+              checkedVoxels.insert(neighbor);\
+              scheduledVoxels.push_back(neighbor);\
+            }\
+          }
+
+          for (int kkk=-1; kkk<=1; ++kkk)
+            for (int jjj=-1; jjj<=1; ++jjj)
+              for (int iii=-1; iii<=1; ++iii)
+              {
+                if ((iii == 0) && (jjj == 0) && (kkk == 0))
+                  continue;
+
+                CHECKNEIGHBOR(iii, jjj, kkk);
+              }
+        }
+      }
+    }
+  }
+}
+
+double DistanceField::pointCCD(Vec3d startPos, Vec3d endPos)
+{
+  int constrainToBox = 0;
+  double startDist = distance(startPos, constrainToBox);
+  double endDist = distance(endPos, constrainToBox);
+
+  if (((startDist > 0) && (endDist > 0)) || (startDist - endDist == 0))
+    return -1.0;
+
+  int flip = 0;
+  if (startDist > 0)
+  {
+    flip = 1;
+    Vec3d bufv = startPos;
+    startPos = endPos;
+    endPos = bufv;
+    double buf = startDist;
+    startDist = endDist;
+    endDist = buf;
+  }
+
+  // now, startPos is inside, and endPos is outside
+
+  double eps = 1E-12;
+  double d;
+  Vec3d x;
+  double t = 0.0;
+  double tLo = 0.0;
+  double tHi = 1.0;
+  Vec3d xLo = startPos;
+  Vec3d xHi = endPos;
+  double distLo = startDist;
+  double distHi = endDist;
+  do
+  {
+    // distLo + deltaT * (distHi - distLo) = 0
+    t += distLo / (distLo - distHi) * (tHi - tLo);
+    x = startPos + t * (endPos - startPos);
+    d = distance(x, constrainToBox);
+
+    if (d <= 0)
+    {
+      // lo must go to new point
+      tLo = t;
+      xLo = x;
+      distLo = d;
+    }
+    else
+    {
+      // hi must go to new point
+      tHi = t;
+      xHi = x;
+      distHi = d;
+    }
+  }
+  while (fabs(d) > eps);
+
+  if (flip)
+    t = 1.0 - t;
+
+  return t;
+}
+
+void DistanceField::offsetDistanceField(double offset)
+{
+  vegalong numGridPoints = (resolutionX+1) * (resolutionY+1) * (resolutionZ+1);
+
+  printf("Applying offset %G to the distance field. Resolution is %d x %d x %d.\n", offset, resolutionX, resolutionY, resolutionZ);
+
+  for(vegalong i=0; i<numGridPoints; i++)
+    distanceData[i] += (float) offset;
+}
+
diff --git a/libraries/distanceField/distanceField.h b/libraries/distanceField/distanceField.h
new file mode 100644
index 0000000000000000000000000000000000000000..7296079ebce867f170baaf78843011fbd4d6fd29
--- /dev/null
+++ b/libraries/distanceField/distanceField.h
@@ -0,0 +1,219 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This code generates a distance filed, either signed or unsigned, 
+  to the given triangle mesh.
+
+  The distance field can be loaded/saved to a file.
+  You can also lookup the field (once computed or loaded) at arbitrary 
+  locations inside the field, using trilinear interpolation.
+
+  Input mesh need not have triangle faces (e.g., can have quads; 
+  they will be triangulated).
+
+  For signed field generation, the input mesh must be a closed manifold mesh.
+
+  Input mesh must be given in the .obj format:
+  http://www.royriggs.com/obj.html
+
+  By default, the bounding box will be a cube, obtained by fitting the  
+  smallest cube to the geometry, and then expanded (scaled) from its 
+  center by a factor of 1.5. You can provide your own bounding boxes. 
+  However, note: (1) the provided bounding box must cover all the geometry, 
+  and (2) bounding boxes that are not cubes were not (yet) tested.
+
+  The bounding box will be divided into a "resolution" number of 
+  cubes ("voxels") along each axis. The distance field will be computed at the 
+  vertices of these voxels. So, if resolution is 256, the bounding box 
+  will get divided into 256 x 256 x 256 voxels, and the distance field 
+  will be computed on the resulting 257 x 257 x 257 grid of voxel vertices. 
+  Note that when indexing voxels, the indices (i,j,k) will run from 0 to 255 
+  inclusive, whereas when indexing voxel vertices (also called "grid vertices"), 
+  they will run from 0 to 256 inclusive.
+
+  Distance field data is stored at voxel vertices. 
+  In memory, distance field value at voxel vertex (i,j,k) is stored 
+  at location k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i .
+
+  Internally, the code builds an octree on top of the triangle mesh. 
+  There are two parameters that control this process (you can keep them 
+  at default values, which worked well in practice for us) :
+  the max depth of the octree is "maxDepth", and
+  the max number of triangles intersecting an octree cell is "maxTriCount".
+  Note: once max depth level is reached, the maxTriCount bound is not imposed any more.
+*/
+
+#ifndef _DISTANCEFIELD_H_
+#define _DISTANCEFIELD_H_
+
+#include "objMesh.h"
+#include "objMeshOrientable.h"
+#include "objMeshOctree.h"
+#include "distanceFieldBase.h"
+#include "vegalong.h"
+
+class DistanceField : public DistanceFieldBase
+{
+public:
+
+  DistanceField();
+  virtual ~DistanceField();
+
+  DistanceField(int resolutionX, int resolutionY, int resolutionZ);
+
+  // computes unsigned distance field
+  virtual int computeUnsignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ,int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+
+  // computes signed distance field
+  virtual int computeSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+
+  // computes signed distance field using flood fill
+  // must call computeUnsignedField for the same objMesh and same resolution first
+  virtual int computeFloodFillSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+
+  void enableVoronoiDiagramComputation(bool computeVoronoiDiagram);
+
+  // loads a previously computed distance field from a disk file
+  virtual int load(const std::string& filename); // returns 0 on success
+
+  // opens the distance field for stream processing
+  // this is advanced routine, don't use in normal circumstances; use "load" instead
+  int openStreamDistanceField(const std::string& filename, Vec3d * bmin, Vec3d * bmax, int * resolutionX, int * resolutionY, int * resolutionZ, bool * floatData, std::ifstream & fin);
+  void retrieveZSlice(std::ifstream & fin, bool floatData, int resolutionX, int resolutionY, int resolutionZ, float * slice);
+
+  // saves the current distance field to a disk file (e.g. after computing it once, for later fast reloading) 
+  virtual int save(const std::string& filename, bool doublePrecision);
+  int saveVoronoiDiagram(const std::string& filename);
+
+  // exports the distance field to a file, in text format
+  virtual int saveToText(const std::string& filename);
+
+  // sets the distance field to the given external data
+  virtual void set(int resolutionX, int resolutionY, int resolutionZ, Vec3d bmin_, Vec3d bmax_, float * distanceData);
+
+  // Is data from the given file in single or double precision?
+  // note: this class uses single precision everywhere, but some older code
+  // used double precision, so routines were necessary to load binary distance field files computed using old versions
+  int isDoublePrecision(const std::string & filename, bool & doublePrecision);
+
+  // return distance field value at grid vertex (i,j,k)
+  // each of i,j,k must be an integer from {0, ..., resolution{X,Y,Z}}
+  virtual inline float distance(int i, int j, int k) const { return distanceData[(k * (resolutionY + 1) + j ) * (resolutionX + 1) + i]; }
+  // computes distance and gradient at arbitrary position
+  virtual float distance(Vec3d pos, int constrainToBox=0) const;
+
+  // alters the distance at a particular grid vertex (i,j,k)
+  virtual inline void setDistance(int i, int j, int k, float value) { distanceData[(k * (resolutionY + 1) + j ) * (resolutionX + 1) + i] = value; }
+
+  // gradient is computed with respect to trilinear interpolation
+  // note: gradient is discontinuous at the cell boundaries
+  virtual Vec3d gradient(const Vec3d& pos);
+
+  virtual bool sanityCheck(); // checks if distance for any two adjacent voxels is less than voxel grid spacing apart (which it must be by triangle inequality, for both signed and unsigned fields)
+
+  virtual float maxValue();
+  virtual float minValue();
+  virtual void  maxMinValue(float* maxValue, float* minValue);
+
+  virtual float maxAbsValue();
+  virtual float maxAbsValue(float threshold); // only abs values up to threshold
+  virtual float maxNonInftyAbsValue();
+
+  // compute and return minimum distance value on the surface of the bounding box
+  // the value is also saved so that it can be retrieved by calling getMinBoundaryValue()
+  float computeMinBoundaryValue(); 
+  float getMinBoundaryValue() { return minBoundaryDistance;}
+
+  // returns the entire distance data, by allocating the buffer and copying the distance data into it 
+  virtual void getDistanceData(float ** floatBuffer);
+
+  // returns the pointer to the internally allocated data
+  const float * getDistanceDatap() const { return distanceData; }
+  float * getDistanceDatap() { return distanceData; }
+
+  // Is voxel with indices (i,j,k) intersecting the zero-isocontour? (only applies to signed distance fields)
+  bool isSurfaceVoxel(int i, int j, int k); 
+  // total number of such voxels
+  int numSurfaceVoxels(float levelSetValue = 0.0); 
+  // If distance field were resampled to customResolution, is voxel with indices (i,j,k) intersecting the isocontour with value levelSetValue ?
+  bool isSurfaceVoxel(int customResolutionX, int customResolutionY, int customResolutionZ, int i, int j, int k, float levelSetValue);
+  // total number of such voxels
+  int numSurfaceVoxels(int customResolutionX, int customResolutionY, int customResolutionZ, float levelSetValue); 
+
+  // sets the computation range for the z-coordinate (by default, the entire z-range will be computed)
+  inline void setComputationZRange(int zMin, int zMax);
+
+  virtual void print();
+
+  virtual int ZigZagSigned(void * objMeshOctree, void * meshGraph);
+  virtual int ZigZagUnsigned(void * objMeshOctree, void * meshGraph);
+  virtual int ZigZagFloodFillSigned(void * objMeshOctree, void * meshGraph);
+
+  virtual int ZigZagSigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+  virtual int ZigZagUnsigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+  virtual int ZigZagFloodFillSigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+
+  void computeFloodFillTag(ObjMesh* objMesh);
+  void freeMemory(){free(distanceData); distanceData = NULL;};
+
+ // returns earliest contact time assuming point travels with constant velocity
+  // returns -1.0 if there is no contact
+  double pointCCD(Vec3d startPos, Vec3d endPos);
+
+  virtual void offsetDistanceField(double offset);
+protected:
+  int maxTriCount;
+  int maxDepth;
+ 
+  float * distanceData; // the raw distance data
+  float * pseudoData; // for debug
+
+  char * floodFillTag;
+  int * voronoiDiagram; 
+  bool computeVoronoiDiagram;
+
+  int zMin, zMax;
+  //ObjMeshOctree<TriangleWithCollisionInfo> * objMeshOctree;
+  //ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals> * objMeshOrientedOctree;
+  float minBoundaryDistance;
+  vegalong GetFilesize(const char *filename);
+};
+
+inline void DistanceField::setComputationZRange(int zMin, int zMax)
+{
+  this->zMin = zMin;
+  this->zMax = zMax;
+}
+
+#endif
+
diff --git a/libraries/distanceField/distanceFieldBase.cpp b/libraries/distanceField/distanceFieldBase.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4133dd598eb666bd043a50d880c0d303527e5b74
--- /dev/null
+++ b/libraries/distanceField/distanceFieldBase.cpp
@@ -0,0 +1,151 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This code generates a distance filed, either signed or unsigned, 
+  to the given triangle mesh.
+
+  The distance field can be loaded/saved to a file.
+  You can also lookup the field (once computed or loaded) at arbitrary 
+  locations inside the field, using trilinear interpolation.
+
+  Input mesh need not have triangle faces (e.g., can have quads; 
+  they will be triangulated).
+
+  For signed field generation, the input mesh must be a closed manifold mesh.
+
+  Input mesh must be given in the .obj format:
+  http://www.royriggs.com/obj.html
+
+  By default, the bounding box will be a cube, obtained by fitting the  
+  smallest cube to the geometry, and then expanded (scaled) from its 
+  center by a factor of 1.5. You can provide your own bounding boxes. 
+  However, note: (1) the provided bounding box must cover all the geometry, 
+  and (2) bounding boxes that are not cubes were not (yet) tested.
+
+  The bounding box will be divided into a "resolution" number of 
+  cubes ("voxels") along each axis. The distance field will be computed at the 
+  vertices of these voxels. So, if resolution is 256, the bounding box 
+  will get divided into 256 x 256 x 256 voxels, and the distance field 
+  will be computed on the resulting 257 x 257 x 257 grid of voxel vertices. 
+  Note that when indexing voxels, the indices (i,j,k) will run from 0 to 255 
+  inclusive, whereas when indexing voxel vertices (also called "grid vertices"), 
+  they will run from 0 to 256 inclusive.
+
+  Distance field data is stored at voxel vertices. 
+  In memory, distance field value at voxel vertex (i,j,k) is stored 
+  at location k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i .
+
+  Internally, the code builds an octree on top of the triangle mesh. 
+  There are two parameters that control this process (you can keep them 
+  at default values, which worked well in practice for us) :
+  the max depth of the octree is "maxDepth", and
+  the max number of triangles intersecting an octree cell is "maxTriCount".
+  Note: once max depth level is reached, the maxTriCount bound is not imposed any more.
+*/
+
+#include <iostream>
+#include "objMeshOrientable.h"
+#include "objMeshOctree.h"
+#include "distanceFieldBase.h"
+using namespace std;
+
+DistanceFieldBase::DistanceFieldBase() 
+{
+  resolutionX = 1;
+  resolutionY = 1;
+  resolutionZ = 1;
+  setAutomaticBoundingBox();
+  bmin_ = Vec3d(0,0,0);
+  bmax_ = Vec3d(1,1,1);
+  setGridParameters();
+  bboxComputed = false;
+}
+
+DistanceFieldBase::DistanceFieldBase(int resolutionX_, int resolutionY_, int resolutionZ_) : resolutionX(resolutionX_), resolutionY(resolutionY_), resolutionZ(resolutionZ_)
+{
+  setAutomaticBoundingBox();
+  bmin_ = Vec3d(0,0,0);
+  bmax_ = Vec3d(1,1,1);
+  setGridParameters();
+  bboxComputed = false;
+}
+
+void DistanceFieldBase::computeBoundingBox(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ)
+{
+  Vec3d bminTemp, bmaxTemp;
+  objMesh->getBoundingBox(1.0, &bminTemp, &bmaxTemp);
+
+  // set aspect ratio that corresponds to the resolutions
+  Vec3d bcenterTemp = 0.5 * (bminTemp + bmaxTemp);
+
+  cout << "Tight bounding box:" << endl << "  " << bminTemp << endl << "  " << bmaxTemp << endl;
+
+  Vec3d sideTemp = bmaxTemp - bminTemp;
+  if (sideTemp[0] / resolutionX < sideTemp[1] / resolutionY)
+  {
+    // increase x
+    sideTemp[0] = sideTemp[1] / resolutionY * resolutionX;
+  }
+  else
+  {
+    // increase y
+    sideTemp[1] = sideTemp[0] / resolutionX * resolutionY;
+  }
+
+  // now x,y are ok, must adjust z
+  if (sideTemp[1] / resolutionY < sideTemp[2] / resolutionZ)
+  {
+    // increase x and y
+    double factor = (sideTemp[2] / resolutionZ * resolutionY) / sideTemp[1];
+    sideTemp[1] *= factor;
+    sideTemp[0] *= factor;
+  }
+  else
+  {
+    // increase z
+    sideTemp[2] = sideTemp[1] / resolutionY * resolutionZ;
+  }
+
+  BoundingBox bbox(bminTemp, bmaxTemp);
+  bbox.setbmin(bcenterTemp - 0.5 * sideTemp);
+  bbox.setbmax(bcenterTemp + 0.5 * sideTemp);
+  if (allBoxSidesEqual)
+    bbox.regularize();
+  bbox.expand(expansionRatio);
+
+  bmin_ = bbox.bmin();
+  bmax_ = bbox.bmax();
+
+  bboxComputed = true;
+}
+
diff --git a/libraries/distanceField/distanceFieldBase.h b/libraries/distanceField/distanceFieldBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..6b2f1b2eb54d01c2454bb0ad8136e06643bee58b
--- /dev/null
+++ b/libraries/distanceField/distanceFieldBase.h
@@ -0,0 +1,317 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This code generates a distance filed, either signed or unsigned, 
+  to the given triangle mesh.
+
+  The distance field can be loaded/saved to a file.
+  You can also lookup the field (once computed or loaded) at arbitrary 
+  locations inside the field, using trilinear interpolation.
+
+  Input mesh need not have triangle faces (e.g., can have quads; 
+  they will be triangulated).
+
+  For signed field generation, the input mesh must be a closed manifold mesh.
+
+  Input mesh must be given in the .obj format:
+  http://www.royriggs.com/obj.html
+
+  By default, the bounding box will be a cube, obtained by fitting the  
+  smallest cube to the geometry, and then expanded (scaled) from its 
+  center by a factor of 1.5. You can provide your own bounding boxes. 
+  However, note: (1) the provided bounding box must cover all the geometry, 
+  and (2) bounding boxes that are not cubes were not (yet) tested.
+
+  The bounding box will be divided into a "resolution" number of 
+  cubes ("voxels") along each axis. The distance field will be computed at the 
+  vertices of these voxels. So, if resolution is 256, the bounding box 
+  will get divided into 256 x 256 x 256 voxels, and the distance field 
+  will be computed on the resulting 257 x 257 x 257 grid of voxel vertices. 
+  Note that when indexing voxels, the indices (i,j,k) will run from 0 to 255 
+  inclusive, whereas when indexing voxel vertices (also called "grid vertices"), 
+  they will run from 0 to 256 inclusive.
+
+  Distance field data is stored at voxel vertices. 
+  In memory, distance field value at voxel vertex (i,j,k) is stored 
+  at location k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i .
+
+  Internally, the code builds an octree on top of the triangle mesh. 
+  There are two parameters that control this process (you can keep them 
+  at default values, which worked well in practice for us) :
+  the max depth of the octree is "maxDepth", and
+  the max number of triangles intersecting an octree cell is "maxTriCount".
+  Note: once max depth level is reached, the maxTriCount bound is not imposed any more.
+*/
+
+#ifndef _DISTANCEFIELDBASE_H_
+#define _DISTANCEFIELDBASE_H_
+
+#include "objMesh.h"
+#include <algorithm>
+
+class DistanceFieldBase
+{
+public:
+
+  DistanceFieldBase();
+  virtual ~DistanceFieldBase() {};
+
+  DistanceFieldBase(int resolutionX, int resolutionY, int resolutionZ);
+
+  // sets the bounding box within which the distance field will be computed
+  // set it before calling computeSigned/UnsignedField
+  void setBoundingBox(const Vec3d & bmin, const Vec3d & bmax);
+  void getBoundingBox(Vec3d & bmin, Vec3d & bmax) const;
+  // set a tight-fitting bounding box around the model, and expand it by the expansion ratio given
+  // note: if you don't set any bounding boxes, you get an automaticBoundingBox with 1.5 expansion ratio by default
+  void setAutomaticBoundingBox(bool allBoxSidesEqual=true, double expansionRatio=1.5);
+  
+  // compute bounding box with uniform cube voxels, of the specified resolution
+  // this routine uses the parameters set by the "setAutomaticBoundingBox" 
+  // if allBoxSidesEqual=true, the voxels will not necessarily be cubes
+  void computeBoundingBox(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ);
+
+  // loads a previously computed distance field from a disk file
+  virtual int load(const std::string& filename) = 0; // returns 0 on success
+
+  // saves the current distance field to a disk file (e.g. after computing it once, for later fast reloading) 
+  virtual int save(const std::string& filename, bool doublePrecision) = 0;
+
+  // exports the distance field to a file, in text format
+  virtual int saveToText(const std::string& filename) = 0;
+
+  // return distance field value at grid vertex (i,j,k)
+  // each of i,j,k must be an integer from {0, ..., resolution{X,Y,Z}}
+  virtual float distance(int i, int j, int k) const = 0;
+  // computes distance and gradient at arbitrary position
+  virtual float distance(Vec3d pos, int constrainToBox=0) const = 0;
+  // alters the distance at a particular grid vertex (i,j,k)
+  virtual void setDistance(int i, int j, int k, float value) = 0;
+  
+  // gradient is computed with respect to trilinear interpolation
+  // note: gradient is discontinuous at the cell boundaries
+  virtual Vec3d gradient(const Vec3d& pos) = 0;
+
+  // pack/unpack index
+  inline int packedVoxelIndex(int i, int j, int k) const; // returns a 32-bit unique voxel index
+  inline int packedVoxelIndex(const Vec3d & pos) const; // returns a 32-bit unique voxel index, for voxel constaining pos
+  inline void unpackVoxelIndex(int packedIndex, int & i, int & j, int & k) const;
+  
+  inline void voxelIndices(const Vec3d & pos, int * i, int * j, int * k) const; // returns indices of voxel containing pos
+
+  inline bool validGridIndex(int i, int j, int k); // tells whether the grid index is valid: 0<=i<=resolutionX, 0<=j<=resolutionY, 0<=z<=resolutionZ
+  inline bool validVoxelIndex(int i, int j, int k);// tells whether the voxel index is valid: 0<=i<resolutionX, 0<=j<resolutionY, 0<=z<resolutionZ
+
+  inline bool insideBox(const Vec3d & pos) const; // tells whether the pos is inside box or not
+  
+  // returns the world-coordinate position of the grid vertex with indices (i,j,k)
+  // must have: 0 <= i,j,k <= resolution{X,Y,Z}   (i,j,k of course not necessarily sorted)
+  inline Vec3d getGridPosition(int i, int j, int k) const;
+
+  // /set/get distance field resolution
+  inline void setResolution(int resolutionX, int resolutionY, int resolutionZ) { this->resolutionX = resolutionX; this->resolutionY = resolutionY; this->resolutionZ = resolutionZ; setGridParameters(); }
+  inline int getResolutionX() const { return resolutionX; }
+  inline int getResolutionY() const { return resolutionY; }
+  inline int getResolutionZ() const { return resolutionZ; }
+
+  // get the diagonal of the bounding box
+  inline double diagonal() const { return len(bmax_-bmin_);}
+
+  // get the lower-left-front corner of bounding box
+  inline const Vec3d & bmin() const { return bmin_; }
+  // get the upper-right-back corner of bounding box
+  inline const Vec3d & bmax() const { return bmax_; }
+  // alternative interface to bmin, bmax functions above
+  inline void bmin(float * bmin) const { bmin[0] = (float) bmin_[0]; bmin[1] = (float) bmin_[1]; bmin[2] = (float) bmin_[2]; }
+  inline void bmax(float * bmax) const { bmax[0] = (float) bmax_[0]; bmax[1] = (float) bmax_[1]; bmax[2] = (float) bmax_[2]; }
+  inline void bmin(double * bmin) const { bmin[0] = bmin_[0]; bmin[1] = bmin_[1]; bmin[2] = bmin_[2]; }
+  inline void bmax(double * bmax) const { bmax[0] = bmax_[0]; bmax[1] = bmax_[1]; bmax[2] = bmax_[2]; }
+
+  // returns the spatial dimensions of the voxels
+  // i.e., this is the spatial distance between consecutive grid vertices along the three dimensions
+  void getGridSpacing(double * gridX, double * gridY, double * gridZ) const;
+  void getInvGridSpacing(double * invGridX, double * invGridY, double * invGridZ) const;
+
+  // conversion between absolute units to grid units
+  double absoluteUnitsToGridUnits(double absoluteUnits) const;
+  double gridUnitsToAbsoluteUnits(double gridUnits) const;
+
+  virtual bool sanityCheck() = 0; // checks if distance for any two adjacent voxels is less than voxel grid spacing apart (which it must be by triangle inequality, for both signed and unsigned fields)
+
+  virtual float maxValue() = 0;
+  virtual float minValue() = 0;
+
+  virtual float maxAbsValue() = 0;
+  virtual float maxAbsValue(float threshold) = 0; // only abs values up to threshold
+  virtual float maxNonInftyAbsValue() = 0;
+
+  //virtual void print() = 0;
+
+  virtual void offsetDistanceField(double offset) = 0;
+
+protected:
+  // set side, gridX, girdY, gridZ, invGridX, invGridY, invGridZ
+  void setGridParameters();
+
+  int resolutionX, resolutionY, resolutionZ;
+  Vec3d bmin_, bmax_;
+  Vec3d side;
+  double gridX, gridY, gridZ;           // spatial distance between consecutive grid vertices along the three dimensions
+  double invGridX, invGridY, invGridZ;  // inverse of gridX/Y/Z
+
+  bool useAutomaticBox;
+  double expansionRatio;
+  bool allBoxSidesEqual;
+
+  bool bboxComputed;
+  //float minBoundaryDistance;
+};
+
+
+inline void DistanceFieldBase::getBoundingBox(Vec3d & bmin, Vec3d & bmax) const
+{
+  bmin = bmin_;
+  bmax = bmax_;
+}
+
+inline void DistanceFieldBase::setBoundingBox(const Vec3d& bmin, const Vec3d& bmax)
+{
+  useAutomaticBox = false;
+  bmin_ = bmin;
+  bmax_ = bmax;
+  setGridParameters();
+}
+
+inline void DistanceFieldBase::setGridParameters() 
+{
+  side = bmax_ - bmin_;
+  gridX = side[0] / resolutionX;
+  gridY = side[1] / resolutionY;
+  gridZ = side[2] / resolutionZ;
+  invGridX = 1.0 / gridX;
+  invGridY = 1.0 / gridY;
+  invGridZ = 1.0 / gridZ;
+}
+
+inline void DistanceFieldBase::setAutomaticBoundingBox(bool allBoxSidesEqual, double expansionRatio)
+{
+  useAutomaticBox = true;
+  this->expansionRatio = expansionRatio;
+  this->allBoxSidesEqual = allBoxSidesEqual;
+}
+
+inline void DistanceFieldBase::getGridSpacing(double * gridX, double * gridY, double * gridZ) const
+{
+  *gridX = this->gridX;
+  *gridY = this->gridY;
+  *gridZ = this->gridZ;
+}
+
+inline void DistanceFieldBase::getInvGridSpacing(double * invGridX, double * invGridY, double * invGridZ) const
+{
+  *invGridX = this->invGridX;
+  *invGridY = this->invGridY;
+  *invGridZ = this->invGridZ;
+}
+
+inline Vec3d DistanceFieldBase::getGridPosition(int i, int j, int k) const
+{
+ return Vec3d (bmin_[0] + i * gridX, bmin_[1] + j * gridY, bmin_[2] + k * gridZ);
+}
+
+
+inline void DistanceFieldBase::voxelIndices(const Vec3d& pos, int * i, int * j, int * k) const // returns indices of voxel containing pos
+{
+  *i = (int)((pos[0] - bmin_[0]) * invGridX);
+  *j = (int)((pos[1] - bmin_[1]) * invGridY);
+  *k = (int)((pos[2] - bmin_[2]) * invGridZ);
+}
+
+
+inline bool DistanceFieldBase::insideBox(const Vec3d& pos) const
+{
+  int i = (int)((pos[0] - bmin_[0]) * invGridX);
+  int j = (int)((pos[1] - bmin_[1]) * invGridY);
+  int k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  return ((i >= 0) && (i < resolutionX) && (j >= 0) && (j < resolutionY) && (k >= 0) && (k < resolutionZ));
+}
+
+inline bool DistanceFieldBase::validGridIndex(int i, int j, int k)
+{
+  return ((i >= 0) && (i <= resolutionX) && (j >= 0) && (j <= resolutionY) && (k >= 0) && (k <= resolutionZ));
+}
+
+inline bool DistanceFieldBase::validVoxelIndex(int i, int j, int k)
+{
+  return ((i >= 0) && (i < resolutionX) && (j >= 0) && (j < resolutionY) && (k >= 0) && (k < resolutionZ));
+}
+
+inline int DistanceFieldBase::packedVoxelIndex(const Vec3d & pos) const
+{
+  // get the indices
+  int i = (int)((pos[0] - bmin_[0]) * invGridX);
+  int j = (int)((pos[1] - bmin_[1]) * invGridY);
+  int k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  return (k * resolutionY + j ) * resolutionX + i;
+}
+
+inline int DistanceFieldBase::packedVoxelIndex(int i, int j, int k) const
+{
+  return (k * resolutionY + j ) * resolutionX + i;
+}
+
+inline void DistanceFieldBase::unpackVoxelIndex(int packedIndex, int & i, int & j, int & k) const
+{
+  i = packedIndex % resolutionX;
+  packedIndex = packedIndex / resolutionX;
+  j = packedIndex % resolutionY;
+  k = packedIndex / resolutionY;
+}
+
+inline double DistanceFieldBase::absoluteUnitsToGridUnits(double absoluteUnits) const
+{
+  double invGridSize = (std::min)((std::min)(invGridX, invGridY), invGridZ);
+  return absoluteUnits * invGridSize;
+}
+
+inline double DistanceFieldBase::gridUnitsToAbsoluteUnits(double gridUnits) const
+{
+  double gridSize = (std::max)((std::max)(gridX, gridY), gridZ);
+  return gridUnits * gridSize;
+}
+
+
+#endif
+
diff --git a/libraries/distanceField/distanceFieldCreator.cpp b/libraries/distanceField/distanceFieldCreator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c73a2245a28c0e39acbfe05d3d893a6a5d29377a
--- /dev/null
+++ b/libraries/distanceField/distanceFieldCreator.cpp
@@ -0,0 +1,208 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Hongyi Xu, Jernej Barbic                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <cassert>
+#include "distanceFieldCreator.h"
+#include "signedDistanceFieldFromPolygonSoup.h"
+#include "marchingCubes.h"
+using namespace::std;
+
+DistanceFieldCreator::DistanceFieldCreator(ObjMesh * objMesh_, double expansionRatio_, bool useCubicBox_, const Vec3d * bbmin_, const Vec3d * bbmax_): objMesh(objMesh_), expansionRatio(expansionRatio_), useCubicBox(useCubicBox_), bbmin(0.0), bbmax(1.0)
+{
+  if (bbmin_ != NULL && bbmax_ != NULL) // caller provides bmin and bmax
+  {
+    bbmin = *bbmin_;
+    bbmax = *bbmax_;
+    assert((bbmin[0] < bbmax[0]) && (bbmin[1] < bbmax[1]) && (bbmin[2] < bbmax[2]));
+    autoBoundingBox = false;
+  }
+  else
+    autoBoundingBox = true;
+}
+
+void DistanceFieldCreator::setBoundingBox(DistanceFieldBase * field, int resolutionX, int resolutionY, int resolutionZ)
+{
+  if (autoBoundingBox)
+  {
+    if (useCubicBox)
+      printf("Forcing a cube-sized bounding box.\n");
+    field->setAutomaticBoundingBox(useCubicBox, expansionRatio);
+    field->computeBoundingBox(objMesh, resolutionX, resolutionY, resolutionZ);
+    field->getBoundingBox(bbmin, bbmax);
+  }
+  else
+    field->setBoundingBox(bbmin,bbmax);
+}
+
+DistanceField * DistanceFieldCreator::ComputeDistanceField(int resolutionX, int resolutionY, int resolutionZ, int signField, SignedFieldCreationMode mode, 
+  double sigma, int subtractSigma, bool computeVoronoiDiagram, int maxTriCount, int maxDepth, int closestPointFlag,
+  const char* precomputedUnsignedFieldFilename)
+{
+  DistanceField * field = NULL;
+  if (signField && (mode == POLYGONSOUP)) // compute signField using PolygonSoup
+  {
+    printf("********* Computing signed distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+    SignedDistanceFieldFromPolygonSoup signedDistanceFieldFromPolygonSoup(objMesh, expansionRatio, useCubicBox, 
+      (autoBoundingBox ? NULL : &bbmin), (autoBoundingBox ? NULL : &bbmax));
+    field = signedDistanceFieldFromPolygonSoup.ComputeDistanceField(resolutionX, resolutionY, resolutionZ, sigma, subtractSigma, 
+	computeVoronoiDiagram, maxTriCount, maxDepth, closestPointFlag, precomputedUnsignedFieldFilename);
+  }
+  else // Use DistaneField->compute... first
+  {
+    if (!closestPointFlag)
+    {
+      field = new DistanceField();
+    }
+    else
+      field = new ClosestPointField();
+
+    field->enableVoronoiDiagramComputation(computeVoronoiDiagram);
+
+    setBoundingBox(field, resolutionX, resolutionY, resolutionZ);    
+
+    int code = 0;
+    if (signField)
+    {
+      printf("********* Computing signed distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+      code = field->computeSignedField(objMesh, resolutionX, resolutionY, resolutionZ, maxTriCount, maxDepth);
+
+      if (code != 0)
+      {
+        delete field;
+        if (mode == BASIC)
+        {
+          printf("Error computing the signed distance field. Non-manifold geometry. \n");
+          return NULL;
+        }
+
+        // use polygon-soup
+        printf("Non-manifold geometry. Switching to polygon-soup signed distance field computation.\n");
+        SignedDistanceFieldFromPolygonSoup signedDistanceFieldFromPolygonSoup(objMesh, expansionRatio, useCubicBox, &bbmin, &bbmax);
+        field = signedDistanceFieldFromPolygonSoup.ComputeDistanceField(resolutionX, resolutionY, resolutionZ, sigma, subtractSigma, 
+	    computeVoronoiDiagram, maxTriCount, maxDepth, closestPointFlag, precomputedUnsignedFieldFilename);
+        if (field == NULL)
+          return NULL;
+      }
+    }
+    else
+    {
+      printf("********* Computing unsigned distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+      code = field->computeUnsignedField(objMesh, resolutionX, resolutionY, resolutionZ, maxTriCount, maxDepth);
+
+      if (code !=0)
+      {
+        printf("Error computing unsigned distance field.\n");
+        delete field;
+        return NULL;
+      }
+    }
+  }
+
+  printf("Computation completed. Performing sanity check...\n");
+  if (!field->sanityCheck())
+    printf("Sanity check failed.\n");
+
+  return field;
+}
+
+DistanceFieldNarrowBand * DistanceFieldCreator::ComputeDistanceFieldNarrowBand(int resolutionX, int resolutionY, int resolutionZ, double bandWidth,
+  int signField, SignedFieldCreationMode mode, double sigma, int subtractSigma, int maxTriCount, int maxDepth,
+  const char * precomputedUnsignedFieldFilename)
+{
+  int code = 0;
+  DistanceFieldNarrowBand * field = NULL; 
+
+  if (signField && (mode == POLYGONSOUP)) // compute signField using PolygonSoup
+  {
+    printf("********* Computing signed distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+    SignedDistanceFieldFromPolygonSoup signedDistanceFieldFromPolygonSoup(objMesh, expansionRatio, useCubicBox, 
+      (autoBoundingBox ? NULL : &bbmin), (autoBoundingBox ? NULL : &bbmax));
+    field = signedDistanceFieldFromPolygonSoup.ComputeDistanceFieldNarrowBand(resolutionX, resolutionY, resolutionZ, bandWidth,
+      sigma, subtractSigma, maxTriCount, maxDepth, precomputedUnsignedFieldFilename);
+  }
+  else
+  {
+    field = new DistanceFieldNarrowBand();
+    setBoundingBox(field, resolutionX, resolutionY, resolutionZ);
+
+    if (signField)
+    {
+      printf("********* Computing signed distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+      code = field->computeSignedField(objMesh, resolutionX, resolutionY, resolutionZ, bandWidth, maxTriCount, maxDepth);
+
+      if (code != 0)
+      {
+        delete field;
+        if (mode == BASIC)
+        {
+          printf("Error computing the signed distance field. Non-manifold geometry. \n");
+          return NULL;
+        }
+
+        // use polygon-soup
+        printf("Non-manifold geometry. Switching to polygon-soup signed distance field computation.\n");
+        if (sigma >= bandWidth)
+        {
+          printf("Error: sigma is not smaller than the bandwidth...\n");
+          return NULL;
+        }
+
+        SignedDistanceFieldFromPolygonSoup signedDistanceFieldFromPolygonSoup(objMesh, expansionRatio, useCubicBox, &bbmin, &bbmax);
+        field = signedDistanceFieldFromPolygonSoup.ComputeDistanceFieldNarrowBand(resolutionX, resolutionY, resolutionZ, bandWidth,
+          sigma, subtractSigma, maxTriCount, maxDepth, precomputedUnsignedFieldFilename);
+
+        if (field == NULL)
+          return NULL;
+      }
+    }
+    else
+    {
+      printf("********* Computing unsigned distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+      code = field->computeUnsignedField(objMesh, resolutionX, resolutionY, resolutionZ, bandWidth, maxTriCount, maxDepth);
+
+      if (code !=0)
+      {
+        printf("Error computing unsigned distance field.\n");
+        delete field;
+        return NULL;
+      }
+    }
+  }
+
+  printf("Computation completed. Performing sanity check...\n");
+  if (!field->sanityCheck())
+    printf("Sanity check failed.\n");
+
+  return field;
+}
+
diff --git a/libraries/distanceField/distanceFieldCreator.h b/libraries/distanceField/distanceFieldCreator.h
new file mode 100644
index 0000000000000000000000000000000000000000..da1f02fad5be29b44b4598270fd9fa74305d4281
--- /dev/null
+++ b/libraries/distanceField/distanceFieldCreator.h
@@ -0,0 +1,92 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Hongyi Xu, Jernej Barbic                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This is a "master" class to compute unsigned or signed distance fields.
+  The specific algorithm is controlled using "SignedFieldCreationMode" (below).
+*/
+
+#ifndef _DISTANCEFIELD_CREATOR_H_
+#define _DISTANCEFIELD_CREATOR_H_
+
+#include <vector>
+#include "distanceField.h"
+#include "distanceFieldNarrowBand.h"
+#include "closestPointField.h"
+#include "objMesh.h"
+
+class DistanceFieldCreator
+{
+public:
+  // if useCubicBox=0, the bounding box will not be a cube, but the aspect ratios of the bounding box will be set so that voxels are cubes, by following the resolutionX, resolutionY, resolutionZ parameters in the Compute* routines below
+  // if useCubicBox=1, the bounding box will be a cube, but the voxels will not necessarily be cubes
+  DistanceFieldCreator(ObjMesh * objMesh, double expansionRatio = 1.5, bool useCubicBox = true, const Vec3d * bbmin = NULL, const Vec3d * bbmax = NULL);
+  virtual ~DistanceFieldCreator() {}
+
+  enum SignedFieldCreationMode
+  {
+    BASIC,       // assumes the input obj mesh is manifold and self-intersection-free
+    POLYGONSOUP, // handles non-manifold and/or self-intersecting meshes, using the SignedDistanceFieldFromPolygonSoup pipeline,
+                 // as published in:
+                 // Hongyi Xu, Jernej Barbic: 
+                 // Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014, Montreal, Canada
+    AUTO         // uses BASIC if mesh is manifold, otherwise POLYGONSOUP
+  };
+
+  // Compute a distance field from objmesh
+  // If calculateSignedField=0, an unsigned field will be computed.
+  // If calculateSignedField=1, a signed field will be computed.
+  // computeVoronoiDiagram: whether to compute a Voronoi diagram (stored in DistanceField)
+  // closestPointFlag: whether to return a ClosestPointField or not
+  DistanceField * ComputeDistanceField(int resolutionX, int resolutionY, int resolutionZ, int calculateSignedField, SignedFieldCreationMode mode,
+    double sigma, int subtractSigma = 1, bool computeVoronoiDiagram = false, int maxTriCount = 15, int maxDepth = 10, int closestPointFlag = 0,
+    const char * precomputedUnsignedFieldFilename = NULL);
+
+  // Compute a distance field in a narrow band
+  DistanceFieldNarrowBand * ComputeDistanceFieldNarrowBand(int resolutionX, int resolutionY, int resolutionZ, double bandWidth, int calculateSignedField,
+    SignedFieldCreationMode mode, double sigma, int subtractSigma = 1, int maxTriCount = 15, int maxDepth = 10,
+    const char * precomputedUnsignedFieldFilename = NULL);
+
+protected:
+
+  ObjMesh * objMesh;
+
+  double expansionRatio;
+  bool useCubicBox;
+  bool autoBoundingBox;
+  Vec3d bbmin, bbmax;
+
+  void setBoundingBox(DistanceFieldBase* field, int resolutionX, int resolutionY, int resolutionZ);
+};
+
+#endif
+
diff --git a/libraries/distanceField/distanceFieldNarrowBand.cpp b/libraries/distanceField/distanceFieldNarrowBand.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..46582c254608e9efb811a2dc799149061c922e90
--- /dev/null
+++ b/libraries/distanceField/distanceFieldNarrowBand.cpp
@@ -0,0 +1,718 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Hongyi Xu, Jernej Barbic                                 *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Narrowband distance field computation.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <float.h>
+#include <fstream>
+#include <set>
+#include "triangle.h"
+#include "boundingBox.h"
+#include "distanceFieldNarrowBand.h"
+#include "objMeshOrientable.h"
+#include "objMeshOctree.h"
+#include "trilinearInterpolation.h"
+#include "vegalong.h"
+using namespace std;
+
+vegalong DistanceFieldNarrowBand::GetFilesize(const char *filename)
+{
+  FILE * f = fopen(filename, "rb");  /* open the file in read only */
+
+  vegalong size = 0;
+  if (fseek(f, 0, SEEK_END) == 0) /* seek was successful */
+      size = ftell(f);
+  fclose(f);
+  return size;
+}
+
+DistanceFieldNarrowBand::DistanceFieldNarrowBand() : DistanceFieldBase()
+{ 
+  gridPointStatus = NULL;
+  signFieldFlooded = 0;
+}
+
+DistanceFieldNarrowBand::~DistanceFieldNarrowBand()
+{
+  free(gridPointStatus);
+}
+
+// the routines for signed and unsigned distance field computation
+#define COMPUTE_SIGNED_FIELD_NARROWBAND
+  #define COMPUTE_INTERIOR_FIELD_NARROWBAND
+    #include "computeFieldNarrowBand.cpp"
+  #undef COMPUTE_INTERIOR_FIELD_NARROWBAND
+    #include "computeFieldNarrowBand.cpp"
+#undef COMPUTE_SIGNED_FIELD_NARROWBAND
+  #include "computeFieldNarrowBand.cpp"
+
+int DistanceFieldNarrowBand::load(const std::string& filename)
+{
+  ifstream fin(filename.c_str(),ios::binary);
+  if (!fin)
+    return 1;
+
+  fin.read((char*)&resolutionX, sizeof(int));
+
+  // the type of data (single-precision or double-precision) is encoded as the sign of the x-resolution
+  bool floatData = (resolutionX < 0);
+  if (floatData)
+    resolutionX = -resolutionX;
+   
+  fin.read((char*)&resolutionY, sizeof(int)); 
+
+  if (resolutionY < 0) // negative second resolution implies closest point data
+    return 1;
+
+  fin.read((char*)&resolutionZ, sizeof(int));
+
+  vegalong size =  (resolutionZ + 1) * (resolutionY + 1) * (resolutionX + 1);
+  gridPointStatus = (char*) realloc (gridPointStatus, sizeof(char) * size);
+  for(vegalong i=0; i<size; i++)
+    gridPointStatus[i] = DistanceFieldNarrowBand::EXTERIOR_UNCOMPUTED;
+
+  fin.read((char*)&(bmin_[0]), sizeof(double));
+  fin.read((char*)&(bmin_[1]), sizeof(double));
+  fin.read((char*)&(bmin_[2]), sizeof(double));
+
+  fin.read((char*)&(bmax_[0]), sizeof(double));
+  fin.read((char*)&(bmax_[1]), sizeof(double));
+  fin.read((char*)&(bmax_[2]), sizeof(double));
+
+  setGridParameters();
+
+  int numGridPoints = 0;
+  fin.read((char*)&numGridPoints, sizeof(int));
+  
+  int i, j, k;
+  double * buffer = (double*)malloc(sizeof(double));
+  for(int p = 0; p < numGridPoints; p++)
+  {
+    fin.read((char*)&i, sizeof(int));
+    fin.read((char*)&j, sizeof(int));
+    fin.read((char*)&k, sizeof(int));
+    if (floatData)
+      fin.read((char*)buffer, sizeof(float));
+    else
+      fin.read((char*)buffer, sizeof(double));
+
+    distanceData.insert(pair<gridPoint, float>(gridPoint(i,j,k), *(float*)(buffer)));
+  }
+  free(buffer);
+
+  finalizeGridPointStatus();
+  
+  fin.close();
+  
+  return 0;
+}
+
+int DistanceFieldNarrowBand::saveToText(const std::string& filename)
+{
+  FILE * fout = fopen((char*)filename.c_str(), "w");
+  if (!fout)
+    return 1;
+
+  fprintf(fout, "%d\n", resolutionX);
+  fprintf(fout, "%d\n", resolutionY);
+  fprintf(fout, "%d\n", resolutionZ);
+
+  for (map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    gridPoint v = it->first;
+    fprintf(fout, "%d %d %d %G\n", v.first, v.second, v.third, it->second);
+  }
+
+  fclose(fout);
+
+  return 0;
+}
+
+int DistanceFieldNarrowBand::saveToDistanceField(const std::string& filename, bool doublePrecision)
+{
+  if (doublePrecision)
+    printf("Error: double precision output is not supported. Using float instead.\n");
+  doublePrecision = false;
+
+  ofstream fout;
+  fout.open(filename.c_str(),ios::binary);
+
+  int data = resolutionX;
+  if (!doublePrecision)
+    data = -data;
+
+  fout.write((char*)&data,sizeof(int));
+  fout.write((char*)&resolutionY,sizeof(int));
+  fout.write((char*)&resolutionZ,sizeof(int));
+
+  fout.write((char*)&(bmin_[0]),sizeof(double));
+  fout.write((char*)&(bmin_[1]),sizeof(double));
+  fout.write((char*)&(bmin_[2]),sizeof(double));
+
+  fout.write((char*)&(bmax_[0]),sizeof(double));
+  fout.write((char*)&(bmax_[1]),sizeof(double));
+  fout.write((char*)&(bmax_[2]),sizeof(double));
+
+  int flag = 1;
+  for(vegalong k=0; k<=resolutionZ; k++)
+    for(vegalong j=0; j<=resolutionY; j++)
+      for(vegalong i=0; i<=resolutionX; i++)
+      {
+        float dist = distance(i,j,k);
+        if (fabs(dist) != FLT_MAX)
+          flag = (dist >= 0) ? 1 : -1;
+        else
+        {
+          if (flag == -1)
+            dist = -FLT_MAX;
+        }
+        fout.write((char*)&dist, sizeof(float));
+      }
+  fout.close();
+
+  return 0;
+}
+
+int DistanceFieldNarrowBand::save(const std::string& filename, bool doublePrecision)
+{
+  if (doublePrecision)
+    printf("Error: double precision output is not supported. Using float instead.\n");
+  doublePrecision = false;
+
+  ofstream fout;
+  fout.open(filename.c_str(),ios::binary);
+
+  int data = resolutionX;
+  if (!doublePrecision)
+    data = -data;
+
+  fout.write((char*)&data,sizeof(int));
+  fout.write((char*)&resolutionY,sizeof(int));
+  fout.write((char*)&resolutionZ,sizeof(int));
+
+  fout.write((char*)&(bmin_[0]),sizeof(double));
+  fout.write((char*)&(bmin_[1]),sizeof(double));
+  fout.write((char*)&(bmin_[2]),sizeof(double));
+
+  fout.write((char*)&(bmax_[0]),sizeof(double));
+  fout.write((char*)&(bmax_[1]),sizeof(double));
+  fout.write((char*)&(bmax_[2]),sizeof(double));
+
+  vegalong numGridPoints = distanceData.size();
+  fout.write((char*)&(numGridPoints),sizeof(int));
+
+  for (map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    gridPoint v = it->first;
+    fout.write((char*)&(v.first), sizeof(int));
+    fout.write((char*)&(v.second), sizeof(int));
+    fout.write((char*)&(v.third), sizeof(int));
+    float value = it->second;
+    fout.write((char*)&(value), sizeof(float));
+  }
+
+  fout.close();
+
+  return 0;
+}
+
+void DistanceFieldNarrowBand::finalizeGridPointStatus()
+{
+  // assumption: bounding box covers the entire geometry
+
+  int flag = 1;
+  vegalong index = 0;
+  for(vegalong k=0; k<=resolutionZ; k++)
+    for(vegalong j=0; j<=resolutionY; j++)
+      for(vegalong i=0; i<=resolutionX; i++)
+      {
+        float dist = distance(i,j,k);
+        if ((dist == FLT_MAX) || (dist == -FLT_MAX))
+        {
+          gridPointStatus[index] = (flag == -1) ? DistanceFieldNarrowBand::INTERIOR_UNCOMPUTED : DistanceFieldNarrowBand::EXTERIOR_UNCOMPUTED; 
+        }
+        else
+        {
+          flag = (dist >= 0) ? 1 : -1;
+          gridPointStatus[index] = DistanceFieldNarrowBand::COMPUTED; 
+        }
+
+        index++;
+      }
+}
+
+typedef struct
+{
+  int i,j,k;
+  int di,dj,dk;
+  double fieldDist, gridDist, relError; 
+} errorData;
+
+struct more_errorData : public std::binary_function< errorData, errorData, bool > {
+  bool operator()(const errorData& x, const errorData& y) {
+    return((x.relError) > (y.relError)); 
+  }
+};
+
+bool DistanceFieldNarrowBand::sanityCheck()
+{
+  //double diagonal = sqrt(gridX*gridX + gridY*gridY + gridZ*gridZ);
+
+  bool result = true;
+  int count = 0;
+
+  const int numErrorsPrinted = 3;
+
+  vector<errorData> relErrors;
+  errorData emptyEntry = {0,0,0,0,0,0,0.0,0.0,-1.0};
+  for(int i=0; i<numErrorsPrinted; i++)
+   relErrors.push_back(emptyEntry);
+
+  int counter = 0;
+  for (map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    gridPoint v = it->first; 
+    int i = v.first;
+    int j = v.second;
+    int k = v.third;
+
+    float d = it->second;
+    float d1;
+    float sanity;
+    float relError;
+    float fieldDist;
+    float gridDist;
+
+    #define PROCESS(di,dj,dk)\
+    if ((i+(di) <= resolutionX) && (i+(di) >= 0) &&\
+        (j+(dj) <= resolutionY) && (j+(dj) >= 0) &&\
+        (k+(dk) <= resolutionZ) && (k+(dk) >= 0))\
+    {\
+      d1 = distance(i+(di),j+(dj),k+(dk));\
+      if ((d1 != -FLT_MAX) && (d1 != FLT_MAX))\
+      {\
+        gridDist = (float) (len(Vec3d((di)*gridX,(dj)*gridY,(dk)*gridZ)));\
+        fieldDist = fabs(d-d1);\
+        sanity = fieldDist - gridDist;\
+        if (sanity > 1E-6)\
+        {\
+          relError = sanity/gridDist;\
+          if (relError > relErrors[numErrorsPrinted-1].relError)\
+          {\
+            errorData errorEntry = {i,j,k,di,dj,dk,fieldDist,gridDist,relError};\
+            relErrors[numErrorsPrinted-1] = errorEntry;\
+            sort(relErrors.begin(),relErrors.end(),more_errorData());\
+          }\
+          result = false;\
+          count++;\
+        }\
+      }\
+    }
+    
+    PROCESS(1,0,0);
+    PROCESS(1,1,0);
+    PROCESS(0,1,0);
+    PROCESS(-1,1,0);
+    PROCESS(-1,0,0);
+    PROCESS(-1,-1,0);
+    PROCESS(0,-1,0);
+    PROCESS(1,-1,0);
+
+    PROCESS(0,0,1);
+    PROCESS(1,0,1);
+    PROCESS(1,1,1);
+    PROCESS(0,1,1);
+    PROCESS(-1,1,1);
+    PROCESS(-1,0,1);
+    PROCESS(-1,-1,1);
+    PROCESS(0,-1,1);
+    PROCESS(1,-1,1);
+
+    PROCESS(0,0,-1);
+    PROCESS(1,0,-1);
+    PROCESS(1,1,-1);
+    PROCESS(0,1,-1);
+    PROCESS(-1,1,-1);
+    PROCESS(-1,0,-1);
+    PROCESS(-1,-1,-1);
+    PROCESS(0,-1,-1);
+    PROCESS(1,-1,-1);
+
+    counter++;
+    if (counter % (resolutionX * resolutionY)== 0)
+    {
+      counter = 0;
+      cout << "." << flush;
+    }
+  }
+
+  cout << endl;
+
+  if (count == 0)
+    cout << "Success: sanity check passed." << endl;
+  else
+  {
+    cout << "Encountered " << count << " possible errors. Largest top " << numErrorsPrinted << " errors (or all errors if fewer):" << endl;
+    for(int i=0; i< (count < numErrorsPrinted ? count : numErrorsPrinted); i++)
+    {
+      errorData * errorEntry = &relErrors[i]; 
+      float d1 = distance(errorEntry->i,errorEntry->j,errorEntry->k);
+      float d2 = distance(errorEntry->i + errorEntry->di ,errorEntry->j + errorEntry->dj, errorEntry->k + errorEntry->dk);
+      cout << "Distance field change too large. [" << errorEntry->i << "," << errorEntry->j << "," << errorEntry->k << "] to [" << errorEntry->i + (errorEntry->di) << "," << errorEntry->j + (errorEntry->dj) << "," << errorEntry->k + (errorEntry->dk) << "]" << " Dist 1: " << d1 << " Dist 2: " << d2 << " Reported change in distance field: " << errorEntry->fieldDist << " Grid distance: " << errorEntry->gridDist << " Relative error: " << errorEntry->relError << endl;
+    }
+  }
+  return result;
+}
+
+float DistanceFieldNarrowBand::distance(Vec3d pos, int constrainToBox) const
+{
+  // get the index coordinate of the lower-right-bottom corner of the voxel containing 'pos'
+  int i = (int)((pos[0] - bmin_[0]) * invGridX);
+  int j = (int)((pos[1] - bmin_[1]) * invGridY);
+  int k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  if (((i<0) || (i>=resolutionX) || (j<0) || (j>=resolutionY) || (k<0) || (k>=resolutionZ)) && (!constrainToBox))
+  {
+    printf("Warning: querying the distance field outside of the bounding box: (i,j,k)=(%d,%d,%d), (x, y, z)=(%lf,%lf,%lf), resolution=(%d,%d,%d)\n",i, j, k, pos[0], pos[1], pos[2], resolutionX, resolutionY, resolutionZ);
+    return FLT_MAX;
+  }
+
+  if (constrainToBox)
+  {
+    if (i >= resolutionX)
+    {
+      i = resolutionX - 1;
+      pos[0] = bmax_[0];
+    }
+
+    if (i < 0)
+    {
+      i = 0;
+      pos[0] = bmin_[0];
+    }
+
+    if (j >= resolutionY)
+    {
+      j = resolutionY - 1;
+      pos[1] = bmax_[1];
+    }
+
+    if (j < 0)
+    {
+      j = 0;
+      pos[1] = bmin_[1];
+    }
+
+    if (k >= resolutionZ)
+    {
+      k = resolutionZ - 1;
+      pos[2] = bmax_[2];
+    }
+
+    if (k < 0)
+    {
+      k = 0;
+      pos[2] = bmin_[2];
+    }
+  }
+
+  float distances[8];
+  
+  #define PROCESS2(i,j,k,l)\
+   {\
+     distances[(l)] = distance((i), (j), (k));\
+     if (fabs(distances[(l)]) == FLT_MAX)\
+     {\
+       return distances[(l)];\
+     }\
+   }
+
+  PROCESS2(i,j,k,0);
+  PROCESS2(i+1,j,k,1);
+  PROCESS2(i+1,j+1,k,2);
+  PROCESS2(i,j+1,k,3);
+  PROCESS2(i,j,k+1,4);
+  PROCESS2(i+1,j,k+1,5);
+  PROCESS2(i+1,j+1,k+1,6);
+  PROCESS2(i,j+1,k+1,7);
+  
+  double wx,wy,wz;
+  wx = ((pos[0]-bmin_[0]) / gridX) - i;
+  wy = ((pos[1]-bmin_[1]) / gridY) - j;
+  wz = ((pos[2]-bmin_[2]) / gridZ) - k;
+
+  return (float)(TRILINEAR_INTERPOLATION(wx,wy,wz,distances[0],distances[1],distances[2],distances[3],
+                                distances[4],distances[5],distances[6],distances[7]));
+}
+
+float DistanceFieldNarrowBand::maxValue()
+{
+  float maxValue=-FLT_MAX;
+  
+  for(map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    if ((it->second) > maxValue)
+      maxValue = it->second;
+  }
+ 
+  return maxValue;
+}
+
+float DistanceFieldNarrowBand::minValue()
+{
+  float minValue=FLT_MAX;
+ 
+  for(map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    if ((it->second) < minValue)
+      minValue = it->second;
+  }
+  
+  return minValue;
+}
+
+void DistanceFieldNarrowBand::maxMinValue(float* maxValue, float* minValue)
+{
+  *minValue=FLT_MAX;
+  *maxValue=-FLT_MAX;
+  
+  for(map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    float dist = it->second;
+    if (dist < *minValue)
+      *minValue = dist;
+
+    if (dist > *maxValue)
+      *maxValue = dist;
+  }
+}
+
+float DistanceFieldNarrowBand::maxAbsValue()
+{
+  float maxValue=0;
+
+  for(map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    float dist = fabs(it->second);
+    if (dist > maxValue)
+      maxValue = dist;
+  } 
+
+  return maxValue;
+}
+
+float DistanceFieldNarrowBand::maxNonInftyAbsValue()
+{
+  float maxValue=0;
+
+  for(map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    float dist = fabs(it->second);
+    if ((dist > maxValue) && (dist != FLT_MAX))
+      maxValue = dist;
+  } 
+  
+  return maxValue;
+}
+
+float DistanceFieldNarrowBand::maxAbsValue(float threshold)
+{
+  float maxValue=0;
+ 
+  for(map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+  {
+    float dist = fabs(it->second);
+    if ((dist > maxValue) && (dist < threshold))
+      maxValue = dist;
+  } 
+
+  return maxValue;
+}
+
+Vec3d DistanceFieldNarrowBand::gradient(const Vec3d& pos)
+{
+  int i,j,k;
+
+  // get the indices
+  i = (int)((pos[0] - bmin_[0]) * invGridX);
+  j = (int)((pos[1] - bmin_[1]) * invGridY);
+  k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  if ((i<=0) || (i>=resolutionX) || (j<=0) || (j>=resolutionY) || (k<=0) || (k>=resolutionZ))
+  {
+    return Vec3d(0,0,0);
+  }
+                                                                                                                                                             
+  double wx,wy,wz;
+  wx = ((pos[0]-bmin_[0]) / gridX) - i;
+  wy = ((pos[1]-bmin_[1]) / gridY) - j;
+  wz = ((pos[2]-bmin_[2]) / gridZ) - k;
+
+  // gradient with respect to trilinear interpolation
+  float distances[8];
+  
+  #define PROCESS3(i,j,k,l)\
+  {\
+    distances[(l)] = distance(i, j, k);\
+    if ((distances[(l)] == FLT_MAX) || (distances[(l)] == -FLT_MAX))\
+    {\
+      return Vec3d(distances[(l)]);\
+    }\
+  }
+
+  PROCESS3(i,j,k,0);
+  PROCESS3(i+1,j,k,1);
+  PROCESS3(i+1,j+1,k,2);
+  PROCESS3(i,j+1,k,3);
+  PROCESS3(i,j,k+1,4);
+  PROCESS3(i+1,j,k+1,5);
+  PROCESS3(i+1,j+1,k+1,6);
+  PROCESS3(i,j+1,k+1,7);
+
+  return Vec3d(
+    GRADIENT_COMPONENT_X(wx,wy,wz,distances[0],distances[1],distances[2],distances[3],distances[4],distances[5],distances[6],distances[7]),
+    GRADIENT_COMPONENT_Y(wx,wy,wz,distances[0],distances[1],distances[2],distances[3],distances[4],distances[5],distances[6],distances[7]),
+    GRADIENT_COMPONENT_Z(wx,wy,wz,distances[0],distances[1],distances[2],distances[3],distances[4],distances[5],distances[6],distances[7]) );
+                              
+}
+
+void DistanceFieldNarrowBand::findSurfaceGridPoints(ObjMesh* objMeshIn)
+{
+  ObjMesh objMesh(*objMeshIn);
+
+  surfaceGridPoints.clear();
+  //typedef triple<int,int,int> gridPoint;
+
+  std::set<gridPoint> checkedGridPoints;
+  std::vector<gridPoint> scheduledGridPoints;
+
+  for(unsigned int i=0; i < objMesh.getNumGroups(); ++i)
+  {
+    const ObjMesh::Group * group = objMesh.getGroupHandle(i);
+
+    for (unsigned int j=0; j<group->getNumFaces(); ++j)
+    {
+      Vec3d p0 = objMesh.getPosition(group->getFace(j).getVertex(0).getPositionIndex());
+      Vec3d p1 = objMesh.getPosition(group->getFace(j).getVertex(1).getPositionIndex());
+      Vec3d p2 = objMesh.getPosition(group->getFace(j).getVertex(2).getPositionIndex());
+
+      TriangleBasic triangle(p0,p1,p2);
+      
+      Vec3d center = 1.0/3 * (p0 + p1 + p2);
+      Vec3d recCenter = center - bmin_;
+
+      int vi,vj,vk;
+
+      vi = (int)(recCenter[0] / gridX);
+      vj = (int)(recCenter[1] / gridY);
+      vk = (int)(recCenter[2] / gridZ);
+
+      checkedGridPoints.clear();
+      checkedGridPoints.insert(gridPoint(vi, vj, vk));
+
+      scheduledGridPoints.clear();
+      scheduledGridPoints.push_back(gridPoint(vi,vj,vk));
+
+      while (!scheduledGridPoints.empty())
+      {
+        gridPoint v = scheduledGridPoints.back();
+        scheduledGridPoints.pop_back();
+
+        Vec3d bbmin = bmin_ + Vec3d(v.first * gridX, v.second * gridY, v.third * gridZ);
+        Vec3d bbmax = bbmin + Vec3d(gridX, gridY, gridZ);
+
+        BoundingBox bbox(bbmin, bbmax);
+
+        if (triangle.doesIntersectBox(bbox))
+        {
+          //surfaceVoxels.insert(v);
+          //floodFillTag[(v.third * (resolutionY+1) + v.second) * (resolutionX + 1) + v.first] = 1;
+
+          gridPoint neighbor;
+          vegalong index;
+          #define TAGNEIGHBOR(ii,jj,kk)\
+          neighbor = gridPoint(v.first+(ii), v.second+(jj), v.third+(kk));\
+          if ((neighbor.first <= resolutionX) &&\
+              (neighbor.second <= resolutionY) &&\
+              (neighbor.third <= resolutionZ))\
+          {\
+            index = (neighbor.third * (resolutionY+1) + neighbor.second) * (resolutionX + 1) + neighbor.first;\
+                  surfaceGridPoints.push_back(index);\
+          }
+
+          for (int iii=0; iii<=1; ++iii)
+            for (int jjj=0; jjj<=1; ++jjj)
+              for (int kkk=0; kkk<=1; ++kkk)
+              {
+                TAGNEIGHBOR(iii, jjj, kkk);
+              }
+
+
+          #define CHECKNEIGHBOR(ii,jj,kk)\
+          neighbor = gridPoint(v.first+(ii), v.second+(jj), v.third+(kk));\
+                if ((neighbor.first >= 0) && (neighbor.first <= resolutionX) &&\
+              (neighbor.second >= 0) && (neighbor.second <= resolutionY) &&\
+              (neighbor.third >= 0) && (neighbor.third <= resolutionZ))\
+          {\
+            if (checkedGridPoints.find(neighbor) == checkedGridPoints.end())\
+            {\
+              checkedGridPoints.insert(neighbor);\
+              scheduledGridPoints.push_back(neighbor);\
+            }\
+          }
+
+          for (int iii=-1; iii<=1; ++iii)
+            for (int jjj=-1; jjj<=1; ++jjj)
+              for (int kkk=-1; kkk<=1; ++kkk)
+              {
+                if ((iii == 0) && (jjj == 0) && (kkk == 0))
+                  continue;
+
+                CHECKNEIGHBOR(iii, jjj, kkk);
+              }
+        }
+      }
+    }
+  }
+}
+
+void DistanceFieldNarrowBand::offsetDistanceField(double offset)
+{
+  for(std::map<gridPoint, float>::iterator it = distanceData.begin(); it != distanceData.end(); it++)
+    it->second += (float) offset;
+}
+
diff --git a/libraries/distanceField/distanceFieldNarrowBand.h b/libraries/distanceField/distanceFieldNarrowBand.h
new file mode 100644
index 0000000000000000000000000000000000000000..f7c5714e990472bb90e1e96ed8d1cd358b14a9cc
--- /dev/null
+++ b/libraries/distanceField/distanceFieldNarrowBand.h
@@ -0,0 +1,154 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Narrowband distance field computation.
+*/
+
+#ifndef _DISTANCEFIELDNARROWBAND_H_
+#define _DISTANCEFIELDNARROWBAND_H_
+
+#include "objMesh.h"
+#include "triple.h"
+#include "distanceFieldBase.h"
+#include "vegalong.h"
+#include <map>
+#include <float.h>
+
+class DistanceFieldNarrowBand : public DistanceFieldBase
+{
+public:
+
+  DistanceFieldNarrowBand();
+  virtual ~DistanceFieldNarrowBand();
+
+  // computes unsigned distance field in a narrow band
+  // filename is the obj filename
+  // bandWidth is given in absolute units
+  virtual int computeUnsignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, double bandWidth, int maxTriCount=15, int maxDepth=10);
+
+  // computes signed distance field in a narrow band
+  // filename is the obj filename
+  // offset is given in absolute units
+  virtual int computeSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, double bandWidth, int maxTriCount=15, int maxDepth=10);
+
+  virtual void offsetDistanceField(double sigma); // add sigma to all the distance field values
+  // computes the signed distance field, in the interior region (bounded by the zero isosurface), in a narrow band
+  // must call computeUnsignedField using the same objMesh and same resolution first
+  // also, you should first offset the unsigned distance field by a proper 'sigma' (typically, by passing -sigma, where sigma > 0)
+  virtual int computeInteriorSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, double bandWidth, int maxTriCount=15, int maxDepth=10);
+
+  // loads a previously computed distance field from a disk file
+  virtual int load(const std::string& filename); // returns 0 on success
+
+  // saves the current distance field to a disk file (e.g. after computing it once, for later fast reloading) 
+  virtual int save(const std::string& filename, bool doublePrecision); // saves in a compressed format (saves the computed grid points only)
+  virtual int saveToText(const std::string& filename);
+  int saveToDistanceField(const std::string& filename, bool doublePrecision); // saves in full format (FLT_MAX or -FLT_MAX are stored for uncomputed grid points)
+
+  // return distance field value at grid vertex (i,j,k)
+  // each of i,j,k must be an integer from {0, ..., resolution{X,Y,Z}}
+  virtual inline float distance(int i, int j, int k) const;
+  // computes distance and gradient at arbitrary position
+  virtual float distance(Vec3d pos, int constrainToBox=0) const;
+  // alters the distance at a particular grid vertex (i,j,k)
+  virtual inline void setDistance(int i, int j, int k, float value);
+
+  virtual Vec3d gradient(const Vec3d & pos);
+
+  virtual bool sanityCheck(); // checks if distance for any two adjacent voxels is less than voxel grid spacing apart (which it must be by triangle inequality, for both signed and unsigned fields)
+
+  virtual float maxValue();
+  virtual float minValue();
+  virtual void  maxMinValue(float* maxValue, float* minValue);
+
+  virtual float maxAbsValue();
+  virtual float maxAbsValue(float threshold); // only abs values up to threshold
+  virtual float maxNonInftyAbsValue();
+
+  virtual int breadthFirstTraversalSigned(void * objMeshOctree, float offset, int zLo, int zHi, int asterisk=0);
+  virtual int breadthFirstTraversalUnsigned(void * objMeshOctree, float offset, int zLo, int zHi, int asterisk=0);
+  virtual int breadthFirstTraversalInteriorSigned(void * objMeshOctree, float offset, int zLo, int zHi, int asterisk=0);
+
+  void freeMemory();
+  
+  void findSurfaceGridPoints(ObjMesh* objMeshIn);
+
+  typedef triple<int,int,int> gridPoint;
+  std::map<gridPoint, float> * getDistanceData() {return &distanceData;};
+  
+protected:
+  int maxTriCount;
+  int maxDepth;
+
+  void finalizeGridPointStatus();
+  enum { COMPUTED, EXTERIOR_UNCOMPUTED, INTERIOR_UNCOMPUTED };
+  char * gridPointStatus; // needed for the sign on uncomputed grid points; unsigned field only has two values: COMPUTED, EXTERIOR_UNCOMPUTED
+
+  int signFieldFlooded;
+  //float * fieldData;
+  std::map<gridPoint, float> distanceData;
+
+  vegalong GetFilesize(const char *filename);
+  
+  std::vector<vegalong> surfaceGridPoints;
+};
+
+inline float DistanceFieldNarrowBand::distance(int i, int j, int k) const
+{
+  vegalong index = (k * (resolutionY+1) + j) * (resolutionX + 1) + i;
+
+  std::map<gridPoint, float>::const_iterator it = distanceData.find(gridPoint(i,j,k));
+
+  if (it == distanceData.end())
+  {
+    if (gridPointStatus[index] == DistanceFieldNarrowBand::INTERIOR_UNCOMPUTED)
+      return -FLT_MAX;
+    else 
+      return FLT_MAX;
+  }
+  else
+    return it->second;
+}
+
+inline void DistanceFieldNarrowBand::setDistance(int i, int j, int k, float value)
+{
+  std::map<gridPoint, float>::iterator it = distanceData.find(gridPoint(i,j,k));
+  
+  if (it == distanceData.end())
+    distanceData.insert(std::pair<gridPoint, float>(gridPoint(i,j,k), value));
+  else
+    it->second = value;
+}
+
+#endif
+
diff --git a/libraries/distanceField/marchingCubes-basicCases.nb b/libraries/distanceField/marchingCubes-basicCases.nb
new file mode 100644
index 0000000000000000000000000000000000000000..bfc4b51fa2ba5daa0c4215b84e3da1eb7c521cb4
--- /dev/null
+++ b/libraries/distanceField/marchingCubes-basicCases.nb
@@ -0,0 +1,1594 @@
+(* Content-type: application/vnd.wolfram.mathematica *)
+
+(*** Wolfram Notebook File ***)
+(* http://www.wolfram.com/nb *)
+
+(* CreatedBy='Mathematica 8.0' *)
+
+(*CacheID: 234*)
+(* Internal cache information:
+NotebookFileLineBreakTest
+NotebookFileLineBreakTest
+NotebookDataPosition[       157,          7]
+NotebookDataLength[     62486,       1585]
+NotebookOptionsPosition[     58092,       1478]
+NotebookOutlinePosition[     58465,       1495]
+CellTagsIndexPosition[     58422,       1492]
+WindowFrame->Normal*)
+
+(* Beginning of Notebook Content *)
+Notebook[{
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", "\[IndentingNewLine]", " ", 
+   RowBox[{
+    RowBox[{
+    "Vega", " ", "FEM", " ", "Simulation", " ", "Library", " ", "Version", 
+     " ", "3.0", "\[IndentingNewLine]", "\[IndentingNewLine]", 
+     "\"\<distance field\>\"", " ", "library"}], ",", 
+    RowBox[{"Copyright", " ", 
+     RowBox[{"(", "C", ")"}], " ", "2007", " ", "CMU"}], ",", 
+    RowBox[{
+    "2016", " ", "USC", "\[IndentingNewLine]", "All", " ", "rights", " ", 
+     RowBox[{
+     "reserved", ".", "                                                  ", 
+      "\[IndentingNewLine]", 
+      "                                                                       \
+", "\[IndentingNewLine]", "Code"}], " ", 
+     RowBox[{"authors", ":", " ", 
+      RowBox[{"Danyong", " ", "Zhao"}]}]}], ",", 
+    RowBox[{
+     RowBox[{"Jernej", " ", "Barbic", " ", "\[IndentingNewLine]", 
+      RowBox[{"http", ":"}]}], "//", 
+     RowBox[{
+      RowBox[{
+       RowBox[{"www", ".", "jernejbarbic", ".", "com"}], "/", "code"}], 
+      "\[IndentingNewLine]", 
+      "                                                                       \
+", "\[IndentingNewLine]", 
+      RowBox[{"Research", ":", " ", 
+       RowBox[{"Jernej", " ", "Barbic"}]}]}]}], ",", " ", 
+    RowBox[{"Hongyi", " ", "Xu"}], ",", " ", 
+    RowBox[{"Doug", " ", 
+     RowBox[{"L", ".", "James"}], "\[IndentingNewLine]", 
+     "                                                                       \
+", "\[IndentingNewLine]", 
+     RowBox[{"Funding", ":", " ", 
+      RowBox[{"National", " ", "Science", " ", "Foundation"}]}]}], ",", " ", 
+    RowBox[{"Link", " ", "Foundation"}], ",", "                ", 
+    "\[IndentingNewLine]", " ", 
+    RowBox[{
+     RowBox[{
+     "Zumberge", " ", "Research", " ", "and", " ", "Innovation", " ", "Fund", 
+      " ", "at", " ", "USC", "\[IndentingNewLine]", 
+      "                                                                       \
+", "\[IndentingNewLine]", " ", "This", " ", "library", " ", "is", " ", "free",
+       " ", "software"}], ";", 
+     RowBox[{
+      RowBox[{"you", " ", "can", " ", "redistribute", " ", "it", " ", 
+       RowBox[{"and", "/", "or"}], "\[IndentingNewLine]", "modify", " ", "it",
+        " ", "under", " ", "the", " ", "terms", " ", "of", " ", "the", " ", 
+       "BSD"}], "-", 
+      RowBox[{
+      "style", " ", "license", " ", "that", " ", "is", "\[IndentingNewLine]", 
+       "included", " ", "with", " ", "this", " ", "library", " ", "in", " ", 
+       "the", " ", "file", " ", 
+       RowBox[{"LICENSE", ".", "txt"}], "\[IndentingNewLine]", 
+       "                                                                      \
+ ", "\[IndentingNewLine]", "This", " ", "library", " ", "is", " ", 
+       "distributed", " ", "in", " ", "the", " ", "hope", " ", "that", " ", 
+       "it", " ", "will", " ", "be", " ", "useful"}]}]}], ",", "       ", 
+    "\[IndentingNewLine]", "  ", 
+    RowBox[{
+     RowBox[{"but", " ", "WITHOUT", " ", "ANY", " ", "WARRANTY"}], ";", 
+     RowBox[{
+     "without", " ", "even", " ", "the", " ", "implied", " ", "warranty", " ",
+       "of", "\[IndentingNewLine]", " ", "MERCHANTABILITY", " ", "or", " ", 
+      "FITNESS", " ", "FOR", " ", "A", " ", "PARTICULAR", " ", 
+      RowBox[{"PURPOSE", ".", "See"}], " ", "the", " ", "file", 
+      "\[IndentingNewLine]", " ", 
+      RowBox[{"LICENSE", ".", "TXT"}], " ", "for", " ", "more", " ", 
+      RowBox[{"details", "."}]}]}]}], 
+   "                                         ", "\[IndentingNewLine]", "*)"}],
+   "\[IndentingNewLine]", "\[IndentingNewLine]", 
+  RowBox[{"(*", "\[IndentingNewLine]", "  ", 
+   RowBox[{
+    RowBox[{
+    "This", " ", "file", " ", "demonstrates", " ", "the", " ", "basic", " ", 
+     "marching", " ", "cube", " ", 
+     RowBox[{"cases", ".", "\[IndentingNewLine]", "Jernej"}], " ", "Barbic"}],
+     ",", " ", 
+    RowBox[{"Danyong", " ", "Zhao"}], ",", " ", "USC", ",", " ", "2016"}], 
+   "\[IndentingNewLine]", "*)"}]}]], "Input",
+ CellChangeTimes->{{3.679969564997608*^9, 3.679969596512731*^9}, {
+  3.6799696268918333`*^9, 3.679969799875928*^9}}],
+
+Cell[BoxData[{
+ RowBox[{"Clear", "[", "F", "]"}], "\[IndentingNewLine]", 
+ RowBox[{
+  RowBox[{"F", "[", 
+   RowBox[{
+   "x_", ",", "y_", ",", "z_", ",", "a000_", ",", "a100_", ",", "a010_", ",", 
+    "a110_", ",", "a001_", ",", "a101_", ",", "a011_", ",", "a111_"}], "]"}], 
+  ":=", 
+  RowBox[{
+   RowBox[{
+    RowBox[{"(", 
+     RowBox[{"1", "-", "x"}], ")"}], "*", 
+    RowBox[{"(", 
+     RowBox[{"1", "-", "y"}], ")"}], "*", 
+    RowBox[{"(", 
+     RowBox[{"1", "-", "z"}], ")"}], "*", "a000"}], "+", 
+   RowBox[{"x", "*", 
+    RowBox[{"(", 
+     RowBox[{"1", "-", "y"}], ")"}], "*", 
+    RowBox[{"(", 
+     RowBox[{"1", "-", "z"}], ")"}], "*", "a100"}], "+", 
+   RowBox[{
+    RowBox[{"(", 
+     RowBox[{"1", "-", "x"}], ")"}], "*", "y", "*", 
+    RowBox[{"(", 
+     RowBox[{"1", "-", "z"}], ")"}], "*", "a010"}], "+", 
+   RowBox[{"x", "*", "y", "*", 
+    RowBox[{"(", 
+     RowBox[{"1", "-", "z"}], ")"}], "*", "a110"}], "+", 
+   RowBox[{
+    RowBox[{"(", 
+     RowBox[{"1", "-", "x"}], ")"}], "*", 
+    RowBox[{"(", 
+     RowBox[{"1", "-", "y"}], ")"}], "*", "z", "*", "a001"}], "+", 
+   RowBox[{"x", "*", 
+    RowBox[{"(", 
+     RowBox[{"1", "-", "y"}], ")"}], "*", "z", "*", "a101"}], "+", 
+   RowBox[{
+    RowBox[{"(", 
+     RowBox[{"1", "-", "x"}], ")"}], "*", "y", "*", "z", "*", "a011"}], "+", 
+   RowBox[{"x", "*", "y", "*", "z", "*", "a111"}]}]}]}], "Input"],
+
+Cell[BoxData[{
+ RowBox[{"Clear", "[", "IsosurfacePlot", "]"}], "\[IndentingNewLine]", 
+ RowBox[{
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{
+    "a000_", ",", "a100_", ",", "a010_", ",", "a110_", ",", "a001_", ",", 
+     "a101_", ",", "a011_", ",", "a111_"}], "]"}], ":=", 
+   RowBox[{"Show", "[", 
+    RowBox[{
+     RowBox[{"ContourPlot3D", "[", " ", 
+      RowBox[{
+       RowBox[{
+        RowBox[{"Evaluate", "[", 
+         RowBox[{"F", "[", 
+          RowBox[{
+          "x", ",", "y", ",", "z", ",", "a000", ",", "a100", ",", "a010", ",",
+            "a110", ",", "a001", ",", "a101", ",", "a011", ",", "a111"}], 
+          "]"}], "]"}], "\[Equal]", "0"}], ",", 
+       RowBox[{"{", 
+        RowBox[{"x", ",", "0", ",", "1"}], "}"}], ",", 
+       RowBox[{"{", 
+        RowBox[{"y", ",", "0", ",", "1"}], "}"}], ",", 
+       RowBox[{"{", 
+        RowBox[{"z", ",", "0", ",", "1"}], "}"}], ",", 
+       RowBox[{"Ticks", "\[Rule]", "None"}], ",", 
+       RowBox[{"PlotPoints", "\[Rule]", "100"}]}], "]"}], ",", 
+     RowBox[{"Graphics3D", "[", 
+      RowBox[{"{", "\[IndentingNewLine]", 
+       RowBox[{
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<0\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<1\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<2\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "1.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<3\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "1.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<4\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<5\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<6\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "1.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<7\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "1.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<0\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.5", ",", "0.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<1\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.5", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<2\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.5", ",", "1.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<3\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.5", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<4\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.5", ",", "0.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<5\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.5", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<6\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.5", ",", "1.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<7\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.5", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<8\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.0", ",", "0.5"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<9\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.0", ",", "0.5"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<10\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "1.0", ",", "0.5"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<11\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "1.0", ",", "0.5"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}]}], 
+       "\[IndentingNewLine]", "}"}], "]"}]}], "]"}]}], ";"}]}], "Input"],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{
+   "13", "th", " ", "point", " ", "is", " ", "the", " ", "cube", " ", 
+    "center"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"Clear", "[", "midpoints", "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{"midpoints", " ", "=", " ", 
+     RowBox[{"Partition", "[", 
+      RowBox[{
+       RowBox[{"{", " ", 
+        RowBox[{
+        "0.5", ",", "0", ",", "0", ",", "1", ",", "0.5", ",", "0", ",", "0.5",
+          ",", "1", ",", "0", ",", "0", ",", "0.5", ",", "0", ",", "0.5", ",",
+          "0", ",", "1", ",", "1", ",", "0.5", ",", "1", ",", "0.5", ",", "1",
+          ",", "1", ",", "0", ",", "0.5", ",", "1", ",", "0", ",", "0", ",", 
+         "0.5", ",", "1", ",", "0", ",", "0.5", ",", "1", ",", "1", ",", 
+         "0.5", ",", "0", ",", "1", ",", "0.5", ",", "0.5", ",", "0.5", ",", 
+         "0.5"}], " ", "}"}], ",", " ", "3"}], "]"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"Clear", "[", "TriangulatedIsosurfacePlot", "]"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"TriangulatedIsosurfacePlot", "[", "l_", "]"}], " ", ":=", " ", 
+     RowBox[{"Graphics3D", "[", 
+      RowBox[{"{", 
+       RowBox[{
+        RowBox[{"Polygon", "[", 
+         RowBox[{"Partition", "[", 
+          RowBox[{
+           RowBox[{"Map", "[", 
+            RowBox[{
+             RowBox[{
+              RowBox[{"(", 
+               RowBox[{"midpoints", "[", 
+                RowBox[{"[", 
+                 RowBox[{"#", "+", "1"}], "]"}], "]"}], ")"}], "&"}], ",", 
+             "l"}], "]"}], ",", "3"}], "]"}], "]"}], ",", 
+        RowBox[{"Point", "[", 
+         RowBox[{"{", 
+          RowBox[{"0", ",", "0", ",", "0"}], "}"}], "]"}], ",", 
+        RowBox[{"Point", "[", 
+         RowBox[{"{", 
+          RowBox[{"1", ",", "1", ",", "1"}], "}"}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<0\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<1\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<2\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "1.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<3\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "1.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<4\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<5\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<6\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "1.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<7\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "1.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "Red"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<0\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.5", ",", "0.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<1\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.5", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<2\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.5", ",", "1.0", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<3\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.5", ",", "0.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<4\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.5", ",", "0.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<5\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.5", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<6\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.5", ",", "1.0", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<7\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.5", ",", "1.0"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<8\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "0.0", ",", "0.5"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<9\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "0.0", ",", "0.5"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<10\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"1.0", ",", "1.0", ",", "0.5"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}], ",", 
+        "\[IndentingNewLine]", 
+        RowBox[{"Text", "[", 
+         RowBox[{
+          RowBox[{"Style", "[", 
+           RowBox[{"\"\<11\>\"", ",", "Large", ",", "Bold"}], "]"}], ",", 
+          RowBox[{"{", 
+           RowBox[{"0.0", ",", "1.0", ",", "0.5"}], "}"}], ",", 
+          RowBox[{"Background", "\[Rule]", "LightBlue"}]}], "]"}]}], "}"}], 
+      "]"}]}], ";"}]}]}]], "Input",
+ CellChangeTimes->{{3.6313934615899477`*^9, 3.631393488792502*^9}, {
+  3.63139361894834*^9, 3.631393633210388*^9}, {3.631393666177969*^9, 
+  3.631393679266924*^9}, {3.6313937599072104`*^9, 3.631393778602406*^9}, {
+  3.631393849338855*^9, 3.6313938856644487`*^9}, {3.631394003570389*^9, 
+  3.631394024974069*^9}, {3.631394089474285*^9, 3.6313941053870897`*^9}, {
+  3.6313941760036383`*^9, 3.631394177995611*^9}, {3.631394274026045*^9, 
+  3.631394288324555*^9}, {3.631394319376296*^9, 3.631394326938594*^9}, {
+  3.6313944256003447`*^9, 3.631394432194352*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{
+   "midpoints", " ", "of", " ", "each", " ", "three", " ", "consecutive", " ",
+     "edges", " ", "form", " ", "a", " ", 
+    RowBox[{"triangle", "."}]}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{"Clear", "[", "edges", "]"}]}]], "Input",
+ CellChangeTimes->{{3.6313937216470423`*^9, 3.6313937248972807`*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "1"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "5"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "1", "]"}], "=", " ", 
+     RowBox[{"{", 
+      RowBox[{"8", ",", "3", ",", "0"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "1", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631383810013504*^9, 3.631383810018098*^9}, {
+   3.631392404624531*^9, 3.631392456553012*^9}, {3.631392511649755*^9, 
+   3.63139254308486*^9}, {3.6313930571086473`*^9, 3.63139312165226*^9}, {
+   3.631393248319087*^9, 3.631393249085165*^9}, {3.6313934219636602`*^9, 
+   3.6313934222954807`*^9}, {3.631393719611774*^9, 3.63139373620759*^9}, {
+   3.631394480541627*^9, 3.631394512246395*^9}, 3.633120796388268*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562945316177*^9, 3.6805629453329077`*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562946581703*^9, 3.680562946593453*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "2"}], " ", "*)"}], "\n", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", "4", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "5"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "5"}], ",", 
+     RowBox[{"-", "2"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "2", "]"}], "=", " ", 
+     RowBox[{"{", 
+      RowBox[{"3", ",", "1", ",", "8", ",", "9", ",", "8", ",", "1"}], 
+      "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "2", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631393162736228*^9, 3.6313931878618803`*^9}, {
+   3.63139453261344*^9, 3.631394552119534*^9}, 3.633120805990396*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562948522873*^9, 3.6805629485596724`*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562949597859*^9, 3.680562949609926*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "3.1"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"1", ",", 
+     RowBox[{"-", "5"}], ",", 
+     RowBox[{"-", "3"}], ",", "2", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "2"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "5"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "3.1", "]"}], "=", " ", 
+     RowBox[{"{", 
+      RowBox[{"1", ",", "2", ",", "10", ",", "8", ",", "3", ",", "0"}], 
+      "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "3.1", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.63139457178467*^9, 3.631394612061417*^9}, 
+   3.633120817477078*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805629507826014`*^9, 3.6805629507995577`*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562951964685*^9, 3.680562951974996*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "3.2"}], " ", "*)"}], "\n", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "2"}], ",", "4", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "5"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "3.2", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "8", ",", "3", ",", "10", ",", "10", ",", "1", ",", "0", ",", "0", ",", 
+       "8", ",", "10", ",", "2", ",", "10", ",", "3"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "3.2", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.63140664353224*^9, 3.631406649375805*^9}, {
+   3.631406787430591*^9, 3.631406806301588*^9}, {3.631406901422658*^9, 
+   3.6314069046771*^9}, 3.63312082897829*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562953583303*^9, 3.6805629536228943`*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562956286108*^9, 3.680562956297806*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "4.1"}], " ", "*)"}], "\n", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"3", ",", 
+     RowBox[{"-", "5"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "2"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "2"}], ",", "2"}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "4.1", "]"}], " ", "=", 
+     RowBox[{"{", 
+      RowBox[{"6", ",", "5", ",", "10", ",", "3", ",", "0", ",", "8"}], 
+      "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "4.1", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{
+  3.63139495383048*^9, {3.6314070175449657`*^9, 3.631407043787496*^9}, 
+   3.633120841423759*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.68056295862567*^9, 3.680562958653616*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562959916731*^9, 3.680562959925932*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "4.2"}], " ", "*)"}], "\n", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"4", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "2"}], ",", "4"}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "4.2", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "8", ",", "6", ",", "5", ",", "8", ",", "5", ",", "0", ",", "6", ",", 
+       "3", ",", "10", ",", "0", ",", "5", ",", "10", ",", "0", ",", "10", 
+       ",", "3", ",", "3", ",", "6", ",", "8"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "4.2", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.6314071495879307`*^9, 3.63140717085966*^9}, 
+   3.633120849959127*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562962325264*^9, 3.680562962383195*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562963766201*^9, 3.6805629637784557`*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "5"}], "  ", "*)"}], "\n", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"1", ",", "4", ",", 
+     RowBox[{"-", "4"}], ",", "2", ",", 
+     RowBox[{"-", "2"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "2"}], ",", 
+     RowBox[{"-", "2"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "5", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "8", ",", "3", ",", "2", ",", "10", ",", "8", ",", "2", ",", "10", ",", 
+       "9", ",", "8"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "5", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{
+  3.631394932494561*^9, {3.6314071836012573`*^9, 3.631407209075837*^9}, 
+   3.633121157276601*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562965969612*^9, 3.680562966012204*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.68056296811032*^9, 3.680562968122045*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "6.1", ".1"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", "1", ",", 
+     RowBox[{"-", "5"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "5"}], ",", "2"}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "6.11", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "6", ",", "5", ",", "10", ",", "8", ",", "1", ",", "9", ",", "8", ",", 
+       "3", ",", "1"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "6.11", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394922851939*^9, 3.6313949251185083`*^9}, {
+   3.631407230594528*^9, 3.631407270785904*^9}, {3.631407545691231*^9, 
+   3.631407562370605*^9}, 3.633121168522482*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562969935062*^9, 3.680562969961299*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805629712213793`*^9, 3.680562971233778*^9}}],
+
+Cell[BoxData[
+ RowBox[{"(*", " ", 
+  RowBox[{"Case", " ", "6.1", ".2", " ", 
+   RowBox[{"{", 
+    RowBox[{
+    "1", ",", "10", ",", "3", ",", "3", ",", "10", ",", "6", ",", "3", ",", 
+     "6", ",", "8", ",", "5", ",", "8", ",", "6", ",", "5", ",", "9", ",", 
+     "8", ",", "1", ",", "9", ",", "5", ",", "1", ",", "5", ",", "10"}], 
+    "}"}], " ", "missing"}], " ", "*)"}]], "Input",
+ CellChangeTimes->{{3.631394915677146*^9, 3.631394918457716*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "6.2"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", "4", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "2"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "2"}], ",", 
+     RowBox[{"-", "4"}], ",", "3"}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "6.2", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "8", ",", "6", ",", "5", ",", "10", ",", "3", ",", "1", ",", "8", ",", 
+       "3", ",", "6", ",", "9", ",", "8", ",", "5", ",", "6", ",", "3", ",", 
+       "10"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "6.2", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394907500346*^9, 3.631394909198427*^9}, {
+   3.631407571579081*^9, 3.6314075947819147`*^9}, 3.633121181447743*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562973444973*^9, 3.680562973492066*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805629756140127`*^9, 3.680562975626401*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "7.1"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"1", ",", 
+     RowBox[{"-", "5"}], ",", 
+     RowBox[{"-", "3"}], ",", "4", ",", 
+     RowBox[{"-", "3"}], ",", "4", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "5"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "7.1", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "1", ",", "2", ",", "10", ",", "9", ",", "5", ",", "4", ",", "8", ",", 
+       "3", ",", "0"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "7.1", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394899787757*^9, 3.631394901365316*^9}, {
+   3.631407675342209*^9, 3.631407703788311*^9}, 3.63312119343121*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562979513479*^9, 3.680562979543145*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.68056298074191*^9, 3.680562980754003*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "7.2"}], " ", "*)"}], "\n", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"4", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "2"}], ",", "1", ",", 
+     RowBox[{"-", "1"}], ",", "3", ",", 
+     RowBox[{"-", "5"}], ",", 
+     RowBox[{"-", "1"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "7.2", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "1", ",", "2", ",", "10", ",", "4", ",", "8", ",", "3", ",", "5", ",", 
+       "4", ",", "3", ",", "0", ",", "5", ",", "3", ",", "5", ",", "0", ",", 
+       "9"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "7.2", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.6314077584117727`*^9, 3.631407780085137*^9}, 
+   3.633121204120735*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562982940031*^9, 3.680562982984496*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562984997466*^9, 3.680562985009152*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "7.3"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "3"}], ",", "1", ",", 
+     RowBox[{"-", "1"}], ",", "4", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "1"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "7.3", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "12", ",", "1", ",", "2", ",", "12", ",", "9", ",", "1", ",", "5", ",", 
+       "12", ",", "10", ",", "9", ",", "12", ",", "0", ",", "12", ",", "8", 
+       ",", "3", ",", "12", ",", "5", ",", "4", ",", "0", ",", "12", ",", "3",
+        ",", "10", ",", "12", ",", "2", ",", "4", ",", "8", ",", "12"}], 
+      "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "7.3", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394881418009*^9, 3.631394883985857*^9}, {
+   3.631407899381012*^9, 3.631407915963846*^9}, 3.631407965561245*^9, 
+   3.633121214204296*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562988453167*^9, 3.680562988499811*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805629898686323`*^9, 3.68056298987906*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "7.4", ".1"}], " ", "*)"}], "\n", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"4", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", "2", ",", 
+     RowBox[{"-", "3"}], ",", "2", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "2"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "7.41", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "5", ",", "4", ",", "8", ",", "3", ",", "2", ",", "8", ",", "9", ",", 
+       "1", ",", "0", ",", "2", ",", "5", ",", "8", ",", "5", ",", "2", ",", 
+       "10"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "7.41", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{
+  3.631394875983251*^9, {3.63140792250747*^9, 3.631407922945261*^9}, {
+   3.631407976090848*^9, 3.631408003573235*^9}, 3.633121222875579*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562992276504*^9, 3.680562992327524*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680562993468965*^9, 3.680562993481633*^9}}],
+
+Cell[BoxData[
+ RowBox[{"(*", " ", 
+  RowBox[{"Case", " ", "7.4", ".2", " ", 
+   RowBox[{"{", 
+    RowBox[{
+    "9", ",", "4", ",", "0", ",", "4", ",", "8", ",", "0", ",", "4", ",", "9",
+      ",", "5", ",", "10", ",", "5", ",", "1", ",", "1", ",", "5", ",", "9", 
+     ",", "10", ",", "1", ",", "2", ",", "0", ",", "2", ",", "1", ",", "2", 
+     ",", "0", ",", "3", ",", "8", ",", "3", ",", "0"}], "}"}], " ", 
+   "missing"}], " ", "*)"}]], "Input",
+ CellChangeTimes->{{3.631394864511746*^9, 3.631394867570568*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "8"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", "4", ",", "3", ",", "3", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "5"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "8", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{"10", ",", "9", ",", "8", ",", "8", ",", "11", ",", "10"}], 
+      "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "8", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394856154524*^9, 3.631394858591331*^9}, {
+   3.63140792499454*^9, 3.631407925346727*^9}, {3.631408011049806*^9, 
+   3.631408028920038*^9}, 3.6331212339771214`*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805629961139107`*^9, 3.680562996146295*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805629976932096`*^9, 3.6805629977062263`*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "9"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"4", ",", "3", ",", 
+     RowBox[{"-", "3"}], ",", "4", ",", 
+     RowBox[{"-", "1"}], ",", "4", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "2"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "9", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "3", ",", "5", ",", "8", ",", "3", ",", "10", ",", "5", ",", "2", ",", 
+       "10", ",", "3", ",", "8", ",", "5", ",", "4"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "9", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.6313948451644297`*^9, 3.6313948491818047`*^9}, {
+   3.631407928127941*^9, 3.6314079284627113`*^9}, {3.631408053985044*^9, 
+   3.631408067440769*^9}, 3.6331212414007072`*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563000163797*^9, 3.680563000206311*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.68056300206859*^9, 3.680563002078245*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "10.1", ".1"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", "1", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "4"}], ",", "1", ",", "3"}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "10.11", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "11", ",", "7", ",", "10", ",", "3", ",", "1", ",", "8", ",", "1", ",", 
+       "9", ",", "8", ",", "7", ",", "5", ",", "10"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "10.11", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394819132061*^9, 3.6313948218945*^9}, {
+   3.6314079305952578`*^9, 3.6314079309694433`*^9}, {3.631408089849622*^9, 
+   3.6314081060351667`*^9}, 3.6331212499350643`*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563004009939*^9, 3.680563004058179*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563005220869*^9, 3.680563005232911*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "10.1", ".2"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"1", ",", "4", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "5"}], ",", "3", ",", "1"}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "10.12", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "11", ",", "3", ",", "10", ",", "9", ",", "7", ",", "5", ",", "9", ",", 
+       "5", ",", "10", ",", "1", ",", "10", ",", "3", ",", "7", ",", "9", ",",
+        "8", ",", "3", ",", "7", ",", "8", ",", "9", ",", "10", ",", "1", ",",
+        "7", ",", "3", ",", "11"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "10.12", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.6313948089203568`*^9, 3.631394811553084*^9}, {
+   3.631407933939434*^9, 3.631407934287516*^9}, {3.631408117045911*^9, 
+   3.631408132752613*^9}, 3.631408198169902*^9, 3.6331212625521107`*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563008067267*^9, 3.6805630081180487`*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563009277154*^9, 3.680563009289278*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "10.2"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"3", ",", "2", ",", 
+     RowBox[{"-", "2"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "1"}], ",", "1", ",", "3"}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "10.2", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "12", ",", "3", ",", "1", ",", "7", ",", "12", ",", "11", ",", "12", 
+       ",", "7", ",", "5", ",", "12", ",", "8", ",", "3", ",", "1", ",", "10",
+        ",", "12", ",", "11", ",", "12", ",", "10", ",", "5", ",", "9", ",", 
+       "12", ",", "12", ",", "9", ",", "8"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "10.2", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394803094509*^9, 3.631394813657806*^9}, {
+   3.631407936093664*^9, 3.6314079364952927`*^9}, {3.631408238884272*^9, 
+   3.631408255699586*^9}, 3.633121273941856*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563012205556*^9, 3.680563012256349*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563013517186*^9, 3.680563013529673*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "11"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", "4", ",", 
+     RowBox[{"-", "2"}], ",", "4", ",", "1", ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "5"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "11", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "4", ",", "7", ",", "9", ",", "2", ",", "10", ",", "9", ",", "7", ",", 
+       "3", ",", "2", ",", "7", ",", "2", ",", "9"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "11", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.6313947976715117`*^9, 3.631394799030184*^9}, {
+   3.631407938955155*^9, 3.63140793933766*^9}, {3.63140828085923*^9, 
+   3.6314082825200987`*^9}, {3.6314085078841143`*^9, 
+   3.6314085083080053`*^9}, {3.631408545204936*^9, 3.631408554349113*^9}, 
+   3.633121284619705*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805630159129543`*^9, 3.6805630159538927`*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563018839376*^9, 3.680563018851039*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", 
+   RowBox[{"Case", " ", "12.1", ".1"}], "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"4", ",", "3", ",", 
+     RowBox[{"-", "4"}], ",", "4", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "1"}], ",", "2", ",", 
+     RowBox[{"-", "4"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{"edges", "[", "12.11", "]"}], " ", "=", " ", 
+    RowBox[{"{", 
+     RowBox[{
+     "3", ",", "2", ",", "10", ",", "8", ",", "3", ",", "10", ",", "6", ",", 
+      "11", ",", "7", ",", "8", ",", "10", ",", "9"}], "}"}]}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "12.11", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631407941571385*^9, 3.6314079418829117`*^9}, {
+   3.63140858865242*^9, 3.631408613420938*^9}, 3.6331212940280857`*^9}],
+
+Cell[CellGroupData[{
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563021163294*^9, 3.680563021206586*^9}}],
+
+Cell[BoxData[
+ RowBox[{"{", 
+  RowBox[{
+  "7", ",", "6", ",", "11", ",", "10", ",", "3", ",", "2", ",", "3", ",", 
+   "10", ",", "8", ",", "9", ",", "8", ",", "10"}], "}"}]], "Output",
+ CellChangeTimes->{{3.631408591931617*^9, 3.631408614493143*^9}}]
+}, Open  ]],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563023164904*^9, 3.6805630231759367`*^9}}],
+
+Cell[BoxData[
+ RowBox[{"(*", " ", 
+  RowBox[{"Case", " ", "12.1", ".2", " ", 
+   RowBox[{"{", 
+    RowBox[{
+    "7", ",", "3", ",", "11", ",", "3", ",", "7", ",", "8", ",", "9", ",", 
+     "8", ",", "7", ",", "6", ",", "9", ",", "7", ",", "9", ",", "6", ",", 
+     "10", ",", "2", ",", "10", ",", "6", ",", "11", ",", "2", ",", "6", ",", 
+     "2", ",", "11", ",", "3"}], "}"}], " ", "missing"}], " ", 
+  "*)"}]], "Input"],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "12.2"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"3", ",", "3", ",", 
+     RowBox[{"-", "2"}], ",", "1", ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "3"}], ",", "3", ",", 
+     RowBox[{"-", "3"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "12.2", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "12", ",", "6", ",", "11", ",", "9", ",", "12", ",", "10", ",", "8", 
+       ",", "7", ",", "12", ",", "2", ",", "12", ",", "3", ",", "3", ",", 
+       "12", ",", "11", ",", "12", ",", "9", ",", "8", ",", "12", ",", "2", 
+       ",", "10", ",", "6", ",", "12", ",", "7"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "12.2", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394737253972*^9, 3.631394760553743*^9}, {
+   3.631407944469575*^9, 3.631407944815346*^9}, {3.631408631821705*^9, 
+   3.6314086478804197`*^9}, 3.633121303440853*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563025748065*^9, 3.680563025795826*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563026869272*^9, 3.680563026881921*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "13.1"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"4", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "2"}], ",", "2", ",", 
+     RowBox[{"-", "5"}], ",", "4", ",", "1", ",", 
+     RowBox[{"-", "3"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "13.1", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "10", ",", "1", ",", "2", ",", "7", ",", "6", ",", "11", ",", "0", ",", 
+       "8", ",", "3", ",", "5", ",", "4", ",", "9"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "13.1", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394727493661*^9, 3.631394758238652*^9}, {
+   3.63140794704698*^9, 3.631407947363307*^9}, {3.631408662082938*^9, 
+   3.6314086763963957`*^9}, 3.633121311710182*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805630296515913`*^9, 3.6805630296957293`*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805630307890263`*^9, 3.680563030801896*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "13.2"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"3", ",", 
+     RowBox[{"-", "5"}], ",", 
+     RowBox[{"-", "3"}], ",", "3", ",", 
+     RowBox[{"-", "2"}], ",", "4", ",", "1", ",", 
+     RowBox[{"-", "3"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "13.2", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "1", ",", "2", ",", "10", ",", "11", ",", "7", ",", "6", ",", "5", ",", 
+       "4", ",", "3", ",", "0", ",", "9", ",", "5", ",", "4", ",", "8", ",", 
+       "3", ",", "5", ",", "3", ",", "0"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "13.2", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.63139471862879*^9, 3.6313947330710983`*^9}, 
+   3.63139483385598*^9, {3.631407950378997*^9, 3.631407950723626*^9}, {
+   3.631408684868675*^9, 3.631408701318103*^9}, 3.6331213220729637`*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563033025518*^9, 3.680563033074341*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563034308807*^9, 3.680563034321159*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "13.3"}], " ", "*)"}], "\n", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"1", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "5"}], ",", "3", ",", 
+     RowBox[{"-", "2"}], ",", "4", ",", "1", ",", 
+     RowBox[{"-", "5"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "13.3", "]"}], "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "4", ",", "12", ",", "5", ",", "12", ",", "2", ",", "10", ",", "1", ",",
+        "12", ",", "9", ",", "12", ",", "1", ",", "2", ",", "12", ",", "8", 
+       ",", "3", ",", "5", ",", "12", ",", "10", ",", "9", ",", "12", ",", 
+       "0", ",", "6", ",", "11", ",", "7", ",", "3", ",", "0", ",", "12", ",",
+        "8", ",", "12", ",", "4"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "13.3", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.63139470671773*^9, 3.631394724547656*^9}, {
+   3.631394825476151*^9, 3.6313948259706297`*^9}, {3.6313949755464687`*^9, 
+   3.631394996412354*^9}, 3.6331213313343*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563037251895*^9, 3.680563037296956*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563038460827*^9, 3.680563038473641*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "13.4"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"3", ",", 
+     RowBox[{"-", "2"}], ",", 
+     RowBox[{"-", "5"}], ",", "1", ",", 
+     RowBox[{"-", "4"}], ",", "4", ",", "3", ",", 
+     RowBox[{"-", "1"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "13.4", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "12", ",", "4", ",", "8", ",", "10", ",", "5", ",", "12", ",", "0", ",",
+        "12", ",", "3", ",", "4", ",", "12", ",", "7", ",", "2", ",", "12", 
+       ",", "1", ",", "12", ",", "0", ",", "9", ",", "1", ",", "12", ",", "9",
+        ",", "12", ",", "11", ",", "7", ",", "8", ",", "3", ",", "12", ",", 
+       "6", ",", "12", ",", "5", ",", "2", ",", "10", ",", "12", ",", "6", 
+       ",", "11", ",", "12"}], "}"}]}], ";"}], "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "13.4", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.631394690476223*^9, 3.63139471402639*^9}, 
+   3.631394827211952*^9, {3.631407954100053*^9, 3.631407954450738*^9}, {
+   3.631408719998084*^9, 3.631408735104987*^9}, 3.6331213404170103`*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563041741757*^9, 3.680563041793129*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805630428528967`*^9, 3.680563042866377*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "13.5", ".1"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"3", ",", 
+     RowBox[{"-", "1"}], ",", 
+     RowBox[{"-", "5"}], ",", "3", ",", 
+     RowBox[{"-", "3"}], ",", "3", ",", "1", ",", 
+     RowBox[{"-", "3"}]}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "13.51", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "2", ",", "10", ",", "4", ",", "4", ",", "8", ",", "2", ",", "1", ",", 
+       "0", ",", "9", ",", "2", ",", "8", ",", "3", ",", "6", ",", "11", ",", 
+       "7", ",", "4", ",", "10", ",", "5"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "13.51", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{{3.63139468530927*^9, 3.631394696847465*^9}, {
+   3.631394828304537*^9, 3.631394830359082*^9}, {3.631407956132036*^9, 
+   3.631407956500371*^9}, {3.631408752012755*^9, 3.6314087657974443`*^9}, 
+   3.63312134963776*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563046628022*^9, 3.6805630466724443`*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563048621201*^9, 3.680563048633296*^9}}],
+
+Cell[BoxData[
+ RowBox[{"(*", " ", 
+  RowBox[{"13.5", ".2", " ", 
+   RowBox[{"{", 
+    RowBox[{
+    "1", ",", "0", ",", "9", ",", "7", ",", "4", ",", "8", ",", "7", ",", "8",
+      ",", "3", ",", "7", ",", "3", ",", "11", ",", "2", ",", "11", ",", "3", 
+     ",", "11", ",", "2", ",", "6", ",", "2", ",", "10", ",", "6", ",", "5", 
+     ",", "6", ",", "10", ",", "6", ",", "5", ",", "7", ",", "4", ",", "7", 
+     ",", "5"}], "}"}], " ", "missing"}], " ", "*)"}]], "Input",
+ CellChangeTimes->{{3.631394699503543*^9, 3.631394701167807*^9}}],
+
+Cell[BoxData[
+ RowBox[{
+  RowBox[{"(*", " ", 
+   RowBox[{"Case", " ", "14"}], " ", "*)"}], "\[IndentingNewLine]", 
+  RowBox[{
+   RowBox[{"IsosurfacePlot", "[", 
+    RowBox[{"2", ",", "3", ",", 
+     RowBox[{"-", "3"}], ",", "3", ",", 
+     RowBox[{"-", "4"}], ",", 
+     RowBox[{"-", "3"}], ",", 
+     RowBox[{"-", "5"}], ",", "1"}], "]"}], "\[IndentingNewLine]", 
+   RowBox[{
+    RowBox[{
+     RowBox[{"edges", "[", "14", "]"}], " ", "=", " ", 
+     RowBox[{"{", 
+      RowBox[{
+      "2", ",", "6", ",", "3", ",", "6", ",", "5", ",", "9", ",", "6", ",", 
+       "9", ",", "8", ",", "3", ",", "6", ",", "8"}], "}"}]}], ";"}], 
+   "\[IndentingNewLine]", 
+   RowBox[{"TriangulatedIsosurfacePlot", "[", 
+    RowBox[{"edges", "[", "14", "]"}], "]"}]}]}]], "Input",
+ CellChangeTimes->{
+  3.631394702763289*^9, {3.631407958010763*^9, 3.631407959771985*^9}, {
+   3.631408772362403*^9, 3.631408788250387*^9}, 3.6331213594095*^9}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.6805630510747833`*^9, 3.680563051117893*^9}}],
+
+Cell[BoxData[""], "Input",
+ CellChangeTimes->{{3.680563052396535*^9, 3.680563052407886*^9}}]
+},
+WindowSize->{1440, 791},
+WindowMargins->{{0, Automatic}, {Automatic, 0}},
+ShowSelection->True,
+FrontEndVersion->"8.0 for Mac OS X x86 (32-bit, 64-bit Kernel) (July 22, \
+2012)",
+StyleDefinitions->"Default.nb"
+]
+(* End of Notebook Content *)
+
+(* Internal cache information *)
+(*CellTagsOutline
+CellTagsIndex->{}
+*)
+(*CellTagsIndex
+CellTagsIndex->{}
+*)
+(*NotebookFileOutline
+Notebook[{
+Cell[557, 20, 4093, 83, 433, "Input"],
+Cell[4653, 105, 1371, 40, 58, "Input"],
+Cell[6027, 147, 8092, 189, 373, "Input"],
+Cell[14122, 338, 9392, 215, 388, "Input"],
+Cell[23517, 555, 371, 8, 43, "Input"],
+Cell[23891, 565, 1144, 27, 73, "Input"],
+Cell[25038, 594, 94, 1, 27, InheritFromParent],
+Cell[25135, 597, 92, 1, 27, InheritFromParent],
+Cell[25230, 600, 845, 22, 73, "Input"],
+Cell[26078, 624, 94, 1, 27, InheritFromParent],
+Cell[26175, 627, 92, 1, 27, InheritFromParent],
+Cell[26270, 630, 821, 22, 73, "Input"],
+Cell[27094, 654, 96, 1, 27, InheritFromParent],
+Cell[27193, 657, 92, 1, 27, InheritFromParent],
+Cell[27288, 660, 977, 25, 73, "Input"],
+Cell[28268, 687, 94, 1, 27, InheritFromParent],
+Cell[28365, 690, 92, 1, 27, InheritFromParent],
+Cell[28460, 693, 831, 23, 73, "Input"],
+Cell[29294, 718, 91, 1, 27, InheritFromParent],
+Cell[29388, 721, 92, 1, 27, InheritFromParent],
+Cell[29483, 724, 953, 25, 73, "Input"],
+Cell[30439, 751, 92, 1, 27, InheritFromParent],
+Cell[30534, 754, 94, 1, 27, InheritFromParent],
+Cell[30631, 757, 850, 23, 73, "Input"],
+Cell[31484, 782, 92, 1, 27, InheritFromParent],
+Cell[31579, 785, 91, 1, 27, InheritFromParent],
+Cell[31673, 788, 950, 23, 73, "Input"],
+Cell[32626, 813, 92, 1, 27, InheritFromParent],
+Cell[32721, 816, 94, 1, 27, InheritFromParent],
+Cell[32818, 819, 451, 9, 27, "Input"],
+Cell[33272, 830, 961, 23, 73, "Input"],
+Cell[34236, 855, 92, 1, 27, InheritFromParent],
+Cell[34331, 858, 94, 1, 27, InheritFromParent],
+Cell[34428, 861, 889, 22, 73, "Input"],
+Cell[35320, 885, 92, 1, 27, InheritFromParent],
+Cell[35415, 888, 91, 1, 27, InheritFromParent],
+Cell[35509, 891, 897, 23, 73, "Input"],
+Cell[36409, 916, 92, 1, 27, InheritFromParent],
+Cell[36504, 919, 92, 1, 27, InheritFromParent],
+Cell[36599, 922, 1129, 26, 73, "Input"],
+Cell[37731, 950, 92, 1, 27, InheritFromParent],
+Cell[37826, 953, 93, 1, 27, InheritFromParent],
+Cell[37922, 956, 973, 24, 73, "Input"],
+Cell[38898, 982, 92, 1, 27, InheritFromParent],
+Cell[38993, 985, 92, 1, 27, InheritFromParent],
+Cell[39088, 988, 515, 10, 27, "Input"],
+Cell[39606, 1000, 878, 21, 73, "Input"],
+Cell[40487, 1023, 94, 1, 27, InheritFromParent],
+Cell[40584, 1026, 96, 1, 27, InheritFromParent],
+Cell[40683, 1029, 956, 23, 73, "Input"],
+Cell[41642, 1054, 92, 1, 27, InheritFromParent],
+Cell[41737, 1057, 91, 1, 27, InheritFromParent],
+Cell[41831, 1060, 972, 23, 73, "Input"],
+Cell[42806, 1085, 92, 1, 27, InheritFromParent],
+Cell[42901, 1088, 92, 1, 27, InheritFromParent],
+Cell[42996, 1091, 1127, 24, 73, "Input"],
+Cell[44126, 1117, 94, 1, 27, InheritFromParent],
+Cell[44223, 1120, 92, 1, 27, InheritFromParent],
+Cell[44318, 1123, 1105, 25, 73, "Input"],
+Cell[45426, 1150, 92, 1, 27, InheritFromParent],
+Cell[45521, 1153, 92, 1, 27, InheritFromParent],
+Cell[45616, 1156, 1056, 25, 73, "Input"],
+Cell[46675, 1183, 96, 1, 27, InheritFromParent],
+Cell[46774, 1186, 92, 1, 27, InheritFromParent],
+Cell[46869, 1189, 885, 21, 73, "Input"],
+Cell[CellGroupData[{
+Cell[47779, 1214, 92, 1, 27, InheritFromParent],
+Cell[47874, 1217, 250, 5, 27, "Output"]
+}, Open  ]],
+Cell[48139, 1225, 94, 1, 27, InheritFromParent],
+Cell[48236, 1228, 422, 9, 27, "Input"],
+Cell[48661, 1239, 1105, 25, 73, "Input"],
+Cell[49769, 1266, 92, 1, 27, InheritFromParent],
+Cell[49864, 1269, 92, 1, 27, InheritFromParent],
+Cell[49959, 1272, 958, 23, 73, "Input"],
+Cell[50920, 1297, 96, 1, 27, InheritFromParent],
+Cell[51019, 1300, 94, 1, 27, InheritFromParent],
+Cell[51116, 1303, 1049, 24, 73, "Input"],
+Cell[52168, 1329, 92, 1, 27, InheritFromParent],
+Cell[52263, 1332, 92, 1, 27, InheritFromParent],
+Cell[52358, 1335, 1146, 25, 73, "Input"],
+Cell[53507, 1362, 92, 1, 27, InheritFromParent],
+Cell[53602, 1365, 92, 1, 27, InheritFromParent],
+Cell[53697, 1368, 1262, 26, 73, "Input"],
+Cell[54962, 1396, 92, 1, 27, InheritFromParent],
+Cell[55057, 1399, 94, 1, 27, InheritFromParent],
+Cell[55154, 1402, 1084, 25, 73, "Input"],
+Cell[56241, 1429, 94, 1, 27, InheritFromParent],
+Cell[56338, 1432, 92, 1, 27, InheritFromParent],
+Cell[56433, 1435, 538, 10, 27, "Input"],
+Cell[56974, 1447, 922, 23, 73, "Input"],
+Cell[57899, 1472, 94, 1, 27, InheritFromParent],
+Cell[57996, 1475, 92, 1, 27, InheritFromParent]
+}
+]
+*)
+
+(* End of internal cache information *)
diff --git a/libraries/distanceField/marchingCubes.cpp b/libraries/distanceField/marchingCubes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..29e0b32978dfd515b0b013f116e4ca8f44bd458c
--- /dev/null
+++ b/libraries/distanceField/marchingCubes.cpp
@@ -0,0 +1,1366 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Jernej Barbic                             *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  The labelings of the vertices, edges and faces of the cube follow the one in:
+
+  Thomas Lewiner, Hélio Lopes, Antônio Wilson Vieira, Geovan Tavares:
+  Efficient implementation of marching cubes cases with topological guarantees
+  Journal of Graphics Tools 8(2): pp. 1-15, 2003
+*/
+
+// when a static table is not used, the table is generated from scratch using our code ("createTable()")
+// otherwise, the marching cubes table is read from a static array (that was previously generated using "createTable()")
+#define USE_STATIC_TABLE
+
+#include <float.h>
+#include <memory.h>
+#include <string>
+#include <algorithm>
+#include <set>
+#include <iomanip>
+#include <fstream>
+#include <cassert>
+#include <unordered_map>
+using namespace std;
+#include "vec3d.h"
+#include "marchingCubes.h"
+#include "performanceCounter.h"
+
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#else
+  #include "range.h"
+#endif
+
+static const unsigned char edge_vertices[12][2] =
+{
+  { 0, 1 },
+  { 1, 2 },
+  { 2, 3 },
+  { 0, 3 },
+  { 4, 5 },
+  { 5, 6 },
+  { 6, 7 },
+  { 4, 7 },
+  { 0, 4 },
+  { 1, 5 },
+  { 2, 6 },
+  { 3, 7 } };
+
+static const unsigned char face_vertices[6][4] =
+{
+  { 0, 1, 4, 5 },
+  { 1, 2, 5, 6 },
+  { 2, 3, 6, 7 },
+  { 0, 3, 4, 7 },
+  { 0, 1, 2, 3 },
+  { 4, 5, 6, 7 } };
+
+/*
+ The 24 symmetries are:
+ Identity
+ 3 rotations (by +- pi/2 or pi) about centres of 3 pairs of opposite faces. (9)
+ 1 rotation (by pi) about centres of 6 pairs of opposite edges. (6)
+ 2 rotations (by +- 2pi/3) about 4 pairs of opposite vertices (diagonals). (8)
+*/
+
+// this table was generated manually, by physically manufacturing two cubes, 
+// labeling the corners and applying each transformation
+static const unsigned char cubeSymmetryVertices[25][8] =
+{
+    { 0, 1, 2, 3, 4, 5, 6, 7 }, // identity                                     1
+    { 3, 2, 6, 7, 0, 1, 5, 4 }, // rotation by +pi/2 about faces 1,3            2
+    { 1, 5, 6, 2, 0, 4, 7, 3 }, // rotation by +pi/2 about faces 0,2            3
+    { 1, 2, 3, 0, 5, 6, 7, 4 }, // rotation by +pi/2 about faces 4,5            4
+    { 4, 5, 1, 0, 7, 6, 2, 3 }, // rotation by -pi/2 about faces 1,3            5
+    { 4, 0, 3, 7, 5, 1, 2, 6 }, // rotation by -pi/2 about faces 0,2            6
+    { 3, 0, 1, 2, 7, 4, 5, 6 }, // rotation by -pi/2 about faces 4,5            7
+    { 7, 6, 5, 4, 3, 2, 1, 0 }, // rotation by pi about faces 1,3               8
+    { 5, 4, 7, 6, 1, 0, 3, 2 }, // rotation by pi about faces 0,2               9
+    { 2, 3, 0, 1, 6, 7, 4, 5 }, // rotation by pi about faces 4,5               10
+    { 1, 0, 4, 5, 2, 3, 7, 6 }, // rotation by pi about edges 0, 6              11
+    { 6, 2, 1, 5, 7, 3, 0, 4 }, // rotation by pi about edges 1, 7              12
+    { 6, 7, 3, 2, 5, 4, 0, 1 }, // rotation by pi about edges 2, 4              13
+    { 3, 7, 4, 0, 2, 6, 5, 1 }, // rotation by pi about edges 3, 5              14
+    { 4, 7, 6, 5, 0, 3, 2, 1 }, // rotation by pi about edges 8, 10             15
+    { 6, 5, 4, 7, 2, 1, 0, 3 }, // rotation by pi about edges 9, 11             16
+    { 0, 4, 5, 1, 3, 7, 6, 2 }, // rotation by +2pi/3 about vertices 0, 6       17
+    { 5, 1, 0, 4, 6, 2, 3, 7 }, // rotation by +2pi/3 about vertices 1, 7       18
+    { 5, 6, 2, 1, 4, 7, 3, 0 }, // rotation by +2pi/3 about vertices 2, 4
+    { 7, 4, 0, 3, 6, 5, 1, 2 }, // rotation by +2pi/3 about vertices 3, 5
+    { 0, 3, 7, 4, 1, 2, 6, 5 }, // rotation by -2pi/3 about vertices 0, 6
+    { 2, 1, 5, 6, 3, 0, 4, 7 }, // rotation by -2pi/3 about vertices 1, 7
+    { 7, 3, 2, 6, 4, 0, 1, 5 }, // rotation by -2pi/3 about vertices 2, 4
+    { 2, 6, 7, 3, 1, 5, 4, 0 }, // rotation by -2pi/3 about vertices 3, 5
+    { 4, 5, 6, 7, 0, 1, 2, 3 } };
+
+// The positions of the eight vertices
+static const unsigned char vertex_position[8][3] =
+{
+  { 0, 0, 0 },
+  { 1, 0, 0 },
+  { 1, 1, 0 },
+  { 0, 1, 0 },
+  { 0, 0, 1 },
+  { 1, 0, 1 },
+  { 1, 1, 1 },
+  { 0, 1, 1 } 
+};
+
+// The four corners of the six faces 
+static const unsigned char face_vertex[6][4] =
+{
+  { 0, 1, 5, 4 },
+  { 1, 2, 6, 5 },
+  { 3, 2, 6, 7 },
+  { 0, 3, 7, 4 },
+  { 0, 1, 2, 3 },
+  { 4, 5, 6, 7 } 
+};
+
+// edge_map and edge_vertex: how vertices are placed around an edge
+/*
+  edge_map
+  first index can be 0, 1 or 2. 0 means this edge is parallel to X axis. 1 means parallel to Y axis. 2 means parallel to Z axis.
+  second index: which specific place is the edge in the X, Y or Z direction.
+  This to indices point to the start position of edge_vertex
+*/
+static const unsigned char edge_map[12][2] =
+{
+  { 0, 0 },
+  { 1, 0 },
+  { 0, 2 },
+  { 1, 2 },
+  { 0, 6 },
+  { 1, 6 },
+  { 0, 4 },
+  { 1, 4 },
+  { 2, 0 },
+  { 2, 6 },
+  { 2, 4 },
+  { 2, 2 } };
+
+/*
+  edge_vertex: how the 8 vertices are placed around an edge
+  The start vertex is determined by edge_map, and then pick up 8 consecutive vertices.
+  The first two vertices are the two endpoints of this edge.
+  The next 6 vertices make up 3 pairs(two consecutive vertices make up one pair). Each pair is the two endpoints of an edge whose direction is the same of this edge.
+  The four edges are in clockwise or counter clockwise (depending on how you look).
+*/
+
+static const unsigned char edge_vertex[3][14] =
+{
+  { 0, 1, 3, 2, 7, 6, 4, 5, 0, 1, 3, 2, 7, 6 },  // X direction
+  { 1, 2, 0, 3, 4, 7, 5, 6, 1, 2, 0, 3, 4, 7 },  // Y direction
+  { 0, 4, 3, 7, 2, 6, 1, 5, 0, 4, 3, 7, 2, 6 }   // Z direction
+};
+
+#ifdef USE_STATIC_TABLE
+  #include "marchingCubesTable.h"
+#else
+  static char cubeSymmetryEdges[25][13]; // determined algorithmically from cubeSymmetryVertices
+
+  static char cubeSymmetryFaces[24][6]; // determined algorithmically from cubeSymmetryVertices
+
+  // if first entry is negative, this is a representative case, second entry has no meaning (it is always set to 0)
+  // if first entry i is non-negative, this is a symmetry and/or complement of the representative case i; second entry specifies the symmetry and/or complement;
+  // if second entry j is positive, this is a symmetry using the permutation j from cubeSymmetryVertices; 
+  // if second entry j is zero or negative,  this is symmetry |j|, plus complement
+  // can determine algorithmically from cubeSymmetryVertices
+  static int marchingCubeSymmetries[256][3];
+
+  //used to build the look up table for case 13 mentioned in Chernyaev’s paper
+  const int oppositeFace[3][2] = {{1, 3}, {2, 4}, {5, 6}};
+  const int vtxAdjacentFaces[8][3] = {{1,4,5}, {1,2,5}, {2,3,5}, {3,4,5}, {1,4,6}, {1,2,6}, {2,3,6}, {3,4,6}};
+
+  static const unsigned char caseNumber[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 9, 14, 12, 10, 13 };
+
+  static const char representativeCase1[3] = { 8, 3, 0 };
+  static const char representativeCase2[6] = { 3, 1, 8, 9, 8, 1 };
+  static const char representativeCase3_1[6] = { 1, 2, 10, 8, 3, 0 };
+  static const char representativeCase3_2[12] = { 8, 3, 10, 10, 1, 0, 0, 8, 10, 2, 10, 3 };
+  static const char representativeCase4_1[6] = { 6, 5, 10, 3, 0, 8 };
+  static const char representativeCase4_2[18] = { 8, 6, 5, 8, 5, 0, 6, 3, 10, 0, 5, 10, 0, 10, 3, 3, 6, 8 };
+  static const char representativeCase5[9] = { 8, 3, 2, 10, 8, 2, 10, 9, 8 };
+  static const char representativeCase6_1_1[9] = { 6, 5, 10, 8, 1, 9, 8, 3, 1 };
+  static const char representativeCase6_2[15] = { 8, 6, 5, 10, 3, 1, 8, 3, 6, 9, 8, 5, 6, 3, 10 };
+  static const char representativeCase7_1[9] = { 1, 2, 10, 9, 5, 4, 8, 3, 0 };
+  static const char representativeCase7_2_0[15] = { 1, 2, 10, 4, 8, 3, 5, 4, 3, 0, 5, 3, 5, 0, 9 };
+  static char representativeCase7_2_1[15]; // determined algorithmically
+  static char representativeCase7_2_2[15]; // determined algorithmically
+  static const char representativeCase7_3_0[27] = { 12, 1, 2, 12, 9, 1, 5, 12, 10, 9, 12, 0, 12, 8, 3, 12, 5, 4, 0, 12, 3, 10, 12, 2, 4, 8, 12 };
+  static char representativeCase7_3_1[27]; // determined algorithmically
+  static char representativeCase7_3_2[27]; // determined algorithmically
+  static const char representativeCase7_4_1[15] = { 5, 4, 8, 3, 2, 8, 9, 1, 0, 2, 5, 8, 5, 2, 10 };
+  static const char representativeCase8[6] = { 10, 9, 8, 8, 11, 10 };
+  static const char representativeCase9[12] = { 3, 5, 8, 3, 10, 5, 2, 10, 3, 8, 5, 4 };
+  static const char representativeCase10_1_1[12] = { 11, 7, 10, 3, 1, 8, 1, 9, 8, 7, 5, 10 };
+  static char representativeCase10_1_1_[12]; // determined algorithmically
+  static const char representativeCase10_1_2[24] = { 11, 3, 10, 9, 7, 5, 9, 5, 10, 1, 10, 3, 7, 9, 8, 3, 7, 8, 9, 10, 1, 7, 3, 11 };
+  static const char representativeCase10_2[24] = { 12, 3, 1, 7, 12, 11, 12, 7, 5, 12, 8, 3, 1, 10, 12, 11, 12, 10, 5, 9, 12, 12, 9, 8 };
+  static char representativeCase10_2_[24]; // determined algorithmically
+  static const char representativeCase11[12] = { 4, 7, 9, 2, 10, 9, 7, 3, 2, 7, 2, 9 };
+  static const char representativeCase12_1_1[12] = { 3, 2, 10, 8, 3, 10, 6, 11, 7, 8, 10, 9 };
+  static char representativeCase12_1_1_[12]; // determined algorithmically
+  static const char representativeCase12_2[24] = { 12, 6, 11, 9, 12, 10, 8, 7, 12, 2, 12, 3, 3, 12, 11, 12, 9, 8, 12, 2, 10, 6, 12, 7 };
+  static char representativeCase12_2_[24]; // determined algorithmically
+
+  /* The subcases of Case 13 are not clearly defined in Lewiner's paper, they are described in Chernyaev’s paper */
+  static const char representativeCase13_1[12] = { 10, 1, 2, 7, 6, 11, 0, 8, 3, 5, 4, 9 };
+  static char representativeCase13_1_[12]; // determined algorithmically
+  static const char representativeCase13_2_0[18] = { 1, 2, 10, 11, 7, 6, 5, 4, 3, 0, 9, 5, 4, 8, 3, 5, 3, 0 };
+  static char representativeCase13_2[5][18]; // determined algorithmically
+  static char representativeCase13_2_0_[18]; // determined algorithmically
+  static char representativeCase13_2_[5][18]; // determined algorithmically
+  static const char representativeCase13_3_0[30] = { 4, 12, 5, 12, 2, 10, 1, 12, 9, 12, 1, 2, 12, 8, 3, 5, 12, 10, 9, 12, 0, 6, 11, 7, 3, 0, 12, 8, 12, 4 };
+  static char representativeCase13_3[11][30]; // determined algorithmically
+  static char representativeCase13_3_0_[30]; // determined algorithmically
+  static char representativeCase13_3_[11][30]; // determined algorithmically
+  static const char representativeCase13_4_0[36] = { 12, 4, 8, 10, 5, 12, 0, 12, 3, 4, 12, 7, 2, 12, 1, 12, 0, 9, 1, 12, 9, 12, 11, 7, 8, 3, 12, 6, 12, 5, 2, 10, 12, 6, 11, 12 };
+  static char representativeCase13_4[3][36]; // determined algorithmically
+  static const char representativeCase13_5_1_0[18] = { 2, 10, 4, 4, 8, 2, 1, 0, 9, 2, 8, 3, 6, 11, 7, 4, 10, 5 };
+  static char representativeCase13_5_1[3][18]; // determined algorithmically
+  static const char representativeCase14[12] = { 2, 6, 3, 6, 5, 9, 6, 9, 8, 3, 6, 8 };
+
+  // the numbers of faces should be used in face test for all 256 cases
+  static vector<char> faceTest_num[256];
+  // may be removed later 
+  static char interiorTest_num[256];
+
+  static vector<char> ambiguityTable[15];
+   
+  /*  
+    "triangleTable" is a three-dimensional data structure.
+    The first dimension is the 256 cases, depending on the signs of values of eight corners.
+    The second dimension is for each case, there may exist some subcases depending on the results of face tests and interior tests.
+    The third dimension is an array, the midpoints of each three consecutive edges form a triangle and a boolean value indicating whether this case has an point in the center of a cube, encoded as 12.
+  */
+  static vector<vector<unsigned char> > triangleTable[256];
+  static vector<bool> centerVertexNeeded[256];
+
+  /*  
+    "rc" is a three-dimensional data structure.
+    The first dimension is the 15 representive cases, depending on the signs of values of eight corners.
+    The second dimension is for each representive case, there may exist some subcases depending on the results of face tests and interior tests.
+    The third dimension is an array, the center of the cube, encoded as 12, the midpoints of each three consecutive edges form a triangle.
+  */
+  static std::vector<std::vector<char> > rc[15];
+
+  static const char faceTest3[1] = { 5 };
+  static const char faceTest6[1] = { 2 };
+  static const char faceTest7[3] = { 1, 2, 5 };
+  static const char faceTest10[2] = { 2, 4 };
+  static const char faceTest12[2] = { 4, 3 };
+  static const char faceTest13[6] = { 1, 2, 3, 4, 5, 6 };
+  static const char * faceTest_number[15] = { 0, 0, 0, faceTest3, 0, 0, faceTest6, faceTest7, 0, 0, faceTest10, 0, faceTest12, faceTest13, 0 };
+  static const char faceTest_size[15] = { 0, 0, 0, 1, 0, 0, 1, 3, 0, 0, 2, 0, 2, 6, 0 };
+  static const char interiorTest_number[15] = { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 };
+
+  // Whether the look up tables have been loaded. Only need to be loaded once. 
+  static bool tableLoaded = false;
+#endif
+
+MarchingCubes::MarchingCubes(const DistanceFieldBase * field, float iv) : distanceFieldBase(field), isoValue(iv)
+{
+  resolutionX = distanceFieldBase->getResolutionX();
+  resolutionY = distanceFieldBase->getResolutionY();
+  resolutionZ = distanceFieldBase->getResolutionZ();
+}
+
+/*
+  Perform the face test: whether the product of the two positive values is larger than the product of the two negative values.
+  If the face is negative, the result needs to be reversed.
+*/
+bool MarchingCubes::faceTest(int face_, float cube[8])
+{
+  int face = abs(face_) - 1;
+  return (((cube[face_vertex[face][0]] * cube[face_vertex[face][2]] > cube[face_vertex[face][1]] * cube[face_vertex[face][3]])
+      == (face_ > 0)) == (cube[face_vertex[face][0]] > 0));
+}
+
+/*
+  Perform the interior test.
+  If the face is negative, the result needs to be reversed.
+*/
+int MarchingCubes::interiorTest(int edge, float cube[8])
+{
+  //printf("%d", edge);
+  //for (int p = 0; p < 8; p++) printf(" %.5f", cube[p]); printf("\n");
+  double aux1 = (cube[1] - cube[0]) * (cube[6] - cube[7]) - (cube[5] - cube[4]) * (cube[2] - cube[3]);
+  double aux2 = cube[0] * (cube[6] - cube[7]) - cube[4] * (cube[2] - cube[3]) + cube[7] * (cube[1] - cube[0]) - cube[3] * (cube[5] - cube[4]);
+  double s = -aux2 / (2.0 * aux1);
+  if ((s < 0.0) || (s > 1.0))
+    return edge > 0;
+
+  double A = cube[0] + (cube[1] - cube[0]) * s;
+  double B = cube[4] + (cube[5] - cube[4]) * s;
+  double C = cube[7] + (cube[6] - cube[7]) * s;
+  double D = cube[3] + (cube[2] - cube[3]) * s;
+
+  int result = ((A >= 0)) | ((B >= 0) << 1) | ((C >= 0) << 2) | ((D >= 0) << 3);
+  switch (result)
+  {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 6:
+    case 8:
+    case 9:
+    case 12:
+      return edge > 0;
+    case 7:
+    case 11:
+    case 13:
+    case 14:
+    case 15:
+      return edge < 0;
+    case 5:
+      return (A * C < B * D) == (edge > 0);
+    case 10:
+      return (A * C >= B * D) == (edge > 0);
+    default:
+      return -1;
+  }
+}
+
+ObjMesh * MarchingCubes::compute()
+{
+  #ifndef USE_STATIC_TABLE
+    if (tableLoaded == false)
+      createTable(); // data needs to be loaded
+    //printTable();
+  #endif
+  
+  int gpResX = distanceFieldBase->getResolutionX()+1; // grid point resolution
+  int gpResY = distanceFieldBase->getResolutionY()+1;
+  int gpResZ = distanceFieldBase->getResolutionZ()+1;
+  if (gpResX <= 1 || gpResY <= 1 || gpResZ <= 1)
+    return new ObjMesh();
+  
+  double gridSize[3];
+  distanceFieldBase->getGridSpacing(gridSize, gridSize+1, gridSize+2);
+
+//  cout << "Begin computing: " << endl;
+  PerformanceCounter pc;
+
+//
+//  vector<Vec3d> allVertices;
+//  vector<int> allTriangles;
+//  // edgeInersectionID: an ID to each edge if it intersects with the iso-surface
+//  unordered_map<int64_t, int> edgeIntersectionID2MeshVtxID;
+
+
+  pc.StartCounter();
+
+  vector<Vec3d> allVertices;
+  vector<int> allTriangles;
+  unordered_map<int64_t, int> edgeInterID2AllMeshVtxID;
+
+#ifdef USE_TBB
+  struct ThreadLocalData
+  {
+    vector<Vec3d> vertices;
+    vector<int> triangles;
+    unordered_map<int64_t, int> edgeInterID2LocalVtxID;
+  };
+  
+  tbb::enumerable_thread_specific<ThreadLocalData> threadLocalData;
+  // loop over all voxels again to compute triangles
+  tbb::parallel_for(tbb::blocked_range<int>(0, distanceFieldBase->getResolutionZ()), [&](const tbb::blocked_range<int> & rng)
+//  for (int k = 0; k < distanceFieldBase->getResolutionZ(); k++)
+  {
+    auto & localData = threadLocalData.local();
+    auto & edgeInterID2MeshVtxID = localData.edgeInterID2LocalVtxID;
+    auto & meshVertices = localData.vertices;
+    auto & meshTriangles = localData.triangles;
+#else
+    Range<int> rng(0, distanceFieldBase->getResolutionZ());
+    auto & edgeInterID2MeshVtxID = edgeInterID2AllMeshVtxID;
+    auto & meshVertices = allVertices;
+    auto & meshTriangles = allTriangles;
+#endif
+    for (int k = rng.begin(); k != rng.end(); ++k)
+      for (int j = 0; j < distanceFieldBase->getResolutionY(); ++j)
+        for (int i = 0; i < distanceFieldBase->getResolutionX(); ++i)
+        {
+          float cube[8];
+          //get distance field values at 8 cube vertices
+          for (int p = 0; p < 8; p++)
+          {
+            cube[p] = getDistance(i + vertex_position[p][0], j + vertex_position[p][1], k + vertex_position[p][2]);
+          }
+
+          // raw case [0,255] -> major case [0,14]
+
+          int rawCaseNumber = 0; //raw case number, 0-255
+          for (int p = 0; p < 8; p++)
+            rawCaseNumber |= ((cube[p] > 0) << p);
+
+          //get the major case number, 0-14
+          int majorCaseNumber = marchingCubeSymmetries[rawCaseNumber][0];
+          if ((majorCaseNumber == 0) || (majorCaseNumber == 255))
+            continue;   //No triangles
+
+          int faceAndInteriorTestResult = 0;
+
+          int faceTestNumCase = 0;
+          #ifdef USE_STATIC_TABLE
+            // the first entry in the faceTest_num table stores number of entries
+            faceTestNumCase = faceTest_num[rawCaseNumber][0];
+            if (faceTestNumCase > 0)
+              for (int p = faceTestNumCase; p > 0; p--)
+              {
+                int o = faceTest(faceTest_num[rawCaseNumber][p], cube);
+                faceAndInteriorTestResult = (faceAndInteriorTestResult << 1) | o;
+              }
+          #else
+            faceTestNumCase = int(faceTest_num[rawCaseNumber].size());
+            if (!faceTest_num[rawCaseNumber].empty())
+              for (vector<char>::const_iterator p = faceTest_num[rawCaseNumber].end() - 1; p >= faceTest_num[rawCaseNumber].begin(); p--)
+              {
+                int o = faceTest(*p, cube);
+                faceAndInteriorTestResult = (faceAndInteriorTestResult << 1) | o;
+              }
+          #endif
+
+          if (interiorTest_num[rawCaseNumber] != 0)
+            faceAndInteriorTestResult = faceAndInteriorTestResult | (interiorTest(interiorTest_num[rawCaseNumber], cube) << faceTestNumCase);
+
+          int ambiguityNumber = ambiguityTable[majorCaseNumber][faceAndInteriorTestResult];
+          if (ambiguityNumber < 0)
+          {
+            cerr << "Internal error in computing marching cubes, ambiguityNumber is: " << ambiguityNumber << endl;
+            continue;
+          }
+
+          //Get the list of triangle vertices
+          #ifdef USE_STATIC_TABLE
+            unsigned char * triangles = NULL;
+            int numTri = 0;
+            unsigned char ** tableEntry = triangleTable[rawCaseNumber];
+            bool hasCenter = false;
+            assert(tableEntry);
+            {
+              unsigned char * tableEntry2 = tableEntry[ambiguityNumber];
+              numTri = tableEntry2[0] / 3;
+              hasCenter = (tableEntry2[1] != 0);
+              triangles = tableEntry2 + 2;
+            }
+          #else
+            const vector<unsigned char> & triangles = triangleTable[rawCaseNumber][ambiguityNumber];
+            bool hasCenter = centerVertexNeeded[rawCaseNumber][ambiguityNumber];
+            int numTri = triangles.size() / 3;
+          #endif
+
+          int cubeVtxIndices[13] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
+          Vec3d center = Vec3d(0, 0, 0); // an additional vertex in the middle of the cube may be needed
+          int sum = 0;
+          for (int edgeID = 0; edgeID < 12; edgeID++) // go to 12 edges of the cube
+          {
+            int node = edge_vertex[edge_map[edgeID][0]][edge_map[edgeID][1]];
+            int direction = edge_map[edgeID][0];   //can be 0, 1 or 2. 0 means this edge is parallel to X axis. 1 means parallel to Y axis. 2 means parallel to Z axis.
+            assert(direction >= 0 && direction <= 2);
+            int _i = i + vertex_position[node][0];
+            int _j = j + vertex_position[node][1];
+            int _k = k + vertex_position[node][2];
+
+            float edgeStartValue = getDistance(_i, _j, _k);
+            float edgeEndValue = getDistance(_i+(direction==0), _j+(direction==1), _k+(direction==2));
+            if (edgeStartValue * edgeEndValue <= 0 && edgeEndValue != 0) // TODO: check degenerate case here
+            {
+              // int64_t packedIndex = 3 * (_j * (resX+1) + _i) + direction;
+              int64_t edgeInterID = 3 * ((int64_t)gpResX*gpResY*_k + _j*gpResX + _i) + direction;
+              auto it = edgeInterID2MeshVtxID.find(edgeInterID);
+              if (it == edgeInterID2MeshVtxID.end())
+              {
+                // compute pos
+                Vec3d pos(0.0);
+                Vec3d gridPoint = distanceFieldBase->getGridPosition(_i,_j,_k);
+                if (direction == 0)
+                {
+                  pos = Vec3d(gridPoint[0] + gridSize[0] * edgeStartValue / (edgeStartValue - edgeEndValue), gridPoint[1], gridPoint[2]);
+                }
+                else if (direction == 1)
+                {
+                  pos = Vec3d(gridPoint[0], gridPoint[1] + gridSize[1] * edgeStartValue / (edgeStartValue - edgeEndValue), gridPoint[2]);
+                }
+                else // direction == 2
+                {
+                  pos = Vec3d(gridPoint[0], gridPoint[1], gridPoint[2] + gridSize[2] * edgeStartValue / (edgeStartValue - edgeEndValue));
+                }
+                cubeVtxIndices[edgeID] = meshVertices.size();
+                meshVertices.push_back(pos);
+                edgeInterID2MeshVtxID.emplace(edgeInterID, cubeVtxIndices[edgeID]);
+              }
+              else
+              {
+                cubeVtxIndices[edgeID] = it->second;
+              }
+
+              if (hasCenter)
+              {
+                center += meshVertices[cubeVtxIndices[edgeID]];
+                sum++;
+              }
+            }
+          }
+          if (hasCenter)
+          {
+            center /= sum;
+            // store this vertex globally in allVertices
+            cubeVtxIndices[12] = meshVertices.size();
+            meshVertices.push_back(center);
+            int64_t centerID = - (int64_t)gpResX*gpResY*k + j*gpResX + i - 1; // define an ID to store center vtx into edgeInterID2LocalVtxID
+            edgeInterID2MeshVtxID.emplace(centerID, cubeVtxIndices[12]);
+          }
+
+          for(int triID = 0; triID < numTri; triID++)
+          {
+            if ((triangles[3*triID+0] >= 13) || (triangles[3*triID+1] >= 13) || (triangles[3*triID+2] >= 13)) // this is for safety
+              continue;
+            int v1 = cubeVtxIndices[triangles[3*triID+0]];
+            int v2 = cubeVtxIndices[triangles[3*triID+1]];
+            int v3 = cubeVtxIndices[triangles[3*triID+2]];
+            assert(v1 != -1 && v2 != -1 && v3 != -1);
+            meshTriangles.push_back(v1);
+            meshTriangles.push_back(v2);
+            meshTriangles.push_back(v3);
+          }
+        } // end for (distance loop)
+#ifdef USE_TBB
+  }); //end for locations
+
+//  pc.StopCounter();
+//  cout << "Large loop: " << pc.GetElapsedTime() << endl;
+//  pc.StartCounter();
+
+  for(const auto & local : threadLocalData)
+  {
+    vector<int> localVtxID2AllVtxID(local.vertices.size());
+    assert(local.edgeInterID2LocalVtxID.size() == local.vertices.size());
+    for(const auto & localPair : local.edgeInterID2LocalVtxID)
+    {
+      int localID = localPair.second;
+      assert(localID < (int)localVtxID2AllVtxID.size());
+      auto allIt = edgeInterID2AllMeshVtxID.find(localPair.first);
+      if (allIt == edgeInterID2AllMeshVtxID.end())
+      {
+        int globalID = allVertices.size();
+        edgeInterID2AllMeshVtxID.emplace(localPair.first, globalID);
+        localVtxID2AllVtxID[localID] = globalID;
+        allVertices.push_back(local.vertices[localID]);
+      }
+      else
+      {
+        localVtxID2AllVtxID[localID] = allIt->second;
+      }
+    }
+
+    for(size_t i = 0; i < local.triangles.size(); i++)
+    {
+      allTriangles.push_back(localVtxID2AllVtxID[local.triangles[i]]);
+    }
+  }
+#endif
+
+  ObjMesh * objMesh = new ObjMesh(allVertices.size(), (const double*)&allVertices[0], allTriangles.size() / 3, allTriangles.data());
+//  pc.StopCounter();
+//  cout << "build objMesh: " << pc.GetElapsedTime() << endl;
+  return objMesh;
+}
+
+namespace
+{
+
+class ObjSorter
+{
+public:
+  ObjSorter(const ObjMesh * objMesh) : objMesh(objMesh) {}
+  bool operator() (const int a, const int b) 
+  {
+	 return objMesh->getPosition(a) < objMesh->getPosition(b);
+  }
+  bool operator () (const ObjMesh::Face a, ObjMesh::Face b) 
+  {
+	 return triple<Vec3d, Vec3d, Vec3d>(objMesh->getPosition(a.getVertex(0)), objMesh->getPosition(a.getVertex(1)), objMesh->getPosition(a.getVertex(2))) < triple<Vec3d, Vec3d, Vec3d>(objMesh->getPosition(b.getVertex(0)), objMesh->getPosition(b.getVertex(1)), objMesh->getPosition(b.getVertex(2))) ;
+  }
+protected:
+  const ObjMesh * objMesh;
+};
+
+void sortMesh(ObjMesh * objMesh)
+{
+  ObjSorter sorter(objMesh);
+  //sort vertices
+  vector <int> a(objMesh->getNumVertices());
+  for (size_t i = 0; i < objMesh->getNumVertices(); i++) 
+    a[i] = i;
+  sort(a.begin(), a.end(), sorter);
+  vector <int> b(objMesh->getNumVertices());
+  for (size_t i = 0; i < objMesh->getNumVertices(); i++) 
+    b[a[i]] = i;
+  objMesh->renumberVertices(b);
+
+  //sort faces
+  const ObjMesh::Group *group = objMesh->getGroupHandle(0);
+  int n = group->getNumFaces();
+  vector <ObjMesh::Face> faces(n);
+  for (int i = 0; i < n; i++)
+  {
+    const ObjMesh::Face *face = group->getFaceHandle(i);
+    int _min = 0;
+    int m = face->getNumVertices();
+    for(int j = 1; j < m; j++)
+      if (objMesh->getPosition(face->getVertex(j)) < objMesh->getPosition(face->getVertex(_min))) _min = j;
+    for (int j = 0; j < m; j++)
+      faces[i].addVertex(face->getVertex((j + _min) % m));
+  }
+  sort(faces.begin(), faces.end(), sorter);
+  objMesh->removeGroup(0);
+  objMesh->addGroup("Sorted faces");
+  for (int i = 0; i < n; i++) 
+    objMesh->addFaceToGroup(faces[i], 0);
+}
+
+}
+
+ObjMesh * MarchingCubes::compute(const DistanceFieldBase * distanceFieldBase, float isoValue)
+{
+  MarchingCubes m(distanceFieldBase, isoValue);
+  ObjMesh * ret = m.compute();
+  sortMesh(ret);
+  return ret;
+}
+
+#ifdef USE_STATIC_TABLE
+  //create dummy functions
+  void MarchingCubes::printTable() {}
+  void MarchingCubes::createTable() {}
+#else
+  // --- the functions below are used to create the case tables ---
+
+  static void binaray(int num, int * array)
+  {
+    for (int i = 0; i < 8; i++)
+    {
+      array[i] = num & 0x1;
+      num >>= 1;
+    }
+  }
+
+  static int permute(int src_, int dst_, int transform)
+  {
+    int src[8], dst[8], permute[8];
+    binaray(src_, src);
+    binaray(dst_, dst);
+
+    int i;
+    for (i = 0; i < 8; i++)
+      permute[i] = src[cubeSymmetryVertices[transform][i]];
+
+    for (i = 0; i < 8; i++)
+    {
+      if (permute[i] != dst[i])
+        break;
+    }
+
+    if (i == 8)
+      return 1;
+
+    for (i = 0; i < 8; i++)
+    {
+      if (permute[i] != 1 - dst[i])
+        break;
+    }
+
+    if (i == 8)
+      return -1;
+
+    return 0;
+  }
+
+  static inline int f_hash_edge(int a, int b)
+  {
+    return a * a + b * b;
+  }
+
+  static inline int f_hash_face(int a, int b, int c, int d)
+  {
+    return a + b + c + d + (a > 0 && b > 0 && c > 0 && d > 0);
+  }
+
+  static void permute_face(const char *src, char *dst, int n, int permute)
+  {
+    int permute_ = abs(permute);
+    for (int i = 0; i < n; i++)
+      dst[i] = cubeSymmetryFaces[permute_][src[i] - 1] + 1;
+
+    if (permute <= 0)
+    {
+      for (int i = 0; i < n; i++)
+        dst[i] = -dst[i];
+    }
+  }
+
+  static void permute_edge(const char *src, char *dst, int n, int permute)
+  {
+    int permute_ = abs(permute);
+    for (int i = 0; i < n; i++)
+      dst[i] = cubeSymmetryEdges[permute_][(unsigned char) src[i]];
+
+    if (permute <= 0)
+    {
+      for (int i = 1; i < n; i += 3)
+        swap(dst[i - 1], dst[i + 1]);
+    }
+  }
+
+  static vector<char> make_vector_impl(const char *p, int n)
+  {
+    vector<char> ret;
+    ret.resize(n);
+    memcpy(&ret[0], p, sizeof(char) * n);
+    return ret;
+  }
+
+  static bool violate(int k, vector<vector<int> >& binaryRepresentation) 
+  {
+    int p = 0, n = 0;
+    for (int i = 0; i < 3; i++) 
+    {
+      bool r0 = find(binaryRepresentation[k].begin(), binaryRepresentation[k].end(), oppositeFace[i][0]) == binaryRepresentation[k].end();
+      bool r1 = find(binaryRepresentation[k].begin(), binaryRepresentation[k].end(), oppositeFace[i][1]) == binaryRepresentation[k].end();
+      n += r0 && r1;
+      p += !(r0 || r1);
+    }
+
+    return (n > 0) && (p > 0);
+  }
+
+  static bool compare(const int *a, const int *b) 
+  {
+    for (int i = 0; i < 3; i++)
+      if (a[i] != b[i]) return false;
+    return true;
+  }
+
+  static bool cmp(const vector<int> a, const vector<int> b) 
+  {
+    bool interiora = ((a.size() == 3) && (compare(&a[0], vtxAdjacentFaces[1]) || compare(&a[0], vtxAdjacentFaces[3]) || compare(&a[0], vtxAdjacentFaces[4]) || compare(&a[0], vtxAdjacentFaces[6])));
+
+    bool interiorb = ((b.size() == 3) && (compare(&b[0], vtxAdjacentFaces[1]) || compare(&b[0], vtxAdjacentFaces[3]) || compare(&b[0], vtxAdjacentFaces[4]) || compare(&b[0], vtxAdjacentFaces[6])));
+
+    if (!interiora && interiorb) 
+      return true;
+
+    if (interiora && !interiorb) 
+      return false;
+
+    if (a.size() < b.size()) 
+      return true;
+
+    if (a.size() > b.size()) 
+      return false;
+
+    if (a.size() <= 3) 
+    {
+      for (unsigned int i = 0; i < a.size(); i++)
+      {
+        if (a[i] < b[i]) 
+          return true;
+        if (a[i] > b[i]) 
+          return false;
+      }
+      return true;
+    }
+    else 
+    {
+      for (int i = 0; i < 6; i++) 
+      {
+        bool ra = find(a.begin(), a.end(), i) == a.end();
+        bool rb = find(b.begin(), b.end(), i) == b.end();
+        if (ra && !rb) 
+          return true;
+        if (!ra && rb) 
+          return false;
+      }
+      return true;
+    }
+  }
+
+  static vector<int> make_vector13(unsigned int p) 
+  {
+    vector <int> ret;
+    for (int i = 1; i <= 6; i++) 
+    {
+      if ((p | ((1 << i) >> 1)) == p)
+        ret.push_back(i);
+    }
+    return ret;
+  }
+
+  #define make(x, y, z) permute_edge(x, y, sizeof(x) / sizeof(x[0]), z)
+  #define make_vector(x) make_vector_impl(x, sizeof(x) / sizeof(x[0]))
+
+  void MarchingCubes::createTable()
+  {
+    // fill the ambiguity table
+    ambiguityTable[0].push_back(0);
+    ambiguityTable[1].push_back(0);
+    ambiguityTable[2].push_back(0);
+    ambiguityTable[3].push_back(0);
+    ambiguityTable[3].push_back(1);
+    ambiguityTable[4].push_back(1);
+    ambiguityTable[4].push_back(0);
+    ambiguityTable[5].push_back(0);
+
+    ambiguityTable[6].push_back(0);
+    ambiguityTable[6].push_back(1);
+
+    ambiguityTable[7].push_back(0);
+    ambiguityTable[7].push_back(1);
+    ambiguityTable[7].push_back(2);
+    ambiguityTable[7].push_back(4);
+    ambiguityTable[7].push_back(3);
+    ambiguityTable[7].push_back(5);
+    ambiguityTable[7].push_back(6);
+    ambiguityTable[7].push_back(7);
+
+    ambiguityTable[8].push_back(0);
+    ambiguityTable[9].push_back(0);
+
+    ambiguityTable[10].push_back(2);
+    ambiguityTable[10].push_back(3);
+    ambiguityTable[10].push_back(4);
+    ambiguityTable[10].push_back(1);
+    ambiguityTable[10].push_back(0);
+    ambiguityTable[10].push_back(3);
+    ambiguityTable[10].push_back(4);
+    ambiguityTable[10].push_back(1);
+
+    ambiguityTable[11].push_back(0);
+
+    ambiguityTable[12].push_back(0);
+    ambiguityTable[12].push_back(2);
+    ambiguityTable[12].push_back(3);
+    ambiguityTable[12].push_back(1);
+    ambiguityTable[14].push_back(0);
+
+    ambiguityTable[13].resize(64);
+
+    vector<vector<int> > binaryRepresentation;
+
+    for (int i = 0; i < 64; i++)
+      binaryRepresentation.push_back(make_vector13(i));
+
+    for (int i = 63; i >= 0; i--)
+    {
+      if (violate(i, binaryRepresentation)) 
+        binaryRepresentation.erase(binaryRepresentation.begin() + i);
+    }
+
+    sort(binaryRepresentation.begin(), binaryRepresentation.end(), cmp);
+    memset(&ambiguityTable[13][0], 0xff, sizeof(ambiguityTable[13][0]) * 64);
+    int pp = 0;
+    for (unsigned int i = 0; i < binaryRepresentation.size(); i++)
+    {
+      int k = 0;
+      for (unsigned int j = 0; j < binaryRepresentation[i].size(); j++)
+        k += (1 << binaryRepresentation[i][j]) >> 1;
+      ambiguityTable[13][k] = pp++;
+    }
+
+    // build marchingCubeSymmetries
+    int major_case = 0;
+    int sub_case[100];
+    const unsigned char sequence[256] =
+    { 0, 1, 2, 4, 8, 16, 32, 64, 128, 3, 5, 9, 17, 33, 65, 129, 6, 10, 18, 34, 66, 130, 12, 20, 36, 68, 132, 24, 40, 72, 136, 48, 80, 144, 96, 160, 192, 7, 11, 19, 35, 67, 131, 13, 21, 37, 69, 133, 25, 41, 73, 137, 49, 81, 145, 97, 161, 193, 14, 22, 38, 70, 134, 26, 42, 74, 138, 50, 82, 146, 98, 162, 194, 28, 44, 76, 140, 52, 84, 148, 100, 164, 196, 56, 88, 152, 104, 168, 200, 112, 176, 208, 224, 15, 23, 39, 71, 135, 27, 43, 75, 139, 51, 83, 147, 99, 163, 195, 29, 45, 77, 141, 53, 85, 149, 101, 165, 197, 57, 89, 153, 105, 169, 201, 113, 177, 209, 225, 30, 46, 78, 142, 54, 86, 150, 102, 166, 198, 58, 90, 154, 106, 170, 202, 114, 178, 210, 226, 60, 92, 156, 108, 172, 204, 116, 180, 212, 228, 120, 184, 216, 232, 240, 31, 47, 79, 143, 55, 87, 151, 103, 167, 199, 59, 91, 155, 107, 171, 203, 115, 179, 211, 227, 61, 93, 157, 109, 173, 205, 117, 181, 213, 229, 121, 185, 217, 233, 241, 62, 94, 158, 110, 174, 206, 118, 182, 214, 230, 122, 186, 218, 234, 242, 124, 188, 220, 236, 244, 248, 63, 95, 159, 111, 175, 207, 119, 183, 215, 231, 123, 187, 219, 235, 243, 125, 189, 221, 237, 245, 249, 126, 190, 222, 238, 246, 250, 252, 127, 191, 223, 239, 247, 251, 253, 254, 255 };
+
+    vector<char> c[15];
+    pair<int, int> candidate;
+    set<int> am;
+    for (int i_ = 0; i_ < 256; i_++)
+    {
+      int i = sequence[i_];
+      int j = 0, k, p, j_;
+      for (j_ = 0; j_ < i_; j_++)
+      {
+        j = sequence[j_];
+        for (k = 0; k < 24; k++)
+        {
+          p = permute(i, j, k);
+          if (p > 0) 
+          {
+            candidate = make_pair(k, p);
+            break;
+          }
+        }
+
+        if (k < 24)
+          break;
+
+        for (k = 0; k < 24; k++)
+        {
+          p = permute(i, j, k);
+          if (p < 0) 
+          {
+            candidate = make_pair(k, p);
+            break;
+          }
+        }
+
+        if (k < 24) 
+          break;
+      }
+
+      if (i_ == j_)
+      {
+        marchingCubeSymmetries[i][0] = caseNumber[major_case];
+        marchingCubeSymmetries[i][1] = 0;
+        marchingCubeSymmetries[i][2] = 0;
+        sub_case[caseNumber[major_case++]] = 0;
+      }
+      else
+      {
+        k = candidate.first;
+        p = candidate.second;
+        am.insert(marchingCubeSymmetries[j][0]);
+        marchingCubeSymmetries[i][0] = marchingCubeSymmetries[j][0];
+        sub_case[marchingCubeSymmetries[j][0]]++;
+        marchingCubeSymmetries[i][1] = sub_case[marchingCubeSymmetries[j][0]];
+        marchingCubeSymmetries[i][2] = p * k;
+      }
+      c[marchingCubeSymmetries[i][0]].push_back(i);
+    }
+
+    unsigned char hash_edge[86];
+    memset(hash_edge, 0xff, sizeof(hash_edge));
+    for (unsigned int i = 0; i < 12; i++)
+      hash_edge[f_hash_edge(edge_vertices[i][0], edge_vertices[i][1])] = i;
+
+    unsigned char hash_face[23];
+    memset(hash_face, 0xff, sizeof(hash_face));
+    for (unsigned int i = 0; i < 6; i++)
+      hash_face[f_hash_face(face_vertices[i][0], face_vertices[i][1], face_vertices[i][2], face_vertices[i][3])] = i;
+
+    //build cubeSymmetryEdges
+    for (unsigned int i = 0; i < 25; i++)
+    {
+      for (unsigned int j = 0; j < 12; j++)
+      {
+        unsigned int p0 = cubeSymmetryVertices[i][edge_vertices[j][0]];
+        unsigned int p1 = cubeSymmetryVertices[i][edge_vertices[j][1]];
+        cubeSymmetryEdges[i][j] = hash_edge[f_hash_edge(p0, p1)];
+      }
+      cubeSymmetryEdges[i][12] = 12;
+    }
+
+    // build cubeSymmetryFaces
+    for (unsigned int i = 0; i < 24; i++)
+    {
+      for (unsigned int j = 0; j < 6; j++)
+      {
+        unsigned char p0 = cubeSymmetryVertices[i][face_vertices[j][0]];
+        unsigned int p1 = cubeSymmetryVertices[i][face_vertices[j][1]];
+        unsigned int p2 = cubeSymmetryVertices[i][face_vertices[j][2]];
+        unsigned int p3 = cubeSymmetryVertices[i][face_vertices[j][3]];
+        cubeSymmetryFaces[i][j] = hash_face[f_hash_face(p0, p1, p2, p3)];
+      }
+    }
+
+    //fix 13 for marchingCubeSymmetries
+    marchingCubeSymmetries[90][2] = 3;
+
+    //build rc
+    make(representativeCase7_2_0, representativeCase7_2_1, 17);
+    make(representativeCase7_2_0, representativeCase7_2_2, 21);
+    make(representativeCase7_3_0, representativeCase7_3_1, 21);
+    make(representativeCase7_3_0, representativeCase7_3_2, 17);
+    make(representativeCase10_1_1, representativeCase10_1_1_, -1);
+    make(representativeCase10_2, representativeCase10_2_, 12);
+    make(representativeCase12_1_1, representativeCase12_1_1_, 24);
+    make(representativeCase12_2, representativeCase12_2_, 24);
+    make(representativeCase13_1, representativeCase13_1_, -3);
+
+    make(representativeCase13_2_0, representativeCase13_2[0], 23);
+    make(representativeCase13_2_0, representativeCase13_2[1], 9);
+    make(representativeCase13_2_0, representativeCase13_2[2], 16);
+    make(representativeCase13_2_0, representativeCase13_2[3], 20);
+    make(representativeCase13_2_0, representativeCase13_2[4], 19);
+
+    make(representativeCase13_2_0, representativeCase13_2_0_, -2);
+    make(representativeCase13_2_0_, representativeCase13_2_[0], 23);
+    make(representativeCase13_2_0_, representativeCase13_2_[1], 9);
+    make(representativeCase13_2_0_, representativeCase13_2_[2], 16);
+    make(representativeCase13_2_0_, representativeCase13_2_[3], 20);
+    make(representativeCase13_2_0_, representativeCase13_2_[4], 19);
+
+    make(representativeCase13_3_0, representativeCase13_3_0_, 24);
+    make(representativeCase13_3_0, representativeCase13_3[0], 8);
+    make(representativeCase13_3_0_, representativeCase13_3_[0], 8);
+    make(representativeCase13_3_0, representativeCase13_3_[1], -5);
+    make(representativeCase13_3_0_, representativeCase13_3[1], -5);
+    make(representativeCase13_3_0, representativeCase13_3_[2], -2);
+    make(representativeCase13_3_0_, representativeCase13_3[2], -2);
+    make(representativeCase13_3_0, representativeCase13_3_[3], -3);
+    make(representativeCase13_3_0_, representativeCase13_3[3], -3);
+    make(representativeCase13_3_0, representativeCase13_3[4], 17);
+    make(representativeCase13_3_0_, representativeCase13_3_[4], 17);
+    make(representativeCase13_3_0, representativeCase13_3_[5], -4);
+    make(representativeCase13_3_0_, representativeCase13_3[5], -4);
+    make(representativeCase13_3_0, representativeCase13_3[6], 9);
+    make(representativeCase13_3_0_, representativeCase13_3_[6], 9);
+    make(representativeCase13_3_0, representativeCase13_3_[7], -11);
+    make(representativeCase13_3_0_, representativeCase13_3_[7], -11);
+    make(representativeCase13_3_0, representativeCase13_3[8], 18);
+    make(representativeCase13_3_0_, representativeCase13_3_[8], 18);
+    make(representativeCase13_3_0, representativeCase13_3_[9], -10);
+    make(representativeCase13_3_0_, representativeCase13_3[9], -10);
+    make(representativeCase13_3_0, representativeCase13_3[10], 16);
+    make(representativeCase13_3_0_, representativeCase13_3_[10], 16);
+
+    make(representativeCase13_4_0, representativeCase13_4[0], 8);
+    make(representativeCase13_4_0, representativeCase13_4[1], 17);
+    make(representativeCase13_4_0, representativeCase13_4[2], 9);
+    make(representativeCase13_5_1_0, representativeCase13_5_1[0], 8);
+    make(representativeCase13_5_1_0, representativeCase13_5_1[1], 7);
+    make(representativeCase13_5_1_0, representativeCase13_5_1[2], 9);
+    rc[1].push_back(make_vector(representativeCase1));
+    rc[2].push_back(make_vector(representativeCase2));
+    rc[3].push_back(make_vector(representativeCase3_1));
+    rc[3].push_back(make_vector(representativeCase3_2));
+    rc[4].push_back(make_vector(representativeCase4_1));
+    rc[4].push_back(make_vector(representativeCase4_2));
+    rc[5].push_back(make_vector(representativeCase5));
+    rc[6].push_back(make_vector(representativeCase6_1_1));
+    rc[6].push_back(make_vector(representativeCase6_2));
+    rc[7].push_back(make_vector(representativeCase7_1));
+    rc[7].push_back(make_vector(representativeCase7_2_0));
+    rc[7].push_back(make_vector(representativeCase7_2_1));
+    rc[7].push_back(make_vector(representativeCase7_2_2));
+    rc[7].push_back(make_vector(representativeCase7_3_0));
+    rc[7].push_back(make_vector(representativeCase7_3_1));
+    rc[7].push_back(make_vector(representativeCase7_3_2));
+    rc[7].push_back(make_vector(representativeCase7_4_1));
+    rc[8].push_back(make_vector(representativeCase8));
+    rc[9].push_back(make_vector(representativeCase9));
+    rc[10].push_back(make_vector(representativeCase10_1_1));
+    rc[10].push_back(make_vector(representativeCase10_1_1_));
+    rc[10].push_back(make_vector(representativeCase10_1_2));
+    rc[10].push_back(make_vector(representativeCase10_2));
+    rc[10].push_back(make_vector(representativeCase10_2_));
+    rc[11].push_back(make_vector(representativeCase11));
+    rc[12].push_back(make_vector(representativeCase12_1_1));
+    rc[12].push_back(make_vector(representativeCase12_1_1_));
+    rc[12].push_back(make_vector(representativeCase12_2));
+    rc[12].push_back(make_vector(representativeCase12_2_));
+    rc[13].push_back(make_vector(representativeCase13_1));
+
+    rc[13].push_back(make_vector(representativeCase13_2_0));
+    for (unsigned int i = 0; i < 5; i++)
+      rc[13].push_back(make_vector(representativeCase13_2[i]));
+
+    rc[13].push_back(make_vector(representativeCase13_3_0));
+    for (unsigned int i = 0; i < 11; i++)
+      rc[13].push_back(make_vector(representativeCase13_3[i]));
+
+    rc[13].push_back(make_vector(representativeCase13_4_0));
+    for (unsigned int i = 0; i < 3; i++)
+      rc[13].push_back(make_vector(representativeCase13_4[i]));
+
+    rc[13].push_back(make_vector(representativeCase13_3_0_));
+    for (unsigned int i = 0; i < 11; i++)
+      rc[13].push_back(make_vector(representativeCase13_3_[i]));
+
+    rc[13].push_back(make_vector(representativeCase13_2_0_));
+    for (unsigned int i = 0; i < 5; i++)
+      rc[13].push_back(make_vector(representativeCase13_2_[i]));
+
+    rc[13].push_back(make_vector(representativeCase13_1_));
+
+    rc[13].push_back(make_vector(representativeCase13_5_1_0));
+    for (unsigned int i = 0; i < 3; i++)
+      rc[13].push_back(make_vector(representativeCase13_5_1[i]));
+
+    rc[14].push_back(make_vector(representativeCase14));
+
+    //build faceTest vector
+    for (unsigned int i = 0; i < 15; i++)
+    {
+      for (unsigned int j = 0; j < c[i].size(); j++)
+      {
+        const unsigned char k = c[i][j];
+        const char n = faceTest_size[i];
+        faceTest_num[k].resize(n);
+        if (n == 0)
+          continue;
+        if (j == 0)
+          memcpy(&faceTest_num[k][0], faceTest_number[i], sizeof(faceTest_number[i][0]) * n);
+        else
+          permute_face(faceTest_number[i], &faceTest_num[k][0], n, marchingCubeSymmetries[k][2]);
+      }
+    }
+
+    //build interiorTest vector
+    for (unsigned int i = 0; i < 15; i++)
+    {
+      for (unsigned int j = 0; j < c[i].size(); j++)
+      {
+        const unsigned char k = c[i][j];
+        interiorTest_num[k] = interiorTest_number[i];
+        if (marchingCubeSymmetries[k][2] < 0 || (j > 0 && marchingCubeSymmetries[k][2] == 0))
+          interiorTest_num[k] = -interiorTest_num[k];
+      }
+    }
+
+    //build triangleTable
+    for (unsigned int i = 0; i < 256; i++)
+    {
+      const int major_case = marchingCubeSymmetries[i][0];
+      triangleTable[i].resize(rc[major_case].size());
+      centerVertexNeeded[i].resize(rc[major_case].size());
+      for (unsigned int j = 0; j < rc[major_case].size(); j++)
+      {
+        triangleTable[i][j].resize(rc[major_case][j].size());
+        if (marchingCubeSymmetries[i][1] == 0)   //
+        {
+          memcpy(&triangleTable[i][j][0], &rc[major_case][j][0], rc[major_case][j].size() * sizeof(rc[major_case][j][0]));
+        }
+        else
+        {
+          permute_edge(&rc[major_case][j][0], (char*) &triangleTable[i][j][0], rc[major_case][j].size(), marchingCubeSymmetries[i][2]);
+        }
+        centerVertexNeeded[i][j] = false;
+        for (unsigned int k = 0; k < triangleTable[i][j].size(); k++)
+          centerVertexNeeded[i][j] = centerVertexNeeded[i][j] | (triangleTable[i][j][k] == 12);// |= (triangleTable[i][j][k] == 12);
+      }
+    }
+
+    tableLoaded = true;
+  }
+
+  void MarchingCubes::printTable()
+  {
+    // once the file is OK, rename it to marchingCubesTable.h
+    ofstream fout("marchingCubesTable_.h", ios::binary);
+    if (!fout) 
+      return;
+    fout << endl;
+
+    //static vector<char> ambiguityTable[15];
+    unsigned int maxSize = 0;
+    for(unsigned int i = 0; i < 15; i++) 
+    {
+      if (ambiguityTable[i].size() > maxSize) 
+        maxSize = ambiguityTable[i].size();
+    }
+    fout << "static char ambiguityTable[15][" << maxSize << "] = " << endl << "{" << endl;
+    //char buffer[100];
+    //fout << 
+    for(int i = 0; i < 15; i++) 
+    {
+      fout << "  { ";
+      unsigned int j = 0;
+      for(j = 0; j < ambiguityTable[i].size(); j++) 
+      {
+        fout << int(ambiguityTable[i][j]);
+        if (j < maxSize-1)  
+          fout << ", ";
+      }
+      for(; j < maxSize; j++) 
+      {
+        fout << "0";
+        if (j < maxSize-1)  
+          fout << ", ";
+      }
+      fout << " }";
+      if (i < 15 - 1)
+        fout << ", ";
+      fout << endl;
+    }
+    fout << "};" << endl;
+
+    fout << endl;
+
+    // static int marchingCubeSymmetries[256][3];
+    fout << "static int marchingCubeSymmetries[256][3] = {" << endl;
+    for(int i = 0; i < 256; i++) 
+    {
+      fout << "  {";
+      for(int j = 0; j < 3; j++) 
+      {
+        fout << setw(3) << marchingCubeSymmetries[i][j];
+        if (j < 3 - 1) 
+          fout << ",";
+      }
+      fout << " }";
+      if (i < 256 -1)
+        fout << ", ";
+      if (i % 8 == 7) 
+        fout << endl;
+    }
+    fout << "};" << endl;
+
+    fout << endl;
+
+    //static vector<char> faceTest_num[256];
+    maxSize = 0;
+    for(unsigned int i = 0; i < 256; i++) 
+    {
+      if (faceTest_num[i].size() > maxSize) 
+        maxSize = faceTest_num[i].size();
+    }
+    fout << "static char faceTest_num[256][" << maxSize+1 << "] = " << endl << "{" << endl;
+    //char buffer[100];
+    for(int i = 0; i < 256; i++) 
+    {
+      fout << "  {";
+      fout << setw(2) << faceTest_num[i].size();
+      //if (faceTest_num[i].size() > 0)
+      fout << ",";
+
+      unsigned int j = 0;
+      for(j = 0; j < faceTest_num[i].size(); j++) 
+      {
+        fout << setw(2) << int(faceTest_num[i][j]);
+        if (j < maxSize-1)  
+          fout << ",";
+      }
+      for(; j < maxSize; j++) 
+      {
+        fout << " 0";
+        if (j < maxSize-1)  
+          fout << ",";
+      }
+      fout << " }";
+      if (i < 256 - 1)
+        fout << ",";
+      if (i % 8 == 7)
+        fout << endl;
+    }
+    fout << "};" << endl;
+
+    fout << endl;
+
+    //static char interiorTest_num[256];
+    fout << "static char interiorTest_num[256] = " << endl << "  { ";
+    for(int i = 0; i < 256; i++) 
+    {
+      fout << int(interiorTest_num[i]);
+      if (i < 256 - 1)
+        fout << ", ";
+    }
+    fout << " };" << endl;
+
+    fout << endl;
+
+    // static vector< vector<unsigned char> > triangleTable[256];
+    for(int i = 0; i < 256; i++) 
+    {
+      if (triangleTable[i].size() == 0) 
+        continue;
+
+      for(unsigned int j = 0; j < triangleTable[i].size(); j++) 
+      {
+        vector<unsigned char> & table = triangleTable[i][j];
+        unsigned int num = table.size();
+        fout << "static unsigned char triangleTable_" << i << "_" << j << "[" << num+2 << "] = " << " { ";
+        fout << num << ", " << centerVertexNeeded[i][j];
+        if (num > 0)
+          fout << ", ";
+        for(unsigned int k = 0; k < table.size(); k++) 
+        {
+          fout << int(table[k]);
+          if (k < table.size() - 1)
+            fout << ", ";
+        }
+        fout << " };" << endl;
+      }
+
+      unsigned int num = triangleTable[i].size();
+      fout << "static unsigned char * triangleTable_" << i << "[" << num << "] = ";
+      if (num <= 7) 
+      {
+        fout << "{ ";
+        for(unsigned int k = 0; k < num; k++) 
+        {
+          fout << "triangleTable_" << i << "_" << k;
+          if (k < num - 1)
+            fout << ", ";
+        }
+        fout << " };" << endl;
+      }
+      else 
+      {
+        fout << endl << "{ " << endl;
+        for(unsigned int k = 0; k < num; k++) 
+        {
+          if (k % 8 == 0)
+            fout << "  ";
+          fout << "triangleTable_" << i << "_" << k;
+          if (k < num - 1)
+            fout << ", ";
+          if (k % 8 == 7)
+          fout << endl;
+        }
+        if (num % 8 != 0)
+          fout << endl;
+
+        fout << "};" << endl;
+
+      }
+      fout << endl;
+    } //end 256
+
+    fout << "static unsigned char ** triangleTable[256] = " << endl << "{ " << endl;
+    for(unsigned int k = 0; k < 256; k++) 
+    {
+      if (k % 8 == 0)
+        fout << "  ";
+      if (triangleTable[k].size() == 0)
+        fout << "NULL";
+      else
+        fout << "triangleTable_" << k;
+
+      if (k < 256 - 1)
+        fout << ", ";
+      if (k % 8 == 7)
+        fout << endl;
+    }
+    fout << "};" << endl << endl; 
+    fout.close();
+    //exit(0);
+  }
+#endif
diff --git a/libraries/distanceField/marchingCubes.h b/libraries/distanceField/marchingCubes.h
new file mode 100644
index 0000000000000000000000000000000000000000..38d92bbcd216101f996e4f5ebc12c56f0cb68144
--- /dev/null
+++ b/libraries/distanceField/marchingCubes.h
@@ -0,0 +1,95 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Jernej Barbic                             *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This code implements marching cubes with topological guarantees, 
+  similar to the following publications. Our code has been implemented
+  from scratch by Danyong Zhao and Jernej Barbic.
+
+  Thomas Lewiner, Hélio Lopes, Antônio Wilson Vieira, Geovan Tavares:
+  Efficient implementation of marching cubes cases with topological guarantees
+  Journal of Graphics Tools 8(2): pp. 1-15, 2003
+
+  E. V. Chernyaev. Marching Cubes 33: construction of topologically correct isosurfaces. 
+  Technical Report CERN CN 95–17, CERN, 1995.
+*/
+
+#ifndef _MARCHINGCUBES_H_
+#define _MARCHINGCUBES_H_
+
+#include <vector>
+#include <map>
+
+#include "triple.h"
+#include "objMesh.h"
+#include "distanceFieldBase.h"
+
+class MarchingCubes
+{
+public:
+
+  // computes the isosurface mesh, using marching cubes with topological guarantees
+  // the input distance field can be complete, or narrow band
+  // isoValue is the isosurface value to be meshed (given in absolute units, not grid units)
+  // output: a triangle mesh corresponding to the isosurface
+  static ObjMesh * compute(const DistanceFieldBase * distanceField, float isoValue = 0.0);
+
+protected:
+  MarchingCubes(const DistanceFieldBase * distanceField, float isoValue = 0.0);
+  virtual ~MarchingCubes() {}
+
+  const DistanceFieldBase * distanceFieldBase;
+  float isoValue;
+
+  int resolutionX, resolutionY, resolutionZ;
+
+  // executes the marching cubes algorithm
+  ObjMesh * compute();
+
+  // void computeTriangleVertices(int i, int j, int k, bool center, int vtx[13]);
+  bool faceTest(int face, float cube[8]);
+  int interiorTest(int edge, float cube[8]);
+  inline float getDistance(int i, int j, int k) const
+  {
+    float offset = distanceFieldBase->distance(i, j, k) - isoValue;
+//    return offset + (offset == 0.0f) * FLT_EPSILON;
+    if (offset == 0.0f) return FLT_EPSILON;
+    return offset;
+  }
+
+  // computes all the tables needed for marching cubes
+  static void createTable();
+  static void printTable();
+};
+
+#endif
+
diff --git a/libraries/distanceField/marchingCubesTable.h b/libraries/distanceField/marchingCubesTable.h
new file mode 100644
index 0000000000000000000000000000000000000000..2cfb55a0d2ad8a443ddff419ad509f3b4abf40aa
--- /dev/null
+++ b/libraries/distanceField/marchingCubesTable.h
@@ -0,0 +1,1370 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Jernej Barbic                             *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Marching cubes tables. These tables were generated programmatically,
+  using our code.
+*/
+
+static char ambiguityTable[15][64] = 
+{
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 1, 2, 4, 3, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 2, 3, 4, 1, 0, 3, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 2, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 1, 2, 7, 3, -1, 11, -1, 4, 8, -1, -1, 14, -1, -1, -1, 5, 9, 12, 42, 15, -1, 21, 34, 17, 20, -1, 32, 45, 29, 26, 40, 6, 10, 13, 19, 16, -1, 44, 33, 18, 43, -1, 31, 22, 28, 25, 39, -1, -1, -1, 30, -1, -1, 24, 38, -1, 27, -1, 37, 23, 36, 35, 41 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static int marchingCubeSymmetries[256][3] = {
+  {  0,  0,  0 },   {  1,  0,  0 },   {  1,  1,  2 },   {  2,  0,  0 },   {  1,  2,  9 },   {  3,  0,  0 },   {  2,  3,  3 },   {  5,  0,  0 }, 
+  {  1,  3,  1 },   {  2,  1,  6 },   {  3,  3,  3 },   {  5,  1,  6 },   {  2,  5,  1 },   {  5,  4,  9 },   {  5,  9,  3 },   {  8,  0,  0 }, 
+  {  1,  4,  4 },   {  2,  2,  5 },   {  3,  4,  4 },   {  5,  2, 10 },   {  4,  2,  4 },   {  6,  2,  5 },   {  6,  9, 21 },   { 11,  0,  0 }, 
+  {  3,  8,  5 },   {  5,  5,  5 },   {  7,  3,  5 },   {  9,  1,  5 },   {  6, 16,  9 },   { 14,  3,  5 },   { 12, 12,  3 },   {  5, 24, -7 }, 
+  {  1,  5,  8 },   {  3,  1, 16 },   {  2,  4,  2 },   {  5,  3, 17 },   {  3,  6, 18 },   {  7,  0,  0 },   {  5, 10, 21 },   {  9,  0,  0 }, 
+  {  4,  3,  1 },   {  6,  4,  6 },   {  6, 11, 17 },   { 14,  1,  6 },   {  6, 17,  1 },   { 12,  4,  9 },   { 11,  6,  3 },   {  5, 25,-14 }, 
+  {  2,  8,  4 },   {  5,  7, 16 },   {  5, 12,  4 },   {  8,  1,  4 },   {  6, 18,  4 },   { 12,  5, 16 },   { 14,  7,  4 },   {  5, 28,-12 }, 
+  {  6, 21,  8 },   { 11,  4,  5 },   { 12, 15,  4 },   {  5, 30,-23 },   { 10,  5,  1 },   {  6, 32, -7 },   {  6, 39,-12 },   {  2, 12, -7 }, 
+  {  1,  6, 11 },   {  4,  0,  0 },   {  3,  5,  2 },   {  6,  0,  0 },   {  2,  6, 11 },   {  6,  3, 11 },   {  5, 11, 11 },   { 14,  0,  0 }, 
+  {  3,  9,  1 },   {  6,  5, 20 },   {  7,  4,  1 },   { 12,  1,  6 },   {  5, 14,  1 },   { 11,  3,  1 },   {  9,  4,  1 },   {  5, 26, -8 }, 
+  {  3, 10, 14 },   {  6,  6, 16 },   {  7,  5,  2 },   { 12,  2, 10 },   {  6, 19, 23 },   { 10,  1,  5 },   { 12, 13, 11 },   {  6, 24,-13 }, 
+  {  7,  7, 12 },   { 12,  9,  5 },   { 13,  1,  3 },   {  7,  9, -7 },   { 12, 20,  1 },   {  6, 33, -2 },   {  7, 13, -8 },   {  3, 12, -7 }, 
+  {  2, 10, 15 },   {  6,  7, 15 },   {  5, 13,  2 },   { 11,  2,  2 },   {  5, 16, 18 },   { 12,  7, 18 },   {  8,  3,  2 },   {  5, 29,-13 }, 
+  {  6, 22, 18 },   { 10,  2,  6 },   { 12, 17,  2 },   {  6, 27,-14 },   { 14,  9,  1 },   {  6, 34,-19 },   {  5, 39,-19 },   {  2, 14,-14 }, 
+  {  5, 20, 15 },   { 14,  5, 15 },   {  9,  5,  2 },   {  5, 32,-22 },   { 11, 10, 15 },   {  6, 35,-22 },   {  5, 41,-20 },   {  2, 16,-13 }, 
+  { 12, 23, 15 },   {  6, 37, -3 },   {  7, 14, -9 },   {  3, 16,-22 },   {  6, 46,-10 },   {  4,  6, -2 },   {  3, 21,-19 },   {  1,  8, -7 }, 
+  {  1,  7,  7 },   {  3,  2, 19 },   {  4,  1,  2 },   {  6,  1, 10 },   {  3,  7, 22 },   {  7,  1,  9 },   {  6, 10,  3 },   { 12,  0,  0 }, 
+  {  2,  7, 13 },   {  5,  6, 20 },   {  6, 12, 22 },   { 11,  1,  6 },   {  5, 15, 22 },   {  9,  2,  9 },   { 14,  6,  3 },   {  5, 27,-15 }, 
+  {  2,  9, 14 },   {  5,  8, 19 },   {  6, 13, 19 },   { 14,  2, 10 },   {  6, 20, 14 },   { 12,  6, 19 },   { 10,  3,  3 },   {  6, 25,-18 }, 
+  {  5, 18, 13 },   {  8,  2,  5 },   { 12, 16, 13 },   {  5, 31,-18 },   { 11,  9, 13 },   {  5, 34, -2 },   {  6, 40,-15 },   {  2, 13,-15 }, 
+  {  3, 11,  7 },   {  7,  2,  8 },   {  6, 14,  2 },   { 12,  3, 17 },   {  7,  6,  7 },   { 13,  0,  0 },   { 12, 14, 21 },   {  7,  8,-12 }, 
+  {  6, 23, 13 },   { 12, 10, 20 },   { 10,  4,  2 },   {  6, 28,-23 },   { 12, 21, 22 },   {  7, 10, -2 },   {  6, 41,-16 },   {  3, 13,-14 }, 
+  {  5, 21,  8 },   {  9,  3,  8 },   { 11,  8,  4 },   {  5, 33, -1 },   { 12, 22,  8 },   {  7, 11, -1 },   {  6, 42,-20 },   {  3, 14, -1 }, 
+  { 14, 11,  8 },   {  5, 36,-11 },   {  6, 44,-11 },   {  2, 17,-11 },   {  6, 47,  0 },   {  3, 18, -2 },   {  4,  7,  0 },   {  1,  9,-11 }, 
+  {  2, 11,  7 },   {  6,  8, 12 },   {  6, 15,  7 },   { 10,  0,  0 },   {  5, 17, 23 },   { 12,  8, 23 },   { 11,  7, 11 },   {  6, 26, -8 }, 
+  {  5, 19, 12 },   { 14,  4, 12 },   { 12, 18, 12 },   {  6, 29, -4 },   {  8,  4,  1 },   {  5, 35, -4 },   {  5, 40,-16 },   {  2, 15, -4 }, 
+  {  5, 22, 14 },   { 11,  5, 14 },   { 12, 19, 14 },   {  6, 30, -1 },   { 14, 10, 14 },   {  6, 36,-17 },   {  6, 43, -6 },   {  4,  4, -1 }, 
+  {  9,  7, 12 },   {  5, 37,-21 },   {  7, 15,  0 },   {  3, 17,-18 },   {  5, 44,-17 },   {  2, 19, -2 },   {  3, 22,-16 },   {  1, 10, -8 }, 
+  {  5, 23,  7 },   { 12, 11,  7 },   { 14,  8,  2 },   {  6, 31, -9 },   {  9,  6,  7 },   {  7, 12, -5 },   {  5, 42, -5 },   {  3, 15, -5 }, 
+  { 11, 11,  7 },   {  6, 38,-21 },   {  6, 45, -5 },   {  4,  5, -4 },   {  5, 45,-10 },   {  3, 19, -4 },   {  2, 21, -5 },   {  1, 11, -4 }, 
+  {  8,  5,  7 },   {  5, 38, -3 },   {  5, 43, -9 },   {  2, 18, -1 },   {  5, 46, -6 },   {  3, 20, -3 },   {  2, 22, -6 },   {  1, 12, -1 }, 
+  {  5, 47,  0 },   {  2, 20, -3 },   {  3, 23,  0 },   {  1, 13, -9 },   {  2, 23,  0 },   {  1, 14, -2 },   {  1, 15,  0 },   {  0,  1,  0 }
+};
+
+static char faceTest_num[256][7] = 
+{
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 3, 1, 5, 4, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 1, 4, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 3, 1, 2, 5, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 2, 2, 1, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 2, 5, 2, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 4, 5, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 2, 4, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 3, 0, 0, 0, 0, 0 },  { 1, 3, 0, 0, 0, 0, 0 },  { 3, 5, 2, 3, 0, 0, 0 },  { 2, 3, 2, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 6, 0, 0, 0, 0, 0 },  { 1, 6, 0, 0, 0, 0, 0 },  { 3, 1, 6, 2, 0, 0, 0 },  { 2, 2, 6, 0, 0, 0, 0 },  { 1, 6, 0, 0, 0, 0, 0 },  { 2, 5, 6, 0, 0, 0, 0 },  { 2, 6, 1, 0, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },
+  { 3, 6, 4, 3, 0, 0, 0 },  { 2, 6, 3, 0, 0, 0, 0 },  { 6, 2, 3, 4, 1, 5, 6 },  { 3,-3,-2,-6, 0, 0, 0 },  { 2, 4, 6, 0, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },  { 3,-1,-4,-6, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 1, 5, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 3, 0, 0, 0, 0, 0 },  { 2, 1, 3, 0, 0, 0, 0 },  { 2, 5, 3, 0, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 2, 3, 4, 0, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },  { 3,-3,-4,-5, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 4, 0, 0, 0, 0, 0 },  { 1, 3, 0, 0, 0, 0, 0 },  { 3, 3, 4, 5, 0, 0, 0 },  { 1, 3, 0, 0, 0, 0, 0 },  { 2, 4, 3, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 3, 0, 0, 0, 0, 0 },  { 2, 3, 5, 0, 0, 0, 0 },  { 2, 3, 1, 0, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 5, 1, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 6, 0, 0, 0, 0, 0 },  { 3, 1, 4, 6, 0, 0, 0 },  { 1, 6, 0, 0, 0, 0, 0 },  { 2, 6, 4, 0, 0, 0, 0 },  { 3, 3, 2, 6, 0, 0, 0 },  { 6, 1, 2, 3, 4, 5, 6 },  { 2, 3, 6, 0, 0, 0, 0 },  { 3,-6,-4,-3, 0, 0, 0 },
+  { 1, 6, 0, 0, 0, 0, 0 },  { 2, 1, 6, 0, 0, 0, 0 },  { 2, 6, 5, 0, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },  { 2, 6, 2, 0, 0, 0, 0 },  { 3,-1,-6,-2, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 2, 3, 0, 0, 0, 0 },  { 3,-5,-2,-3, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 4, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 2, 2, 4, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 5, 4, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 2, 5, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 1, 2, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 3,-1,-2,-5, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 4, 1, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 3,-1,-5,-4, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static char interiorTest_num[256] = 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static unsigned char triangleTable_1_0[5] =  { 3, 0, 8, 3, 0 };
+static unsigned char * triangleTable_1[1] = { triangleTable_1_0 };
+
+static unsigned char triangleTable_2_0[5] =  { 3, 0, 0, 1, 9 };
+static unsigned char * triangleTable_2[1] = { triangleTable_2_0 };
+
+static unsigned char triangleTable_3_0[8] =  { 6, 0, 3, 1, 8, 9, 8, 1 };
+static unsigned char * triangleTable_3[1] = { triangleTable_3_0 };
+
+static unsigned char triangleTable_4_0[5] =  { 3, 0, 10, 1, 2 };
+static unsigned char * triangleTable_4[1] = { triangleTable_4_0 };
+
+static unsigned char triangleTable_5_0[8] =  { 6, 0, 1, 2, 10, 8, 3, 0 };
+static unsigned char triangleTable_5_1[14] =  { 12, 0, 8, 3, 10, 10, 1, 0, 0, 8, 10, 2, 10, 3 };
+static unsigned char * triangleTable_5[2] = { triangleTable_5_0, triangleTable_5_1 };
+
+static unsigned char triangleTable_6_0[8] =  { 6, 0, 0, 2, 9, 10, 9, 2 };
+static unsigned char * triangleTable_6[1] = { triangleTable_6_0 };
+
+static unsigned char triangleTable_7_0[11] =  { 9, 0, 8, 3, 2, 10, 8, 2, 10, 9, 8 };
+static unsigned char * triangleTable_7[1] = { triangleTable_7_0 };
+
+static unsigned char triangleTable_8_0[5] =  { 3, 0, 3, 11, 2 };
+static unsigned char * triangleTable_8[1] = { triangleTable_8_0 };
+
+static unsigned char triangleTable_9_0[8] =  { 6, 0, 2, 0, 11, 8, 11, 0 };
+static unsigned char * triangleTable_9[1] = { triangleTable_9_0 };
+
+static unsigned char triangleTable_10_0[8] =  { 6, 0, 2, 3, 11, 9, 0, 1 };
+static unsigned char triangleTable_10_1[14] =  { 12, 0, 9, 0, 11, 11, 2, 1, 1, 9, 11, 3, 11, 0 };
+static unsigned char * triangleTable_10[2] = { triangleTable_10_0, triangleTable_10_1 };
+
+static unsigned char triangleTable_11_0[11] =  { 9, 0, 11, 2, 1, 9, 11, 1, 9, 8, 11 };
+static unsigned char * triangleTable_11[1] = { triangleTable_11_0 };
+
+static unsigned char triangleTable_12_0[8] =  { 6, 0, 11, 10, 3, 1, 3, 10 };
+static unsigned char * triangleTable_12[1] = { triangleTable_12_0 };
+
+static unsigned char triangleTable_13_0[11] =  { 9, 0, 10, 1, 0, 8, 10, 0, 8, 11, 10 };
+static unsigned char * triangleTable_13[1] = { triangleTable_13_0 };
+
+static unsigned char triangleTable_14_0[11] =  { 9, 0, 9, 0, 3, 11, 9, 3, 11, 10, 9 };
+static unsigned char * triangleTable_14[1] = { triangleTable_14_0 };
+
+static unsigned char triangleTable_15_0[8] =  { 6, 0, 10, 9, 8, 8, 11, 10 };
+static unsigned char * triangleTable_15[1] = { triangleTable_15_0 };
+
+static unsigned char triangleTable_16_0[5] =  { 3, 0, 7, 8, 4 };
+static unsigned char * triangleTable_16[1] = { triangleTable_16_0 };
+
+static unsigned char triangleTable_17_0[8] =  { 6, 0, 7, 3, 4, 0, 4, 3 };
+static unsigned char * triangleTable_17[1] = { triangleTable_17_0 };
+
+static unsigned char triangleTable_18_0[8] =  { 6, 0, 9, 0, 1, 7, 8, 4 };
+static unsigned char triangleTable_18_1[14] =  { 12, 0, 7, 8, 1, 1, 9, 4, 4, 7, 1, 0, 1, 8 };
+static unsigned char * triangleTable_18[2] = { triangleTable_18_0, triangleTable_18_1 };
+
+static unsigned char triangleTable_19_0[11] =  { 9, 0, 1, 9, 4, 7, 1, 4, 7, 3, 1 };
+static unsigned char * triangleTable_19[1] = { triangleTable_19_0 };
+
+static unsigned char triangleTable_20_0[8] =  { 6, 0, 2, 10, 1, 8, 4, 7 };
+static unsigned char triangleTable_20_1[20] =  { 18, 0, 7, 2, 10, 7, 10, 4, 2, 8, 1, 4, 10, 1, 4, 1, 8, 8, 2, 7 };
+static unsigned char * triangleTable_20[2] = { triangleTable_20_0, triangleTable_20_1 };
+
+static unsigned char triangleTable_21_0[11] =  { 9, 0, 10, 1, 2, 4, 3, 0, 4, 7, 3 };
+static unsigned char triangleTable_21_1[17] =  { 15, 0, 4, 10, 1, 2, 7, 3, 4, 7, 10, 0, 4, 1, 10, 7, 2 };
+static unsigned char * triangleTable_21[2] = { triangleTable_21_0, triangleTable_21_1 };
+
+static unsigned char triangleTable_22_0[11] =  { 9, 0, 7, 8, 4, 2, 9, 0, 2, 10, 9 };
+static unsigned char triangleTable_22_1[17] =  { 15, 0, 2, 7, 8, 4, 10, 9, 2, 10, 7, 0, 2, 8, 7, 10, 4 };
+static unsigned char * triangleTable_22[2] = { triangleTable_22_0, triangleTable_22_1 };
+
+static unsigned char triangleTable_23_0[14] =  { 12, 0, 4, 7, 9, 2, 10, 9, 7, 3, 2, 7, 2, 9 };
+static unsigned char * triangleTable_23[1] = { triangleTable_23_0 };
+
+static unsigned char triangleTable_24_0[8] =  { 6, 0, 3, 11, 2, 4, 7, 8 };
+static unsigned char triangleTable_24_1[14] =  { 12, 0, 4, 7, 2, 2, 3, 8, 8, 4, 2, 11, 2, 7 };
+static unsigned char * triangleTable_24[2] = { triangleTable_24_0, triangleTable_24_1 };
+
+static unsigned char triangleTable_25_0[11] =  { 9, 0, 4, 7, 11, 2, 4, 11, 2, 0, 4 };
+static unsigned char * triangleTable_25[1] = { triangleTable_25_0 };
+
+static unsigned char triangleTable_26_0[11] =  { 9, 0, 3, 11, 2, 0, 1, 9, 4, 7, 8 };
+static unsigned char triangleTable_26_1[17] =  { 15, 0, 3, 11, 2, 9, 4, 7, 1, 9, 7, 8, 1, 7, 1, 8, 0 };
+static unsigned char triangleTable_26_2[17] =  { 15, 0, 8, 4, 7, 2, 1, 9, 11, 2, 9, 0, 11, 9, 11, 0, 3 };
+static unsigned char triangleTable_26_3[17] =  { 15, 0, 0, 1, 9, 7, 11, 2, 4, 7, 2, 3, 4, 2, 4, 3, 8 };
+static unsigned char triangleTable_26_4[29] =  { 27, 1, 12, 3, 11, 12, 0, 3, 1, 12, 2, 0, 12, 8, 12, 4, 7, 12, 1, 9, 8, 12, 7, 2, 12, 11, 9, 4, 12 };
+static unsigned char triangleTable_26_5[29] =  { 27, 1, 12, 0, 1, 12, 8, 0, 4, 12, 9, 8, 12, 3, 12, 11, 2, 12, 4, 7, 3, 12, 2, 9, 12, 1, 7, 11, 12 };
+static unsigned char triangleTable_26_6[29] =  { 27, 1, 12, 8, 4, 12, 3, 8, 11, 12, 7, 3, 12, 0, 12, 1, 9, 12, 11, 2, 0, 12, 9, 7, 12, 4, 2, 1, 12 };
+static unsigned char triangleTable_26_7[17] =  { 15, 0, 1, 9, 4, 7, 11, 4, 0, 3, 8, 11, 1, 4, 1, 11, 2 };
+static unsigned char * triangleTable_26[8] = 
+{ 
+  triangleTable_26_0, triangleTable_26_1, triangleTable_26_2, triangleTable_26_3, triangleTable_26_4, triangleTable_26_5, triangleTable_26_6, triangleTable_26_7
+};
+
+static unsigned char triangleTable_27_0[14] =  { 12, 0, 7, 1, 4, 7, 2, 1, 11, 2, 7, 4, 1, 9 };
+static unsigned char * triangleTable_27[1] = { triangleTable_27_0 };
+
+static unsigned char triangleTable_28_0[11] =  { 9, 0, 4, 7, 8, 10, 3, 11, 10, 1, 3 };
+static unsigned char triangleTable_28_1[17] =  { 15, 0, 10, 4, 7, 8, 1, 3, 10, 1, 4, 11, 10, 7, 4, 1, 8 };
+static unsigned char * triangleTable_28[2] = { triangleTable_28_0, triangleTable_28_1 };
+
+static unsigned char triangleTable_29_0[14] =  { 12, 0, 11, 10, 7, 10, 1, 0, 10, 0, 4, 7, 10, 4 };
+static unsigned char * triangleTable_29[1] = { triangleTable_29_0 };
+
+static unsigned char triangleTable_30_0[14] =  { 12, 0, 0, 3, 11, 9, 0, 11, 7, 8, 4, 9, 11, 10 };
+static unsigned char triangleTable_30_1[14] =  { 12, 0, 4, 7, 11, 9, 4, 11, 3, 8, 0, 9, 11, 10 };
+static unsigned char triangleTable_30_2[26] =  { 24, 1, 12, 7, 8, 10, 12, 11, 9, 4, 12, 3, 12, 0, 0, 12, 8, 12, 10, 9, 12, 3, 11, 7, 12, 4 };
+static unsigned char triangleTable_30_3[26] =  { 24, 1, 12, 3, 8, 10, 12, 11, 9, 0, 12, 7, 12, 4, 4, 12, 8, 12, 10, 9, 12, 7, 11, 3, 12, 0 };
+static unsigned char * triangleTable_30[4] = { triangleTable_30_0, triangleTable_30_1, triangleTable_30_2, triangleTable_30_3 };
+
+static unsigned char triangleTable_31_0[11] =  { 9, 0, 4, 7, 11, 4, 11, 9, 11, 10, 9 };
+static unsigned char * triangleTable_31[1] = { triangleTable_31_0 };
+
+static unsigned char triangleTable_32_0[5] =  { 3, 0, 9, 5, 4 };
+static unsigned char * triangleTable_32[1] = { triangleTable_32_0 };
+
+static unsigned char triangleTable_33_0[8] =  { 6, 0, 4, 9, 5, 3, 0, 8 };
+static unsigned char triangleTable_33_1[14] =  { 12, 0, 3, 0, 5, 5, 4, 8, 8, 3, 5, 9, 5, 0 };
+static unsigned char * triangleTable_33[2] = { triangleTable_33_0, triangleTable_33_1 };
+
+static unsigned char triangleTable_34_0[8] =  { 6, 0, 1, 5, 0, 4, 0, 5 };
+static unsigned char * triangleTable_34[1] = { triangleTable_34_0 };
+
+static unsigned char triangleTable_35_0[11] =  { 9, 0, 5, 4, 8, 3, 5, 8, 3, 1, 5 };
+static unsigned char * triangleTable_35[1] = { triangleTable_35_0 };
+
+static unsigned char triangleTable_36_0[8] =  { 6, 0, 10, 1, 2, 4, 9, 5 };
+static unsigned char triangleTable_36_1[14] =  { 12, 0, 4, 9, 2, 2, 10, 5, 5, 4, 2, 1, 2, 9 };
+static unsigned char * triangleTable_36[2] = { triangleTable_36_0, triangleTable_36_1 };
+
+static unsigned char triangleTable_37_0[11] =  { 9, 0, 1, 2, 10, 9, 5, 4, 8, 3, 0 };
+static unsigned char triangleTable_37_1[17] =  { 15, 0, 1, 2, 10, 4, 8, 3, 5, 4, 3, 0, 5, 3, 5, 0, 9 };
+static unsigned char triangleTable_37_2[17] =  { 15, 0, 0, 8, 3, 10, 5, 4, 2, 10, 4, 9, 2, 4, 2, 9, 1 };
+static unsigned char triangleTable_37_3[17] =  { 15, 0, 9, 5, 4, 3, 2, 10, 8, 3, 10, 1, 8, 10, 8, 1, 0 };
+static unsigned char triangleTable_37_4[29] =  { 27, 1, 12, 1, 2, 12, 9, 1, 5, 12, 10, 9, 12, 0, 12, 8, 3, 12, 5, 4, 0, 12, 3, 10, 12, 2, 4, 8, 12 };
+static unsigned char triangleTable_37_5[29] =  { 27, 1, 12, 9, 5, 12, 0, 9, 8, 12, 4, 0, 12, 1, 12, 2, 10, 12, 8, 3, 1, 12, 10, 4, 12, 5, 3, 2, 12 };
+static unsigned char triangleTable_37_6[29] =  { 27, 1, 12, 0, 8, 12, 1, 0, 2, 12, 3, 1, 12, 9, 12, 5, 4, 12, 2, 10, 9, 12, 4, 3, 12, 8, 10, 5, 12 };
+static unsigned char triangleTable_37_7[17] =  { 15, 0, 5, 4, 8, 3, 2, 8, 9, 1, 0, 2, 5, 8, 5, 2, 10 };
+static unsigned char * triangleTable_37[8] = 
+{ 
+  triangleTable_37_0, triangleTable_37_1, triangleTable_37_2, triangleTable_37_3, triangleTable_37_4, triangleTable_37_5, triangleTable_37_6, triangleTable_37_7
+};
+
+static unsigned char triangleTable_38_0[11] =  { 9, 0, 2, 10, 5, 4, 2, 5, 4, 0, 2 };
+static unsigned char * triangleTable_38[1] = { triangleTable_38_0 };
+
+static unsigned char triangleTable_39_0[14] =  { 12, 0, 3, 5, 8, 3, 10, 5, 2, 10, 3, 8, 5, 4 };
+static unsigned char * triangleTable_39[1] = { triangleTable_39_0 };
+
+static unsigned char triangleTable_40_0[8] =  { 6, 0, 4, 9, 5, 11, 2, 3 };
+static unsigned char triangleTable_40_1[20] =  { 18, 0, 3, 4, 9, 3, 9, 2, 4, 11, 5, 2, 9, 5, 2, 5, 11, 11, 4, 3 };
+static unsigned char * triangleTable_40[2] = { triangleTable_40_0, triangleTable_40_1 };
+
+static unsigned char triangleTable_41_0[11] =  { 9, 0, 5, 4, 9, 11, 0, 8, 11, 2, 0 };
+static unsigned char triangleTable_41_1[17] =  { 15, 0, 11, 5, 4, 9, 2, 0, 11, 2, 5, 8, 11, 4, 5, 2, 9 };
+static unsigned char * triangleTable_41[2] = { triangleTable_41_0, triangleTable_41_1 };
+
+static unsigned char triangleTable_42_0[11] =  { 9, 0, 11, 2, 3, 5, 0, 1, 5, 4, 0 };
+static unsigned char triangleTable_42_1[17] =  { 15, 0, 5, 11, 2, 3, 4, 0, 5, 4, 11, 1, 5, 2, 11, 4, 3 };
+static unsigned char * triangleTable_42[2] = { triangleTable_42_0, triangleTable_42_1 };
+
+static unsigned char triangleTable_43_0[14] =  { 12, 0, 1, 5, 2, 5, 4, 8, 5, 8, 11, 2, 5, 11 };
+static unsigned char * triangleTable_43[1] = { triangleTable_43_0 };
+
+static unsigned char triangleTable_44_0[11] =  { 9, 0, 4, 9, 5, 3, 10, 1, 3, 11, 10 };
+static unsigned char triangleTable_44_1[17] =  { 15, 0, 3, 4, 9, 5, 11, 10, 3, 11, 4, 1, 3, 9, 4, 11, 5 };
+static unsigned char * triangleTable_44[2] = { triangleTable_44_0, triangleTable_44_1 };
+
+static unsigned char triangleTable_45_0[14] =  { 12, 0, 1, 0, 8, 10, 1, 8, 4, 9, 5, 10, 8, 11 };
+static unsigned char triangleTable_45_1[14] =  { 12, 0, 5, 4, 8, 10, 5, 8, 0, 9, 1, 10, 8, 11 };
+static unsigned char triangleTable_45_2[26] =  { 24, 1, 12, 4, 9, 11, 12, 8, 10, 5, 12, 0, 12, 1, 1, 12, 9, 12, 11, 10, 12, 0, 8, 4, 12, 5 };
+static unsigned char triangleTable_45_3[26] =  { 24, 1, 12, 0, 9, 11, 12, 8, 10, 1, 12, 4, 12, 5, 5, 12, 9, 12, 11, 10, 12, 4, 8, 0, 12, 1 };
+static unsigned char * triangleTable_45[4] = { triangleTable_45_0, triangleTable_45_1, triangleTable_45_2, triangleTable_45_3 };
+
+static unsigned char triangleTable_46_0[14] =  { 12, 0, 5, 4, 10, 3, 11, 10, 4, 0, 3, 4, 3, 10 };
+static unsigned char * triangleTable_46[1] = { triangleTable_46_0 };
+
+static unsigned char triangleTable_47_0[11] =  { 9, 0, 5, 4, 8, 5, 8, 10, 8, 11, 10 };
+static unsigned char * triangleTable_47[1] = { triangleTable_47_0 };
+
+static unsigned char triangleTable_48_0[8] =  { 6, 0, 8, 9, 7, 5, 7, 9 };
+static unsigned char * triangleTable_48[1] = { triangleTable_48_0 };
+
+static unsigned char triangleTable_49_0[11] =  { 9, 0, 3, 0, 9, 5, 3, 9, 5, 7, 3 };
+static unsigned char * triangleTable_49[1] = { triangleTable_49_0 };
+
+static unsigned char triangleTable_50_0[11] =  { 9, 0, 7, 8, 0, 1, 7, 0, 1, 5, 7 };
+static unsigned char * triangleTable_50[1] = { triangleTable_50_0 };
+
+static unsigned char triangleTable_51_0[8] =  { 6, 0, 1, 5, 7, 7, 3, 1 };
+static unsigned char * triangleTable_51[1] = { triangleTable_51_0 };
+
+static unsigned char triangleTable_52_0[11] =  { 9, 0, 2, 10, 1, 7, 9, 5, 7, 8, 9 };
+static unsigned char triangleTable_52_1[17] =  { 15, 0, 7, 2, 10, 1, 8, 9, 7, 8, 2, 5, 7, 10, 2, 8, 1 };
+static unsigned char * triangleTable_52[2] = { triangleTable_52_0, triangleTable_52_1 };
+
+static unsigned char triangleTable_53_0[14] =  { 12, 0, 0, 9, 5, 3, 0, 5, 10, 1, 2, 3, 5, 7 };
+static unsigned char triangleTable_53_1[14] =  { 12, 0, 2, 10, 5, 3, 2, 5, 9, 1, 0, 3, 5, 7 };
+static unsigned char triangleTable_53_2[26] =  { 24, 1, 12, 10, 1, 7, 12, 5, 3, 2, 12, 9, 12, 0, 0, 12, 1, 12, 7, 3, 12, 9, 5, 10, 12, 2 };
+static unsigned char triangleTable_53_3[26] =  { 24, 1, 12, 9, 1, 7, 12, 5, 3, 0, 12, 10, 12, 2, 2, 12, 1, 12, 7, 3, 12, 10, 5, 9, 12, 0 };
+static unsigned char * triangleTable_53[4] = { triangleTable_53_0, triangleTable_53_1, triangleTable_53_2, triangleTable_53_3 };
+
+static unsigned char triangleTable_54_0[14] =  { 12, 0, 0, 2, 8, 2, 10, 5, 2, 5, 7, 8, 2, 7 };
+static unsigned char * triangleTable_54[1] = { triangleTable_54_0 };
+
+static unsigned char triangleTable_55_0[11] =  { 9, 0, 2, 10, 5, 2, 5, 3, 5, 7, 3 };
+static unsigned char * triangleTable_55[1] = { triangleTable_55_0 };
+
+static unsigned char triangleTable_56_0[11] =  { 9, 0, 2, 3, 11, 9, 7, 8, 9, 5, 7 };
+static unsigned char triangleTable_56_1[17] =  { 15, 0, 9, 2, 3, 11, 5, 7, 9, 5, 2, 8, 9, 3, 2, 5, 11 };
+static unsigned char * triangleTable_56[2] = { triangleTable_56_0, triangleTable_56_1 };
+
+static unsigned char triangleTable_57_0[14] =  { 12, 0, 9, 5, 0, 11, 2, 0, 5, 7, 11, 5, 11, 0 };
+static unsigned char * triangleTable_57[1] = { triangleTable_57_0 };
+
+static unsigned char triangleTable_58_0[14] =  { 12, 0, 8, 0, 1, 7, 8, 1, 2, 3, 11, 7, 1, 5 };
+static unsigned char triangleTable_58_1[14] =  { 12, 0, 11, 2, 1, 7, 11, 1, 0, 3, 8, 7, 1, 5 };
+static unsigned char triangleTable_58_2[26] =  { 24, 1, 12, 2, 3, 5, 12, 1, 7, 11, 12, 0, 12, 8, 8, 12, 3, 12, 5, 7, 12, 0, 1, 2, 12, 11 };
+static unsigned char triangleTable_58_3[26] =  { 24, 1, 12, 0, 3, 5, 12, 1, 7, 8, 12, 2, 12, 11, 11, 12, 3, 12, 5, 7, 12, 2, 1, 0, 12, 8 };
+static unsigned char * triangleTable_58[4] = { triangleTable_58_0, triangleTable_58_1, triangleTable_58_2, triangleTable_58_3 };
+
+static unsigned char triangleTable_59_0[11] =  { 9, 0, 11, 2, 1, 11, 1, 7, 1, 5, 7 };
+static unsigned char * triangleTable_59[1] = { triangleTable_59_0 };
+
+static unsigned char triangleTable_60_0[14] =  { 12, 0, 7, 8, 5, 11, 10, 3, 10, 1, 3, 8, 9, 5 };
+static unsigned char triangleTable_60_1[14] =  { 12, 0, 9, 3, 8, 11, 5, 7, 11, 10, 5, 9, 1, 3 };
+static unsigned char triangleTable_60_2[26] =  { 24, 0, 7, 11, 5, 1, 8, 9, 1, 9, 5, 10, 5, 11, 8, 1, 3, 11, 8, 3, 1, 5, 10, 8, 11, 7 };
+static unsigned char triangleTable_60_3[26] =  { 24, 1, 12, 11, 10, 8, 12, 7, 12, 8, 9, 12, 3, 11, 10, 5, 12, 7, 12, 5, 9, 1, 12, 12, 1, 3 };
+static unsigned char triangleTable_60_4[26] =  { 24, 1, 12, 5, 7, 1, 12, 10, 12, 1, 3, 12, 9, 5, 7, 11, 12, 10, 12, 11, 3, 8, 12, 12, 8, 9 };
+static unsigned char * triangleTable_60[5] = { triangleTable_60_0, triangleTable_60_1, triangleTable_60_2, triangleTable_60_3, triangleTable_60_4 };
+
+static unsigned char triangleTable_61_0[11] =  { 9, 0, 9, 1, 0, 10, 5, 11, 5, 7, 11 };
+static unsigned char triangleTable_61_1[17] =  { 15, 0, 1, 0, 11, 5, 7, 9, 0, 7, 11, 1, 11, 10, 9, 7, 0 };
+static unsigned char * triangleTable_61[2] = { triangleTable_61_0, triangleTable_61_1 };
+
+static unsigned char triangleTable_62_0[11] =  { 9, 0, 3, 8, 0, 7, 11, 5, 11, 10, 5 };
+static unsigned char triangleTable_62_1[17] =  { 15, 0, 8, 0, 5, 11, 10, 3, 0, 10, 5, 8, 5, 7, 3, 10, 0 };
+static unsigned char * triangleTable_62[2] = { triangleTable_62_0, triangleTable_62_1 };
+
+static unsigned char triangleTable_63_0[8] =  { 6, 0, 11, 5, 7, 5, 11, 10 };
+static unsigned char * triangleTable_63[1] = { triangleTable_63_0 };
+
+static unsigned char triangleTable_64_0[5] =  { 3, 0, 6, 5, 10 };
+static unsigned char * triangleTable_64[1] = { triangleTable_64_0 };
+
+static unsigned char triangleTable_65_0[8] =  { 6, 0, 6, 5, 10, 3, 0, 8 };
+static unsigned char triangleTable_65_1[20] =  { 18, 0, 8, 6, 5, 8, 5, 0, 6, 3, 10, 0, 5, 10, 0, 10, 3, 3, 6, 8 };
+static unsigned char * triangleTable_65[2] = { triangleTable_65_0, triangleTable_65_1 };
+
+static unsigned char triangleTable_66_0[8] =  { 6, 0, 5, 10, 6, 0, 1, 9 };
+static unsigned char triangleTable_66_1[14] =  { 12, 0, 0, 1, 6, 6, 5, 9, 9, 0, 6, 10, 6, 1 };
+static unsigned char * triangleTable_66[2] = { triangleTable_66_0, triangleTable_66_1 };
+
+static unsigned char triangleTable_67_0[11] =  { 9, 0, 6, 5, 10, 8, 1, 9, 8, 3, 1 };
+static unsigned char triangleTable_67_1[17] =  { 15, 0, 8, 6, 5, 10, 3, 1, 8, 3, 6, 9, 8, 5, 6, 3, 10 };
+static unsigned char * triangleTable_67[2] = { triangleTable_67_0, triangleTable_67_1 };
+
+static unsigned char triangleTable_68_0[8] =  { 6, 0, 5, 1, 6, 2, 6, 1 };
+static unsigned char * triangleTable_68[1] = { triangleTable_68_0 };
+
+static unsigned char triangleTable_69_0[11] =  { 9, 0, 8, 3, 0, 6, 1, 2, 6, 5, 1 };
+static unsigned char triangleTable_69_1[17] =  { 15, 0, 6, 8, 3, 0, 5, 1, 6, 5, 8, 2, 6, 3, 8, 5, 0 };
+static unsigned char * triangleTable_69[2] = { triangleTable_69_0, triangleTable_69_1 };
+
+static unsigned char triangleTable_70_0[11] =  { 9, 0, 6, 5, 9, 0, 6, 9, 0, 2, 6 };
+static unsigned char * triangleTable_70[1] = { triangleTable_70_0 };
+
+static unsigned char triangleTable_71_0[14] =  { 12, 0, 2, 6, 3, 6, 5, 9, 6, 9, 8, 3, 6, 8 };
+static unsigned char * triangleTable_71[1] = { triangleTable_71_0 };
+
+static unsigned char triangleTable_72_0[8] =  { 6, 0, 10, 6, 5, 3, 11, 2 };
+static unsigned char triangleTable_72_1[14] =  { 12, 0, 3, 11, 5, 5, 10, 2, 2, 3, 5, 6, 5, 11 };
+static unsigned char * triangleTable_72[2] = { triangleTable_72_0, triangleTable_72_1 };
+
+static unsigned char triangleTable_73_0[11] =  { 9, 0, 5, 10, 6, 0, 11, 2, 0, 8, 11 };
+static unsigned char triangleTable_73_1[17] =  { 15, 0, 0, 5, 10, 6, 8, 11, 0, 8, 5, 2, 0, 10, 5, 8, 6 };
+static unsigned char * triangleTable_73[2] = { triangleTable_73_0, triangleTable_73_1 };
+
+static unsigned char triangleTable_74_0[11] =  { 9, 0, 10, 6, 5, 1, 9, 0, 3, 11, 2 };
+static unsigned char triangleTable_74_1[17] =  { 15, 0, 10, 6, 5, 0, 3, 11, 9, 0, 11, 2, 9, 11, 9, 2, 1 };
+static unsigned char triangleTable_74_2[17] =  { 15, 0, 2, 3, 11, 5, 9, 0, 6, 5, 0, 1, 6, 0, 6, 1, 10 };
+static unsigned char triangleTable_74_3[17] =  { 15, 0, 1, 9, 0, 11, 6, 5, 3, 11, 5, 10, 3, 5, 3, 10, 2 };
+static unsigned char triangleTable_74_4[29] =  { 27, 1, 12, 10, 6, 12, 1, 10, 9, 12, 5, 1, 12, 2, 12, 3, 11, 12, 9, 0, 2, 12, 11, 5, 12, 6, 0, 3, 12 };
+static unsigned char triangleTable_74_5[29] =  { 27, 1, 12, 1, 9, 12, 2, 1, 3, 12, 0, 2, 12, 10, 12, 6, 5, 12, 3, 11, 10, 12, 5, 0, 12, 9, 11, 6, 12 };
+static unsigned char triangleTable_74_6[29] =  { 27, 1, 12, 2, 3, 12, 10, 2, 6, 12, 11, 10, 12, 1, 12, 9, 0, 12, 6, 5, 1, 12, 0, 11, 12, 3, 5, 9, 12 };
+static unsigned char triangleTable_74_7[17] =  { 15, 0, 9, 0, 3, 11, 6, 3, 1, 10, 2, 6, 9, 3, 9, 6, 5 };
+static unsigned char * triangleTable_74[8] = 
+{ 
+  triangleTable_74_0, triangleTable_74_1, triangleTable_74_2, triangleTable_74_3, triangleTable_74_4, triangleTable_74_5, triangleTable_74_6, triangleTable_74_7
+};
+
+static unsigned char triangleTable_75_0[14] =  { 12, 0, 2, 1, 9, 11, 2, 9, 5, 10, 6, 11, 9, 8 };
+static unsigned char triangleTable_75_1[14] =  { 12, 0, 6, 5, 9, 11, 6, 9, 1, 10, 2, 11, 9, 8 };
+static unsigned char triangleTable_75_2[26] =  { 24, 1, 12, 5, 10, 8, 12, 9, 11, 6, 12, 1, 12, 2, 2, 12, 10, 12, 8, 11, 12, 1, 9, 5, 12, 6 };
+static unsigned char triangleTable_75_3[26] =  { 24, 1, 12, 1, 10, 8, 12, 9, 11, 2, 12, 5, 12, 6, 6, 12, 10, 12, 8, 11, 12, 5, 9, 1, 12, 2 };
+static unsigned char * triangleTable_75[4] = { triangleTable_75_0, triangleTable_75_1, triangleTable_75_2, triangleTable_75_3 };
+
+static unsigned char triangleTable_76_0[11] =  { 9, 0, 3, 11, 6, 5, 3, 6, 5, 1, 3 };
+static unsigned char * triangleTable_76[1] = { triangleTable_76_0 };
+
+static unsigned char triangleTable_77_0[14] =  { 12, 0, 0, 8, 1, 6, 5, 1, 8, 11, 6, 8, 6, 1 };
+static unsigned char * triangleTable_77[1] = { triangleTable_77_0 };
+
+static unsigned char triangleTable_78_0[14] =  { 12, 0, 11, 9, 3, 11, 5, 9, 6, 5, 11, 3, 9, 0 };
+static unsigned char * triangleTable_78[1] = { triangleTable_78_0 };
+
+static unsigned char triangleTable_79_0[11] =  { 9, 0, 6, 5, 9, 6, 9, 11, 9, 8, 11 };
+static unsigned char * triangleTable_79[1] = { triangleTable_79_0 };
+
+static unsigned char triangleTable_80_0[8] =  { 6, 0, 6, 5, 10, 8, 4, 7 };
+static unsigned char triangleTable_80_1[14] =  { 12, 0, 8, 4, 10, 10, 6, 7, 7, 8, 10, 5, 10, 4 };
+static unsigned char * triangleTable_80[2] = { triangleTable_80_0, triangleTable_80_1 };
+
+static unsigned char triangleTable_81_0[11] =  { 9, 0, 10, 6, 5, 3, 4, 7, 3, 0, 4 };
+static unsigned char triangleTable_81_1[17] =  { 15, 0, 3, 10, 6, 5, 0, 4, 3, 0, 10, 7, 3, 6, 10, 0, 5 };
+static unsigned char * triangleTable_81[2] = { triangleTable_81_0, triangleTable_81_1 };
+
+static unsigned char triangleTable_82_0[11] =  { 9, 0, 5, 10, 6, 4, 7, 8, 0, 1, 9 };
+static unsigned char triangleTable_82_1[17] =  { 15, 0, 5, 10, 6, 8, 0, 1, 7, 8, 1, 9, 7, 1, 7, 9, 4 };
+static unsigned char triangleTable_82_2[17] =  { 15, 0, 9, 0, 1, 6, 7, 8, 10, 6, 8, 4, 10, 8, 10, 4, 5 };
+static unsigned char triangleTable_82_3[17] =  { 15, 0, 4, 7, 8, 1, 10, 6, 0, 1, 6, 5, 0, 6, 0, 5, 9 };
+static unsigned char triangleTable_82_4[29] =  { 27, 1, 12, 5, 10, 12, 4, 5, 7, 12, 6, 4, 12, 9, 12, 0, 1, 12, 7, 8, 9, 12, 1, 6, 12, 10, 8, 0, 12 };
+static unsigned char triangleTable_82_5[29] =  { 27, 1, 12, 4, 7, 12, 9, 4, 0, 12, 8, 9, 12, 5, 12, 10, 6, 12, 0, 1, 5, 12, 6, 8, 12, 7, 1, 10, 12 };
+static unsigned char triangleTable_82_6[29] =  { 27, 1, 12, 9, 0, 12, 5, 9, 10, 12, 1, 5, 12, 4, 12, 7, 8, 12, 10, 6, 4, 12, 8, 1, 12, 0, 6, 7, 12 };
+static unsigned char triangleTable_82_7[17] =  { 15, 0, 7, 8, 0, 1, 10, 0, 4, 5, 9, 10, 7, 0, 7, 10, 6 };
+static unsigned char * triangleTable_82[8] = 
+{ 
+  triangleTable_82_0, triangleTable_82_1, triangleTable_82_2, triangleTable_82_3, triangleTable_82_4, triangleTable_82_5, triangleTable_82_6, triangleTable_82_7
+};
+
+static unsigned char triangleTable_83_0[14] =  { 12, 0, 9, 4, 7, 1, 9, 7, 6, 5, 10, 1, 7, 3 };
+static unsigned char triangleTable_83_1[14] =  { 12, 0, 10, 6, 7, 1, 10, 7, 4, 5, 9, 1, 7, 3 };
+static unsigned char triangleTable_83_2[26] =  { 24, 1, 12, 6, 5, 3, 12, 7, 1, 10, 12, 4, 12, 9, 9, 12, 5, 12, 3, 1, 12, 4, 7, 6, 12, 10 };
+static unsigned char triangleTable_83_3[26] =  { 24, 1, 12, 4, 5, 3, 12, 7, 1, 9, 12, 6, 12, 10, 10, 12, 5, 12, 3, 1, 12, 6, 7, 4, 12, 9 };
+static unsigned char * triangleTable_83[4] = { triangleTable_83_0, triangleTable_83_1, triangleTable_83_2, triangleTable_83_3 };
+
+static unsigned char triangleTable_84_0[11] =  { 9, 0, 8, 4, 7, 1, 6, 5, 1, 2, 6 };
+static unsigned char triangleTable_84_1[17] =  { 15, 0, 1, 8, 4, 7, 2, 6, 1, 2, 8, 5, 1, 4, 8, 2, 7 };
+static unsigned char * triangleTable_84[2] = { triangleTable_84_0, triangleTable_84_1 };
+
+static unsigned char triangleTable_85_0[14] =  { 12, 0, 6, 5, 2, 7, 3, 4, 3, 0, 4, 5, 1, 2 };
+static unsigned char triangleTable_85_1[14] =  { 12, 0, 1, 4, 5, 7, 2, 6, 7, 3, 2, 1, 0, 4 };
+static unsigned char triangleTable_85_2[26] =  { 24, 0, 6, 7, 2, 0, 5, 1, 0, 1, 2, 3, 2, 7, 5, 0, 4, 7, 5, 4, 0, 2, 3, 5, 7, 6 };
+static unsigned char triangleTable_85_3[26] =  { 24, 1, 12, 7, 3, 5, 12, 6, 12, 5, 1, 12, 4, 7, 3, 2, 12, 6, 12, 2, 1, 0, 12, 12, 0, 4 };
+static unsigned char triangleTable_85_4[26] =  { 24, 1, 12, 2, 6, 0, 12, 3, 12, 0, 4, 12, 1, 2, 6, 7, 12, 3, 12, 7, 4, 5, 12, 12, 5, 1 };
+static unsigned char * triangleTable_85[5] = { triangleTable_85_0, triangleTable_85_1, triangleTable_85_2, triangleTable_85_3, triangleTable_85_4 };
+
+static unsigned char triangleTable_86_0[14] =  { 12, 0, 5, 9, 0, 6, 5, 0, 8, 4, 7, 6, 0, 2 };
+static unsigned char triangleTable_86_1[14] =  { 12, 0, 7, 8, 0, 6, 7, 0, 9, 4, 5, 6, 0, 2 };
+static unsigned char triangleTable_86_2[26] =  { 24, 1, 12, 8, 4, 2, 12, 0, 6, 7, 12, 9, 12, 5, 5, 12, 4, 12, 2, 6, 12, 9, 0, 8, 12, 7 };
+static unsigned char triangleTable_86_3[26] =  { 24, 1, 12, 9, 4, 2, 12, 0, 6, 5, 12, 8, 12, 7, 7, 12, 4, 12, 2, 6, 12, 8, 0, 9, 12, 5 };
+static unsigned char * triangleTable_86[4] = { triangleTable_86_0, triangleTable_86_1, triangleTable_86_2, triangleTable_86_3 };
+
+static unsigned char triangleTable_87_0[11] =  { 9, 0, 4, 5, 9, 6, 7, 2, 7, 3, 2 };
+static unsigned char triangleTable_87_1[17] =  { 15, 0, 5, 9, 2, 7, 3, 4, 9, 3, 2, 5, 2, 6, 4, 3, 9 };
+static unsigned char * triangleTable_87[2] = { triangleTable_87_0, triangleTable_87_1 };
+
+static unsigned char triangleTable_88_0[11] =  { 9, 0, 11, 2, 3, 7, 8, 4, 5, 10, 6 };
+static unsigned char triangleTable_88_1[17] =  { 15, 0, 11, 2, 3, 4, 5, 10, 8, 4, 10, 6, 8, 10, 8, 6, 7 };
+static unsigned char triangleTable_88_2[17] =  { 15, 0, 6, 5, 10, 3, 8, 4, 2, 3, 4, 7, 2, 4, 2, 7, 11 };
+static unsigned char triangleTable_88_3[17] =  { 15, 0, 7, 8, 4, 10, 2, 3, 5, 10, 3, 11, 5, 3, 5, 11, 6 };
+static unsigned char triangleTable_88_4[29] =  { 27, 1, 12, 11, 2, 12, 7, 11, 8, 12, 3, 7, 12, 6, 12, 5, 10, 12, 8, 4, 6, 12, 10, 3, 12, 2, 4, 5, 12 };
+static unsigned char triangleTable_88_5[29] =  { 27, 1, 12, 7, 8, 12, 6, 7, 5, 12, 4, 6, 12, 11, 12, 2, 3, 12, 5, 10, 11, 12, 3, 4, 12, 8, 10, 2, 12 };
+static unsigned char triangleTable_88_6[29] =  { 27, 1, 12, 6, 5, 12, 11, 6, 2, 12, 10, 11, 12, 7, 12, 8, 4, 12, 2, 3, 7, 12, 4, 10, 12, 5, 3, 8, 12 };
+static unsigned char triangleTable_88_7[17] =  { 15, 0, 8, 4, 5, 10, 2, 5, 7, 11, 6, 2, 8, 5, 8, 2, 3 };
+static unsigned char * triangleTable_88[8] = 
+{ 
+  triangleTable_88_0, triangleTable_88_1, triangleTable_88_2, triangleTable_88_3, triangleTable_88_4, triangleTable_88_5, triangleTable_88_6, triangleTable_88_7
+};
+
+static unsigned char triangleTable_89_0[14] =  { 12, 0, 7, 11, 2, 4, 7, 2, 10, 6, 5, 4, 2, 0 };
+static unsigned char triangleTable_89_1[14] =  { 12, 0, 5, 10, 2, 4, 5, 2, 11, 6, 7, 4, 2, 0 };
+static unsigned char triangleTable_89_2[26] =  { 24, 1, 12, 10, 6, 0, 12, 2, 4, 5, 12, 11, 12, 7, 7, 12, 6, 12, 0, 4, 12, 11, 2, 10, 12, 5 };
+static unsigned char triangleTable_89_3[26] =  { 24, 1, 12, 11, 6, 0, 12, 2, 4, 7, 12, 10, 12, 5, 5, 12, 6, 12, 0, 4, 12, 10, 2, 11, 12, 7 };
+static unsigned char * triangleTable_89[4] = { triangleTable_89_0, triangleTable_89_1, triangleTable_89_2, triangleTable_89_3 };
+
+static unsigned char triangleTable_90_0[14] =  { 12, 0, 11, 2, 3, 4, 7, 8, 1, 9, 0, 6, 5, 10 };
+static unsigned char triangleTable_90_1[20] =  { 18, 0, 2, 3, 11, 8, 4, 7, 6, 5, 0, 1, 10, 6, 5, 9, 0, 6, 0, 1 };
+static unsigned char triangleTable_90_2[20] =  { 18, 0, 7, 8, 4, 0, 1, 9, 5, 10, 3, 11, 6, 5, 10, 2, 3, 5, 3, 11 };
+static unsigned char triangleTable_90_3[20] =  { 18, 0, 0, 1, 9, 10, 6, 5, 4, 7, 2, 3, 8, 4, 7, 11, 2, 4, 2, 3 };
+static unsigned char triangleTable_90_4[20] =  { 18, 0, 5, 10, 6, 2, 3, 11, 7, 8, 1, 9, 4, 7, 8, 0, 1, 7, 1, 9 };
+static unsigned char triangleTable_90_5[20] =  { 18, 0, 8, 4, 7, 5, 10, 6, 11, 2, 9, 0, 3, 11, 2, 1, 9, 11, 9, 0 };
+static unsigned char triangleTable_90_6[20] =  { 18, 0, 9, 0, 1, 3, 11, 2, 10, 6, 8, 4, 5, 10, 6, 7, 8, 10, 8, 4 };
+static unsigned char triangleTable_90_7[32] =  { 30, 1, 5, 12, 6, 12, 3, 11, 2, 12, 10, 12, 2, 3, 12, 9, 0, 6, 12, 11, 10, 12, 1, 7, 8, 4, 0, 1, 12, 9, 12, 5 };
+static unsigned char triangleTable_90_8[32] =  { 30, 1, 1, 12, 0, 12, 7, 8, 4, 12, 9, 12, 4, 7, 12, 10, 6, 0, 12, 8, 9, 12, 5, 3, 11, 2, 6, 5, 12, 10, 12, 1 };
+static unsigned char triangleTable_90_9[32] =  { 30, 1, 0, 12, 9, 3, 11, 12, 1, 12, 2, 11, 2, 12, 6, 5, 12, 3, 12, 0, 10, 12, 1, 4, 7, 8, 12, 10, 6, 9, 12, 5 };
+static unsigned char triangleTable_90_10[32] =  { 30, 1, 6, 12, 10, 7, 8, 12, 5, 12, 4, 8, 4, 12, 0, 1, 12, 7, 12, 6, 9, 12, 5, 2, 3, 11, 12, 9, 0, 10, 12, 1 };
+static unsigned char triangleTable_90_11[32] =  { 30, 1, 3, 12, 2, 8, 4, 12, 11, 12, 7, 4, 7, 12, 5, 10, 12, 8, 12, 3, 6, 12, 11, 1, 9, 0, 12, 6, 5, 2, 12, 10 };
+static unsigned char triangleTable_90_12[32] =  { 30, 1, 11, 12, 3, 12, 9, 0, 1, 12, 2, 12, 1, 9, 12, 6, 5, 3, 12, 0, 2, 12, 10, 8, 4, 7, 5, 10, 12, 6, 12, 11 };
+static unsigned char triangleTable_90_13[32] =  { 30, 1, 10, 12, 5, 2, 3, 12, 6, 12, 11, 3, 11, 12, 8, 4, 12, 2, 12, 10, 7, 12, 6, 9, 0, 1, 12, 7, 8, 5, 12, 4 };
+static unsigned char triangleTable_90_14[32] =  { 30, 1, 7, 12, 4, 12, 1, 9, 0, 12, 8, 12, 0, 1, 12, 11, 2, 4, 12, 9, 8, 12, 3, 5, 10, 6, 2, 3, 12, 11, 12, 7 };
+static unsigned char triangleTable_90_15[32] =  { 30, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+static unsigned char triangleTable_90_16[32] =  { 30, 1, 4, 12, 8, 12, 2, 3, 11, 12, 7, 12, 11, 2, 12, 5, 10, 8, 12, 3, 7, 12, 6, 0, 1, 9, 10, 6, 12, 5, 12, 4 };
+static unsigned char triangleTable_90_17[32] =  { 30, 1, 9, 12, 1, 4, 7, 12, 0, 12, 8, 7, 8, 12, 11, 2, 12, 4, 12, 9, 3, 12, 0, 10, 6, 5, 12, 3, 11, 1, 12, 2 };
+static unsigned char triangleTable_90_18[32] =  { 30, 1, 8, 12, 7, 12, 10, 6, 5, 12, 4, 12, 5, 10, 12, 0, 1, 7, 12, 6, 4, 12, 9, 11, 2, 3, 1, 9, 12, 0, 12, 8 };
+static unsigned char triangleTable_90_19[38] =  { 36, 1, 12, 5, 9, 11, 6, 12, 1, 12, 0, 5, 12, 4, 3, 12, 2, 12, 1, 10, 2, 12, 10, 12, 8, 4, 9, 0, 12, 7, 12, 6, 3, 11, 12, 7, 8, 12 };
+static unsigned char triangleTable_90_20[38] =  { 36, 1, 12, 1, 10, 8, 0, 12, 5, 12, 6, 1, 12, 2, 7, 12, 4, 12, 5, 9, 4, 12, 9, 12, 11, 2, 10, 6, 12, 3, 12, 0, 7, 8, 12, 3, 11, 12 };
+static unsigned char triangleTable_90_21[38] =  { 36, 1, 12, 11, 6, 0, 3, 12, 10, 12, 5, 11, 12, 7, 9, 12, 1, 12, 10, 2, 1, 12, 2, 12, 4, 7, 6, 5, 12, 8, 12, 3, 9, 0, 12, 8, 4, 12 };
+static unsigned char triangleTable_90_22[38] =  { 36, 1, 12, 7, 11, 9, 4, 12, 3, 12, 2, 7, 12, 6, 1, 12, 0, 12, 3, 8, 0, 12, 8, 12, 10, 6, 11, 2, 12, 5, 12, 4, 1, 9, 12, 5, 10, 12 };
+static unsigned char triangleTable_90_23[32] =  { 30, 1, 1, 12, 2, 12, 7, 11, 6, 12, 10, 12, 6, 7, 12, 9, 4, 2, 12, 11, 10, 12, 5, 3, 8, 0, 4, 5, 12, 9, 12, 1 };
+static unsigned char triangleTable_90_24[32] =  { 30, 1, 5, 12, 4, 12, 3, 8, 0, 12, 9, 12, 0, 3, 12, 10, 2, 4, 12, 8, 9, 12, 1, 7, 11, 6, 2, 1, 12, 10, 12, 5 };
+static unsigned char triangleTable_90_25[32] =  { 30, 1, 2, 12, 10, 3, 8, 12, 1, 12, 0, 8, 0, 12, 4, 5, 12, 3, 12, 2, 9, 12, 1, 6, 7, 11, 12, 9, 4, 10, 12, 5 };
+static unsigned char triangleTable_90_26[32] =  { 30, 1, 4, 12, 9, 7, 11, 12, 5, 12, 6, 11, 6, 12, 2, 1, 12, 7, 12, 4, 10, 12, 5, 0, 3, 8, 12, 10, 2, 9, 12, 1 };
+static unsigned char triangleTable_90_27[32] =  { 30, 1, 7, 12, 6, 8, 0, 12, 11, 12, 3, 0, 3, 12, 1, 10, 12, 8, 12, 7, 2, 12, 11, 5, 9, 4, 12, 2, 1, 6, 12, 10 };
+static unsigned char triangleTable_90_28[32] =  { 30, 1, 10, 12, 1, 12, 8, 0, 3, 12, 2, 12, 3, 8, 12, 6, 7, 1, 12, 0, 2, 12, 11, 9, 4, 5, 7, 11, 12, 6, 12, 10 };
+static unsigned char triangleTable_90_29[32] =  { 30, 1, 11, 12, 7, 2, 1, 12, 6, 12, 10, 1, 10, 12, 9, 4, 12, 2, 12, 11, 5, 12, 6, 8, 0, 3, 12, 5, 9, 7, 12, 4 };
+static unsigned char triangleTable_90_30[32] =  { 30, 1, 3, 12, 0, 12, 5, 9, 4, 12, 8, 12, 4, 5, 12, 11, 6, 0, 12, 9, 8, 12, 7, 1, 10, 2, 6, 7, 12, 11, 12, 3 };
+static unsigned char triangleTable_90_31[32] =  { 30, 1, 2, 12, 11, 1, 9, 12, 3, 12, 0, 9, 0, 12, 4, 7, 12, 1, 12, 2, 8, 12, 3, 6, 5, 10, 12, 8, 4, 11, 12, 7 };
+static unsigned char triangleTable_90_32[32] =  { 30, 1, 6, 12, 11, 12, 0, 3, 8, 12, 7, 12, 8, 0, 12, 5, 9, 11, 12, 3, 7, 12, 4, 2, 1, 10, 9, 4, 12, 5, 12, 6 };
+static unsigned char triangleTable_90_33[32] =  { 30, 1, 8, 12, 3, 4, 5, 12, 0, 12, 9, 5, 9, 12, 10, 2, 12, 4, 12, 8, 1, 12, 0, 11, 6, 7, 12, 1, 10, 3, 12, 2 };
+static unsigned char triangleTable_90_34[32] =  { 30, 1, 9, 12, 5, 12, 11, 6, 7, 12, 4, 12, 7, 11, 12, 0, 3, 5, 12, 6, 4, 12, 8, 10, 2, 1, 3, 8, 12, 0, 12, 9 };
+static unsigned char triangleTable_90_35[20] =  { 18, 0, 7, 11, 6, 8, 0, 3, 2, 9, 4, 4, 5, 10, 2, 1, 9, 10, 2, 4 };
+static unsigned char triangleTable_90_36[20] =  { 18, 0, 9, 4, 5, 0, 3, 8, 7, 2, 1, 1, 10, 6, 7, 11, 2, 6, 7, 1 };
+static unsigned char triangleTable_90_37[20] =  { 18, 0, 5, 9, 4, 10, 2, 1, 0, 11, 6, 6, 7, 8, 0, 3, 11, 8, 0, 6 };
+static unsigned char triangleTable_90_38[20] =  { 18, 0, 11, 6, 7, 2, 1, 10, 5, 0, 3, 3, 8, 4, 5, 9, 0, 4, 5, 3 };
+static unsigned char triangleTable_90_39[20] =  { 18, 0, 6, 7, 11, 5, 9, 4, 8, 1, 10, 10, 2, 3, 8, 0, 1, 3, 8, 10 };
+static unsigned char triangleTable_90_40[20] =  { 18, 0, 2, 1, 10, 3, 8, 0, 9, 7, 11, 11, 6, 5, 9, 4, 7, 5, 9, 11 };
+static unsigned char triangleTable_90_41[14] =  { 12, 0, 0, 3, 8, 9, 4, 5, 1, 10, 2, 11, 6, 7 };
+static unsigned char triangleTable_90_42[20] =  { 18, 0, 3, 11, 5, 5, 9, 3, 2, 1, 10, 3, 9, 0, 7, 8, 4, 5, 11, 6 };
+static unsigned char triangleTable_90_43[20] =  { 18, 0, 7, 8, 1, 1, 10, 7, 4, 5, 9, 7, 10, 6, 3, 11, 2, 1, 8, 0 };
+static unsigned char triangleTable_90_44[20] =  { 18, 0, 5, 10, 3, 3, 8, 5, 6, 7, 11, 5, 8, 4, 1, 9, 0, 3, 10, 2 };
+static unsigned char triangleTable_90_45[20] =  { 18, 0, 1, 9, 7, 7, 11, 1, 0, 3, 8, 1, 11, 2, 5, 10, 6, 7, 9, 4 };
+static unsigned char * triangleTable_90[46] = 
+{ 
+  triangleTable_90_0, triangleTable_90_1, triangleTable_90_2, triangleTable_90_3, triangleTable_90_4, triangleTable_90_5, triangleTable_90_6, triangleTable_90_7, 
+  triangleTable_90_8, triangleTable_90_9, triangleTable_90_10, triangleTable_90_11, triangleTable_90_12, triangleTable_90_13, triangleTable_90_14, triangleTable_90_15, 
+  triangleTable_90_16, triangleTable_90_17, triangleTable_90_18, triangleTable_90_19, triangleTable_90_20, triangleTable_90_21, triangleTable_90_22, triangleTable_90_23, 
+  triangleTable_90_24, triangleTable_90_25, triangleTable_90_26, triangleTable_90_27, triangleTable_90_28, triangleTable_90_29, triangleTable_90_30, triangleTable_90_31, 
+  triangleTable_90_32, triangleTable_90_33, triangleTable_90_34, triangleTable_90_35, triangleTable_90_36, triangleTable_90_37, triangleTable_90_38, triangleTable_90_39, 
+  triangleTable_90_40, triangleTable_90_41, triangleTable_90_42, triangleTable_90_43, triangleTable_90_44, triangleTable_90_45
+};
+
+static unsigned char triangleTable_91_0[11] =  { 9, 0, 9, 4, 5, 2, 1, 10, 6, 7, 11 };
+static unsigned char triangleTable_91_1[17] =  { 15, 0, 9, 4, 5, 7, 11, 2, 7, 2, 1, 7, 1, 6, 10, 6, 1 };
+static unsigned char triangleTable_91_2[17] =  { 15, 0, 7, 11, 6, 2, 1, 9, 2, 9, 4, 2, 4, 10, 5, 10, 4 };
+static unsigned char triangleTable_91_3[17] =  { 15, 0, 2, 1, 10, 9, 4, 7, 9, 7, 11, 9, 11, 5, 6, 5, 11 };
+static unsigned char triangleTable_91_4[29] =  { 27, 1, 4, 5, 12, 5, 10, 12, 9, 12, 1, 6, 12, 10, 7, 11, 12, 2, 1, 12, 7, 12, 6, 4, 12, 9, 12, 11, 2 };
+static unsigned char triangleTable_91_5[29] =  { 27, 1, 1, 10, 12, 10, 6, 12, 2, 12, 11, 5, 12, 6, 9, 4, 12, 7, 11, 12, 9, 12, 5, 1, 12, 2, 12, 4, 7 };
+static unsigned char triangleTable_91_6[29] =  { 27, 1, 11, 6, 12, 6, 5, 12, 7, 12, 4, 10, 12, 5, 2, 1, 12, 9, 4, 12, 2, 12, 10, 11, 12, 7, 12, 1, 9 };
+static unsigned char triangleTable_91_7[17] =  { 15, 0, 11, 2, 1, 11, 4, 7, 6, 5, 10, 11, 1, 4, 9, 4, 1 };
+static unsigned char * triangleTable_91[8] = 
+{ 
+  triangleTable_91_0, triangleTable_91_1, triangleTable_91_2, triangleTable_91_3, triangleTable_91_4, triangleTable_91_5, triangleTable_91_6, triangleTable_91_7
+};
+
+static unsigned char triangleTable_92_0[14] =  { 12, 0, 11, 6, 5, 3, 11, 5, 4, 7, 8, 3, 5, 1 };
+static unsigned char triangleTable_92_1[14] =  { 12, 0, 8, 4, 5, 3, 8, 5, 6, 7, 11, 3, 5, 1 };
+static unsigned char triangleTable_92_2[26] =  { 24, 1, 12, 4, 7, 1, 12, 5, 3, 8, 12, 6, 12, 11, 11, 12, 7, 12, 1, 3, 12, 6, 5, 4, 12, 8 };
+static unsigned char triangleTable_92_3[26] =  { 24, 1, 12, 6, 7, 1, 12, 5, 3, 11, 12, 4, 12, 8, 8, 12, 7, 12, 1, 3, 12, 4, 5, 6, 12, 11 };
+static unsigned char * triangleTable_92[4] = { triangleTable_92_0, triangleTable_92_1, triangleTable_92_2, triangleTable_92_3 };
+
+static unsigned char triangleTable_93_0[11] =  { 9, 0, 6, 7, 11, 4, 5, 0, 5, 1, 0 };
+static unsigned char triangleTable_93_1[17] =  { 15, 0, 7, 11, 0, 5, 1, 6, 11, 1, 0, 7, 0, 4, 6, 1, 11 };
+static unsigned char * triangleTable_93[2] = { triangleTable_93_0, triangleTable_93_1 };
+
+static unsigned char triangleTable_94_0[11] =  { 9, 0, 11, 6, 7, 0, 3, 8, 4, 5, 9 };
+static unsigned char triangleTable_94_1[17] =  { 15, 0, 11, 6, 7, 5, 9, 0, 5, 0, 3, 5, 3, 4, 8, 4, 3 };
+static unsigned char triangleTable_94_2[17] =  { 15, 0, 5, 9, 4, 0, 3, 11, 0, 11, 6, 0, 6, 8, 7, 8, 6 };
+static unsigned char triangleTable_94_3[17] =  { 15, 0, 0, 3, 8, 11, 6, 5, 11, 5, 9, 11, 9, 7, 4, 7, 9 };
+static unsigned char triangleTable_94_4[29] =  { 27, 1, 6, 7, 12, 7, 8, 12, 11, 12, 3, 4, 12, 8, 5, 9, 12, 0, 3, 12, 5, 12, 4, 6, 12, 11, 12, 9, 0 };
+static unsigned char triangleTable_94_5[29] =  { 27, 1, 3, 8, 12, 8, 4, 12, 0, 12, 9, 7, 12, 4, 11, 6, 12, 5, 9, 12, 11, 12, 7, 3, 12, 0, 12, 6, 5 };
+static unsigned char triangleTable_94_6[29] =  { 27, 1, 9, 4, 12, 4, 7, 12, 5, 12, 6, 8, 12, 7, 0, 3, 12, 11, 6, 12, 0, 12, 8, 9, 12, 5, 12, 3, 11 };
+static unsigned char triangleTable_94_7[17] =  { 15, 0, 9, 0, 3, 9, 6, 5, 4, 7, 8, 9, 3, 6, 11, 6, 3 };
+static unsigned char * triangleTable_94[8] = 
+{ 
+  triangleTable_94_0, triangleTable_94_1, triangleTable_94_2, triangleTable_94_3, triangleTable_94_4, triangleTable_94_5, triangleTable_94_6, triangleTable_94_7
+};
+
+static unsigned char triangleTable_95_0[8] =  { 6, 0, 9, 4, 5, 6, 7, 11 };
+static unsigned char triangleTable_95_1[14] =  { 12, 0, 9, 7, 11, 6, 5, 9, 9, 11, 6, 7, 9, 4 };
+static unsigned char * triangleTable_95[2] = { triangleTable_95_0, triangleTable_95_1 };
+
+static unsigned char triangleTable_96_0[8] =  { 6, 0, 6, 4, 10, 9, 10, 4 };
+static unsigned char * triangleTable_96[1] = { triangleTable_96_0 };
+
+static unsigned char triangleTable_97_0[11] =  { 9, 0, 3, 0, 8, 10, 4, 9, 10, 6, 4 };
+static unsigned char triangleTable_97_1[17] =  { 15, 0, 10, 3, 0, 8, 6, 4, 10, 6, 3, 9, 10, 0, 3, 6, 8 };
+static unsigned char * triangleTable_97[2] = { triangleTable_97_0, triangleTable_97_1 };
+
+static unsigned char triangleTable_98_0[11] =  { 9, 0, 0, 1, 10, 6, 0, 10, 6, 4, 0 };
+static unsigned char * triangleTable_98[1] = { triangleTable_98_0 };
+
+static unsigned char triangleTable_99_0[14] =  { 12, 0, 8, 3, 4, 10, 6, 4, 3, 1, 10, 3, 10, 4 };
+static unsigned char * triangleTable_99[1] = { triangleTable_99_0 };
+
+static unsigned char triangleTable_100_0[11] =  { 9, 0, 4, 9, 1, 2, 4, 1, 2, 6, 4 };
+static unsigned char * triangleTable_100[1] = { triangleTable_100_0 };
+
+static unsigned char triangleTable_101_0[14] =  { 12, 0, 9, 1, 2, 4, 9, 2, 3, 0, 8, 4, 2, 6 };
+static unsigned char triangleTable_101_1[14] =  { 12, 0, 8, 3, 2, 4, 8, 2, 1, 0, 9, 4, 2, 6 };
+static unsigned char triangleTable_101_2[26] =  { 24, 1, 12, 3, 0, 6, 12, 2, 4, 8, 12, 1, 12, 9, 9, 12, 0, 12, 6, 4, 12, 1, 2, 3, 12, 8 };
+static unsigned char triangleTable_101_3[26] =  { 24, 1, 12, 1, 0, 6, 12, 2, 4, 9, 12, 3, 12, 8, 8, 12, 0, 12, 6, 4, 12, 3, 2, 1, 12, 9 };
+static unsigned char * triangleTable_101[4] = { triangleTable_101_0, triangleTable_101_1, triangleTable_101_2, triangleTable_101_3 };
+
+static unsigned char triangleTable_102_0[8] =  { 6, 0, 6, 4, 0, 0, 2, 6 };
+static unsigned char * triangleTable_102[1] = { triangleTable_102_0 };
+
+static unsigned char triangleTable_103_0[11] =  { 9, 0, 8, 3, 2, 8, 2, 4, 2, 6, 4 };
+static unsigned char * triangleTable_103[1] = { triangleTable_103_0 };
+
+static unsigned char triangleTable_104_0[11] =  { 9, 0, 3, 11, 2, 4, 10, 6, 4, 9, 10 };
+static unsigned char triangleTable_104_1[17] =  { 15, 0, 4, 3, 11, 2, 9, 10, 4, 9, 3, 6, 4, 11, 3, 9, 2 };
+static unsigned char * triangleTable_104[2] = { triangleTable_104_0, triangleTable_104_1 };
+
+static unsigned char triangleTable_105_0[14] =  { 12, 0, 10, 6, 9, 2, 0, 11, 0, 8, 11, 6, 4, 9 };
+static unsigned char triangleTable_105_1[14] =  { 12, 0, 4, 11, 6, 2, 9, 10, 2, 0, 9, 4, 8, 11 };
+static unsigned char triangleTable_105_2[26] =  { 24, 0, 10, 2, 9, 8, 6, 4, 8, 4, 9, 0, 9, 2, 6, 8, 11, 2, 6, 11, 8, 9, 0, 6, 2, 10 };
+static unsigned char triangleTable_105_3[26] =  { 24, 1, 12, 2, 0, 6, 12, 10, 12, 6, 4, 12, 11, 2, 0, 9, 12, 10, 12, 9, 4, 8, 12, 12, 8, 11 };
+static unsigned char triangleTable_105_4[26] =  { 24, 1, 12, 9, 10, 8, 12, 0, 12, 8, 11, 12, 4, 9, 10, 2, 12, 0, 12, 2, 11, 6, 12, 12, 6, 4 };
+static unsigned char * triangleTable_105[5] = { triangleTable_105_0, triangleTable_105_1, triangleTable_105_2, triangleTable_105_3, triangleTable_105_4 };
+
+static unsigned char triangleTable_106_0[14] =  { 12, 0, 1, 10, 6, 0, 1, 6, 11, 2, 3, 0, 6, 4 };
+static unsigned char triangleTable_106_1[14] =  { 12, 0, 3, 11, 6, 0, 3, 6, 10, 2, 1, 0, 6, 4 };
+static unsigned char triangleTable_106_2[26] =  { 24, 1, 12, 11, 2, 4, 12, 6, 0, 3, 12, 10, 12, 1, 1, 12, 2, 12, 4, 0, 12, 10, 6, 11, 12, 3 };
+static unsigned char triangleTable_106_3[26] =  { 24, 1, 12, 10, 2, 4, 12, 6, 0, 1, 12, 11, 12, 3, 3, 12, 2, 12, 4, 0, 12, 11, 6, 10, 12, 1 };
+static unsigned char * triangleTable_106[4] = { triangleTable_106_0, triangleTable_106_1, triangleTable_106_2, triangleTable_106_3 };
+
+static unsigned char triangleTable_107_0[11] =  { 9, 0, 10, 2, 1, 11, 6, 8, 6, 4, 8 };
+static unsigned char triangleTable_107_1[17] =  { 15, 0, 2, 1, 8, 6, 4, 10, 1, 4, 8, 2, 8, 11, 10, 4, 1 };
+static unsigned char * triangleTable_107[2] = { triangleTable_107_0, triangleTable_107_1 };
+
+static unsigned char triangleTable_108_0[14] =  { 12, 0, 6, 4, 11, 4, 9, 1, 4, 1, 3, 11, 4, 3 };
+static unsigned char * triangleTable_108[1] = { triangleTable_108_0 };
+
+static unsigned char triangleTable_109_0[11] =  { 9, 0, 0, 9, 1, 4, 8, 6, 8, 11, 6 };
+static unsigned char triangleTable_109_1[17] =  { 15, 0, 9, 1, 6, 8, 11, 0, 1, 11, 6, 9, 6, 4, 0, 11, 1 };
+static unsigned char * triangleTable_109[2] = { triangleTable_109_0, triangleTable_109_1 };
+
+static unsigned char triangleTable_110_0[11] =  { 9, 0, 3, 11, 6, 3, 6, 0, 6, 4, 0 };
+static unsigned char * triangleTable_110[1] = { triangleTable_110_0 };
+
+static unsigned char triangleTable_111_0[8] =  { 6, 0, 8, 6, 4, 6, 8, 11 };
+static unsigned char * triangleTable_111[1] = { triangleTable_111_0 };
+
+static unsigned char triangleTable_112_0[11] =  { 9, 0, 10, 6, 7, 8, 10, 7, 8, 9, 10 };
+static unsigned char * triangleTable_112[1] = { triangleTable_112_0 };
+
+static unsigned char triangleTable_113_0[14] =  { 12, 0, 7, 3, 6, 3, 0, 9, 3, 9, 10, 6, 3, 10 };
+static unsigned char * triangleTable_113[1] = { triangleTable_113_0 };
+
+static unsigned char triangleTable_114_0[14] =  { 12, 0, 1, 7, 0, 1, 6, 7, 10, 6, 1, 0, 7, 8 };
+static unsigned char * triangleTable_114[1] = { triangleTable_114_0 };
+
+static unsigned char triangleTable_115_0[11] =  { 9, 0, 10, 6, 7, 10, 7, 1, 7, 3, 1 };
+static unsigned char * triangleTable_115[1] = { triangleTable_115_0 };
+
+static unsigned char triangleTable_116_0[14] =  { 12, 0, 1, 2, 9, 7, 8, 9, 2, 6, 7, 2, 7, 9 };
+static unsigned char * triangleTable_116[1] = { triangleTable_116_0 };
+
+static unsigned char triangleTable_117_0[11] =  { 9, 0, 1, 0, 9, 3, 2, 7, 2, 6, 7 };
+static unsigned char triangleTable_117_1[17] =  { 15, 0, 0, 9, 7, 2, 6, 1, 9, 6, 7, 0, 7, 3, 1, 6, 9 };
+static unsigned char * triangleTable_117[2] = { triangleTable_117_0, triangleTable_117_1 };
+
+static unsigned char triangleTable_118_0[11] =  { 9, 0, 7, 8, 0, 7, 0, 6, 0, 2, 6 };
+static unsigned char * triangleTable_118[1] = { triangleTable_118_0 };
+
+static unsigned char triangleTable_119_0[8] =  { 6, 0, 2, 7, 3, 7, 2, 6 };
+static unsigned char * triangleTable_119[1] = { triangleTable_119_0 };
+
+static unsigned char triangleTable_120_0[14] =  { 12, 0, 6, 7, 8, 10, 6, 8, 3, 11, 2, 10, 8, 9 };
+static unsigned char triangleTable_120_1[14] =  { 12, 0, 2, 3, 8, 10, 2, 8, 7, 11, 6, 10, 8, 9 };
+static unsigned char triangleTable_120_2[26] =  { 24, 1, 12, 3, 11, 9, 12, 8, 10, 2, 12, 7, 12, 6, 6, 12, 11, 12, 9, 10, 12, 7, 8, 3, 12, 2 };
+static unsigned char triangleTable_120_3[26] =  { 24, 1, 12, 7, 11, 9, 12, 8, 10, 6, 12, 3, 12, 2, 2, 12, 11, 12, 9, 10, 12, 3, 8, 7, 12, 6 };
+static unsigned char * triangleTable_120[4] = { triangleTable_120_0, triangleTable_120_1, triangleTable_120_2, triangleTable_120_3 };
+
+static unsigned char triangleTable_121_0[11] =  { 9, 0, 11, 6, 7, 10, 2, 9, 2, 0, 9 };
+static unsigned char triangleTable_121_1[17] =  { 15, 0, 6, 7, 9, 2, 0, 11, 7, 0, 9, 6, 9, 10, 11, 0, 7 };
+static unsigned char * triangleTable_121[2] = { triangleTable_121_0, triangleTable_121_1 };
+
+static unsigned char triangleTable_122_0[11] =  { 9, 0, 8, 0, 3, 6, 7, 11, 2, 1, 10 };
+static unsigned char triangleTable_122_1[17] =  { 15, 0, 8, 0, 3, 1, 10, 6, 1, 6, 7, 1, 7, 2, 11, 2, 7 };
+static unsigned char triangleTable_122_2[17] =  { 15, 0, 1, 10, 2, 6, 7, 8, 6, 8, 0, 6, 0, 11, 3, 11, 0 };
+static unsigned char triangleTable_122_3[17] =  { 15, 0, 6, 7, 11, 8, 0, 1, 8, 1, 10, 8, 10, 3, 2, 3, 10 };
+static unsigned char triangleTable_122_4[29] =  { 27, 1, 0, 3, 12, 3, 11, 12, 8, 12, 7, 2, 12, 11, 1, 10, 12, 6, 7, 12, 1, 12, 2, 0, 12, 8, 12, 10, 6 };
+static unsigned char triangleTable_122_5[29] =  { 27, 1, 7, 11, 12, 11, 2, 12, 6, 12, 10, 3, 12, 2, 8, 0, 12, 1, 10, 12, 8, 12, 3, 7, 12, 6, 12, 0, 1 };
+static unsigned char triangleTable_122_6[29] =  { 27, 1, 10, 2, 12, 2, 3, 12, 1, 12, 0, 11, 12, 3, 6, 7, 12, 8, 0, 12, 6, 12, 11, 10, 12, 1, 12, 7, 8 };
+static unsigned char triangleTable_122_7[17] =  { 15, 0, 10, 6, 7, 10, 0, 1, 2, 3, 11, 10, 7, 0, 8, 0, 7 };
+static unsigned char * triangleTable_122[8] = 
+{ 
+  triangleTable_122_0, triangleTable_122_1, triangleTable_122_2, triangleTable_122_3, triangleTable_122_4, triangleTable_122_5, triangleTable_122_6, triangleTable_122_7
+};
+
+static unsigned char triangleTable_123_0[8] =  { 6, 0, 1, 10, 2, 11, 6, 7 };
+static unsigned char triangleTable_123_1[14] =  { 12, 0, 1, 6, 7, 11, 2, 1, 1, 7, 11, 6, 1, 10 };
+static unsigned char * triangleTable_123[2] = { triangleTable_123_0, triangleTable_123_1 };
+
+static unsigned char triangleTable_124_0[11] =  { 9, 0, 7, 11, 6, 3, 8, 1, 8, 9, 1 };
+static unsigned char triangleTable_124_1[17] =  { 15, 0, 11, 6, 1, 8, 9, 7, 6, 9, 1, 11, 1, 3, 7, 9, 6 };
+static unsigned char * triangleTable_124[2] = { triangleTable_124_0, triangleTable_124_1 };
+
+static unsigned char triangleTable_125_0[8] =  { 6, 0, 6, 7, 11, 0, 9, 1 };
+static unsigned char triangleTable_125_1[20] =  { 18, 0, 7, 11, 0, 9, 7, 0, 6, 1, 11, 6, 7, 9, 1, 6, 9, 0, 11, 1 };
+static unsigned char * triangleTable_125[2] = { triangleTable_125_0, triangleTable_125_1 };
+
+static unsigned char triangleTable_126_0[8] =  { 6, 0, 0, 3, 8, 7, 11, 6 };
+static unsigned char triangleTable_126_1[14] =  { 12, 0, 0, 11, 6, 7, 8, 0, 0, 6, 7, 11, 0, 3 };
+static unsigned char * triangleTable_126[2] = { triangleTable_126_0, triangleTable_126_1 };
+
+static unsigned char triangleTable_127_0[5] =  { 3, 0, 6, 7, 11 };
+static unsigned char * triangleTable_127[1] = { triangleTable_127_0 };
+
+static unsigned char triangleTable_128_0[5] =  { 3, 0, 11, 7, 6 };
+static unsigned char * triangleTable_128[1] = { triangleTable_128_0 };
+
+static unsigned char triangleTable_129_0[8] =  { 6, 0, 8, 3, 0, 6, 11, 7 };
+static unsigned char triangleTable_129_1[14] =  { 12, 0, 6, 11, 0, 0, 8, 7, 7, 6, 0, 3, 0, 11 };
+static unsigned char * triangleTable_129[2] = { triangleTable_129_0, triangleTable_129_1 };
+
+static unsigned char triangleTable_130_0[8] =  { 6, 0, 11, 7, 6, 1, 9, 0 };
+static unsigned char triangleTable_130_1[20] =  { 18, 0, 0, 11, 7, 0, 7, 9, 11, 1, 6, 9, 7, 6, 9, 6, 1, 1, 11, 0 };
+static unsigned char * triangleTable_130[2] = { triangleTable_130_0, triangleTable_130_1 };
+
+static unsigned char triangleTable_131_0[11] =  { 9, 0, 6, 11, 7, 1, 8, 3, 1, 9, 8 };
+static unsigned char triangleTable_131_1[17] =  { 15, 0, 1, 6, 11, 7, 9, 8, 1, 9, 6, 3, 1, 11, 6, 9, 7 };
+static unsigned char * triangleTable_131[2] = { triangleTable_131_0, triangleTable_131_1 };
+
+static unsigned char triangleTable_132_0[8] =  { 6, 0, 2, 10, 1, 7, 6, 11 };
+static unsigned char triangleTable_132_1[14] =  { 12, 0, 7, 6, 1, 1, 2, 11, 11, 7, 1, 10, 1, 6 };
+static unsigned char * triangleTable_132[2] = { triangleTable_132_0, triangleTable_132_1 };
+
+static unsigned char triangleTable_133_0[11] =  { 9, 0, 3, 0, 8, 11, 7, 6, 10, 1, 2 };
+static unsigned char triangleTable_133_1[17] =  { 15, 0, 3, 0, 8, 6, 10, 1, 7, 6, 1, 2, 7, 1, 7, 2, 11 };
+static unsigned char triangleTable_133_2[17] =  { 15, 0, 2, 10, 1, 8, 7, 6, 0, 8, 6, 11, 0, 6, 0, 11, 3 };
+static unsigned char triangleTable_133_3[17] =  { 15, 0, 11, 7, 6, 1, 0, 8, 10, 1, 8, 3, 10, 8, 10, 3, 2 };
+static unsigned char triangleTable_133_4[29] =  { 27, 1, 12, 3, 0, 12, 11, 3, 7, 12, 8, 11, 12, 2, 12, 10, 1, 12, 7, 6, 2, 12, 1, 8, 12, 0, 6, 10, 12 };
+static unsigned char triangleTable_133_5[29] =  { 27, 1, 12, 11, 7, 12, 2, 11, 10, 12, 6, 2, 12, 3, 12, 0, 8, 12, 10, 1, 3, 12, 8, 6, 12, 7, 1, 0, 12 };
+static unsigned char triangleTable_133_6[29] =  { 27, 1, 12, 2, 10, 12, 3, 2, 0, 12, 1, 3, 12, 11, 12, 7, 6, 12, 0, 8, 11, 12, 6, 1, 12, 10, 8, 7, 12 };
+static unsigned char triangleTable_133_7[17] =  { 15, 0, 7, 6, 10, 1, 0, 10, 11, 3, 2, 0, 7, 10, 7, 0, 8 };
+static unsigned char * triangleTable_133[8] = 
+{ 
+  triangleTable_133_0, triangleTable_133_1, triangleTable_133_2, triangleTable_133_3, triangleTable_133_4, triangleTable_133_5, triangleTable_133_6, triangleTable_133_7
+};
+
+static unsigned char triangleTable_134_0[11] =  { 9, 0, 7, 6, 11, 9, 2, 10, 9, 0, 2 };
+static unsigned char triangleTable_134_1[17] =  { 15, 0, 9, 7, 6, 11, 0, 2, 9, 0, 7, 10, 9, 6, 7, 0, 11 };
+static unsigned char * triangleTable_134[2] = { triangleTable_134_0, triangleTable_134_1 };
+
+static unsigned char triangleTable_135_0[14] =  { 12, 0, 3, 2, 10, 8, 3, 10, 6, 11, 7, 8, 10, 9 };
+static unsigned char triangleTable_135_1[14] =  { 12, 0, 7, 6, 10, 8, 7, 10, 2, 11, 3, 8, 10, 9 };
+static unsigned char triangleTable_135_2[26] =  { 24, 1, 12, 6, 11, 9, 12, 10, 8, 7, 12, 2, 12, 3, 3, 12, 11, 12, 9, 8, 12, 2, 10, 6, 12, 7 };
+static unsigned char triangleTable_135_3[26] =  { 24, 1, 12, 2, 11, 9, 12, 10, 8, 3, 12, 6, 12, 7, 7, 12, 11, 12, 9, 8, 12, 6, 10, 2, 12, 3 };
+static unsigned char * triangleTable_135[4] = { triangleTable_135_0, triangleTable_135_1, triangleTable_135_2, triangleTable_135_3 };
+
+static unsigned char triangleTable_136_0[8] =  { 6, 0, 3, 7, 2, 6, 2, 7 };
+static unsigned char * triangleTable_136[1] = { triangleTable_136_0 };
+
+static unsigned char triangleTable_137_0[11] =  { 9, 0, 0, 8, 7, 6, 0, 7, 6, 2, 0 };
+static unsigned char * triangleTable_137[1] = { triangleTable_137_0 };
+
+static unsigned char triangleTable_138_0[11] =  { 9, 0, 9, 0, 1, 7, 2, 3, 7, 6, 2 };
+static unsigned char triangleTable_138_1[17] =  { 15, 0, 7, 9, 0, 1, 6, 2, 7, 6, 9, 3, 7, 0, 9, 6, 1 };
+static unsigned char * triangleTable_138[2] = { triangleTable_138_0, triangleTable_138_1 };
+
+static unsigned char triangleTable_139_0[14] =  { 12, 0, 7, 6, 8, 1, 9, 8, 6, 2, 1, 6, 1, 8 };
+static unsigned char * triangleTable_139[1] = { triangleTable_139_0 };
+
+static unsigned char triangleTable_140_0[11] =  { 9, 0, 7, 6, 10, 1, 7, 10, 1, 3, 7 };
+static unsigned char * triangleTable_140[1] = { triangleTable_140_0 };
+
+static unsigned char triangleTable_141_0[14] =  { 12, 0, 1, 7, 10, 1, 8, 7, 0, 8, 1, 10, 7, 6 };
+static unsigned char * triangleTable_141[1] = { triangleTable_141_0 };
+
+static unsigned char triangleTable_142_0[14] =  { 12, 0, 3, 7, 0, 7, 6, 10, 7, 10, 9, 0, 7, 9 };
+static unsigned char * triangleTable_142[1] = { triangleTable_142_0 };
+
+static unsigned char triangleTable_143_0[11] =  { 9, 0, 7, 6, 10, 7, 10, 8, 10, 9, 8 };
+static unsigned char * triangleTable_143[1] = { triangleTable_143_0 };
+
+static unsigned char triangleTable_144_0[8] =  { 6, 0, 4, 6, 8, 11, 8, 6 };
+static unsigned char * triangleTable_144[1] = { triangleTable_144_0 };
+
+static unsigned char triangleTable_145_0[11] =  { 9, 0, 6, 11, 3, 0, 6, 3, 0, 4, 6 };
+static unsigned char * triangleTable_145[1] = { triangleTable_145_0 };
+
+static unsigned char triangleTable_146_0[11] =  { 9, 0, 1, 9, 0, 6, 8, 4, 6, 11, 8 };
+static unsigned char triangleTable_146_1[17] =  { 15, 0, 6, 1, 9, 0, 11, 8, 6, 11, 1, 4, 6, 9, 1, 11, 0 };
+static unsigned char * triangleTable_146[2] = { triangleTable_146_0, triangleTable_146_1 };
+
+static unsigned char triangleTable_147_0[14] =  { 12, 0, 4, 6, 9, 6, 11, 3, 6, 3, 1, 9, 6, 1 };
+static unsigned char * triangleTable_147[1] = { triangleTable_147_0 };
+
+static unsigned char triangleTable_148_0[11] =  { 9, 0, 1, 2, 10, 8, 6, 11, 8, 4, 6 };
+static unsigned char triangleTable_148_1[17] =  { 15, 0, 8, 1, 2, 10, 4, 6, 8, 4, 1, 11, 8, 2, 1, 4, 10 };
+static unsigned char * triangleTable_148[2] = { triangleTable_148_0, triangleTable_148_1 };
+
+static unsigned char triangleTable_149_0[14] =  { 12, 0, 11, 3, 0, 6, 11, 0, 1, 2, 10, 6, 0, 4 };
+static unsigned char triangleTable_149_1[14] =  { 12, 0, 10, 1, 0, 6, 10, 0, 3, 2, 11, 6, 0, 4 };
+static unsigned char triangleTable_149_2[26] =  { 24, 1, 12, 1, 2, 4, 12, 0, 6, 10, 12, 3, 12, 11, 11, 12, 2, 12, 4, 6, 12, 3, 0, 1, 12, 10 };
+static unsigned char triangleTable_149_3[26] =  { 24, 1, 12, 3, 2, 4, 12, 0, 6, 11, 12, 1, 12, 10, 10, 12, 2, 12, 4, 6, 12, 1, 0, 3, 12, 11 };
+static unsigned char * triangleTable_149[4] = { triangleTable_149_0, triangleTable_149_1, triangleTable_149_2, triangleTable_149_3 };
+
+static unsigned char triangleTable_150_0[14] =  { 12, 0, 8, 4, 11, 0, 2, 9, 2, 10, 9, 4, 6, 11 };
+static unsigned char triangleTable_150_1[14] =  { 12, 0, 6, 9, 4, 0, 11, 8, 0, 2, 11, 6, 10, 9 };
+static unsigned char triangleTable_150_2[26] =  { 24, 0, 8, 0, 11, 10, 4, 6, 10, 6, 11, 2, 11, 0, 4, 10, 9, 0, 4, 9, 10, 11, 2, 4, 0, 8 };
+static unsigned char triangleTable_150_3[26] =  { 24, 1, 12, 0, 2, 4, 12, 8, 12, 4, 6, 12, 9, 0, 2, 11, 12, 8, 12, 11, 6, 10, 12, 12, 10, 9 };
+static unsigned char triangleTable_150_4[26] =  { 24, 1, 12, 11, 8, 10, 12, 2, 12, 10, 9, 12, 6, 11, 8, 0, 12, 2, 12, 0, 9, 4, 12, 12, 4, 6 };
+static unsigned char * triangleTable_150[5] = { triangleTable_150_0, triangleTable_150_1, triangleTable_150_2, triangleTable_150_3, triangleTable_150_4 };
+
+static unsigned char triangleTable_151_0[11] =  { 9, 0, 2, 11, 3, 6, 10, 4, 10, 9, 4 };
+static unsigned char triangleTable_151_1[17] =  { 15, 0, 11, 3, 4, 10, 9, 2, 3, 9, 4, 11, 4, 6, 2, 9, 3 };
+static unsigned char * triangleTable_151[2] = { triangleTable_151_0, triangleTable_151_1 };
+
+static unsigned char triangleTable_152_0[11] =  { 9, 0, 2, 3, 8, 4, 2, 8, 4, 6, 2 };
+static unsigned char * triangleTable_152[1] = { triangleTable_152_0 };
+
+static unsigned char triangleTable_153_0[8] =  { 6, 0, 2, 0, 4, 4, 6, 2 };
+static unsigned char * triangleTable_153[1] = { triangleTable_153_0 };
+
+static unsigned char triangleTable_154_0[14] =  { 12, 0, 3, 8, 4, 2, 3, 4, 9, 0, 1, 2, 4, 6 };
+static unsigned char triangleTable_154_1[14] =  { 12, 0, 1, 9, 4, 2, 1, 4, 8, 0, 3, 2, 4, 6 };
+static unsigned char triangleTable_154_2[26] =  { 24, 1, 12, 9, 0, 6, 12, 4, 2, 1, 12, 8, 12, 3, 3, 12, 0, 12, 6, 2, 12, 8, 4, 9, 12, 1 };
+static unsigned char triangleTable_154_3[26] =  { 24, 1, 12, 8, 0, 6, 12, 4, 2, 3, 12, 9, 12, 1, 1, 12, 0, 12, 6, 2, 12, 9, 4, 8, 12, 3 };
+static unsigned char * triangleTable_154[4] = { triangleTable_154_0, triangleTable_154_1, triangleTable_154_2, triangleTable_154_3 };
+
+static unsigned char triangleTable_155_0[11] =  { 9, 0, 1, 9, 4, 1, 4, 2, 4, 6, 2 };
+static unsigned char * triangleTable_155[1] = { triangleTable_155_0 };
+
+static unsigned char triangleTable_156_0[14] =  { 12, 0, 10, 1, 6, 8, 4, 6, 1, 3, 8, 1, 8, 6 };
+static unsigned char * triangleTable_156[1] = { triangleTable_156_0 };
+
+static unsigned char triangleTable_157_0[11] =  { 9, 0, 10, 1, 0, 10, 0, 6, 0, 4, 6 };
+static unsigned char * triangleTable_157[1] = { triangleTable_157_0 };
+
+static unsigned char triangleTable_158_0[11] =  { 9, 0, 8, 0, 3, 9, 4, 10, 4, 6, 10 };
+static unsigned char triangleTable_158_1[17] =  { 15, 0, 0, 3, 10, 4, 6, 8, 3, 6, 10, 0, 10, 9, 8, 6, 3 };
+static unsigned char * triangleTable_158[2] = { triangleTable_158_0, triangleTable_158_1 };
+
+static unsigned char triangleTable_159_0[8] =  { 6, 0, 10, 4, 6, 4, 10, 9 };
+static unsigned char * triangleTable_159[1] = { triangleTable_159_0 };
+
+static unsigned char triangleTable_160_0[8] =  { 6, 0, 5, 4, 9, 11, 7, 6 };
+static unsigned char triangleTable_160_1[14] =  { 12, 0, 11, 7, 9, 9, 5, 6, 6, 11, 9, 4, 9, 7 };
+static unsigned char * triangleTable_160[2] = { triangleTable_160_0, triangleTable_160_1 };
+
+static unsigned char triangleTable_161_0[11] =  { 9, 0, 7, 6, 11, 8, 3, 0, 9, 5, 4 };
+static unsigned char triangleTable_161_1[17] =  { 15, 0, 7, 6, 11, 0, 9, 5, 3, 0, 5, 4, 3, 5, 3, 4, 8 };
+static unsigned char triangleTable_161_2[17] =  { 15, 0, 4, 9, 5, 11, 3, 0, 6, 11, 0, 8, 6, 0, 6, 8, 7 };
+static unsigned char triangleTable_161_3[17] =  { 15, 0, 8, 3, 0, 5, 6, 11, 9, 5, 11, 7, 9, 11, 9, 7, 4 };
+static unsigned char triangleTable_161_4[29] =  { 27, 1, 12, 7, 6, 12, 8, 7, 3, 12, 11, 8, 12, 4, 12, 9, 5, 12, 3, 0, 4, 12, 5, 11, 12, 6, 0, 9, 12 };
+static unsigned char triangleTable_161_5[29] =  { 27, 1, 12, 8, 3, 12, 4, 8, 9, 12, 0, 4, 12, 7, 12, 6, 11, 12, 9, 5, 7, 12, 11, 0, 12, 3, 5, 6, 12 };
+static unsigned char triangleTable_161_6[29] =  { 27, 1, 12, 4, 9, 12, 7, 4, 6, 12, 5, 7, 12, 8, 12, 3, 0, 12, 6, 11, 8, 12, 0, 5, 12, 9, 11, 3, 12 };
+static unsigned char triangleTable_161_7[17] =  { 15, 0, 3, 0, 9, 5, 6, 9, 8, 7, 4, 6, 3, 9, 3, 6, 11 };
+static unsigned char * triangleTable_161[8] = 
+{ 
+  triangleTable_161_0, triangleTable_161_1, triangleTable_161_2, triangleTable_161_3, triangleTable_161_4, triangleTable_161_5, triangleTable_161_6, triangleTable_161_7
+};
+
+static unsigned char triangleTable_162_0[11] =  { 9, 0, 11, 7, 6, 0, 5, 4, 0, 1, 5 };
+static unsigned char triangleTable_162_1[17] =  { 15, 0, 0, 11, 7, 6, 1, 5, 0, 1, 11, 4, 0, 7, 11, 1, 6 };
+static unsigned char * triangleTable_162[2] = { triangleTable_162_0, triangleTable_162_1 };
+
+static unsigned char triangleTable_163_0[14] =  { 12, 0, 4, 8, 3, 5, 4, 3, 11, 7, 6, 5, 3, 1 };
+static unsigned char triangleTable_163_1[14] =  { 12, 0, 6, 11, 3, 5, 6, 3, 8, 7, 4, 5, 3, 1 };
+static unsigned char triangleTable_163_2[26] =  { 24, 1, 12, 11, 7, 1, 12, 3, 5, 6, 12, 8, 12, 4, 4, 12, 7, 12, 1, 5, 12, 8, 3, 11, 12, 6 };
+static unsigned char triangleTable_163_3[26] =  { 24, 1, 12, 8, 7, 1, 12, 3, 5, 4, 12, 11, 12, 6, 6, 12, 7, 12, 1, 5, 12, 11, 3, 8, 12, 4 };
+static unsigned char * triangleTable_163[4] = { triangleTable_163_0, triangleTable_163_1, triangleTable_163_2, triangleTable_163_3 };
+
+static unsigned char triangleTable_164_0[11] =  { 9, 0, 5, 4, 9, 10, 1, 2, 11, 7, 6 };
+static unsigned char triangleTable_164_1[17] =  { 15, 0, 5, 4, 9, 2, 11, 7, 1, 2, 7, 6, 1, 7, 1, 6, 10 };
+static unsigned char triangleTable_164_2[17] =  { 15, 0, 6, 11, 7, 9, 1, 2, 4, 9, 2, 10, 4, 2, 4, 10, 5 };
+static unsigned char triangleTable_164_3[17] =  { 15, 0, 10, 1, 2, 7, 4, 9, 11, 7, 9, 5, 11, 9, 11, 5, 6 };
+static unsigned char triangleTable_164_4[29] =  { 27, 1, 12, 5, 4, 12, 10, 5, 1, 12, 9, 10, 12, 6, 12, 11, 7, 12, 1, 2, 6, 12, 7, 9, 12, 4, 2, 11, 12 };
+static unsigned char triangleTable_164_5[29] =  { 27, 1, 12, 10, 1, 12, 6, 10, 11, 12, 2, 6, 12, 5, 12, 4, 9, 12, 11, 7, 5, 12, 9, 2, 12, 1, 7, 4, 12 };
+static unsigned char triangleTable_164_6[29] =  { 27, 1, 12, 6, 11, 12, 5, 6, 4, 12, 7, 5, 12, 10, 12, 1, 2, 12, 4, 9, 10, 12, 2, 7, 12, 11, 9, 1, 12 };
+static unsigned char triangleTable_164_7[17] =  { 15, 0, 1, 2, 11, 7, 4, 11, 10, 5, 6, 4, 1, 11, 1, 4, 9 };
+static unsigned char * triangleTable_164[8] = 
+{ 
+  triangleTable_164_0, triangleTable_164_1, triangleTable_164_2, triangleTable_164_3, triangleTable_164_4, triangleTable_164_5, triangleTable_164_6, triangleTable_164_7
+};
+
+static unsigned char triangleTable_165_0[14] =  { 12, 0, 10, 1, 2, 7, 6, 11, 0, 8, 3, 5, 4, 9 };
+static unsigned char triangleTable_165_1[20] =  { 18, 0, 1, 2, 10, 11, 7, 6, 5, 4, 3, 0, 9, 5, 4, 8, 3, 5, 3, 0 };
+static unsigned char triangleTable_165_2[20] =  { 18, 0, 6, 11, 7, 3, 0, 8, 4, 9, 2, 10, 5, 4, 9, 1, 2, 4, 2, 10 };
+static unsigned char triangleTable_165_3[20] =  { 18, 0, 3, 0, 8, 9, 5, 4, 7, 6, 1, 2, 11, 7, 6, 10, 1, 7, 1, 2 };
+static unsigned char triangleTable_165_4[20] =  { 18, 0, 4, 9, 5, 1, 2, 10, 6, 11, 0, 8, 7, 6, 11, 3, 0, 6, 0, 8 };
+static unsigned char triangleTable_165_5[20] =  { 18, 0, 11, 7, 6, 4, 9, 5, 10, 1, 8, 3, 2, 10, 1, 0, 8, 10, 8, 3 };
+static unsigned char triangleTable_165_6[20] =  { 18, 0, 8, 3, 0, 2, 10, 1, 9, 5, 11, 7, 4, 9, 5, 6, 11, 9, 11, 7 };
+static unsigned char triangleTable_165_7[32] =  { 30, 1, 4, 12, 5, 12, 2, 10, 1, 12, 9, 12, 1, 2, 12, 8, 3, 5, 12, 10, 9, 12, 0, 6, 11, 7, 3, 0, 12, 8, 12, 4 };
+static unsigned char triangleTable_165_8[32] =  { 30, 1, 0, 12, 3, 12, 6, 11, 7, 12, 8, 12, 7, 6, 12, 9, 5, 3, 12, 11, 8, 12, 4, 2, 10, 1, 5, 4, 12, 9, 12, 0 };
+static unsigned char triangleTable_165_9[32] =  { 30, 1, 3, 12, 8, 2, 10, 12, 0, 12, 1, 10, 1, 12, 5, 4, 12, 2, 12, 3, 9, 12, 0, 7, 6, 11, 12, 9, 5, 8, 12, 4 };
+static unsigned char triangleTable_165_10[32] =  { 30, 1, 5, 12, 9, 6, 11, 12, 4, 12, 7, 11, 7, 12, 3, 0, 12, 6, 12, 5, 8, 12, 4, 1, 2, 10, 12, 8, 3, 9, 12, 0 };
+static unsigned char triangleTable_165_11[32] =  { 30, 1, 2, 12, 1, 11, 7, 12, 10, 12, 6, 7, 6, 12, 4, 9, 12, 11, 12, 2, 5, 12, 10, 0, 8, 3, 12, 5, 4, 1, 12, 9 };
+static unsigned char triangleTable_165_12[32] =  { 30, 1, 10, 12, 2, 12, 8, 3, 0, 12, 1, 12, 0, 8, 12, 5, 4, 2, 12, 3, 1, 12, 9, 11, 7, 6, 4, 9, 12, 5, 12, 10 };
+static unsigned char triangleTable_165_13[32] =  { 30, 1, 9, 12, 4, 1, 2, 12, 5, 12, 10, 2, 10, 12, 11, 7, 12, 1, 12, 9, 6, 12, 5, 8, 3, 0, 12, 6, 11, 4, 12, 7 };
+static unsigned char triangleTable_165_14[32] =  { 30, 1, 6, 12, 7, 12, 0, 8, 3, 12, 11, 12, 3, 0, 12, 10, 1, 7, 12, 8, 11, 12, 2, 4, 9, 5, 1, 2, 12, 10, 12, 6 };
+static unsigned char triangleTable_165_15[32] =  { 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char triangleTable_165_16[32] =  { 30, 1, 7, 12, 11, 12, 1, 2, 10, 12, 6, 12, 10, 1, 12, 4, 9, 11, 12, 2, 6, 12, 5, 3, 0, 8, 9, 5, 12, 4, 12, 7 };
+static unsigned char triangleTable_165_17[32] =  { 30, 1, 8, 12, 0, 7, 6, 12, 3, 12, 11, 6, 11, 12, 10, 1, 12, 7, 12, 8, 2, 12, 3, 9, 5, 4, 12, 2, 10, 0, 12, 1 };
+static unsigned char triangleTable_165_18[32] =  { 30, 1, 11, 12, 6, 12, 9, 5, 4, 12, 7, 12, 4, 9, 12, 3, 0, 6, 12, 5, 7, 12, 8, 10, 1, 2, 0, 8, 12, 3, 12, 11 };
+static unsigned char triangleTable_165_19[38] =  { 36, 1, 12, 4, 8, 10, 5, 12, 0, 12, 3, 4, 12, 7, 2, 12, 1, 12, 0, 9, 1, 12, 9, 12, 11, 7, 8, 3, 12, 6, 12, 5, 2, 10, 12, 6, 11, 12 };
+static unsigned char triangleTable_165_20[38] =  { 36, 1, 12, 0, 9, 11, 3, 12, 4, 12, 5, 0, 12, 1, 6, 12, 7, 12, 4, 8, 7, 12, 8, 12, 10, 1, 9, 5, 12, 2, 12, 3, 6, 11, 12, 2, 10, 12 };
+static unsigned char triangleTable_165_21[38] =  { 36, 1, 12, 10, 5, 3, 2, 12, 9, 12, 4, 10, 12, 6, 8, 12, 0, 12, 9, 1, 0, 12, 1, 12, 7, 6, 5, 4, 12, 11, 12, 2, 8, 3, 12, 11, 7, 12 };
+static unsigned char triangleTable_165_22[38] =  { 36, 1, 12, 6, 10, 8, 7, 12, 2, 12, 1, 6, 12, 5, 0, 12, 3, 12, 2, 11, 3, 12, 11, 12, 9, 5, 10, 1, 12, 4, 12, 7, 0, 8, 12, 4, 9, 12 };
+static unsigned char triangleTable_165_23[32] =  { 30, 1, 0, 12, 1, 12, 6, 10, 5, 12, 9, 12, 5, 6, 12, 8, 7, 1, 12, 10, 9, 12, 4, 2, 11, 3, 7, 4, 12, 8, 12, 0 };
+static unsigned char triangleTable_165_24[32] =  { 30, 1, 4, 12, 7, 12, 2, 11, 3, 12, 8, 12, 3, 2, 12, 9, 1, 7, 12, 11, 8, 12, 0, 6, 10, 5, 1, 0, 12, 9, 12, 4 };
+static unsigned char triangleTable_165_25[32] =  { 30, 1, 1, 12, 9, 2, 11, 12, 0, 12, 3, 11, 3, 12, 7, 4, 12, 2, 12, 1, 8, 12, 0, 5, 6, 10, 12, 8, 7, 9, 12, 4 };
+static unsigned char triangleTable_165_26[32] =  { 30, 1, 7, 12, 8, 6, 10, 12, 4, 12, 5, 10, 5, 12, 1, 0, 12, 6, 12, 7, 9, 12, 4, 3, 2, 11, 12, 9, 1, 8, 12, 0 };
+static unsigned char triangleTable_165_27[32] =  { 30, 1, 6, 12, 5, 11, 3, 12, 10, 12, 2, 3, 2, 12, 0, 9, 12, 11, 12, 6, 1, 12, 10, 4, 8, 7, 12, 1, 0, 5, 12, 9 };
+static unsigned char triangleTable_165_28[32] =  { 30, 1, 9, 12, 0, 12, 11, 3, 2, 12, 1, 12, 2, 11, 12, 5, 6, 0, 12, 3, 1, 12, 10, 8, 7, 4, 6, 10, 12, 5, 12, 9 };
+static unsigned char triangleTable_165_29[32] =  { 30, 1, 10, 12, 6, 1, 0, 12, 5, 12, 9, 0, 9, 12, 8, 7, 12, 1, 12, 10, 4, 12, 5, 11, 3, 2, 12, 4, 8, 6, 12, 7 };
+static unsigned char triangleTable_165_30[32] =  { 30, 1, 2, 12, 3, 12, 4, 8, 7, 12, 11, 12, 7, 4, 12, 10, 5, 3, 12, 8, 11, 12, 6, 0, 9, 1, 5, 6, 12, 10, 12, 2 };
+static unsigned char triangleTable_165_31[32] =  { 30, 1, 1, 12, 10, 0, 8, 12, 2, 12, 3, 8, 3, 12, 7, 6, 12, 0, 12, 1, 11, 12, 2, 5, 4, 9, 12, 11, 7, 10, 12, 6 };
+static unsigned char triangleTable_165_32[32] =  { 30, 1, 5, 12, 10, 12, 3, 2, 11, 12, 6, 12, 11, 3, 12, 4, 8, 10, 12, 2, 6, 12, 7, 1, 0, 9, 8, 7, 12, 4, 12, 5 };
+static unsigned char triangleTable_165_33[32] =  { 30, 1, 11, 12, 2, 7, 4, 12, 3, 12, 8, 4, 8, 12, 9, 1, 12, 7, 12, 11, 0, 12, 3, 10, 5, 6, 12, 0, 9, 2, 12, 1 };
+static unsigned char triangleTable_165_34[32] =  { 30, 1, 8, 12, 4, 12, 10, 5, 6, 12, 7, 12, 6, 10, 12, 3, 2, 4, 12, 5, 7, 12, 11, 9, 1, 0, 2, 11, 12, 3, 12, 8 };
+static unsigned char triangleTable_165_35[20] =  { 18, 0, 6, 10, 5, 11, 3, 2, 1, 8, 7, 7, 4, 9, 1, 0, 8, 9, 1, 7 };
+static unsigned char triangleTable_165_36[20] =  { 18, 0, 8, 7, 4, 3, 2, 11, 6, 1, 0, 0, 9, 5, 6, 10, 1, 5, 6, 0 };
+static unsigned char triangleTable_165_37[20] =  { 18, 0, 4, 8, 7, 9, 1, 0, 3, 10, 5, 5, 6, 11, 3, 2, 10, 11, 3, 5 };
+static unsigned char triangleTable_165_38[20] =  { 18, 0, 10, 5, 6, 1, 0, 9, 4, 3, 2, 2, 11, 7, 4, 8, 3, 7, 4, 2 };
+static unsigned char triangleTable_165_39[20] =  { 18, 0, 5, 6, 10, 4, 8, 7, 11, 0, 9, 9, 1, 2, 11, 3, 0, 2, 11, 9 };
+static unsigned char triangleTable_165_40[20] =  { 18, 0, 1, 0, 9, 2, 11, 3, 8, 6, 10, 10, 5, 4, 8, 7, 6, 4, 8, 10 };
+static unsigned char triangleTable_165_41[14] =  { 12, 0, 3, 2, 11, 8, 7, 4, 0, 9, 1, 10, 5, 6 };
+static unsigned char triangleTable_165_42[20] =  { 18, 0, 2, 10, 4, 4, 8, 2, 1, 0, 9, 2, 8, 3, 6, 11, 7, 4, 10, 5 };
+static unsigned char triangleTable_165_43[20] =  { 18, 0, 6, 11, 0, 0, 9, 6, 7, 4, 8, 6, 9, 5, 2, 10, 1, 0, 11, 3 };
+static unsigned char triangleTable_165_44[20] =  { 18, 0, 4, 9, 2, 2, 11, 4, 5, 6, 10, 4, 11, 7, 0, 8, 3, 2, 9, 1 };
+static unsigned char triangleTable_165_45[20] =  { 18, 0, 0, 8, 6, 6, 10, 0, 3, 2, 11, 0, 10, 1, 4, 9, 5, 6, 8, 7 };
+static unsigned char * triangleTable_165[46] = 
+{ 
+  triangleTable_165_0, triangleTable_165_1, triangleTable_165_2, triangleTable_165_3, triangleTable_165_4, triangleTable_165_5, triangleTable_165_6, triangleTable_165_7, 
+  triangleTable_165_8, triangleTable_165_9, triangleTable_165_10, triangleTable_165_11, triangleTable_165_12, triangleTable_165_13, triangleTable_165_14, triangleTable_165_15, 
+  triangleTable_165_16, triangleTable_165_17, triangleTable_165_18, triangleTable_165_19, triangleTable_165_20, triangleTable_165_21, triangleTable_165_22, triangleTable_165_23, 
+  triangleTable_165_24, triangleTable_165_25, triangleTable_165_26, triangleTable_165_27, triangleTable_165_28, triangleTable_165_29, triangleTable_165_30, triangleTable_165_31, 
+  triangleTable_165_32, triangleTable_165_33, triangleTable_165_34, triangleTable_165_35, triangleTable_165_36, triangleTable_165_37, triangleTable_165_38, triangleTable_165_39, 
+  triangleTable_165_40, triangleTable_165_41, triangleTable_165_42, triangleTable_165_43, triangleTable_165_44, triangleTable_165_45
+};
+
+static unsigned char triangleTable_166_0[14] =  { 12, 0, 10, 5, 4, 2, 10, 4, 7, 6, 11, 2, 4, 0 };
+static unsigned char triangleTable_166_1[14] =  { 12, 0, 11, 7, 4, 2, 11, 4, 5, 6, 10, 2, 4, 0 };
+static unsigned char triangleTable_166_2[26] =  { 24, 1, 12, 7, 6, 0, 12, 4, 2, 11, 12, 5, 12, 10, 10, 12, 6, 12, 0, 2, 12, 5, 4, 7, 12, 11 };
+static unsigned char triangleTable_166_3[26] =  { 24, 1, 12, 5, 6, 0, 12, 4, 2, 10, 12, 7, 12, 11, 11, 12, 6, 12, 0, 2, 12, 7, 4, 5, 12, 10 };
+static unsigned char * triangleTable_166[4] = { triangleTable_166_0, triangleTable_166_1, triangleTable_166_2, triangleTable_166_3 };
+
+static unsigned char triangleTable_167_0[11] =  { 9, 0, 3, 2, 11, 4, 8, 7, 6, 10, 5 };
+static unsigned char triangleTable_167_1[17] =  { 15, 0, 3, 2, 11, 10, 5, 4, 10, 4, 8, 10, 8, 6, 7, 6, 8 };
+static unsigned char triangleTable_167_2[17] =  { 15, 0, 10, 5, 6, 4, 8, 3, 4, 3, 2, 4, 2, 7, 11, 7, 2 };
+static unsigned char triangleTable_167_3[17] =  { 15, 0, 4, 8, 7, 3, 2, 10, 3, 10, 5, 3, 5, 11, 6, 11, 5 };
+static unsigned char triangleTable_167_4[29] =  { 27, 1, 2, 11, 12, 11, 7, 12, 3, 12, 8, 6, 12, 7, 10, 5, 12, 4, 8, 12, 10, 12, 6, 2, 12, 3, 12, 5, 4 };
+static unsigned char triangleTable_167_5[29] =  { 27, 1, 8, 7, 12, 7, 6, 12, 4, 12, 5, 11, 12, 6, 3, 2, 12, 10, 5, 12, 3, 12, 11, 8, 12, 4, 12, 2, 10 };
+static unsigned char triangleTable_167_6[29] =  { 27, 1, 5, 6, 12, 6, 11, 12, 10, 12, 2, 7, 12, 11, 4, 8, 12, 3, 2, 12, 4, 12, 7, 5, 12, 10, 12, 8, 3 };
+static unsigned char triangleTable_167_7[17] =  { 15, 0, 5, 4, 8, 5, 2, 10, 6, 11, 7, 5, 8, 2, 3, 2, 8 };
+static unsigned char * triangleTable_167[8] = 
+{ 
+  triangleTable_167_0, triangleTable_167_1, triangleTable_167_2, triangleTable_167_3, triangleTable_167_4, triangleTable_167_5, triangleTable_167_6, triangleTable_167_7
+};
+
+static unsigned char triangleTable_168_0[11] =  { 9, 0, 9, 5, 4, 2, 7, 6, 2, 3, 7 };
+static unsigned char triangleTable_168_1[17] =  { 15, 0, 2, 9, 5, 4, 3, 7, 2, 3, 9, 6, 2, 5, 9, 3, 4 };
+static unsigned char * triangleTable_168[2] = { triangleTable_168_0, triangleTable_168_1 };
+
+static unsigned char triangleTable_169_0[14] =  { 12, 0, 8, 7, 6, 0, 8, 6, 5, 4, 9, 0, 6, 2 };
+static unsigned char triangleTable_169_1[14] =  { 12, 0, 9, 5, 6, 0, 9, 6, 7, 4, 8, 0, 6, 2 };
+static unsigned char triangleTable_169_2[26] =  { 24, 1, 12, 5, 4, 2, 12, 6, 0, 9, 12, 7, 12, 8, 8, 12, 4, 12, 2, 0, 12, 7, 6, 5, 12, 9 };
+static unsigned char triangleTable_169_3[26] =  { 24, 1, 12, 7, 4, 2, 12, 6, 0, 8, 12, 5, 12, 9, 9, 12, 4, 12, 2, 0, 12, 5, 6, 7, 12, 8 };
+static unsigned char * triangleTable_169[4] = { triangleTable_169_0, triangleTable_169_1, triangleTable_169_2, triangleTable_169_3 };
+
+static unsigned char triangleTable_170_0[14] =  { 12, 0, 2, 3, 6, 1, 5, 0, 5, 4, 0, 3, 7, 6 };
+static unsigned char triangleTable_170_1[14] =  { 12, 0, 7, 0, 3, 1, 6, 2, 1, 5, 6, 7, 4, 0 };
+static unsigned char triangleTable_170_2[26] =  { 24, 0, 2, 1, 6, 4, 3, 7, 4, 7, 6, 5, 6, 1, 3, 4, 0, 1, 3, 0, 4, 6, 5, 3, 1, 2 };
+static unsigned char triangleTable_170_3[26] =  { 24, 1, 12, 1, 5, 3, 12, 2, 12, 3, 7, 12, 0, 1, 5, 6, 12, 2, 12, 6, 7, 4, 12, 12, 4, 0 };
+static unsigned char triangleTable_170_4[26] =  { 24, 1, 12, 6, 2, 4, 12, 5, 12, 4, 0, 12, 7, 6, 2, 1, 12, 5, 12, 1, 0, 3, 12, 12, 3, 7 };
+static unsigned char * triangleTable_170[5] = { triangleTable_170_0, triangleTable_170_1, triangleTable_170_2, triangleTable_170_3, triangleTable_170_4 };
+
+static unsigned char triangleTable_171_0[11] =  { 9, 0, 7, 4, 8, 5, 6, 1, 6, 2, 1 };
+static unsigned char triangleTable_171_1[17] =  { 15, 0, 4, 8, 1, 6, 2, 7, 8, 2, 1, 4, 1, 5, 7, 2, 8 };
+static unsigned char * triangleTable_171[2] = { triangleTable_171_0, triangleTable_171_1 };
+
+static unsigned char triangleTable_172_0[14] =  { 12, 0, 6, 10, 1, 7, 6, 1, 9, 5, 4, 7, 1, 3 };
+static unsigned char triangleTable_172_1[14] =  { 12, 0, 4, 9, 1, 7, 4, 1, 10, 5, 6, 7, 1, 3 };
+static unsigned char triangleTable_172_2[26] =  { 24, 1, 12, 9, 5, 3, 12, 1, 7, 4, 12, 10, 12, 6, 6, 12, 5, 12, 3, 7, 12, 10, 1, 9, 12, 4 };
+static unsigned char triangleTable_172_3[26] =  { 24, 1, 12, 10, 5, 3, 12, 1, 7, 6, 12, 9, 12, 4, 4, 12, 5, 12, 3, 7, 12, 9, 1, 10, 12, 6 };
+static unsigned char * triangleTable_172[4] = { triangleTable_172_0, triangleTable_172_1, triangleTable_172_2, triangleTable_172_3 };
+
+static unsigned char triangleTable_173_0[11] =  { 9, 0, 6, 10, 5, 8, 7, 4, 9, 1, 0 };
+static unsigned char triangleTable_173_1[17] =  { 15, 0, 6, 10, 5, 1, 0, 8, 1, 8, 7, 1, 7, 9, 4, 9, 7 };
+static unsigned char triangleTable_173_2[17] =  { 15, 0, 1, 0, 9, 8, 7, 6, 8, 6, 10, 8, 10, 4, 5, 4, 10 };
+static unsigned char triangleTable_173_3[17] =  { 15, 0, 8, 7, 4, 6, 10, 1, 6, 1, 0, 6, 0, 5, 9, 5, 0 };
+static unsigned char triangleTable_173_4[29] =  { 27, 1, 10, 5, 12, 5, 4, 12, 6, 12, 7, 9, 12, 4, 1, 0, 12, 8, 7, 12, 1, 12, 9, 10, 12, 6, 12, 0, 8 };
+static unsigned char triangleTable_173_5[29] =  { 27, 1, 7, 4, 12, 4, 9, 12, 8, 12, 0, 5, 12, 9, 6, 10, 12, 1, 0, 12, 6, 12, 5, 7, 12, 8, 12, 10, 1 };
+static unsigned char triangleTable_173_6[29] =  { 27, 1, 0, 9, 12, 9, 5, 12, 1, 12, 10, 4, 12, 5, 8, 7, 12, 6, 10, 12, 8, 12, 4, 0, 12, 1, 12, 7, 6 };
+static unsigned char triangleTable_173_7[17] =  { 15, 0, 0, 8, 7, 0, 10, 1, 9, 5, 4, 0, 7, 10, 6, 10, 7 };
+static unsigned char * triangleTable_173[8] = 
+{ 
+  triangleTable_173_0, triangleTable_173_1, triangleTable_173_2, triangleTable_173_3, triangleTable_173_4, triangleTable_173_5, triangleTable_173_6, triangleTable_173_7
+};
+
+static unsigned char triangleTable_174_0[11] =  { 9, 0, 5, 6, 10, 7, 4, 3, 4, 0, 3 };
+static unsigned char triangleTable_174_1[17] =  { 15, 0, 6, 10, 3, 4, 0, 5, 10, 0, 3, 6, 3, 7, 5, 0, 10 };
+static unsigned char * triangleTable_174[2] = { triangleTable_174_0, triangleTable_174_1 };
+
+static unsigned char triangleTable_175_0[8] =  { 6, 0, 10, 5, 6, 7, 4, 8 };
+static unsigned char triangleTable_175_1[14] =  { 12, 0, 10, 4, 8, 7, 6, 10, 10, 8, 7, 4, 10, 5 };
+static unsigned char * triangleTable_175[2] = { triangleTable_175_0, triangleTable_175_1 };
+
+static unsigned char triangleTable_176_0[11] =  { 9, 0, 9, 5, 6, 11, 9, 6, 11, 8, 9 };
+static unsigned char * triangleTable_176[1] = { triangleTable_176_0 };
+
+static unsigned char triangleTable_177_0[14] =  { 12, 0, 5, 3, 9, 5, 11, 3, 6, 11, 5, 9, 3, 0 };
+static unsigned char * triangleTable_177[1] = { triangleTable_177_0 };
+
+static unsigned char triangleTable_178_0[14] =  { 12, 0, 6, 11, 5, 0, 1, 5, 11, 8, 0, 11, 0, 5 };
+static unsigned char * triangleTable_178[1] = { triangleTable_178_0 };
+
+static unsigned char triangleTable_179_0[11] =  { 9, 0, 6, 11, 3, 6, 3, 5, 3, 1, 5 };
+static unsigned char * triangleTable_179[1] = { triangleTable_179_0 };
+
+static unsigned char triangleTable_180_0[14] =  { 12, 0, 5, 6, 11, 9, 5, 11, 2, 10, 1, 9, 11, 8 };
+static unsigned char triangleTable_180_1[14] =  { 12, 0, 1, 2, 11, 9, 1, 11, 6, 10, 5, 9, 11, 8 };
+static unsigned char triangleTable_180_2[26] =  { 24, 1, 12, 2, 10, 8, 12, 11, 9, 1, 12, 6, 12, 5, 5, 12, 10, 12, 8, 9, 12, 6, 11, 2, 12, 1 };
+static unsigned char triangleTable_180_3[26] =  { 24, 1, 12, 6, 10, 8, 12, 11, 9, 5, 12, 2, 12, 1, 1, 12, 10, 12, 8, 9, 12, 2, 11, 6, 12, 5 };
+static unsigned char * triangleTable_180[4] = { triangleTable_180_0, triangleTable_180_1, triangleTable_180_2, triangleTable_180_3 };
+
+static unsigned char triangleTable_181_0[11] =  { 9, 0, 5, 6, 10, 0, 9, 1, 2, 11, 3 };
+static unsigned char triangleTable_181_1[17] =  { 15, 0, 5, 6, 10, 11, 3, 0, 11, 0, 9, 11, 9, 2, 1, 2, 9 };
+static unsigned char triangleTable_181_2[17] =  { 15, 0, 11, 3, 2, 0, 9, 5, 0, 5, 6, 0, 6, 1, 10, 1, 6 };
+static unsigned char triangleTable_181_3[17] =  { 15, 0, 0, 9, 1, 5, 6, 11, 5, 11, 3, 5, 3, 10, 2, 10, 3 };
+static unsigned char triangleTable_181_4[29] =  { 27, 1, 6, 10, 12, 10, 1, 12, 5, 12, 9, 2, 12, 1, 11, 3, 12, 0, 9, 12, 11, 12, 2, 6, 12, 5, 12, 3, 0 };
+static unsigned char triangleTable_181_5[29] =  { 27, 1, 9, 1, 12, 1, 2, 12, 0, 12, 3, 10, 12, 2, 5, 6, 12, 11, 3, 12, 5, 12, 10, 9, 12, 0, 12, 6, 11 };
+static unsigned char triangleTable_181_6[29] =  { 27, 1, 3, 2, 12, 2, 10, 12, 11, 12, 6, 1, 12, 10, 0, 9, 12, 5, 6, 12, 0, 12, 1, 3, 12, 11, 12, 9, 5 };
+static unsigned char triangleTable_181_7[17] =  { 15, 0, 3, 0, 9, 3, 6, 11, 2, 10, 1, 3, 9, 6, 5, 6, 9 };
+static unsigned char * triangleTable_181[8] = 
+{ 
+  triangleTable_181_0, triangleTable_181_1, triangleTable_181_2, triangleTable_181_3, triangleTable_181_4, triangleTable_181_5, triangleTable_181_6, triangleTable_181_7
+};
+
+static unsigned char triangleTable_182_0[11] =  { 9, 0, 6, 10, 5, 2, 11, 0, 11, 8, 0 };
+static unsigned char triangleTable_182_1[17] =  { 15, 0, 10, 5, 0, 11, 8, 6, 5, 8, 0, 10, 0, 2, 6, 8, 5 };
+static unsigned char * triangleTable_182[2] = { triangleTable_182_0, triangleTable_182_1 };
+
+static unsigned char triangleTable_183_0[8] =  { 6, 0, 5, 6, 10, 2, 11, 3 };
+static unsigned char triangleTable_183_1[14] =  { 12, 0, 5, 11, 3, 2, 10, 5, 5, 3, 2, 11, 5, 6 };
+static unsigned char * triangleTable_183[2] = { triangleTable_183_0, triangleTable_183_1 };
+
+static unsigned char triangleTable_184_0[14] =  { 12, 0, 6, 2, 5, 2, 3, 8, 2, 8, 9, 5, 2, 9 };
+static unsigned char * triangleTable_184[1] = { triangleTable_184_0 };
+
+static unsigned char triangleTable_185_0[11] =  { 9, 0, 9, 5, 6, 9, 6, 0, 6, 2, 0 };
+static unsigned char * triangleTable_185[1] = { triangleTable_185_0 };
+
+static unsigned char triangleTable_186_0[11] =  { 9, 0, 0, 3, 8, 2, 1, 6, 1, 5, 6 };
+static unsigned char triangleTable_186_1[17] =  { 15, 0, 3, 8, 6, 1, 5, 0, 8, 5, 6, 3, 6, 2, 0, 5, 8 };
+static unsigned char * triangleTable_186[2] = { triangleTable_186_0, triangleTable_186_1 };
+
+static unsigned char triangleTable_187_0[8] =  { 6, 0, 6, 1, 5, 1, 6, 2 };
+static unsigned char * triangleTable_187[1] = { triangleTable_187_0 };
+
+static unsigned char triangleTable_188_0[11] =  { 9, 0, 10, 5, 6, 9, 1, 8, 1, 3, 8 };
+static unsigned char triangleTable_188_1[17] =  { 15, 0, 5, 6, 8, 1, 3, 10, 6, 3, 8, 5, 8, 9, 10, 3, 6 };
+static unsigned char * triangleTable_188[2] = { triangleTable_188_0, triangleTable_188_1 };
+
+static unsigned char triangleTable_189_0[8] =  { 6, 0, 6, 10, 5, 9, 1, 0 };
+static unsigned char triangleTable_189_1[14] =  { 12, 0, 6, 1, 0, 9, 5, 6, 6, 0, 9, 1, 6, 10 };
+static unsigned char * triangleTable_189[2] = { triangleTable_189_0, triangleTable_189_1 };
+
+static unsigned char triangleTable_190_0[8] =  { 6, 0, 10, 5, 6, 8, 0, 3 };
+static unsigned char triangleTable_190_1[20] =  { 18, 0, 5, 6, 8, 0, 5, 8, 10, 3, 6, 10, 5, 0, 3, 10, 0, 8, 6, 3 };
+static unsigned char * triangleTable_190[2] = { triangleTable_190_0, triangleTable_190_1 };
+
+static unsigned char triangleTable_191_0[5] =  { 3, 0, 10, 5, 6 };
+static unsigned char * triangleTable_191[1] = { triangleTable_191_0 };
+
+static unsigned char triangleTable_192_0[8] =  { 6, 0, 7, 5, 11, 10, 11, 5 };
+static unsigned char * triangleTable_192[1] = { triangleTable_192_0 };
+
+static unsigned char triangleTable_193_0[11] =  { 9, 0, 0, 8, 3, 5, 11, 7, 5, 10, 11 };
+static unsigned char triangleTable_193_1[17] =  { 15, 0, 5, 0, 8, 3, 10, 11, 5, 10, 0, 7, 5, 8, 0, 10, 3 };
+static unsigned char * triangleTable_193[2] = { triangleTable_193_0, triangleTable_193_1 };
+
+static unsigned char triangleTable_194_0[11] =  { 9, 0, 0, 1, 9, 11, 5, 10, 11, 7, 5 };
+static unsigned char triangleTable_194_1[17] =  { 15, 0, 11, 0, 1, 9, 7, 5, 11, 7, 0, 10, 11, 1, 0, 7, 9 };
+static unsigned char * triangleTable_194[2] = { triangleTable_194_0, triangleTable_194_1 };
+
+static unsigned char triangleTable_195_0[14] =  { 12, 0, 11, 7, 10, 3, 1, 8, 1, 9, 8, 7, 5, 10 };
+static unsigned char triangleTable_195_1[14] =  { 12, 0, 5, 8, 7, 3, 10, 11, 3, 1, 10, 5, 9, 8 };
+static unsigned char triangleTable_195_2[26] =  { 24, 0, 11, 3, 10, 9, 7, 5, 9, 5, 10, 1, 10, 3, 7, 9, 8, 3, 7, 8, 9, 10, 1, 7, 3, 11 };
+static unsigned char triangleTable_195_3[26] =  { 24, 1, 12, 3, 1, 7, 12, 11, 12, 7, 5, 12, 8, 3, 1, 10, 12, 11, 12, 10, 5, 9, 12, 12, 9, 8 };
+static unsigned char triangleTable_195_4[26] =  { 24, 1, 12, 10, 11, 9, 12, 1, 12, 9, 8, 12, 5, 10, 11, 3, 12, 1, 12, 3, 8, 7, 12, 12, 7, 5 };
+static unsigned char * triangleTable_195[5] = { triangleTable_195_0, triangleTable_195_1, triangleTable_195_2, triangleTable_195_3, triangleTable_195_4 };
+
+static unsigned char triangleTable_196_0[11] =  { 9, 0, 1, 2, 11, 7, 1, 11, 7, 5, 1 };
+static unsigned char * triangleTable_196[1] = { triangleTable_196_0 };
+
+static unsigned char triangleTable_197_0[14] =  { 12, 0, 2, 11, 7, 1, 2, 7, 8, 3, 0, 1, 7, 5 };
+static unsigned char triangleTable_197_1[14] =  { 12, 0, 0, 8, 7, 1, 0, 7, 11, 3, 2, 1, 7, 5 };
+static unsigned char triangleTable_197_2[26] =  { 24, 1, 12, 8, 3, 5, 12, 7, 1, 0, 12, 11, 12, 2, 2, 12, 3, 12, 5, 1, 12, 11, 7, 8, 12, 0 };
+static unsigned char triangleTable_197_3[26] =  { 24, 1, 12, 11, 3, 5, 12, 7, 1, 2, 12, 8, 12, 0, 0, 12, 3, 12, 5, 1, 12, 8, 7, 11, 12, 2 };
+static unsigned char * triangleTable_197[4] = { triangleTable_197_0, triangleTable_197_1, triangleTable_197_2, triangleTable_197_3 };
+
+static unsigned char triangleTable_198_0[14] =  { 12, 0, 11, 7, 2, 9, 0, 2, 7, 5, 9, 7, 9, 2 };
+static unsigned char * triangleTable_198[1] = { triangleTable_198_0 };
+
+static unsigned char triangleTable_199_0[11] =  { 9, 0, 11, 3, 2, 8, 7, 9, 7, 5, 9 };
+static unsigned char triangleTable_199_1[17] =  { 15, 0, 3, 2, 9, 7, 5, 11, 2, 5, 9, 3, 9, 8, 11, 5, 2 };
+static unsigned char * triangleTable_199[2] = { triangleTable_199_0, triangleTable_199_1 };
+
+static unsigned char triangleTable_200_0[11] =  { 9, 0, 5, 10, 2, 3, 5, 2, 3, 7, 5 };
+static unsigned char * triangleTable_200[1] = { triangleTable_200_0 };
+
+static unsigned char triangleTable_201_0[14] =  { 12, 0, 2, 0, 10, 0, 8, 7, 0, 7, 5, 10, 0, 5 };
+static unsigned char * triangleTable_201[1] = { triangleTable_201_0 };
+
+static unsigned char triangleTable_202_0[14] =  { 12, 0, 10, 2, 3, 5, 10, 3, 0, 1, 9, 5, 3, 7 };
+static unsigned char triangleTable_202_1[14] =  { 12, 0, 9, 0, 3, 5, 9, 3, 2, 1, 10, 5, 3, 7 };
+static unsigned char triangleTable_202_2[26] =  { 24, 1, 12, 0, 1, 7, 12, 3, 5, 9, 12, 2, 12, 10, 10, 12, 1, 12, 7, 5, 12, 2, 3, 0, 12, 9 };
+static unsigned char triangleTable_202_3[26] =  { 24, 1, 12, 2, 1, 7, 12, 3, 5, 10, 12, 0, 12, 9, 9, 12, 1, 12, 7, 5, 12, 0, 3, 2, 12, 10 };
+static unsigned char * triangleTable_202[4] = { triangleTable_202_0, triangleTable_202_1, triangleTable_202_2, triangleTable_202_3 };
+
+static unsigned char triangleTable_203_0[11] =  { 9, 0, 1, 10, 2, 5, 9, 7, 9, 8, 7 };
+static unsigned char triangleTable_203_1[17] =  { 15, 0, 10, 2, 7, 9, 8, 1, 2, 8, 7, 10, 7, 5, 1, 8, 2 };
+static unsigned char * triangleTable_203[2] = { triangleTable_203_0, triangleTable_203_1 };
+
+static unsigned char triangleTable_204_0[8] =  { 6, 0, 5, 1, 3, 3, 7, 5 };
+static unsigned char * triangleTable_204[1] = { triangleTable_204_0 };
+
+static unsigned char triangleTable_205_0[11] =  { 9, 0, 0, 8, 7, 0, 7, 1, 7, 5, 1 };
+static unsigned char * triangleTable_205[1] = { triangleTable_205_0 };
+
+static unsigned char triangleTable_206_0[11] =  { 9, 0, 9, 0, 3, 9, 3, 5, 3, 7, 5 };
+static unsigned char * triangleTable_206[1] = { triangleTable_206_0 };
+
+static unsigned char triangleTable_207_0[8] =  { 6, 0, 7, 9, 8, 9, 7, 5 };
+static unsigned char * triangleTable_207[1] = { triangleTable_207_0 };
+
+static unsigned char triangleTable_208_0[11] =  { 9, 0, 8, 4, 5, 10, 8, 5, 10, 11, 8 };
+static unsigned char * triangleTable_208[1] = { triangleTable_208_0 };
+
+static unsigned char triangleTable_209_0[14] =  { 12, 0, 3, 0, 11, 5, 10, 11, 0, 4, 5, 0, 5, 11 };
+static unsigned char * triangleTable_209[1] = { triangleTable_209_0 };
+
+static unsigned char triangleTable_210_0[14] =  { 12, 0, 4, 5, 10, 8, 4, 10, 1, 9, 0, 8, 10, 11 };
+static unsigned char triangleTable_210_1[14] =  { 12, 0, 0, 1, 10, 8, 0, 10, 5, 9, 4, 8, 10, 11 };
+static unsigned char triangleTable_210_2[26] =  { 24, 1, 12, 1, 9, 11, 12, 10, 8, 0, 12, 5, 12, 4, 4, 12, 9, 12, 11, 8, 12, 5, 10, 1, 12, 0 };
+static unsigned char triangleTable_210_3[26] =  { 24, 1, 12, 5, 9, 11, 12, 10, 8, 4, 12, 1, 12, 0, 0, 12, 9, 12, 11, 8, 12, 1, 10, 5, 12, 4 };
+static unsigned char * triangleTable_210[4] = { triangleTable_210_0, triangleTable_210_1, triangleTable_210_2, triangleTable_210_3 };
+
+static unsigned char triangleTable_211_0[11] =  { 9, 0, 5, 9, 4, 1, 10, 3, 10, 11, 3 };
+static unsigned char triangleTable_211_1[17] =  { 15, 0, 9, 4, 3, 10, 11, 5, 4, 11, 3, 9, 3, 1, 5, 11, 4 };
+static unsigned char * triangleTable_211[2] = { triangleTable_211_0, triangleTable_211_1 };
+
+static unsigned char triangleTable_212_0[14] =  { 12, 0, 5, 1, 4, 1, 2, 11, 1, 11, 8, 4, 1, 8 };
+static unsigned char * triangleTable_212[1] = { triangleTable_212_0 };
+
+static unsigned char triangleTable_213_0[11] =  { 9, 0, 3, 2, 11, 1, 0, 5, 0, 4, 5 };
+static unsigned char triangleTable_213_1[17] =  { 15, 0, 2, 11, 5, 0, 4, 3, 11, 4, 5, 2, 5, 1, 3, 4, 11 };
+static unsigned char * triangleTable_213[2] = { triangleTable_213_0, triangleTable_213_1 };
+
+static unsigned char triangleTable_214_0[11] =  { 9, 0, 9, 4, 5, 8, 0, 11, 0, 2, 11 };
+static unsigned char triangleTable_214_1[17] =  { 15, 0, 4, 5, 11, 0, 2, 9, 5, 2, 11, 4, 11, 8, 9, 2, 5 };
+static unsigned char * triangleTable_214[2] = { triangleTable_214_0, triangleTable_214_1 };
+
+static unsigned char triangleTable_215_0[8] =  { 6, 0, 5, 9, 4, 3, 2, 11 };
+static unsigned char triangleTable_215_1[20] =  { 18, 0, 9, 4, 3, 2, 9, 3, 5, 11, 4, 5, 9, 2, 11, 5, 2, 3, 4, 11 };
+static unsigned char * triangleTable_215[2] = { triangleTable_215_0, triangleTable_215_1 };
+
+static unsigned char triangleTable_216_0[14] =  { 12, 0, 10, 8, 5, 10, 3, 8, 2, 3, 10, 5, 8, 4 };
+static unsigned char * triangleTable_216[1] = { triangleTable_216_0 };
+
+static unsigned char triangleTable_217_0[11] =  { 9, 0, 5, 10, 2, 5, 2, 4, 2, 0, 4 };
+static unsigned char * triangleTable_217[1] = { triangleTable_217_0 };
+
+static unsigned char triangleTable_218_0[11] =  { 9, 0, 10, 2, 1, 4, 5, 9, 0, 3, 8 };
+static unsigned char triangleTable_218_1[17] =  { 15, 0, 10, 2, 1, 3, 8, 4, 3, 4, 5, 3, 5, 0, 9, 0, 5 };
+static unsigned char triangleTable_218_2[17] =  { 15, 0, 3, 8, 0, 4, 5, 10, 4, 10, 2, 4, 2, 9, 1, 9, 2 };
+static unsigned char triangleTable_218_3[17] =  { 15, 0, 4, 5, 9, 10, 2, 3, 10, 3, 8, 10, 8, 1, 0, 1, 8 };
+static unsigned char triangleTable_218_4[29] =  { 27, 1, 2, 1, 12, 1, 9, 12, 10, 12, 5, 0, 12, 9, 3, 8, 12, 4, 5, 12, 3, 12, 0, 2, 12, 10, 12, 8, 4 };
+static unsigned char triangleTable_218_5[29] =  { 27, 1, 5, 9, 12, 9, 0, 12, 4, 12, 8, 1, 12, 0, 10, 2, 12, 3, 8, 12, 10, 12, 1, 5, 12, 4, 12, 2, 3 };
+static unsigned char triangleTable_218_6[29] =  { 27, 1, 8, 0, 12, 0, 1, 12, 3, 12, 2, 9, 12, 1, 4, 5, 12, 10, 2, 12, 4, 12, 9, 8, 12, 3, 12, 5, 10 };
+static unsigned char triangleTable_218_7[17] =  { 15, 0, 8, 4, 5, 8, 2, 3, 0, 1, 9, 8, 5, 2, 10, 2, 5 };
+static unsigned char * triangleTable_218[8] = 
+{ 
+  triangleTable_218_0, triangleTable_218_1, triangleTable_218_2, triangleTable_218_3, triangleTable_218_4, triangleTable_218_5, triangleTable_218_6, triangleTable_218_7
+};
+
+static unsigned char triangleTable_219_0[8] =  { 6, 0, 2, 1, 10, 5, 9, 4 };
+static unsigned char triangleTable_219_1[14] =  { 12, 0, 2, 9, 4, 5, 10, 2, 2, 4, 5, 9, 2, 1 };
+static unsigned char * triangleTable_219[2] = { triangleTable_219_0, triangleTable_219_1 };
+
+static unsigned char triangleTable_220_0[11] =  { 9, 0, 8, 4, 5, 8, 5, 3, 5, 1, 3 };
+static unsigned char * triangleTable_220[1] = { triangleTable_220_0 };
+
+static unsigned char triangleTable_221_0[8] =  { 6, 0, 0, 5, 1, 5, 0, 4 };
+static unsigned char * triangleTable_221[1] = { triangleTable_221_0 };
+
+static unsigned char triangleTable_222_0[8] =  { 6, 0, 5, 9, 4, 8, 0, 3 };
+static unsigned char triangleTable_222_1[14] =  { 12, 0, 5, 0, 3, 8, 4, 5, 5, 3, 8, 0, 5, 9 };
+static unsigned char * triangleTable_222[2] = { triangleTable_222_0, triangleTable_222_1 };
+
+static unsigned char triangleTable_223_0[5] =  { 3, 0, 4, 5, 9 };
+static unsigned char * triangleTable_223[1] = { triangleTable_223_0 };
+
+static unsigned char triangleTable_224_0[11] =  { 9, 0, 11, 7, 4, 9, 11, 4, 9, 10, 11 };
+static unsigned char * triangleTable_224[1] = { triangleTable_224_0 };
+
+static unsigned char triangleTable_225_0[14] =  { 12, 0, 7, 4, 9, 11, 7, 9, 0, 8, 3, 11, 9, 10 };
+static unsigned char triangleTable_225_1[14] =  { 12, 0, 3, 0, 9, 11, 3, 9, 4, 8, 7, 11, 9, 10 };
+static unsigned char triangleTable_225_2[26] =  { 24, 1, 12, 0, 8, 10, 12, 9, 11, 3, 12, 4, 12, 7, 7, 12, 8, 12, 10, 11, 12, 4, 9, 0, 12, 3 };
+static unsigned char triangleTable_225_3[26] =  { 24, 1, 12, 4, 8, 10, 12, 9, 11, 7, 12, 0, 12, 3, 3, 12, 8, 12, 10, 11, 12, 0, 9, 4, 12, 7 };
+static unsigned char * triangleTable_225[4] = { triangleTable_225_0, triangleTable_225_1, triangleTable_225_2, triangleTable_225_3 };
+
+static unsigned char triangleTable_226_0[14] =  { 12, 0, 10, 11, 1, 11, 7, 4, 11, 4, 0, 1, 11, 0 };
+static unsigned char * triangleTable_226[1] = { triangleTable_226_0 };
+
+static unsigned char triangleTable_227_0[11] =  { 9, 0, 8, 7, 4, 11, 3, 10, 3, 1, 10 };
+static unsigned char triangleTable_227_1[17] =  { 15, 0, 7, 4, 10, 3, 1, 8, 4, 1, 10, 7, 10, 11, 8, 1, 4 };
+static unsigned char * triangleTable_227[2] = { triangleTable_227_0, triangleTable_227_1 };
+
+static unsigned char triangleTable_228_0[14] =  { 12, 0, 7, 1, 11, 7, 9, 1, 4, 9, 7, 11, 1, 2 };
+static unsigned char * triangleTable_228[1] = { triangleTable_228_0 };
+
+static unsigned char triangleTable_229_0[11] =  { 9, 0, 2, 11, 3, 9, 1, 0, 8, 7, 4 };
+static unsigned char triangleTable_229_1[17] =  { 15, 0, 2, 11, 3, 7, 4, 9, 7, 9, 1, 7, 1, 8, 0, 8, 1 };
+static unsigned char triangleTable_229_2[17] =  { 15, 0, 7, 4, 8, 9, 1, 2, 9, 2, 11, 9, 11, 0, 3, 0, 11 };
+static unsigned char triangleTable_229_3[17] =  { 15, 0, 9, 1, 0, 2, 11, 7, 2, 7, 4, 2, 4, 3, 8, 3, 4 };
+static unsigned char triangleTable_229_4[29] =  { 27, 1, 11, 3, 12, 3, 0, 12, 2, 12, 1, 8, 12, 0, 7, 4, 12, 9, 1, 12, 7, 12, 8, 11, 12, 2, 12, 4, 9 };
+static unsigned char triangleTable_229_5[29] =  { 27, 1, 1, 0, 12, 0, 8, 12, 9, 12, 4, 3, 12, 8, 2, 11, 12, 7, 4, 12, 2, 12, 3, 1, 12, 9, 12, 11, 7 };
+static unsigned char triangleTable_229_6[29] =  { 27, 1, 4, 8, 12, 8, 3, 12, 7, 12, 11, 0, 12, 3, 9, 1, 12, 2, 11, 12, 9, 12, 0, 4, 12, 7, 12, 1, 2 };
+static unsigned char triangleTable_229_7[17] =  { 15, 0, 4, 9, 1, 4, 11, 7, 8, 3, 0, 4, 1, 11, 2, 11, 1 };
+static unsigned char * triangleTable_229[8] = 
+{ 
+  triangleTable_229_0, triangleTable_229_1, triangleTable_229_2, triangleTable_229_3, triangleTable_229_4, triangleTable_229_5, triangleTable_229_6, triangleTable_229_7
+};
+
+static unsigned char triangleTable_230_0[11] =  { 9, 0, 11, 7, 4, 11, 4, 2, 4, 0, 2 };
+static unsigned char * triangleTable_230[1] = { triangleTable_230_0 };
+
+static unsigned char triangleTable_231_0[8] =  { 6, 0, 2, 11, 3, 8, 7, 4 };
+static unsigned char triangleTable_231_1[14] =  { 12, 0, 2, 7, 4, 8, 3, 2, 2, 4, 8, 7, 2, 11 };
+static unsigned char * triangleTable_231[2] = { triangleTable_231_0, triangleTable_231_1 };
+
+static unsigned char triangleTable_232_0[14] =  { 12, 0, 2, 3, 10, 4, 9, 10, 3, 7, 4, 3, 4, 10 };
+static unsigned char * triangleTable_232[1] = { triangleTable_232_0 };
+
+static unsigned char triangleTable_233_0[11] =  { 9, 0, 4, 8, 7, 0, 9, 2, 9, 10, 2 };
+static unsigned char triangleTable_233_1[17] =  { 15, 0, 8, 7, 2, 9, 10, 4, 7, 10, 2, 8, 2, 0, 4, 10, 7 };
+static unsigned char * triangleTable_233[2] = { triangleTable_233_0, triangleTable_233_1 };
+
+static unsigned char triangleTable_234_0[11] =  { 9, 0, 2, 1, 10, 0, 3, 4, 3, 7, 4 };
+static unsigned char triangleTable_234_1[17] =  { 15, 0, 1, 10, 4, 3, 7, 2, 10, 7, 4, 1, 4, 0, 2, 7, 10 };
+static unsigned char * triangleTable_234[2] = { triangleTable_234_0, triangleTable_234_1 };
+
+static unsigned char triangleTable_235_0[8] =  { 6, 0, 1, 10, 2, 7, 4, 8 };
+static unsigned char triangleTable_235_1[20] =  { 18, 0, 10, 2, 7, 4, 10, 7, 1, 8, 2, 1, 10, 4, 8, 1, 4, 7, 2, 8 };
+static unsigned char * triangleTable_235[2] = { triangleTable_235_0, triangleTable_235_1 };
+
+static unsigned char triangleTable_236_0[11] =  { 9, 0, 4, 9, 1, 4, 1, 7, 1, 3, 7 };
+static unsigned char * triangleTable_236[1] = { triangleTable_236_0 };
+
+static unsigned char triangleTable_237_0[8] =  { 6, 0, 1, 0, 9, 4, 8, 7 };
+static unsigned char triangleTable_237_1[14] =  { 12, 0, 1, 8, 7, 4, 9, 1, 1, 7, 4, 8, 1, 0 };
+static unsigned char * triangleTable_237[2] = { triangleTable_237_0, triangleTable_237_1 };
+
+static unsigned char triangleTable_238_0[8] =  { 6, 0, 4, 3, 7, 3, 4, 0 };
+static unsigned char * triangleTable_238[1] = { triangleTable_238_0 };
+
+static unsigned char triangleTable_239_0[5] =  { 3, 0, 4, 8, 7 };
+static unsigned char * triangleTable_239[1] = { triangleTable_239_0 };
+
+static unsigned char triangleTable_240_0[8] =  { 6, 0, 9, 10, 11, 11, 8, 9 };
+static unsigned char * triangleTable_240[1] = { triangleTable_240_0 };
+
+static unsigned char triangleTable_241_0[11] =  { 9, 0, 3, 0, 9, 3, 9, 11, 9, 10, 11 };
+static unsigned char * triangleTable_241[1] = { triangleTable_241_0 };
+
+static unsigned char triangleTable_242_0[11] =  { 9, 0, 0, 1, 10, 0, 10, 8, 10, 11, 8 };
+static unsigned char * triangleTable_242[1] = { triangleTable_242_0 };
+
+static unsigned char triangleTable_243_0[8] =  { 6, 0, 3, 10, 11, 10, 3, 1 };
+static unsigned char * triangleTable_243[1] = { triangleTable_243_0 };
+
+static unsigned char triangleTable_244_0[11] =  { 9, 0, 1, 2, 11, 1, 11, 9, 11, 8, 9 };
+static unsigned char * triangleTable_244[1] = { triangleTable_244_0 };
+
+static unsigned char triangleTable_245_0[8] =  { 6, 0, 11, 3, 2, 1, 0, 9 };
+static unsigned char triangleTable_245_1[14] =  { 12, 0, 11, 0, 9, 1, 2, 11, 11, 9, 1, 0, 11, 3 };
+static unsigned char * triangleTable_245[2] = { triangleTable_245_0, triangleTable_245_1 };
+
+static unsigned char triangleTable_246_0[8] =  { 6, 0, 11, 0, 2, 0, 11, 8 };
+static unsigned char * triangleTable_246[1] = { triangleTable_246_0 };
+
+static unsigned char triangleTable_247_0[5] =  { 3, 0, 2, 11, 3 };
+static unsigned char * triangleTable_247[1] = { triangleTable_247_0 };
+
+static unsigned char triangleTable_248_0[11] =  { 9, 0, 2, 3, 8, 2, 8, 10, 8, 9, 10 };
+static unsigned char * triangleTable_248[1] = { triangleTable_248_0 };
+
+static unsigned char triangleTable_249_0[8] =  { 6, 0, 9, 2, 0, 2, 9, 10 };
+static unsigned char * triangleTable_249[1] = { triangleTable_249_0 };
+
+static unsigned char triangleTable_250_0[8] =  { 6, 0, 10, 2, 1, 0, 3, 8 };
+static unsigned char triangleTable_250_1[14] =  { 12, 0, 10, 3, 8, 0, 1, 10, 10, 8, 0, 3, 10, 2 };
+static unsigned char * triangleTable_250[2] = { triangleTable_250_0, triangleTable_250_1 };
+
+static unsigned char triangleTable_251_0[5] =  { 3, 0, 2, 1, 10 };
+static unsigned char * triangleTable_251[1] = { triangleTable_251_0 };
+
+static unsigned char triangleTable_252_0[8] =  { 6, 0, 8, 1, 3, 1, 8, 9 };
+static unsigned char * triangleTable_252[1] = { triangleTable_252_0 };
+
+static unsigned char triangleTable_253_0[5] =  { 3, 0, 9, 1, 0 };
+static unsigned char * triangleTable_253[1] = { triangleTable_253_0 };
+
+static unsigned char triangleTable_254_0[5] =  { 3, 0, 0, 3, 8 };
+static unsigned char * triangleTable_254[1] = { triangleTable_254_0 };
+
+static unsigned char ** triangleTable[256] = 
+{ 
+  NULL, triangleTable_1, triangleTable_2, triangleTable_3, triangleTable_4, triangleTable_5, triangleTable_6, triangleTable_7, 
+  triangleTable_8, triangleTable_9, triangleTable_10, triangleTable_11, triangleTable_12, triangleTable_13, triangleTable_14, triangleTable_15, 
+  triangleTable_16, triangleTable_17, triangleTable_18, triangleTable_19, triangleTable_20, triangleTable_21, triangleTable_22, triangleTable_23, 
+  triangleTable_24, triangleTable_25, triangleTable_26, triangleTable_27, triangleTable_28, triangleTable_29, triangleTable_30, triangleTable_31, 
+  triangleTable_32, triangleTable_33, triangleTable_34, triangleTable_35, triangleTable_36, triangleTable_37, triangleTable_38, triangleTable_39, 
+  triangleTable_40, triangleTable_41, triangleTable_42, triangleTable_43, triangleTable_44, triangleTable_45, triangleTable_46, triangleTable_47, 
+  triangleTable_48, triangleTable_49, triangleTable_50, triangleTable_51, triangleTable_52, triangleTable_53, triangleTable_54, triangleTable_55, 
+  triangleTable_56, triangleTable_57, triangleTable_58, triangleTable_59, triangleTable_60, triangleTable_61, triangleTable_62, triangleTable_63, 
+  triangleTable_64, triangleTable_65, triangleTable_66, triangleTable_67, triangleTable_68, triangleTable_69, triangleTable_70, triangleTable_71, 
+  triangleTable_72, triangleTable_73, triangleTable_74, triangleTable_75, triangleTable_76, triangleTable_77, triangleTable_78, triangleTable_79, 
+  triangleTable_80, triangleTable_81, triangleTable_82, triangleTable_83, triangleTable_84, triangleTable_85, triangleTable_86, triangleTable_87, 
+  triangleTable_88, triangleTable_89, triangleTable_90, triangleTable_91, triangleTable_92, triangleTable_93, triangleTable_94, triangleTable_95, 
+  triangleTable_96, triangleTable_97, triangleTable_98, triangleTable_99, triangleTable_100, triangleTable_101, triangleTable_102, triangleTable_103, 
+  triangleTable_104, triangleTable_105, triangleTable_106, triangleTable_107, triangleTable_108, triangleTable_109, triangleTable_110, triangleTable_111, 
+  triangleTable_112, triangleTable_113, triangleTable_114, triangleTable_115, triangleTable_116, triangleTable_117, triangleTable_118, triangleTable_119, 
+  triangleTable_120, triangleTable_121, triangleTable_122, triangleTable_123, triangleTable_124, triangleTable_125, triangleTable_126, triangleTable_127, 
+  triangleTable_128, triangleTable_129, triangleTable_130, triangleTable_131, triangleTable_132, triangleTable_133, triangleTable_134, triangleTable_135, 
+  triangleTable_136, triangleTable_137, triangleTable_138, triangleTable_139, triangleTable_140, triangleTable_141, triangleTable_142, triangleTable_143, 
+  triangleTable_144, triangleTable_145, triangleTable_146, triangleTable_147, triangleTable_148, triangleTable_149, triangleTable_150, triangleTable_151, 
+  triangleTable_152, triangleTable_153, triangleTable_154, triangleTable_155, triangleTable_156, triangleTable_157, triangleTable_158, triangleTable_159, 
+  triangleTable_160, triangleTable_161, triangleTable_162, triangleTable_163, triangleTable_164, triangleTable_165, triangleTable_166, triangleTable_167, 
+  triangleTable_168, triangleTable_169, triangleTable_170, triangleTable_171, triangleTable_172, triangleTable_173, triangleTable_174, triangleTable_175, 
+  triangleTable_176, triangleTable_177, triangleTable_178, triangleTable_179, triangleTable_180, triangleTable_181, triangleTable_182, triangleTable_183, 
+  triangleTable_184, triangleTable_185, triangleTable_186, triangleTable_187, triangleTable_188, triangleTable_189, triangleTable_190, triangleTable_191, 
+  triangleTable_192, triangleTable_193, triangleTable_194, triangleTable_195, triangleTable_196, triangleTable_197, triangleTable_198, triangleTable_199, 
+  triangleTable_200, triangleTable_201, triangleTable_202, triangleTable_203, triangleTable_204, triangleTable_205, triangleTable_206, triangleTable_207, 
+  triangleTable_208, triangleTable_209, triangleTable_210, triangleTable_211, triangleTable_212, triangleTable_213, triangleTable_214, triangleTable_215, 
+  triangleTable_216, triangleTable_217, triangleTable_218, triangleTable_219, triangleTable_220, triangleTable_221, triangleTable_222, triangleTable_223, 
+  triangleTable_224, triangleTable_225, triangleTable_226, triangleTable_227, triangleTable_228, triangleTable_229, triangleTable_230, triangleTable_231, 
+  triangleTable_232, triangleTable_233, triangleTable_234, triangleTable_235, triangleTable_236, triangleTable_237, triangleTable_238, triangleTable_239, 
+  triangleTable_240, triangleTable_241, triangleTable_242, triangleTable_243, triangleTable_244, triangleTable_245, triangleTable_246, triangleTable_247, 
+  triangleTable_248, triangleTable_249, triangleTable_250, triangleTable_251, triangleTable_252, triangleTable_253, triangleTable_254, NULL
+};
+
diff --git a/libraries/distanceField/signedDistanceFieldFromPolygonSoup.cpp b/libraries/distanceField/signedDistanceFieldFromPolygonSoup.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d19e6a8db8a59e6094b5ec599efd1b00136923e
--- /dev/null
+++ b/libraries/distanceField/signedDistanceFieldFromPolygonSoup.cpp
@@ -0,0 +1,528 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This class computes a signed distance field from polygon soup geometry, as described in:
+
+  Hongyi Xu, Jernej Barbic: 
+  Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014, Montreal, Canada
+*/
+
+#include <stdio.h>
+#include "signedDistanceFieldFromPolygonSoup.h"
+#include "marchingCubes.h"
+using namespace::std;
+
+SignedDistanceFieldFromPolygonSoup::SignedDistanceFieldFromPolygonSoup(ObjMesh * objMesh_, double expansionRatio_, bool useCubicBox_,
+    Vec3d * bbmin_, Vec3d * bbmax_):
+objMesh(objMesh_), expansionRatio(expansionRatio_), useCubicBox(useCubicBox_)
+{
+  if(bbmin_ != NULL)
+    bbmin = *bbmin_;
+  else
+    bbmin = Vec3d(0.0);
+
+  if(bbmax_ != NULL)
+    bbmax = *bbmax_;
+  else
+    bbmax = Vec3d(1.0);
+
+
+  if(bbmin_ == NULL || bbmax_ == NULL)
+    autoBoundingBox = true;
+  else
+    autoBoundingBox = false;
+}
+
+// construct TriangleWithCollisionInfoAndPseudoNormals for each triangle in the mesh
+bool SignedDistanceFieldFromPolygonSoup::ConstructPseudoNormalComponents(ObjMesh* isoMesh, vector<TriangleWithCollisionInfoAndPseudoNormals*>* triangleList)
+{
+  printf("*************** Computing PseudoNormals *****************\n");
+  ObjMesh* pseudoNormalObjMesh = isoMesh;
+  int numZeroAreaFaces = pseudoNormalObjMesh->removeZeroAreaFaces();
+  printf("Encountered and removed %d faces with zero surface area.\n", numZeroAreaFaces);
+
+  //printf("here:%d %d\n", removedFaces[0].first, removedFaces[0].second);
+
+  pseudoNormalObjMesh->computePseudoNormals();
+  pseudoNormalObjMesh->computeEdgePseudoNormals();
+
+  Vec3d pseudoNormals[7];
+  for (unsigned int i=0; i<pseudoNormalObjMesh->getNumGroups(); ++i)
+  {
+    triangleList[i].clear();
+    for (unsigned int j=0; j<(pseudoNormalObjMesh->getGroupHandle(i))->getNumFaces(); ++j)
+    {
+      unsigned int index0 = pseudoNormalObjMesh->getVertexIndex(i, j, 0);
+      unsigned int index1 = pseudoNormalObjMesh->getVertexIndex(i, j, 1);
+      unsigned int index2 = pseudoNormalObjMesh->getVertexIndex(i, j, 2);
+
+      pseudoNormals[0] = pseudoNormalObjMesh->getPseudoNormal(index0);
+      pseudoNormals[1] = pseudoNormalObjMesh->getPseudoNormal(index1);
+      pseudoNormals[2] = pseudoNormalObjMesh->getPseudoNormal(index2);
+
+      if (pseudoNormalObjMesh->getEdgePseudoNormal(index0, index1, &pseudoNormals[3]) != 0)
+      {
+        printf("Error: encountered an edge without a pseudonormal. Degenerate face? Vertices: %d %d\n", index0, index1);
+        return false;
+      }
+
+      if (pseudoNormalObjMesh->getEdgePseudoNormal(index1, index2, &pseudoNormals[4]) != 0)
+      {
+        printf("Error: encountered an edge without a pseudonormal. Degenerate face? Vertices: %d %d\n", index1, index2);
+        return false;
+      }
+
+      if (pseudoNormalObjMesh->getEdgePseudoNormal(index2, index0, &pseudoNormals[5]) != 0)
+      {
+        printf("Error: encountered an edge without a pseudonormal. Degenerate face? Vertices: %d %d.\n", index2, index0);
+        return false;
+      }
+
+      // face pseudo normal
+      Vec3d p0 = pseudoNormalObjMesh->getPosition(index0);
+      Vec3d p1 = pseudoNormalObjMesh->getPosition(index1);
+      Vec3d p2 = pseudoNormalObjMesh->getPosition(index2);
+
+      pseudoNormals[6] = norm(cross(p1-p0,p2-p0));
+
+      for(int normali=0; normali < 7; normali++)
+      {
+        if (pseudoNormals[normali].hasNaN())
+        {
+          printf("Error: pseudonormal nan encountered: %lf %lf %lf\n", pseudoNormals[normali][0], pseudoNormals[normali][1], pseudoNormals[normali][2]);
+          printf("Group: %d | Triangle: %d\n", i, j);
+          printf("vtx0: %d | vtx1: %d | vtx2: %d\n",index0, index1, index2);
+          return false;
+        }
+      }
+      TriangleWithCollisionInfoAndPseudoNormals * triangle = new TriangleWithCollisionInfoAndPseudoNormals(p0, p1, p2, pseudoNormals);
+      triangleList[i].push_back(triangle);
+    }
+  }
+
+  return true;
+}
+
+// check whether comp1 is strictly inside comp2 using bounding box
+bool SignedDistanceFieldFromPolygonSoup::CheckInAndOutViaBoundingBox(vector<Vec3d> & bmin, vector<Vec3d> & bmax, int compIndex1, int compIndex2)
+{
+  if (bmin[compIndex1][0]>=bmin[compIndex2][0] && bmin[compIndex1][1]>=bmin[compIndex2][1] && bmin[compIndex1][2] >= bmin[compIndex2][2]&& bmax[compIndex1][0]<=bmax[compIndex2][0] && bmax[compIndex1][1]<=bmax[compIndex2][1] && bmax[compIndex1][2] <= bmax[compIndex2][2])
+      return true;
+
+  return false;
+}
+
+// check whether comp2's normal points toward opposite direction as comp1's position
+// this is to prevent the case that comp1's bounding box is inside comp2's, but comp1 is outside comp2
+bool SignedDistanceFieldFromPolygonSoup::CheckInAndOutViaNormalTest(vector<TriangleWithCollisionInfoAndPseudoNormals*>* triangleList, int compIndex1, int compIndex2)
+{
+  if (triangleList[compIndex1].size() == 0)
+    return true;
+
+  Vec3d testPoint = triangleList[compIndex1][0]->first();
+
+  int closestFeature = -1;
+  int closestTriangle = -1;
+  //int indexClosestTriangle = -1;
+  double closestDistance2 = DBL_MAX;
+
+  for (size_t i=0; i<triangleList[compIndex2].size(); ++i)
+  {
+    int closestLocalFeature = -1;
+    double d2 = triangleList[compIndex2][i]->distanceToPoint2(testPoint, &closestLocalFeature);
+    if( d2 < closestDistance2)
+    {
+      closestDistance2 = d2;
+      closestFeature = closestLocalFeature;
+      closestTriangle = (int)i;
+    }
+  }
+
+  Vec3d pseudoNormal = triangleList[compIndex2][closestTriangle]->pseudoNormal(closestFeature);
+  Vec3d pseudoClosestPosition = triangleList[compIndex2][closestTriangle]->pseudoClosestPosition(closestFeature);
+  Vec3d pointVec = testPoint - pseudoClosestPosition;
+
+  if(dot(pseudoNormal,pointVec) < 0)
+  // if (pseudoNormal[0]*pointVec[0]+pseudoNormal[1]*pointVec[1]+pseudoNormal[2]*pointVec[2]<0)
+    return true;
+
+  return false;
+}
+
+ObjMesh * SignedDistanceFieldFromPolygonSoup::RemoveInteriorComponents(ObjMesh * isoMesh)
+{
+  int numGroups = (int) isoMesh->getNumGroups();
+
+  if(numGroups == 1)
+    return isoMesh;
+
+  //building bounding box for each group;
+  vector<Vec3d> bmin(numGroups);
+  vector<Vec3d> bmax(numGroups);
+
+  vector<std::pair<int, int> >groupFaceList;
+
+  for (int i=0; i<numGroups; ++i)
+  {
+    const ObjMesh::Group * group = isoMesh->getGroupHandle(i);
+
+    bmin[i][0] = bmin[i][1] = bmin[i][2] = DBL_MAX;
+    bmax[i][0] = bmax[i][1] = bmax[i][2] = -DBL_MAX;
+
+    for (unsigned int j=0; j<group->getNumFaces(); ++j)
+    {
+      const ObjMesh::Face * face = group->getFaceHandle(j);
+
+      groupFaceList.push_back(std::make_pair(i, j));
+      for (unsigned int v=0; v<face->getNumVertices(); ++v)
+      {
+        const ObjMesh::Vertex * vertex = face->getVertexHandle(v);
+        Vec3d pos = isoMesh->getPosition(vertex->getPositionIndex());
+
+        if(pos[0]<bmin[i][0])
+          bmin[i][0] = pos[0];
+        else if(pos[0]>bmax[i][0])
+          bmax[i][0] = pos[0];
+
+        if(pos[1]<bmin[i][1])
+          bmin[i][1] = pos[1];
+        else if(pos[1]>bmax[i][1])
+          bmax[i][1] = pos[1];
+
+        if(pos[2]<bmin[i][2])
+          bmin[i][2] = pos[2];
+        else if(pos[2]>bmax[i][2])
+          bmax[i][2] = pos[2];
+      }
+    }
+  }
+
+  ObjMesh * cloneMesh = isoMesh->clone(groupFaceList, 0);
+
+  // judge the position relation
+  vector<double> bbVolume(numGroups);
+
+  for(int i = 0; i < numGroups; ++i)
+  {
+    bbVolume[i] = (bmax[i][0] - bmin[i][0]) * (bmax[i][1]- bmin[i][1]) * (bmax[i][2]- bmin[i][2]);
+  }
+
+  vector<int> componentOrder(numGroups);
+  vector<int> tag(numGroups);
+  
+
+  for(int i=0; i<numGroups; i++)
+    tag[i] = numGroups;
+
+  // sort each group according to bounding box volume
+  // tag[groudp id] = rank in bounding box size (0: smallest)
+  for(int i=0; i<numGroups; ++i)
+  {
+    double minValue = DBL_MAX;
+    int minIndex = -1;
+    for(int j=0; j<numGroups; ++j)
+    {
+      if(tag[j]>i && bbVolume[j]<minValue)
+      {
+        minValue = bbVolume[j];
+        minIndex = j;
+      }
+    }
+    tag[minIndex] = i;
+  }
+
+  for(int i=0; i<numGroups; ++i)
+  {
+    componentOrder[tag[i]] = i;
+  }
+
+  vector<int> removedList;
+
+  //order the bbVolume
+  vector<TriangleWithCollisionInfoAndPseudoNormals*>* triangleList = new vector<TriangleWithCollisionInfoAndPseudoNormals*>[numGroups];
+  vector<std::pair<int, int> >removedFaces;
+  if (ConstructPseudoNormalComponents(isoMesh, triangleList) == false)
+  {
+    for(int i=0; i<numGroups; ++i)
+    {
+      for(size_t j = 0; j < triangleList[i].size(); j++)
+        delete triangleList[i][j];
+    }
+    delete [] triangleList;
+    
+    delete isoMesh;
+    return NULL;
+  }
+
+  for(int i = 0; i < numGroups-1; ++i)
+  {
+    int compIndex = componentOrder[i];
+    for(int j = numGroups-1; j > i; --j)
+    { 
+      int compIndex2 = componentOrder[j];
+      if(!CheckInAndOutViaBoundingBox(bmin, bmax, compIndex, compIndex2))
+        continue;
+      if(CheckInAndOutViaNormalTest(triangleList, compIndex, compIndex2))
+      {
+        printf("removing group %d from group %d\n", compIndex, compIndex2);
+        removedList.push_back(compIndex);
+        break;
+      }
+    }
+  }
+
+  printf("Removed %d interior groups: ", (int)removedList.size());
+
+  //Add back the zero area face
+  delete isoMesh;
+  isoMesh = cloneMesh;
+  // sort the list because we have to remove groups in a descending order
+  // otherwise ObjMesh::removeGroup will modify mesh group index order
+  std::sort(removedList.begin(), removedList.end());
+
+  for(int i=(int) removedList.size() - 1; i>=0; --i)
+  {
+    printf("%d ", removedList[i]);
+    isoMesh->removeGroup(removedList[i]);
+  }
+  printf("\n");
+
+  for(int i=0; i<numGroups; ++i)
+  {
+    for(size_t j = 0; j < triangleList[i].size(); j++)
+      delete triangleList[i][j];
+  }
+  delete [] triangleList;
+
+  return isoMesh;
+}
+
+ObjMesh * SignedDistanceFieldFromPolygonSoup::ComputeIsosurface(DistanceFieldBase * distanceField, double sigma)
+{
+  printf("Computing the isosurface using marching cubes. Iso-level: %f.\n", sigma);
+  ObjMesh * marchingCubesResult = MarchingCubes::compute(distanceField, sigma);
+  printf("Finishing marching cubes...\n");
+
+  printf("Removing interior components...\n");
+  ObjMesh * outputMesh = marchingCubesResult->splitIntoConnectedComponents(0);
+  printf("Detected %d groups in the isoMesh.\n", (int)outputMesh->getNumGroups());
+  outputMesh = RemoveInteriorComponents(outputMesh);
+
+  delete(marchingCubesResult);
+
+  return outputMesh;
+}
+
+bool SignedDistanceFieldFromPolygonSoup::setBoundingBox(DistanceFieldBase* field, int resolutionX, int resolutionY, int resolutionZ)
+{
+  if (autoBoundingBox)
+  {
+    if (useCubicBox)
+      printf("Forcing a cube-sized bounding box.\n");
+    field->setAutomaticBoundingBox(useCubicBox, expansionRatio);
+    field->computeBoundingBox(objMesh, resolutionX, resolutionY, resolutionZ);
+  }
+  else
+  {
+    if ((bbmin[0] >= bbmax[0]) || (bbmin[1] >= bbmax[1]) || (bbmin[2] >= bbmax[2]))
+    {
+      printf("Invalid bounding box.\n");
+      return false;
+    }
+    field->setBoundingBox(bbmin,bbmax);
+  }
+  return true;
+}
+
+DistanceField * SignedDistanceFieldFromPolygonSoup::ComputeDistanceField(int resolutionX, int resolutionY, int resolutionZ,
+    double sigma, int subtractSigma, bool computeVoronoiDiagram, int maxTriCount, int maxDepth,
+    int closestPointFlag, const char* precomputedUnsignedFieldFilename)
+{
+  DistanceField * field = NULL;
+
+  if (!closestPointFlag)
+  {
+    field = new DistanceField();
+  }
+  else
+    field = new ClosestPointField();
+
+  field->enableVoronoiDiagramComputation(computeVoronoiDiagram);
+
+  bool ret = setBoundingBox(field, resolutionX, resolutionY, resolutionZ);
+  if (ret == false)
+  {
+    delete field;
+    return NULL;
+  }
+
+  int code = 0;
+
+  if (precomputedUnsignedFieldFilename == NULL)
+  {
+    printf("********* Computing unsigned distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+    code = field->computeUnsignedField(objMesh, resolutionX, resolutionY, resolutionZ, maxTriCount, maxDepth);
+
+    if(code !=0)
+    {
+      printf("Error computing unsigned distance field.\n");
+      delete field;
+      return NULL;
+    }
+  }
+  else
+    field->load(precomputedUnsignedFieldFilename);
+
+  fflush(stdout);
+
+  ObjMesh * isoMesh = ComputeIsosurface(field, sigma);
+
+  printf("********* Recomputing signed distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+  field->offsetDistanceField(-sigma);
+  code = field->computeFloodFillSignedField(isoMesh, resolutionX, resolutionY, resolutionZ, maxTriCount, maxDepth);
+  delete isoMesh;
+
+  fflush(stdout);
+
+  if (code != 0)
+  {
+    printf("Error recomputing signed distance field.\n");
+    delete field;
+    return NULL;
+  }
+
+  if (subtractSigma)
+    field->offsetDistanceField(sigma);
+
+  fflush(stdout);
+
+  return field;
+}
+
+DistanceField *SignedDistanceFieldFromPolygonSoup::ComputeDistanceField(const ObjMesh *mesh, DistanceField *field, double sigma, int subtractSigma, 
+  bool computeVoronoiDiagram, int maxTriCount, int maxDepth, int closestPointFlag)
+{
+  field->enableVoronoiDiagramComputation(computeVoronoiDiagram);
+
+  int code = 0;
+
+  ObjMesh * isoMesh = ComputeIsosurface(field, sigma);
+  printf("********* Recomputing signed distance field (%d x %d x %d) *************\n", field->getResolutionX(), field->getResolutionY(), field->getResolutionZ()); 
+  fflush(stdout);
+
+  field->offsetDistanceField(-sigma);
+  code = field->computeFloodFillSignedField(isoMesh, field->getResolutionX(), field->getResolutionY(), field->getResolutionZ(), maxTriCount, maxDepth);
+  delete isoMesh;
+
+  fflush(stdout);
+
+  if (code != 0)
+  {
+    printf("Error recomputing signed distance field.\n");
+    delete field;
+    return NULL;
+  }
+
+  if (subtractSigma)
+    field->offsetDistanceField(sigma);
+  fflush(stdout);
+
+  return field;
+}
+
+DistanceFieldNarrowBand * SignedDistanceFieldFromPolygonSoup::ComputeDistanceFieldNarrowBand(int resolutionX, int resolutionY, int resolutionZ, double bandwidth, 
+  double sigma, int subtractSigma, int maxTriCount, int maxDepth, const char * precomputedUnsignedFieldFilename)
+{
+  DistanceFieldNarrowBand * field = new DistanceFieldNarrowBand();
+
+  bool ret = setBoundingBox(field, resolutionX, resolutionY, resolutionZ);
+  if (ret == false)
+  {
+    delete field;
+    return NULL;
+  }
+
+  int code = 0;
+
+  if (sigma >= bandwidth)
+  {
+    printf("Error: sigma is not smaller than the offset.\n");
+    delete field;
+    return NULL;
+  }
+
+  if (precomputedUnsignedFieldFilename == NULL)
+  {
+    printf("********* Computing unsigned distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+    code = field->computeUnsignedField(objMesh, resolutionX, resolutionY, resolutionZ, (float) bandwidth, maxTriCount, maxDepth);
+
+    if (code !=0)
+    {
+      printf("Error computing unsigned distance field.\n");
+      delete field;
+      return NULL;
+    }
+
+    //printf("Computation completed. Performing sanity check...\n");
+    //field->sanityCheck();
+  }
+  else
+  {
+    code = field->load(precomputedUnsignedFieldFilename);
+    if(code != 0)
+    {
+      printf("Failed to load precomputed unsigned distance field.\n");
+      delete field;
+      return NULL;
+    }
+  }
+
+  ObjMesh * isoMesh = ComputeIsosurface(field, sigma);
+  printf("********* Computing signed distance field (%d x %d x %d) *************\n", resolutionX, resolutionY, resolutionZ); fflush(NULL);
+  field->offsetDistanceField(-sigma); // shift the computed signed distance field
+  code = field->computeInteriorSignedField(isoMesh, resolutionX, resolutionY, resolutionZ, (float) (bandwidth + sigma), maxTriCount, maxDepth);
+  delete isoMesh;
+
+  if (code != 0)
+  {
+    printf("Error recomputing signed distance field.\n");
+    delete field;
+    return NULL;
+  }
+
+  if (subtractSigma)
+    field->offsetDistanceField(sigma);
+
+  return field;
+}
+
diff --git a/libraries/distanceField/signedDistanceFieldFromPolygonSoup.h b/libraries/distanceField/signedDistanceFieldFromPolygonSoup.h
new file mode 100644
index 0000000000000000000000000000000000000000..ddd048e3a7a5db62978b86fb169b613ccf32d86b
--- /dev/null
+++ b/libraries/distanceField/signedDistanceFieldFromPolygonSoup.h
@@ -0,0 +1,112 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This class computes a signed distance field from polygon soup geometry, as described in:
+
+  Hongyi Xu, Jernej Barbic: 
+  Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014, Montreal, Canada
+*/
+
+
+#ifndef _SIGNEDDISTANCEFIELDFROMPOLYGONSOUP_H_
+#define _SIGNEDDISTANCEFIELDFROMPOLYGONSOUP_H_
+
+#include <vector>
+#include "distanceField.h"
+#include "distanceFieldNarrowBand.h"
+#include "closestPointField.h"
+#include "objMesh.h"
+
+class SignedDistanceFieldFromPolygonSoup
+{
+public:
+  // if useCubicBox=0, the bounding box will not be a cube, but the aspect ratios of the bounding box will be set so that voxels are cubes, by following the resolutionX, resolutionY, resolutionZ parameters in the Compute* routines below
+  // if useCubicBox=1, the bounding box will be a cube, but the voxels will not necessarily be cubes
+  SignedDistanceFieldFromPolygonSoup(ObjMesh * objMesh, double expansionRatio = 1.5, bool useCubicBox = 1,
+      Vec3d * bbmin = NULL, Vec3d * bbmax = NULL);
+
+  SignedDistanceFieldFromPolygonSoup() {}
+
+  ~SignedDistanceFieldFromPolygonSoup() {}
+
+  // Compute a signed distance field from polygon soup geometry, as described in:
+  // Hongyi Xu, Jernej Barbic: 
+  // Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014, Montreal, Canada
+  // 
+  // An unsigned field will be created first (or loaded from precomputedUnsignedFieldFilename if not NULL). 
+  // Then, a new mesh is created around the sigma isosurface. Next, a signed field is computed based on this new mesh. Finally, this signed field is offset by -sigma, to approximate the isosurface at the original mesh.
+  // "sigma" is given in absolute units (not grid units)
+  // subtractSigma: whether to subtract sigma from the computed signed distance field (to match the "zero" isosurface) 
+  // computeVoronoiDiagram: whether to compute Voronoi diagram (stored in DistanceField)
+  // closestPointFlag: whether to return a ClosestPointField or not (default: return DistanceField)
+  DistanceField * ComputeDistanceField(int resolutionX, int resolutionY, int resolutionZ, double sigma, int subtractSigma = 1,
+      bool computeVoronoiDiagram = false, int maxTriCount = 15, int maxDepth = 10, int closestPointFlag = 0,
+      const char * precomputedUnsignedFieldFilename = NULL);
+
+  DistanceField * ComputeDistanceField(const ObjMesh *mesh, DistanceField *field, double sigma, int subtractSigma = 1, bool computeVoronoiDiagram = false, int maxTriCount = 15, int maxDepth = 10, int closestPointFlag = 0);
+
+  // Compute a distance field in a narrow band around the geometry
+  // bandwidth: the width of the band (in either direction) around the geometry where the distance field is computed
+  // "bandwidth" and "sigma" are given in absolute units (not grid units)
+  DistanceFieldNarrowBand * ComputeDistanceFieldNarrowBand(int resolutionX, int resolutionY, int resolutionZ, double bandwidth,
+      double sigma, int subtractSigma = 1, int maxTriCount = 15, int maxDepth = 10, const char * precomputedUnsignedFieldFilename = NULL);
+
+protected:
+  ObjMesh * objMesh;
+  DistanceField *unsignedField;
+
+  double expansionRatio;
+  bool useCubicBox;
+  Vec3d bbmin, bbmax;
+  bool autoBoundingBox;
+
+  //remove the interior components inside the isosurface 
+  ObjMesh * RemoveInteriorComponents(ObjMesh * isoMesh);
+
+  //construct the pseudo-normal for each component for pseudo-normal test
+  bool ConstructPseudoNormalComponents(ObjMesh * isoMesh, std::vector<TriangleWithCollisionInfoAndPseudoNormals*>* triangleList);
+
+  //pseudo-normal test to determine the relation between two components
+  bool CheckInAndOutViaNormalTest(std::vector<TriangleWithCollisionInfoAndPseudoNormals*>* triangleList, int compIndex1, int compIndex2);
+
+  //pre-removal via bounding box test. If a bounding box is completely inside another bounding box, the smaller one can be removed.
+  bool CheckInAndOutViaBoundingBox(std::vector<Vec3d> & bmin, std::vector<Vec3d> & bmax, int compIndex1, int compIndex2);
+
+  //extract isosurface using marching cubes from unsigned distance field. 
+  ObjMesh * ComputeIsosurface(DistanceFieldBase * distanceField, double sigma);
+
+  bool setBoundingBox(DistanceFieldBase* field, int resolutionX, int resolutionY, int resolutionZ);
+};
+
+#endif
+
diff --git a/libraries/distanceField/trilinearInterpolation.h b/libraries/distanceField/trilinearInterpolation.h
new file mode 100644
index 0000000000000000000000000000000000000000..4acd61265a7749e158242df4fc00b2cbcfb40fa8
--- /dev/null
+++ b/libraries/distanceField/trilinearInterpolation.h
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Trilinear interpolation.
+*/
+
+#define TRILINEAR_INTERPOLATION(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) \
+( (wx) * (wy) * (wz) *             (v111) + \
+  (wx) * (wy) * (1-(wz)) *         (v110) + \
+  (wx) * (1-(wy)) * (wz) *         (v101) + \
+  (wx) * (1-(wy)) * (1-(wz)) *     (v100) + \
+  (1-(wx)) * (wy) * (wz) *         (v011) + \
+  (1-(wx)) * (wy) * (1-(wz)) *     (v010) + \
+  (1-(wx)) * (1-(wy)) * (wz) *     (v001) + \
+  (1-(wx)) * (1-(wy)) * (1-(wz)) * (v000))
+
+#define GRADIENT_COMPONENT_X(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) \
+  (((wy) * (wz) *             (v111) + \
+    (wy) * (1-(wz)) *         (v110) + \
+    (1-(wy)) * (wz) *         (v101) + \
+    (1-(wy)) * (1-(wz)) *     (v100) + \
+    (-1) * (wy) * (wz) *         (v011) + \
+    (-1) * (wy) * (1-(wz)) *     (v010) + \
+    (-1) * (1-(wy)) * (wz) *     (v001) + \
+    (-1) * (1-(wy)) * (1-(wz)) * (v000) ) / gridX)
+
+#define GRADIENT_COMPONENT_Y(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) \
+  (((wx) * (wz) *             (v111) + \
+    (wx) * (1-(wz)) *         (v110) + \
+    (wx) * (-1) * (wz) *         (v101) + \
+    (wx) * (-1) * (1-(wz)) *     (v100) + \
+    (1-(wx)) * (wz) *         (v011) + \
+    (1-(wx)) * (1-(wz)) *     (v010) + \
+    (1-(wx)) * (-1) * (wz) *     (v001) + \
+    (1-(wx)) * (-1) * (1-(wz)) * (v000)) / gridY)
+  
+#define GRADIENT_COMPONENT_Z(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) \
+  (((wx) * (wy) *                (v111) + \
+    (wx) * (wy) * (-1) *         (v110) + \
+    (wx) * (1-(wy)) *            (v101) + \
+    (wx) * (1-(wy)) * (-1) *     (v100) + \
+    (1-(wx)) * (wy) *            (v011) + \
+    (1-(wx)) * (wy) * (-1) *     (v010) + \
+    (1-(wx)) * (1-(wy)) *        (v001) + \
+    (1-(wx)) * (1-(wy)) * (-1) * (v000)) / gridZ)
+
diff --git a/src/libelasticForceModel/StVKForceModel.cpp b/libraries/elasticForceModel/StVKForceModel.cpp
similarity index 74%
rename from src/libelasticForceModel/StVKForceModel.cpp
rename to libraries/elasticForceModel/StVKForceModel.cpp
index e4d12361ea1325cdbff9e94c4396a92821ae470a..46f27a511464344b76ca47caea297d5db1840d38 100644
--- a/src/libelasticForceModel/StVKForceModel.cpp
+++ b/libraries/elasticForceModel/StVKForceModel.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,8 +32,6 @@
 
 #include "StVKForceModel.h"
 
-namespace vega
-{
 StVKForceModel::StVKForceModel(StVKInternalForces * stVKInternalForces_, StVKStiffnessMatrix * stVKStiffnessMatrix_): stVKInternalForces(stVKInternalForces_), stVKStiffnessMatrix(stVKStiffnessMatrix_)
 {
   r = 3 * stVKInternalForces->GetVolumetricMesh()->getNumVertices();
@@ -47,7 +49,12 @@ StVKForceModel::~StVKForceModel()
     delete(stVKStiffnessMatrix);
 }
 
-void StVKForceModel::GetInternalForce(double * u, double * internalForces)
+double StVKForceModel::GetElasticEnergy(const double * u)
+{
+  return stVKInternalForces->ComputeEnergy(u);
+}
+
+void StVKForceModel::GetInternalForce(const double * u, double * internalForces)
 {
   stVKInternalForces->ComputeForces(u, internalForces);
 }
@@ -57,9 +64,8 @@ void StVKForceModel::GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentSt
   stVKStiffnessMatrix->GetStiffnessMatrixTopology(tangentStiffnessMatrix);
 }
 
-void StVKForceModel::GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix)
+void StVKForceModel::GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix)
 {
   stVKStiffnessMatrix->ComputeStiffnessMatrix(u, tangentStiffnessMatrix);
 } 
 
-}
diff --git a/src/libelasticForceModel/StVKForceModel.h b/libraries/elasticForceModel/StVKForceModel.h
similarity index 70%
rename from src/libelasticForceModel/StVKForceModel.h
rename to libraries/elasticForceModel/StVKForceModel.h
index b8d9aac307798aadb0d8aa98677feb4e681afe19..09619a79b66254e7e1b543daa173dc97b4a9774f 100644
--- a/src/libelasticForceModel/StVKForceModel.h
+++ b/libraries/elasticForceModel/StVKForceModel.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,23 +41,22 @@
 #include "StVKStiffnessMatrix.h"
 #include "forceModel.h"
 
-namespace vega
-{
-class StVKForceModel : virtual public ForceModel
+class StVKForceModel : public ForceModel
 {
 public:
   StVKForceModel(StVKInternalForces * stVKInternalForces, StVKStiffnessMatrix * stVKStiffnessMatrix=NULL);
   virtual ~StVKForceModel(); 
 
-  virtual void GetInternalForce(double * u, double * internalForces);
-  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix);
-  virtual void GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix); 
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
 
 protected:
   StVKInternalForces * stVKInternalForces;
   StVKStiffnessMatrix * stVKStiffnessMatrix;
   bool ownStiffnessMatrix;
 };
-}
+
 #endif
 
diff --git a/libraries/elasticForceModel/clothBWForceModel.cpp b/libraries/elasticForceModel/clothBWForceModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..07196073dfecce16817f3f7f8d00324b92e0b1a6
--- /dev/null
+++ b/libraries/elasticForceModel/clothBWForceModel.cpp
@@ -0,0 +1,67 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "clothBWForceModel.h"
+
+ClothBWForceModel::ClothBWForceModel(ClothBW * clothBW_): clothBW(clothBW_)
+{ 
+  r = 3 * clothBW->GetNumVertices(); 
+}
+
+ClothBWForceModel::~ClothBWForceModel()
+{
+}
+
+double ClothBWForceModel::GetElasticEnergy(const double * u)
+{
+  return clothBW->ComputeEnergy(u);
+}
+
+void ClothBWForceModel::GetInternalForce(const double * u, double * internalForces)
+{
+  clothBW->ComputeForce(u, internalForces);
+}
+
+void ClothBWForceModel::GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix)
+{
+  clothBW->GenerateStiffnessMatrixTopology(tangentStiffnessMatrix);
+}
+
+void ClothBWForceModel::GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix)
+{
+  clothBW->ComputeStiffnessMatrix(u, tangentStiffnessMatrix);
+} 
+
+void ClothBWForceModel::GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
+{
+  clothBW->ComputeForceAndMatrix(u, internalForces, tangentStiffnessMatrix);
+}
diff --git a/libraries/elasticForceModel/clothBWForceModel.h b/libraries/elasticForceModel/clothBWForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..67cd6bc069699ace00cf9e4ab631fcae63d4410f
--- /dev/null
+++ b/libraries/elasticForceModel/clothBWForceModel.h
@@ -0,0 +1,59 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Cloth model from [Baraff Witkin 1998] 
+*/
+
+#ifndef _CLOTHBWFORCEMODEL_H_
+#define _CLOTHBWFORCEMODEL_H_
+
+#include "clothBW.h"
+#include "forceModel.h"
+
+class ClothBWForceModel : public ForceModel
+{
+public:
+  ClothBWForceModel(ClothBW * clothBW);
+  virtual ~ClothBWForceModel();
+  
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix) override;
+protected:
+  ClothBW * clothBW;
+};
+
+#endif
+
diff --git a/src/libelasticForceModel/corotationalLinearFEMForceModel.cpp b/libraries/elasticForceModel/corotationalLinearFEMForceModel.cpp
similarity index 59%
rename from src/libelasticForceModel/corotationalLinearFEMForceModel.cpp
rename to libraries/elasticForceModel/corotationalLinearFEMForceModel.cpp
index 462a7d323a8b9823474cda601b0533edeb2050e8..d5b0a630f8cc59639ae3d0cceaf66b35d8931205 100644
--- a/src/libelasticForceModel/corotationalLinearFEMForceModel.cpp
+++ b/libraries/elasticForceModel/corotationalLinearFEMForceModel.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,18 +32,23 @@
 
 #include "corotationalLinearFEMForceModel.h"
 
-namespace vega
-{
 CorotationalLinearFEMForceModel::CorotationalLinearFEMForceModel(CorotationalLinearFEM * corotationalLinearFEM_, int warp_): corotationalLinearFEM(corotationalLinearFEM_), warp(warp_)
 {
-  r = 3 * corotationalLinearFEM->GetTetMesh()->getNumVertices();
+  r = 3 * corotationalLinearFEM->GetVolumetricMesh()->getNumVertices();
 }
 
 CorotationalLinearFEMForceModel::~CorotationalLinearFEMForceModel() {}
 
-void CorotationalLinearFEMForceModel::GetInternalForce(double * u, double * internalForces)
+double CorotationalLinearFEMForceModel::GetElasticEnergy(const double * u)
 {
-  corotationalLinearFEM->ComputeForceAndStiffnessMatrix(u, internalForces, NULL, warp);
+  double energy = 0.0;
+  corotationalLinearFEM->ComputeEnergyAndForceAndStiffnessMatrix(u, &energy, NULL, NULL, warp);
+  return energy;
+}
+
+void CorotationalLinearFEMForceModel::GetInternalForce(const double * u, double * internalForces)
+{
+  corotationalLinearFEM->ComputeEnergyAndForceAndStiffnessMatrix(u, NULL, internalForces, NULL, warp);
 }
 
 void CorotationalLinearFEMForceModel::GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix)
@@ -47,14 +56,13 @@ void CorotationalLinearFEMForceModel::GetTangentStiffnessMatrixTopology(SparseMa
   corotationalLinearFEM->GetStiffnessMatrixTopology(tangentStiffnessMatrix);
 }
 
-void CorotationalLinearFEMForceModel::GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix)
+void CorotationalLinearFEMForceModel::GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix)
 {
-  corotationalLinearFEM->ComputeForceAndStiffnessMatrix(u, NULL, tangentStiffnessMatrix, warp);
+  corotationalLinearFEM->ComputeEnergyAndForceAndStiffnessMatrix(u, NULL, NULL, tangentStiffnessMatrix, warp);
 } 
 
-void CorotationalLinearFEMForceModel::GetForceAndMatrix(double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
+void CorotationalLinearFEMForceModel::GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
 {
-  corotationalLinearFEM->ComputeForceAndStiffnessMatrix(u, internalForces, tangentStiffnessMatrix, warp);
+  corotationalLinearFEM->ComputeEnergyAndForceAndStiffnessMatrix(u, NULL, internalForces, tangentStiffnessMatrix, warp);
 }
 
-}
diff --git a/src/libelasticForceModel/corotationalLinearFEMForceModel.h b/libraries/elasticForceModel/corotationalLinearFEMForceModel.h
similarity index 65%
rename from src/libelasticForceModel/corotationalLinearFEMForceModel.h
rename to libraries/elasticForceModel/corotationalLinearFEMForceModel.h
index 706f65e7864065c68fa8b12e76dea611bff58817..e7f9d2a6c81d6d05af07f395f69b5ae83dd71626 100644
--- a/src/libelasticForceModel/corotationalLinearFEMForceModel.h
+++ b/libraries/elasticForceModel/corotationalLinearFEMForceModel.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,26 +40,27 @@
 #include "corotationalLinearFEM.h"
 #include "forceModel.h"
 
-namespace vega
-{
-class CorotationalLinearFEMForceModel : virtual public ForceModel
+class CorotationalLinearFEMForceModel : public ForceModel
 {
 public:
   CorotationalLinearFEMForceModel(CorotationalLinearFEM * corotationalLinearFEM, int warp=1);
   virtual ~CorotationalLinearFEMForceModel(); 
 
-  virtual void GetInternalForce(double * u, double * internalForces);
-  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix);
-  virtual void GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix); 
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
 
-  virtual void GetForceAndMatrix(double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix) override;
 
   inline void SetWarp(int warp) { this->warp = warp; }
+  inline int GetWarp() { return warp; }
+  static inline int GetMaxWarp() { return 2; }
 
 protected:
   CorotationalLinearFEM * corotationalLinearFEM;
   int warp;
 };
-}
+
 #endif
 
diff --git a/src/libelasticForceModel/isotropicHyperelasticFEMForceModel.cpp b/libraries/elasticForceModel/isotropicHyperelasticFEMForceModel.cpp
similarity index 71%
rename from src/libelasticForceModel/isotropicHyperelasticFEMForceModel.cpp
rename to libraries/elasticForceModel/isotropicHyperelasticFEMForceModel.cpp
index 257ff2f2581e37f46a7e59280fd876d48e211f6b..b12dbae10caa3c061b18ac759debb3ab2b61fd65 100644
--- a/src/libelasticForceModel/isotropicHyperelasticFEMForceModel.cpp
+++ b/libraries/elasticForceModel/isotropicHyperelasticFEMForceModel.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,8 +32,6 @@
 
 #include "isotropicHyperelasticFEMForceModel.h"
 
-namespace vega
-{
 IsotropicHyperelasticFEMForceModel::IsotropicHyperelasticFEMForceModel(IsotropicHyperelasticFEM * isotropicHyperelasticFEM_): isotropicHyperelasticFEM(isotropicHyperelasticFEM_)
 {
   r = 3 * isotropicHyperelasticFEM->GetTetMesh()->getNumVertices();
@@ -37,7 +39,12 @@ IsotropicHyperelasticFEMForceModel::IsotropicHyperelasticFEMForceModel(Isotropic
 
 IsotropicHyperelasticFEMForceModel::~IsotropicHyperelasticFEMForceModel() {}
 
-void IsotropicHyperelasticFEMForceModel::GetInternalForce(double * u, double * internalForces)
+double IsotropicHyperelasticFEMForceModel::GetElasticEnergy(const double * u)
+{
+  return isotropicHyperelasticFEM->ComputeEnergy(u);
+}
+
+void IsotropicHyperelasticFEMForceModel::GetInternalForce(const double * u, double * internalForces)
 {
   isotropicHyperelasticFEM->ComputeForces(u, internalForces);
 }
@@ -47,14 +54,13 @@ void IsotropicHyperelasticFEMForceModel::GetTangentStiffnessMatrixTopology(Spars
   isotropicHyperelasticFEM->GetStiffnessMatrixTopology(tangentStiffnessMatrix);
 }
 
-void IsotropicHyperelasticFEMForceModel::GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix)
+void IsotropicHyperelasticFEMForceModel::GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix)
 {
   isotropicHyperelasticFEM->GetTangentStiffnessMatrix(u, tangentStiffnessMatrix);
 } 
 
-void IsotropicHyperelasticFEMForceModel::GetForceAndMatrix(double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
+void IsotropicHyperelasticFEMForceModel::GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
 {
   isotropicHyperelasticFEM->GetForceAndTangentStiffnessMatrix(u, internalForces, tangentStiffnessMatrix);
 }
 
-}
diff --git a/src/libelasticForceModel/isotropicHyperelasticFEMForceModel.h b/libraries/elasticForceModel/isotropicHyperelasticFEMForceModel.h
similarity index 67%
rename from src/libelasticForceModel/isotropicHyperelasticFEMForceModel.h
rename to libraries/elasticForceModel/isotropicHyperelasticFEMForceModel.h
index 3bccc0b1a85fd66425535fbf755cf978af7e3ecd..fdcaa74651e01328106f533cf803d0268df03639 100644
--- a/src/libelasticForceModel/isotropicHyperelasticFEMForceModel.h
+++ b/libraries/elasticForceModel/isotropicHyperelasticFEMForceModel.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,23 +40,22 @@
 #include "isotropicHyperelasticFEM.h"
 #include "forceModel.h"
 
-namespace vega
-{
-class IsotropicHyperelasticFEMForceModel : virtual public ForceModel
+class IsotropicHyperelasticFEMForceModel : public ForceModel
 {
 public:
   IsotropicHyperelasticFEMForceModel(IsotropicHyperelasticFEM * isotropicHyperelasticFEM);
   virtual ~IsotropicHyperelasticFEMForceModel(); 
 
-  virtual void GetInternalForce(double * u, double * internalForces);
-  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix);
-  virtual void GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix); 
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
 
-  virtual void GetForceAndMatrix(double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix) override;
 
 protected:
   IsotropicHyperelasticFEM * isotropicHyperelasticFEM;
 };
-}
+
 #endif
 
diff --git a/src/libelasticForceModel/linearFEMForceModel.cpp b/libraries/elasticForceModel/linearFEMForceModel.cpp
similarity index 72%
rename from src/libelasticForceModel/linearFEMForceModel.cpp
rename to libraries/elasticForceModel/linearFEMForceModel.cpp
index de42591a5929cddea03c69656b81e77a7ca5f750..d83c7c52c6274ef2a53cf5fa00fc7e09fe7981c7 100644
--- a/src/libelasticForceModel/linearFEMForceModel.cpp
+++ b/libraries/elasticForceModel/linearFEMForceModel.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,12 +33,12 @@
 #include "linearFEMForceModel.h"
 #include "StVKStiffnessMatrix.h"
 
-namespace vega
-{
 LinearFEMForceModel::LinearFEMForceModel(StVKInternalForces * stVKInternalForces) 
 {
   StVKStiffnessMatrix * stVKStiffnessMatrix = new StVKStiffnessMatrix(stVKInternalForces);
   stVKStiffnessMatrix->GetStiffnessMatrixTopology(&K);
+  r = K->GetNumRows();
+
   double * zero = (double*) calloc (K->GetNumRows(), sizeof(double));
   stVKStiffnessMatrix->ComputeStiffnessMatrix(zero, K);
   free(zero);
@@ -46,7 +50,12 @@ LinearFEMForceModel::~LinearFEMForceModel()
   delete(K);
 }
 
-void LinearFEMForceModel::GetInternalForce(double * u, double * internalForces)
+double LinearFEMForceModel::GetElasticEnergy(const double * u)
+{
+  return 0.5 * K->QuadraticForm(u);
+}
+
+void LinearFEMForceModel::GetInternalForce(const double * u, double * internalForces)
 {
   K->MultiplyVector(u, internalForces);
 }
@@ -56,9 +65,8 @@ void LinearFEMForceModel::GetTangentStiffnessMatrixTopology(SparseMatrix ** tang
   *tangentStiffnessMatrix = new SparseMatrix(*K);
 }
 
-void LinearFEMForceModel::GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix)
+void LinearFEMForceModel::GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix)
 {
   *tangentStiffnessMatrix = *K;
-}
+} 
 
-}
diff --git a/src/libelasticForceModel/linearFEMForceModel.h b/libraries/elasticForceModel/linearFEMForceModel.h
similarity index 71%
rename from src/libelasticForceModel/linearFEMForceModel.h
rename to libraries/elasticForceModel/linearFEMForceModel.h
index 84a52c5564d3273be715e744fb2256db0d673357..31112e526ea2c328336c5e7ba7c2e4f12e37de54 100644
--- a/src/libelasticForceModel/linearFEMForceModel.h
+++ b/libraries/elasticForceModel/linearFEMForceModel.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -38,21 +42,20 @@
 #include "StVKInternalForces.h"
 #include "forceModel.h"
 
-namespace vega
-{
 class LinearFEMForceModel : public ForceModel
 {
 public:
   LinearFEMForceModel(StVKInternalForces * stVKInternalForces);
   virtual ~LinearFEMForceModel(); 
 
-  virtual void GetInternalForce(double * u, double * internalForces);
-  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix);
-  virtual void GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix); 
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
 
 protected:
   SparseMatrix * K;
 };
-}
+
 #endif
 
diff --git a/src/libelasticForceModel/massSpringSystemForceModel.cpp b/libraries/elasticForceModel/massSpringSystemForceModel.cpp
similarity index 70%
rename from src/libelasticForceModel/massSpringSystemForceModel.cpp
rename to libraries/elasticForceModel/massSpringSystemForceModel.cpp
index 8d73deaec6234d5e0e43b25ec888fee7c76c0a20..af47103358e8c131419ae004401901282ccefb0f 100644
--- a/src/libelasticForceModel/massSpringSystemForceModel.cpp
+++ b/libraries/elasticForceModel/massSpringSystemForceModel.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,14 +32,17 @@
 
 #include "massSpringSystemForceModel.h"
 
-namespace vega
-{
 MassSpringSystemForceModel::MassSpringSystemForceModel(MassSpringSystem * massSpringSystem_): massSpringSystem(massSpringSystem_)
 { 
   r = 3 * massSpringSystem->GetNumParticles(); 
 }
 
-void MassSpringSystemForceModel::GetInternalForce(double * u, double * internalForces)
+double MassSpringSystemForceModel::GetElasticEnergy(const double * u)
+{
+  return massSpringSystem->ComputeEnergy(u);
+}
+
+void MassSpringSystemForceModel::GetInternalForce(const double * u, double * internalForces)
 {
   massSpringSystem->ComputeForce(u, internalForces);
 }
@@ -45,9 +52,8 @@ void MassSpringSystemForceModel::GetTangentStiffnessMatrixTopology(SparseMatrix
   massSpringSystem->GetStiffnessMatrixTopology(tangentStiffnessMatrix);
 }
 
-void MassSpringSystemForceModel::GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix)
+void MassSpringSystemForceModel::GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix)
 {
   massSpringSystem->ComputeStiffnessMatrix(u, tangentStiffnessMatrix);
 } 
 
-}
diff --git a/src/libelasticForceModel/massSpringSystemForceModel.h b/libraries/elasticForceModel/massSpringSystemForceModel.h
similarity index 68%
rename from src/libelasticForceModel/massSpringSystemForceModel.h
rename to libraries/elasticForceModel/massSpringSystemForceModel.h
index f8450c6939c24ac9120dca6eace13cd461dbd2f3..4d5a6aa07adadc18efb3a90f070501ef667660d1 100644
--- a/src/libelasticForceModel/massSpringSystemForceModel.h
+++ b/libraries/elasticForceModel/massSpringSystemForceModel.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,20 +40,19 @@
 #include "massSpringSystem.h"
 #include "forceModel.h"
 
-namespace vega
-{
-class MassSpringSystemForceModel : public virtual ForceModel
+class MassSpringSystemForceModel : public ForceModel
 {
 public:
   MassSpringSystemForceModel(MassSpringSystem * massSpringSystem);
 
-  virtual void GetInternalForce(double * u, double * internalForces);
-  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix);
-  virtual void GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix); 
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
 
 protected:
   MassSpringSystem * massSpringSystem;
 };
-}
+
 #endif
 
diff --git a/libraries/exactArithmetic/er.cpp b/libraries/exactArithmetic/er.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bc6df969866ec437c276afae89ac56d9283982f2
--- /dev/null
+++ b/libraries/exactArithmetic/er.cpp
@@ -0,0 +1,223 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "exactArithmetic" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "er.h"
+
+#ifndef VEGA_USE_CGAL_HEADER
+
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/intersections.h>
+
+typedef CGAL::Exact_predicates_exact_constructions_kernel K;
+
+namespace
+{
+//  const K::RT & RT(const void * p) {
+//    return *(const K::RT*)p;
+//  }
+  K::RT & RT(void * p) {
+    return *(K::RT*)p;
+  }
+}
+
+ER::ER(double s)
+{
+  p = new K::RT(s);
+}
+
+ER::ER(const ER & s)
+{
+  p = new K::RT(RT(s.p));
+}
+
+ER::ER(ER && s)
+{
+  p = s.p;
+  s.p = new K::RT(0.0);
+}
+
+ER::~ER() { delete (K::RT*)p; }
+
+ER & ER::operator = (const ER & s)
+{
+  RT(p) = RT(s.p);
+  return *this;
+}
+
+ER & ER::operator = (ER && s)
+{
+  p = s.p;
+  s.p = new K::RT(0.0);
+  return *this;
+}
+
+ER ER::operator + (const ER & s) const
+{
+  UninitializedLabel l;
+  ER ret(l);
+  ret.p = new K::RT(RT(p) + RT(s.p));
+  return ret;
+}
+
+ER ER::operator - (const ER & s) const
+{
+  UninitializedLabel l;
+  ER ret(l);
+  ret.p = new K::RT(RT(p) - RT(s.p));
+  return ret;
+}
+
+ER ER::operator * (const ER & s) const
+{
+  UninitializedLabel l;
+  ER ret(l);
+  ret.p = new K::RT(RT(p) * RT(s.p));
+  return ret;
+}
+
+ER ER::operator / (const ER & s) const
+{
+  UninitializedLabel l;
+  ER ret(l);
+  ret.p = new K::RT(RT(p) / RT(s.p));
+  return ret;
+}
+
+ER ER::operator - () const
+{
+  UninitializedLabel l;
+  ER ret(l);
+  ret.p = new K::RT(-RT(p));
+  return ret;
+}
+
+bool ER::operator == (const ER & s) const
+{
+  return RT(p) == RT(s.p);
+}
+
+bool ER::operator != (const ER & s) const
+{
+  return RT(p) != RT(s.p);
+}
+
+bool ER::operator <= (const ER & s) const
+{
+  return RT(p) <= RT(s.p);
+}
+
+bool ER::operator < (const ER & s) const
+{
+  return RT(p) < RT(s.p);
+}
+
+bool ER::operator >= (const ER & s) const
+{
+  return RT(p) >= RT(s.p);
+}
+
+bool ER::operator > (const ER & s) const
+{
+  return RT(p) > RT(s.p);
+}
+
+ER & ER::operator += (const ER & s)
+{
+  RT(p) += RT(s.p);
+  return *this;
+}
+
+ER & ER::operator -= (const ER & s)
+{
+  RT(p) -= RT(s.p);
+  return *this;
+}
+
+ER & ER::operator *= (const ER & s)
+{
+  RT(p) *= RT(s.p);
+  return *this;
+}
+
+ER & ER::operator /= (const ER & s)
+{
+  RT(p) /= RT(s.p);
+  return *this;
+}
+
+
+ER ER::abs() const
+{
+  UninitializedLabel l;
+  ER ret(l);
+  ret.p = new K::RT(CGAL::abs(RT(p)));
+  return ret;
+}
+
+char ER::sign() const
+{
+  auto s = CGAL::sign(RT(p));
+  static_assert((char)CGAL::POSITIVE == 1, "Error: CGAL::POSITIVE is not 1");
+  static_assert((char)CGAL::NEGATIVE == -1, "Error: CGAL::NEGATIVE is not -1");
+  static_assert((char)CGAL::ZERO == 0, "Error: CGAL::ZERO is not 0");
+  return (char)s;
+}
+
+double ER::toDouble() const
+{
+  // following code from libigl
+  // FORCE evaluation of the exact type otherwise interval might be huge.
+  const auto cgal = RT(p).exact();
+  const auto interval = CGAL::to_interval(cgal);
+  double d = interval.first;
+  do {
+      const double next = nextafter(d, interval.second);
+      if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next)) break;
+      d = next;
+  } while (d < interval.second);
+  return d;
+}
+
+template <class CGAL_Kernel_RT>
+ER assignCGALToER(const CGAL_Kernel_RT & cgal)
+{
+  ER::UninitializedLabel l;
+  ER ret(l);
+  ret.p = new K::RT(cgal);
+  return ret;
+}
+
+template ER assignCGALToER<K::RT>(const K::RT & cgal);
+
+#endif
+
+
diff --git a/libraries/exactArithmetic/er.h b/libraries/exactArithmetic/er.h
new file mode 100644
index 0000000000000000000000000000000000000000..65e480131f7c4733479261023e65481504187abd
--- /dev/null
+++ b/libraries/exactArithmetic/er.h
@@ -0,0 +1,189 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "exactArithmetic" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef CGALKERNELEXACT_H_
+#define CGALKERNELEXACT_H_
+
+#include <iostream>
+
+#define VEGA_USE_CGAL_HEADER
+//#define VEGA_ER_INHERIT_FROM_CGAL
+
+// A exact arithmetic real number
+// implemented using CGAL's exact kernel without sqrt
+// the benefit is that the code that relies on it won't be affected by the sloooooow CGAL header compilation
+
+#ifdef VEGA_USE_CGAL_HEADER
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+
+
+#ifdef VEGA_ER_INHERIT_FROM_CGAL
+
+class Vec3ER;
+class ER : public CGAL::Exact_predicates_exact_constructions_kernel::RT
+{
+public:
+  typedef CGAL::Exact_predicates_exact_constructions_kernel K;
+  typedef K::RT S;
+
+  ER(double s = 0.0) : S(s) {}
+  ER(const ER & o) : S(o) {}
+  ER(ER && o) : S(move(o)) {}
+  ER(const S & s) : S(s) {}
+  ~ER() {}
+
+  ER & operator = (const ER & o) { S::operator =(o); return *this; }
+  ER & operator = (ER && o) { S::operator =(move(o)); return *this; }
+
+  // arithmetic operations
+  ER operator + (const ER & o) const { return *(const S *)this + *(const S *)&o; }
+  ER operator - (const ER & o) const { return *(const S *)this - *(const S *)&o; }
+  ER operator * (const ER & o) const { return *(const S *)this * *(const S *)&o; }
+  ER operator / (const ER & o) const { return *(const S *)this / *(const S *)&o; }
+  ER operator - () const { return -*(const S *)this; }
+
+  ER & operator += (const ER & o) { S::operator+=(o); return *this; }
+  ER & operator -= (const ER & o) { S::operator-=(o); return *this; }
+  ER & operator *= (const ER & o) { S::operator*=(o); return *this; }
+  ER & operator /= (const ER & o) { S::operator/=(o); return *this; }
+
+  // absolute
+  ER abs() const { return CGAL::abs(*(const S *)this); }
+  // return {-1, 0, +1}
+  char sign() const { return char(CGAL::sign(*(const S*)this)); }
+
+  friend double ER_toDouble(const ER & er);
+  double toDouble() const { return ER_toDouble(*this); }
+
+protected:
+  friend class Vec3ER;
+  template <class CGAL_Kernel_Vector_3>
+  friend Vec3ER assignCGALToVec3ER(const CGAL_Kernel_Vector_3 & cgal);
+  friend bool intersectTriTri(const Vec3ER & pa, const Vec3ER & pb, const Vec3ER & pc, const Vec3ER & qa, const Vec3ER & qb, const Vec3ER & qc);
+};
+
+inline ER ER_abs(const ER & er)
+{
+  return er.abs();
+}
+
+inline char ER_sign(const ER & er)
+{
+  return er.sign();
+}
+
+
+#else // VEGA_ER_INHERIT_FROM_CGAL
+
+typedef CGAL::Exact_predicates_exact_constructions_kernel::RT ER;
+inline ER ER_abs(const ER & er)
+{
+  return CGAL::abs(er);
+}
+
+inline char ER_sign(const ER & er)
+{
+  return char(CGAL::sign(er));;
+}
+
+#endif
+
+double ER_toDouble(const ER & er);
+
+template <class CGAL_Kernel_RT>
+ER assignCGALToER(const CGAL_Kernel_RT & cgal) { return cgal; }
+
+
+#else
+class ER
+{
+public:
+  ER(double s = 0.0);
+  ER(const ER &);
+  ER(ER && );
+  ~ER();
+
+  ER & operator = (const ER &);
+  ER & operator = (ER &&);
+
+  // arithmetic operations
+  ER operator + (const ER &) const;
+  ER operator - (const ER &) const;
+  ER operator * (const ER &) const;
+  ER operator / (const ER &) const;
+  ER operator - () const;
+
+  // comparison
+  bool operator == (const ER &) const;
+  bool operator != (const ER &) const;
+  bool operator <= (const ER &) const;
+  bool operator <  (const ER &) const;
+  bool operator >= (const ER &) const;
+  bool operator >  (const ER &) const;
+
+  // arithmetic updates
+  ER & operator += (const ER &);
+  ER & operator -= (const ER &);
+  ER & operator *= (const ER &);
+  ER & operator /= (const ER &);
+
+  // absolute
+  ER abs() const;
+  char sign() const; // return {-1, 0, +1}
+
+  // conversion
+  double toDouble() const;
+
+  template <class CGAL_Kernel_RT>
+  friend ER assignCGALToER(const CGAL_Kernel_RT & cgal);
+
+  struct UninitializedLabel {}; // used for constructing a S with uninitialized data for performance
+  ER(UninitializedLabel) {}
+
+protected:
+  void * p = nullptr;
+
+  friend class Vec3ER;
+  template <class CGAL_Kernel_Vector_3>
+  friend Vec3ER assignCGALToVec3ER(const CGAL_Kernel_Vector_3 & cgal);
+  friend bool intersectTriTri(const Vec3ER & pa, const Vec3ER & pb, const Vec3ER & pc, const Vec3ER & qa, const Vec3ER & qb, const Vec3ER & qc);
+};
+
+
+
+// used to convert CGAL Exact kernel scalar to ES without the need to include CGAL headers
+template <class CGAL_Kernel_RT>
+ER assignCGALToER(const CGAL_Kernel_RT & cgal);
+
+#endif
+
+#endif
diff --git a/libraries/exactArithmetic/er_toDouble.cpp b/libraries/exactArithmetic/er_toDouble.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b926eacb95402df1dafd6f2c3de30219c14cc81e
--- /dev/null
+++ b/libraries/exactArithmetic/er_toDouble.cpp
@@ -0,0 +1,34 @@
+// This file is modified from libigl, a simple c++ geometry processing library.
+// libigl is under the Mozilla Public License v. 2.0.
+// According to the license restriction, we release this code including the changes
+// we made to libigl code under the same license.
+// 
+// Copyright of our changes: (C) 2018 USC
+// Code authors of our changes: Yijing Li, Jernej Barbic 
+// 
+// This Source Code Form is subject to the terms of the Mozilla Public License 
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
+// obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "er.h"
+
+#ifdef VEGA_USE_CGAL_HEADER
+
+double ER_toDouble(const ER & er)
+{
+  // following code from libigl
+  // FORCE evaluation of the exact type otherwise interval might be huge.
+  const auto cgal = er.exact();
+  const auto interval = CGAL::to_interval(cgal);
+  double d = interval.first;
+  do
+  {
+    const double next = nextafter(d, interval.second);
+    if (CGAL::abs(cgal-d) < CGAL::abs(cgal-next))
+      break;
+    d = next;
+  } while (d < interval.second);
+  return d;
+}
+
+#endif
diff --git a/libraries/exactArithmetic/planeER.cpp b/libraries/exactArithmetic/planeER.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a88dc0dcb5ef90e6b8e5f38959c2bd4a0f7ac53e
--- /dev/null
+++ b/libraries/exactArithmetic/planeER.cpp
@@ -0,0 +1,49 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "exactArithmetic" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "planeER.h"
+
+FastPlaneER::FastPlaneER(const Vec3ER & v0, const Vec3ER & v1, const Vec3ER & v2)
+{
+  Vec3ER e1 = v1 - v0, e2 = v2 - v0;
+  dir = cross(e1, e2);
+  d = dot(v0, dir);
+}
+
+int FastPlaneER::outside(const Vec3ER & v) const {
+  auto s = ER_sign(dot(v,dir) - d);
+  return s;
+}
+
+ER FastPlaneER::scaledDistance(const Vec3ER & v) const {
+  return ER_abs(dot(v,dir) - d);
+}
diff --git a/libraries/exactArithmetic/planeER.h b/libraries/exactArithmetic/planeER.h
new file mode 100644
index 0000000000000000000000000000000000000000..e050ad8af7cb51527dfc56a284dfbbe1f2d056bd
--- /dev/null
+++ b/libraries/exactArithmetic/planeER.h
@@ -0,0 +1,50 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "exactArithmetic" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef PLANEER_H_
+#define PLANEER_H_
+
+#include "vec3ER.h"
+
+struct FastPlaneER
+{
+  FastPlaneER(const Vec3ER & v0, const Vec3ER & v1, const Vec3ER & v2);
+  int outside(const Vec3ER & v) const;
+  ER scaledDistance(const Vec3ER & v) const;
+
+  Vec3ER dir;
+  ER d;
+};
+
+
+
+#endif
diff --git a/libraries/exactArithmetic/vec3ER.cpp b/libraries/exactArithmetic/vec3ER.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c2f862960a3fac2298073fd575648e02b1d54d17
--- /dev/null
+++ b/libraries/exactArithmetic/vec3ER.cpp
@@ -0,0 +1,136 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "exactArithmetic" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "vec3ER.h"
+
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+#include <CGAL/intersections.h>
+
+typedef CGAL::Exact_predicates_exact_constructions_kernel K;
+
+/////////////////////////////////////////////////////////////////
+//                             V3
+/////////////////////////////////////////////////////////////////
+
+const Vec3ER VEC3ER_NULL(0.0, 0.0, 0.0);
+
+#ifdef VEGA_USE_CGAL_HEADER
+template <class CGAL_Kernel_Vector_3>
+Vec3ER assignCGALToVec3ER(const CGAL_Kernel_Vector_3 & cgal)
+{
+  Vec3ER ret;
+  ret[0] = cgal[0];
+  ret[1] = cgal[1];
+  ret[2] = cgal[2];
+  return ret;
+}
+
+template Vec3ER assignCGALToVec3ER<K::Vector_3>(const K::Vector_3 & cgal);
+
+
+bool intersectTriTri(const Vec3ER & pa, const Vec3ER & pb, const Vec3ER & pc, const Vec3ER & qa, const Vec3ER & qb, const Vec3ER & qc)
+{
+  K::Point_3 p0(pa[0], pa[1], pa[2]);
+  K::Point_3 p1(pb[0], pb[1], pb[2]);
+  K::Point_3 p2(pc[0], pc[1], pc[2]);
+
+  K::Point_3 q0(qa[0], qa[1], qa[2]);
+  K::Point_3 q1(qb[0], qb[1], qb[2]);
+  K::Point_3 q2(qc[0], qc[1], qc[2]);
+
+  K::Triangle_3 p(p0, p1, p2), q(q0, q1, q2);
+  auto result =  CGAL::intersection(p, q);
+  if(result) return true;
+  return false;
+}
+
+#else
+
+
+namespace
+{
+//  const K::RT & RT(const void * p) {
+//    return *(const K::RT*)p;
+//  }
+  K::RT & RT(void * p) {
+    return *(K::RT*)p;
+  }
+}
+
+
+template <class CGAL_Kernel_Vector_3>
+Vec3ER assignCGALToVec3ER(const CGAL_Kernel_Vector_3 & cgal)
+{
+  ER::UninitializedLabel l;
+  Vec3ER ret(l);
+  ret.elt[0].p = new K::RT(cgal[0]);
+  ret.elt[1].p = new K::RT(cgal[1]);
+  ret.elt[2].p = new K::RT(cgal[2]);
+  return ret;
+}
+
+template Vec3ER assignCGALToVec3ER<K::Vector_3>(const K::Vector_3 & cgal);
+
+bool intersectTriTri(const Vec3ER & pa, const Vec3ER & pb, const Vec3ER & pc, const Vec3ER & qa, const Vec3ER & qb, const Vec3ER & qc)
+{
+  K::Point_3 p0(RT(pa[0].p), RT(pa[1].p), RT(pa[2].p));
+  K::Point_3 p1(RT(pb[0].p), RT(pb[1].p), RT(pb[2].p));
+  K::Point_3 p2(RT(pc[0].p), RT(pc[1].p), RT(pc[2].p));
+
+  K::Point_3 q0(RT(qa[0].p), RT(qa[1].p), RT(qa[2].p));
+  K::Point_3 q1(RT(qb[0].p), RT(qb[1].p), RT(qb[2].p));
+  K::Point_3 q2(RT(qc[0].p), RT(qc[1].p), RT(qc[2].p));
+
+  K::Triangle_3 p(p0, p1, p2), q(q0, q1, q2);
+  auto result =  CGAL::intersection(p, q);
+  if(result) return true;
+  return false;
+}
+#endif
+
+bool intersectTriAABB(const Vec3ER & ta, const Vec3ER tb, const Vec3ER tc, const Vec3ER bmin, const Vec3ER bmax)
+{
+  assert(bmin[0] <= bmax[0] && bmin[1] <= bmax[1] && bmin[2] <= bmax[2]);
+  if (bmin[0] <= ta[0] && ta[0] <= bmax[0] && bmin[1] <= ta[1] && ta[1] <= bmax[1] && bmin[2] <= ta[2] && ta[2] <= bmax[2]) return true;
+  if (bmin[0] <= tb[0] && tb[0] <= bmax[0] && bmin[1] <= tb[1] && tb[1] <= bmax[1] && bmin[2] <= tb[2] && tb[2] <= bmax[2]) return true;
+  if (bmin[0] <= tc[0] && tc[0] <= bmax[0] && bmin[1] <= tc[1] && tc[1] <= bmax[1] && bmin[2] <= tc[2] && tc[2] <= bmax[2]) return true;
+  // create a box mesh for AABB
+  const Vec3ER p[8] = { {bmin[0], bmin[1], bmin[2]}, { bmax[0], bmin[1], bmin[2] }, { bmax[0], bmax[1], bmin[2] }, { bmin[0], bmax[1], bmin[2] },
+                  { bmin[0], bmin[1], bmax[2] }, { bmax[0], bmin[1], bmax[2] }, {bmax[0], bmax[1], bmax[2]}, { bmin[0], bmax[1], bmax[2] } };
+  const int t[12][3] = { {0,3,2}, {0,2,1}, {4,5,6}, {4,6,7}, {0,1,5}, {0,5,4},
+                                 {3,7,6}, {3,6,2}, {1,2,6}, {1,6,5}, {0,4,7}, {0,7,3} };
+  for(int i = 0; i < 12; i++) {
+    if (intersectTriTri(ta, tb, tc, p[t[i][0]], p[t[i][1]], p[t[i][2]])) return true;
+  }
+  return false;
+}
+
diff --git a/libraries/exactArithmetic/vec3ER.h b/libraries/exactArithmetic/vec3ER.h
new file mode 100644
index 0000000000000000000000000000000000000000..d19eff094ae202075629521964fabad431de6600
--- /dev/null
+++ b/libraries/exactArithmetic/vec3ER.h
@@ -0,0 +1,327 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "exactArithmetic" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef VEC3ER_H
+#define VEC3ER_H
+
+#include "er.h"
+
+// vector of 3 ES
+class Vec3ER
+{
+public:
+  inline Vec3ER() {}
+  inline Vec3ER(ER x, ER y, ER z) : elt { x, y, z } {}
+  inline Vec3ER(double x, double y, double z) : elt { x, y, z } {}
+  // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+  inline explicit Vec3ER(ER entry) : elt { entry, entry, entry } {}
+  inline explicit Vec3ER(double entry) : elt { entry, entry, entry } {}
+  // create a vector from the array of three Ss pointed to by "vec"
+  inline Vec3ER(const ER vec[3]) : elt { vec[0], vec[1], vec[2] } {}
+  inline Vec3ER(const double vec[3]) : elt { vec[0], vec[1], vec[2] } {}
+  inline Vec3ER(const Vec3ER & vec);
+
+  inline void set(ER x0, ER x1, ER x2); // assign vector [x0, x1, x2]
+  inline void set(ER value); // set all elements to value
+
+  inline Vec3ER & operator=(const Vec3ER & source);
+  inline bool operator==(const Vec3ER & vec2) const;
+  inline bool operator!=(const Vec3ER & vec2) const;
+
+  inline const Vec3ER operator+ (const Vec3ER & vec2) const;
+  inline Vec3ER & operator+= (const Vec3ER & vec2);
+
+  inline const Vec3ER operator- (const Vec3ER & vec2) const;
+  inline Vec3ER & operator-= (const Vec3ER & vec2);
+
+  inline const Vec3ER operator* (ER scalar) const;
+  inline Vec3ER & operator*= (ER scalar);
+
+  inline Vec3ER operator/ (ER scalar) const;
+  inline Vec3ER & operator/= (ER scalar);
+
+  // operator for V3 to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec3ER & vec2) const;
+
+  friend inline Vec3ER operator* (ER scalar, const Vec3ER & vec2);
+  friend inline Vec3ER operator/ (ER scalar, const Vec3ER & vec2);
+  friend inline Vec3ER operator- (const Vec3ER & vec1);
+
+  friend inline ER dot(const Vec3ER & vec1, const Vec3ER & vec2); // dot product
+
+  friend inline Vec3ER cross(const Vec3ER & vec1, const Vec3ER & vec2); // cross product
+
+  friend inline std::ostream & operator << (std::ostream &s, const Vec3ER &v);
+  void print() const;
+
+  inline ER & operator[] (int index); // v[i] returns i-th entry of v
+  inline const ER & operator[] (int index) const;
+
+  // copies the vector into an array of length 3
+  inline void convertToArray(ER vecArray[3]) const;
+  // adds the vector into an array of length 3
+  inline void addToArray(ER vecArray[3]) const;
+
+  template <class CGAL_Kernel_Vector_3>
+  friend Vec3ER assignCGALToVec3ER(const CGAL_Kernel_Vector_3 & cgal);
+
+protected:
+  ER elt[3];
+#ifndef VEGA_USE_CGAL_HEADER
+  inline Vec3ER(ER::UninitializedLabel);
+#endif
+};
+
+extern const Vec3ER VEC3ER_NULL;
+
+template <class CGAL_Kernel_Vector_3>
+Vec3ER assignCGALToVec3ER(const CGAL_Kernel_Vector_3 & cgal);
+
+// triangle-triangle intersection
+bool intersectTriTri(const Vec3ER & pa, const Vec3ER & pb, const Vec3ER & pc, const Vec3ER & qa, const Vec3ER & qb, const Vec3ER & qc);
+bool intersectTriAABB(const Vec3ER & tria, const Vec3ER trib, const Vec3ER tric, const Vec3ER bmin, const Vec3ER bmax);
+
+// === below is the implementation ===
+
+inline Vec3ER::Vec3ER(const Vec3ER & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+  elt[2] = vec.elt[2];
+}
+
+inline Vec3ER & Vec3ER::operator=(const Vec3ER & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+  elt[2] = source.elt[2];
+
+  return *this;
+}
+
+inline bool Vec3ER::operator==(const Vec3ER & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]) &&
+          (elt[2] == vec2[2]));
+}
+
+inline bool Vec3ER::operator!=(const Vec3ER & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]));
+}
+
+inline bool Vec3ER::operator<(const Vec3ER & vec2) const
+{
+  if(elt[0] < vec2[0])
+    return true;
+  if(elt[0] > vec2[0])
+    return false;
+  if(elt[1] < vec2[1])
+    return true;
+  if(elt[1] > vec2[1])
+    return false;
+  return elt[2] < vec2[2];
+}
+
+inline Vec3ER operator* (ER scalar, const Vec3ER & vec2)
+{
+  Vec3ER result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+
+  return result;
+}
+
+inline Vec3ER operator/ (ER scalar, const Vec3ER & vec2)
+{
+  Vec3ER result = vec2;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+  result.elt[2] /= scalar;
+
+  return result;
+}
+
+inline Vec3ER operator- (const Vec3ER & vec1)
+{
+  Vec3ER result = vec1;
+  result.elt[0] *= -1.0;
+  result.elt[1] *= -1.0;
+  result.elt[2] *= -1.0;
+
+  return result;
+}
+
+inline const Vec3ER Vec3ER::operator+ (const Vec3ER & vec2) const
+{
+  Vec3ER sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+  sum.elt[2] += vec2.elt[2];
+
+  return sum;
+}
+
+inline Vec3ER & Vec3ER::operator+= (const Vec3ER & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+  elt[2] += vec2.elt[2];
+
+  return *this;
+}
+
+inline const Vec3ER Vec3ER::operator- (const Vec3ER & vec2) const
+{
+  Vec3ER sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+  sum.elt[2] -= vec2.elt[2];
+
+  return sum;
+}
+
+inline Vec3ER & Vec3ER::operator-= (const Vec3ER & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+  elt[2] -= vec2.elt[2];
+
+  return *this;
+}
+
+inline ER & Vec3ER::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const ER & Vec3ER::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline Vec3ER & Vec3ER::operator*= (ER scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  elt[2] *= scalar;
+  return *this;
+}
+
+inline const Vec3ER Vec3ER::operator* (ER scalar) const
+{
+  return (Vec3ER(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar));
+}
+
+inline Vec3ER Vec3ER::operator/ (ER scalar) const
+{
+  return (Vec3ER(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar));
+}
+
+inline Vec3ER & Vec3ER::operator/= (ER scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  elt[2] /= scalar;
+  return *this;
+}
+
+inline std::ostream &operator << (std::ostream & s, const Vec3ER & v)
+{
+  return(s << '[' << ER_toDouble(v[0]) << ' ' << ER_toDouble(v[1]) << ' ' << ER_toDouble(v[2]) << ']');
+}
+
+inline void Vec3ER::convertToArray(ER vecArray[3]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+}
+
+inline void Vec3ER::addToArray(ER vecArray[3]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+}
+
+inline void Vec3ER::print() const
+{
+  printf("[%G %G %G]\n", ER_toDouble(elt[0]), ER_toDouble(elt[1]), ER_toDouble(elt[2]));
+}
+
+inline void Vec3ER::set(ER x0, ER x1, ER x2) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+}
+
+inline void Vec3ER::set(ER value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+  elt[2] = value;
+}
+
+inline ER dot(const Vec3ER & vec1, const Vec3ER & vec2)
+{
+  return (vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2]);
+}
+
+inline Vec3ER cross(const Vec3ER & vec1, const Vec3ER & vec2)
+{
+  Vec3ER result(vec1[1] * vec2[2] - vec2[1] * vec1[2],
+              -vec1[0] * vec2[2] + vec2[0] * vec1[2],
+               vec1[0] * vec2[1] - vec2[0] * vec1[1]);
+
+  return result;
+}
+
+inline ER len2(const Vec3ER & vec1)
+{
+  return(dot(vec1,vec1));
+}
+
+#ifndef VEGA_USE_CGAL_HEADER
+inline Vec3ER::Vec3ER(ER::UninitializedLabel) : elt {ER::UninitializedLabel(), ER::UninitializedLabel(), ER::UninitializedLabel()}
+{
+}
+#endif
+
+
+
+#endif
diff --git a/libraries/forceModel/finiteDifferenceTester.cpp b/libraries/forceModel/finiteDifferenceTester.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d54ec8284f40ddd52aa783fc9628e1beea9b478f
--- /dev/null
+++ b/libraries/forceModel/finiteDifferenceTester.cpp
@@ -0,0 +1,233 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "finiteDifferenceTester.h"
+#include <iostream>
+#include <cmath>
+#include <cassert>
+#include <numeric>
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+#include "range.h"
+using namespace std;
+
+static double squaredEuclideanNorm(const std::vector<double> & v)
+{
+  double sum = 0.0;
+  for(size_t i = 0; i < v.size(); i++)
+     sum += v[i] * v[i];
+  return sum;
+}
+
+static double EuclideanNorm(const std::vector<double> & v)
+{
+  return sqrt(squaredEuclideanNorm(v));
+}
+
+// the Euclidean distance between the two vector of the same size
+static double squaredEuclideanDistance(const std::vector<double> & v1, const std::vector<double> & v2)
+{
+  double sum = 0.0;
+  assert(v1.size() == v2.size());
+  for(size_t i = 0; i < v1.size(); i++)
+  {
+    double value = (v1[i] - v2[i]);
+    sum += value * value;
+  }
+  return sum;
+}
+
+static double EuclideanDistance(const std::vector<double> & v1, const std::vector<double> & v2)
+{
+  return sqrt(squaredEuclideanDistance(v1,v2));
+}
+
+
+FiniteDifferenceTester::FiniteDifferenceTester(ForceModel * fm, double timestep, Mode m, int nt)
+ : forceModel(fm), h(timestep), mode(m), numThreads(nt)
+{
+  r = forceModel->Getr();
+  analyticForce.resize(r);
+  finiteForce.resize(r);
+
+  forceModel->GetTangentStiffnessMatrixTopology(&stiffnessMatrix0);
+  transposedStiffness0 = stiffnessMatrix0->Transpose(r);
+  unsymmetricStiffness = new SparseMatrix(*stiffnessMatrix0);
+}
+
+FiniteDifferenceTester::~FiniteDifferenceTester()
+{
+}
+
+double FiniteDifferenceTester::testInternalForce(const double * u)
+{
+  double e0 = forceModel->GetElasticEnergy((double*)u);
+  analyticForce.assign(r, 0.);
+  forceModel->GetInternalForce((double*)u, analyticForce.data());
+  // Warning: multi-threading works only when forceModel->GetElasticEnergy is parallel-safe
+  #ifdef USE_TBB
+  tbb::parallel_for(tbb::blocked_range<int>(0, r), [&](const tbb::blocked_range<int> & rng)
+  {
+  #else
+  Range<int> rng(0, r);
+  #endif
+    vector<double> localDispBuffer(r);
+    memcpy(localDispBuffer.data(), u, sizeof(double) * r);
+    for (int j = rng.begin(); j != rng.end(); ++j) // j: [0, r)
+    {
+      double oldvalue = localDispBuffer[j];
+      if (mode == FIVE_POINT)
+      {
+        localDispBuffer[j] = oldvalue - 2*h;
+        double eb2h = forceModel->GetElasticEnergy(localDispBuffer.data());
+        localDispBuffer[j] = oldvalue - h;
+        double ebh = forceModel->GetElasticEnergy(localDispBuffer.data());
+        localDispBuffer[j] = oldvalue + h;
+        double efh = forceModel->GetElasticEnergy(localDispBuffer.data());
+        localDispBuffer[j] = oldvalue + 2*h;
+        double ef2h = forceModel->GetElasticEnergy(localDispBuffer.data());
+        finiteForce[j] = (-ef2h + 8 * efh - 8 * ebh + eb2h) / (12 * h);
+      }
+      else
+      {
+        localDispBuffer[j] = oldvalue + h;
+        double e = forceModel->GetElasticEnergy(localDispBuffer.data());
+        finiteForce[j] = (e - e0) / h;
+      }
+      localDispBuffer[j] = oldvalue;
+    }
+    #ifdef USE_TBB
+  }, tbb::static_partitioner());
+  #endif
+
+  double alen = EuclideanNorm(analyticForce);
+  double dist = EuclideanDistance(analyticForce, finiteForce);
+  double relErr = (alen == 0 ? dist : dist / alen);
+
+  return relErr;
+}
+
+double FiniteDifferenceTester::testStiffnessMatrix(const double * u, double * relativeErrorOnUnsymmetry)
+{
+
+  vector<double> & force0 = analyticForce;
+  force0.assign(r, 0.);
+  stiffnessMatrix0->ResetToZero();
+  forceModel->GetForceAndMatrix((double*)u, force0.data(), stiffnessMatrix0);
+  double frobNorm = stiffnessMatrix0->GetFrobeniusNorm();
+  transposedStiffness0->AssignTransposedMatrix(*stiffnessMatrix0);
+  if (relativeErrorOnUnsymmetry)
+    *unsymmetricStiffness = *stiffnessMatrix0 - *transposedStiffness0;
+
+//  double maxAbsEntry = stiffnessMatrix0->GetMaxAbsEntry();
+
+  vector<double> maxRelDiffInEachColumn(r, 0.), sumErrorEachColumn(r, 0.);
+
+  // Warning: multi-threading works only when forceModel->GetInternalForce is parallel-safe
+  // Warning: multi-threading works only when forceModel->GetElasticEnergy is parallel-safe
+  #ifdef USE_TBB
+  tbb::parallel_for(tbb::blocked_range<int>(0, r), [&](const tbb::blocked_range<int> & rng)
+  {
+  #else
+  Range<int> rng(0, r);
+  #endif
+    vector<double> localDispBuffer(3*r); // this buffer stores three vector of size r: disp, force and force2
+    memcpy(localDispBuffer.data(), u, sizeof(double) * r);
+    for (int j = rng.begin(); j != rng.end(); ++j) // j: [0, r)
+    {
+      double oldvalue = localDispBuffer[j];
+      double * dispBuffer = localDispBuffer.data();
+      double * forceBuffer = localDispBuffer.data()+r;
+      double * forceBuffer2 = localDispBuffer.data()+2*r;
+      if (mode == FIVE_POINT)
+      {
+        dispBuffer[j] = oldvalue - 2*h;
+        forceModel->GetInternalForce(dispBuffer, forceBuffer); // forceBuffer stores b2h
+
+        dispBuffer[j] = oldvalue - h;
+        forceModel->GetInternalForce(dispBuffer, forceBuffer2); // forceBuffer2 stores bh
+        for(int k = 0; k < r; k++)
+          forceBuffer[k] = forceBuffer[k] - 8 * forceBuffer2[k]; // forceBuffer stores - 8 * bh + b2h
+
+        dispBuffer[j] = oldvalue + h;
+        forceModel->GetInternalForce(dispBuffer, forceBuffer2); // forceBuffer2 stores fh
+        for(int k = 0; k < r; k++)
+          forceBuffer[k] += 8 * forceBuffer2[k]; // forceBuffer stores 8 * fh - 8 * bh + b2h
+
+        dispBuffer[j] = oldvalue + 2*h;
+        forceModel->GetInternalForce(dispBuffer, forceBuffer2); // forceBuffer2 stores f2h
+        for(int k = 0; k < r; k++)
+          forceBuffer[k] = (forceBuffer[k] - forceBuffer2[k]) / (12 * h); // ( -ef2h + 8 * fh - 8 * bh + b2h ) / (12 h)
+      }
+      else
+      {
+        localDispBuffer[j] = oldvalue + h;
+        forceModel->GetInternalForce(dispBuffer, forceBuffer);
+        for(int k = 0; k < r; k++)
+        {
+          forceBuffer[k] = (forceBuffer[k] - force0[k]) / h; // forceBuffer now contains jth column of the stiffness matrix
+        }
+      }
+
+      // compare with jth row of transposedStiffness0
+      int len = transposedStiffness0->GetRowLength(j);
+      for(int k = 0; k < len; k++)
+      {
+        int index = transposedStiffness0->GetColumnIndex(j, k);
+        double value = transposedStiffness0->GetEntry(j, k);
+        double dif = value - forceBuffer[index];
+        sumErrorEachColumn[j] += dif * dif;
+  //      double absval = fabs(value);
+  //      double reldif = (absval == 0 ? dif : dif / absval);
+  //      if (dif > maxRelDiffInEachColumn[j]) maxRelDiffInEachColumn[j] = reldif;
+      }
+
+      localDispBuffer[j] = oldvalue;
+    }
+  #ifdef USE_TBB
+  }, tbb::static_partitioner());
+  #endif
+
+//  double maxRelDif = *max_element(maxRelDiffInEachColumn.begin(), maxRelDiffInEachColumn.end());
+  double sumErr = accumulate(sumErrorEachColumn.begin(), sumErrorEachColumn.end(), 0.);
+  sumErr = sqrt(sumErr);
+  double relErr = (frobNorm == 0 ? sumErr : sumErr / frobNorm);
+
+  if (relativeErrorOnUnsymmetry)
+  {
+    double unsymErr =  unsymmetricStiffness->GetFrobeniusNorm();
+    *relativeErrorOnUnsymmetry = (frobNorm == 0 ? unsymErr : unsymErr / frobNorm);
+  }
+  return relErr;
+}
+
diff --git a/libraries/forceModel/finiteDifferenceTester.h b/libraries/forceModel/finiteDifferenceTester.h
new file mode 100644
index 0000000000000000000000000000000000000000..88e139d21ace21d809c3eecf84b2015577cdfcbb
--- /dev/null
+++ b/libraries/forceModel/finiteDifferenceTester.h
@@ -0,0 +1,85 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+   Tests a Vega FEM force model, using two-point, or five-point finite differences.
+*/
+
+#ifndef FINITEDIFFERENCETESTER_H
+#define FINITEDIFFERENCETESTER_H
+
+#include "forceModel.h"
+#include "sparseMatrix.h"
+#include <vector>
+
+class FiniteDifferenceTester
+{
+public:
+  enum Mode
+  {
+    TWO_POINT, // f'(x) <- (f(x+h) - f(x)) / h
+    FIVE_POINT // f'(x) <- (-f(x+2h) + 8 f(x+h) - 8 f(x-h) + f(x-2h)) / (12h)
+  };
+
+  // Warning: parallelism enabled by numThreads > 1 only works if forceModel->GetElasticEnergy() and
+  //   forceModel->GetInternalForce() are parallel-safe
+  FiniteDifferenceTester(ForceModel * forceModel, double timestep, Mode mode, int numThreads);
+  virtual ~FiniteDifferenceTester();
+
+  void setTimestep(double timestep) { h = timestep; }
+
+  // use ForceModel::GetInternalForce() to compute analtical force f_a
+  // use ForceModel::GetElasticEnergy() to compute finite difference force f_d
+  // return relative error: ||f_a - f_d|| / ||f_a||, if ||f_a|| == 0, return ||f_a - f_d||
+  double testInternalForce(const double * u);
+
+  // use ForceModel::GetInternalForceAndMatrix() to compute analtical tangent stiffness matrix K_a
+  // use ForceModel::GetInternalForce() to compute finite difference matrix K_d
+  // return relative error: ||K_a - K_d||_F / ||K_a||_F,  || ||_F is frobNorm, if ||K_a|| == 0, return ||K_a - K_d||_F
+  // to save time, we assume the topology (non-zero entry locations) are correct in the stiffness matrix. So when
+  //   computing ||K_a - K_d||_F, we only compare those non-zero entries, ignoring other entries in K_d which is computed densely
+  // relativeErrorOnUnsymmetry returns ||K_a - K_a^T||_F / ||K_a||_F, if ||K_a||_F == 0, return ||K_a - K_a^T||_F
+  double testStiffnessMatrix(const double * u, double * relativeErrorOnUnsymmetry = nullptr);
+
+protected:
+  ForceModel * forceModel = nullptr;
+  double h = 0.0; // timestep
+  Mode mode = TWO_POINT;
+  int numThreads = 0;
+  int r = 0;
+
+  std::vector<double> analyticForce, finiteForce;
+  SparseMatrix * stiffnessMatrix0 = nullptr, * transposedStiffness0 = nullptr;
+  SparseMatrix * unsymmetricStiffness = nullptr;
+};
+
+#endif /* FINITEDIFFERENCETESTER_H_ */
diff --git a/libraries/forceModel/forceModel.cpp b/libraries/forceModel/forceModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fd6e0274f7a988fed140b67e630b59b5507314db
--- /dev/null
+++ b/libraries/forceModel/forceModel.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "forceModel.h"
+
+ForceModel::~ForceModel()
+{
+}
+
+void ForceModel::GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
+{
+  GetInternalForce(u, internalForces);
+  GetTangentStiffnessMatrix(u, tangentStiffnessMatrix);
+}
+
diff --git a/src/libforceModel/forceModel.h b/libraries/forceModel/forceModel.h
similarity index 70%
rename from src/libforceModel/forceModel.h
rename to libraries/forceModel/forceModel.h
index 662f02d2a3a5d4a61a5426e32cf917db95ea11f6..29a49a6450dc593c73515c08209fc784b422be78 100644
--- a/src/libforceModel/forceModel.h
+++ b/libraries/forceModel/forceModel.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "forceModelBase" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,8 +41,6 @@
 #include <stdlib.h>
 #include "sparseMatrix.h"
 
-namespace vega
-{
 class ForceModel
 {
 public:
@@ -46,25 +48,21 @@ public:
 
   inline int Getr() { return r; }
 
-  virtual void GetInternalForce(double * u, double * internalForces) = 0;
+  virtual double GetElasticEnergy(const double * u) { return 0.0; }
+  virtual void GetInternalForce(const double * u, double * internalForces) = 0;
   virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) = 0;
-  virtual void GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix) = 0; 
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) = 0; 
 
   // sometimes computation time can be saved if we know that we will need both internal forces and tangent stiffness matrices:
-  virtual void GetForceAndMatrix (double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix); 
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix); 
 
   // reset routines
   virtual void ResetToZero() {}
   virtual void Reset(double * q) {}
 
-  // test the stiffness matrix, using finite differences
-  // q is the configuration to test, dq is a small delta
-  // if the stiffness matrix is correct, f(q) - f(q + eps * dq) - eps * K(q) * dq should be O(eps^2)
-  void TestStiffnessMatrix(double * q, double * dq);
-
 protected:
   int r;
 };
-}
+
 #endif
 
diff --git a/libraries/getopts/commandLineParser.cpp b/libraries/getopts/commandLineParser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..045183dcd2e18e2989e72814937327efe7088073
--- /dev/null
+++ b/libraries/getopts/commandLineParser.cpp
@@ -0,0 +1,217 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "getopts" library , Copyright (C) 2018 USC                            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "commandLineParser.h"
+#include <iostream>
+#include <string>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+using namespace std;
+
+CommandLineParser::CommandLineParser() {}
+
+void CommandLineParser::addOption(const string & name, int & value)
+{
+  entries.push_back(Entry(name, INT, (void*)&value));
+}
+
+void CommandLineParser::addOption(const string & name, char * value)
+{
+  entries.push_back(Entry(name, CSTR, (void*)value));
+}
+
+void CommandLineParser::addOption(const string & name, bool & value)
+{
+  entries.push_back(Entry(name, BOOL, (void*)&value));
+}
+
+void CommandLineParser::addOption(const std::string & name, vegalong & value)
+{
+  entries.push_back(Entry(name, LONG, (void*)&value));
+}
+
+void CommandLineParser::addOption(const std::string & name, float & value)
+{
+  entries.push_back(Entry(name, FLOAT, (void*)&value));
+}
+
+void CommandLineParser::addOption(const std::string & name, double & value)
+{
+  entries.push_back(Entry(name, DOUBLE, (void*)&value));
+}
+
+void CommandLineParser::addOption(const std::string & name, std::string & value)
+{
+  entries.push_back(Entry(name, STRING, (void*)&value));
+}
+
+int CommandLineParser::parse(int argc, char ** argv, int numSkipArg)
+{
+  return parse(argc, (const char**)argv, numSkipArg);
+}
+
+int CommandLineParser::parse(int argc, const char ** argv, int numSkipArg)
+{
+  for (int i = numSkipArg; i < argc; i++)
+  {
+    const char * arg = argv[i];
+    // option begins with '-' or '/'
+    if ((*arg != '-') && (*arg != '/'))
+      return (i);
+    arg++; // skip '-' or '/'
+
+    for (size_t j = 0; j < entries.size(); j++) //find options for this arg
+    {
+      //options are case sensitive!!
+      size_t l = entries[j].name.size();
+
+      //printf("%s\n", opttable[j].sw);
+      if (strncmp(arg, entries[j].name.data(), l) == 0) //found this option
+      {
+        const char * var = nullptr; //pointers to the input data for this option
+//        bool standAloneData = false; // whether the data for this arg is in a separate string, like -v 123
+
+        // There's ambiguity when a negative number arg is next to a switch, like -f -48.9
+        // For now we only intepret it as two successive switches
+        // A smarter implementation could be to forbid switch name to start with numbers, and check whether the second "switch"
+        // starts with "-\d"
+
+        // if a data string follows this arg string, like: -v 123
+        if ( (strlen(arg) == l) && ( (i+1 < argc) && (argv[i+1] != nullptr) && ( argv[i+1][0] != '-' && argv[i+1][0] != '/' ) ) )
+        {
+          // go to the next data string
+          i++;
+          var = argv[i];
+//          standAloneData = true;
+        }
+        else
+        {
+          //copy the rest in this arg string, like: -v123
+          if ( ( *(arg+l)=='=' ) || (*(arg+l)==' ') ) // accept '=' or ' ' after the switch
+            var = arg + l + 1;
+          else
+            var = arg + l;
+        }
+
+        // if (*var == '\0')
+        //   continue;
+        //printf("%d %s\n",i, var);
+        switch (entries[j].type)
+        {
+        case INT :
+          if (*var != '\0')
+          {
+            *((int *)entries[j].value) = (int)strtol(var,nullptr,10);
+            if (errno == ERANGE)
+              return (i);
+          }
+          else // the data is absent. We consider it an error
+            return i;
+          break;
+
+        case LONG :
+          if (*var != '\0')
+          {
+            *((vegalong *)entries[j].value) = strtol(var,nullptr,10);
+            if (errno == ERANGE)
+              return (i);
+          }
+          else
+            return i;
+          break;
+
+        case FLOAT :
+          if (*var != '\0')
+          {
+            *((float *)entries[j].value) = (float)strtod(var,nullptr);
+            if (errno == ERANGE)
+              return (i);
+          }
+          else
+            return i;
+          break;
+
+        case DOUBLE :
+          if (*var != '\0')
+          {
+            *((double *)entries[j].value) = strtod(var,nullptr);
+            if (errno == ERANGE)
+              return (i);
+          }
+          else
+            return i;
+          break;
+
+          // case OPTSUBOPT :
+          //   //option with more than 1 char as suboptions:
+          //   // /oail or /op
+          //   strcpy((char *)opttable[j].var, arg+l);
+          //   arg += strlen(arg)-1;
+          //   break;
+
+        case CSTR :
+          if (*var != '\0')
+            strcpy((char *)entries[j].value, var);
+          else
+            return i;
+          break;
+
+        case STRING :
+          if (*var != '\0')
+            *((string*)(entries[j].value)) = var;
+          else
+            return i;
+          break;
+
+        case BOOL :
+          //check a + or - after the sw: "/a- /b+"
+          {
+            bool boolValue = true;
+            if ( *var == '-' )
+              boolValue = false;
+            else
+              boolValue = true;
+
+            *((bool *)entries[j].value) = boolValue;
+            break;
+          }
+        }
+        break;      //break the for
+      } // end if (strncmp(arg, entries[j].name.data(), l) == 0) //found this option
+    } // end for (size_t j = 0; j < entries.size(); j++)
+  }
+  return argc;
+}
+
+CommandLineParser::Entry::Entry(const std::string & n, Type t, void * v) : name(n), type(t), value(v) {}
diff --git a/libraries/getopts/commandLineParser.h b/libraries/getopts/commandLineParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..964d7f6064c7bc5a58a4f51804570a01428f27bb
--- /dev/null
+++ b/libraries/getopts/commandLineParser.h
@@ -0,0 +1,108 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "getopts" library , Copyright (C) 2018 USC                            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef COMMANDLINEPARSER_H
+#define COMMANDLINEPARSER_H
+
+#include "vegalong.h"
+#include <string>
+#include <vector>
+
+// parser for command line arguments
+// same argument syntax as in getopts.h:
+//
+// switches come with either "-" or "/", optionally followed by "=". e.g. -a  /h  -x=
+// the parser accepts:
+//
+// string:
+//   -s abc
+//   -sabc
+// whitespace must follow a string argument, no -sstring-a is allowed
+// number
+//   -a123.4
+//   -a 123.4
+//   -v-57
+// there must be no whitespace between the switch and a negative number, no -v -57 is allowed
+// boolean
+//   -b+
+//   -b
+//   -b-
+// the parser accepts a "false" boolean value only when a '-' after the switch without whitespace, like -b-
+// all the other situations are considered as "true"
+
+class CommandLineParser
+{
+public:
+  CommandLineParser();
+  virtual ~CommandLineParser() {}
+
+  void addOption(const std::string & name, int & value);
+  void addOption(const std::string & name, char * value);
+  void addOption(const std::string & name, bool & value);
+  void addOption(const std::string & name, vegalong & value);
+  void addOption(const std::string & name, float & value);
+  void addOption(const std::string & name, double & value);
+  void addOption(const std::string & name, std::string & value);
+
+  // parse args from argv. numSkipArg tells how many entries in argv are skipped before args are reached
+  // on success, return argc
+  // on failure, return the 0-index of the arg that caused failure
+  int parse(int argc, char ** argv, int numSkipArg = 1);
+  int parse(int argc, const char ** argv, int numSkipArg = 1);
+
+protected:
+
+  enum Type
+  {
+    INT = 1,
+    CSTR = 2,
+    BOOL = 3,
+    LONG = 4,
+    FLOAT = 5,
+    DOUBLE = 6,
+    STRING = 7
+  };
+
+  struct Entry
+  {
+    std::string name;
+    Type type;
+    void * value;
+    Entry(const std::string & name, Type type, void * value);
+  };
+  std::vector<Entry> entries;
+};
+
+
+
+
+#endif
diff --git a/src/libvega-getopts/getopts.cpp b/libraries/getopts/getopts.cpp
similarity index 62%
rename from src/libvega-getopts/getopts.cpp
rename to libraries/getopts/getopts.cpp
index 7987e7b82520078c702d75a1b0958fb05f5194d6..f264bea78087cd6d44aab39bb388eb9d37763c1a 100644
--- a/src/libvega-getopts/getopts.cpp
+++ b/libraries/getopts/getopts.cpp
@@ -70,7 +70,7 @@
 /*  Additions by Jernej Barbic, Carnegie Mellon University, 2005:    */
 /*    delim ' ' between opt and value added, eg: /s string           */
 /*    note: function returns argc upon success                       */
-      
+
 /*********************************************************************/
 
 #include "getopts.h"
@@ -78,102 +78,110 @@
 #include <string.h>
 #include <errno.h>
 #include <stdio.h>
+#include "vegalong.h"
 
-namespace vega
-{
 int getopts(int argc, char **argv, opt_t opttable[])
 {
-    int i,j,l;
-    bool bool_;
-    //char header[] = "$Id: GETOPTS.C 1.3 1995/05/01 14:30:33 Reini Exp $";
-    //char credits[] = "written by Paul Edwards, improved by Reini Urban and Jernej Barbic";
+  int i = 0;
+  //char header[] = "$Id: GETOPTS.C 1.3 1995/05/01 14:30:33 Reini Exp $";
+  //char credits[] = "written by Paul Edwards, improved by Reini Urban and Jernej Barbic";
+
+  //skip the first arg
+  argv++;
+  argc--;
+  for (i=1;i<=argc;i++)
+  {
+    char * arg = *argv;
+    //printf("%d %s\n",i, arg);
+    if ((*arg != '-') && (*arg != '/')) return (i);
+    arg++;
 
-    argv++;
-    argc--;
-    for (i=1;i<=argc;i++)
+    for (int j=0; opttable[j].sw != NULL;j++) //find options for this arg
     {
-        //printf("%d\n", i);
-	char * arg = *argv;
-	if ((*arg != '-') && (*arg != '/')) return (i);
-	arg++;
-	while ( *arg )
-	{
-	    for (j=0;opttable[j].sw != NULL;j++)
-	    {
-	      //options are case sensitive!!
-	      l = strlen(opttable[j].sw);
-              //printf("%s\n", opttable[j].sw);
-	      if (strncmp(arg,opttable[j].sw, l) == 0)
-	      {
-	        switch ((int)opttable[j].opttyp)
-	        {
-              	    case OPTINT :
-			if ( ( *(arg+l)=='=' ) || (*(arg+l)==' ') ) // accept '=' or ' ' after the switch
-	               	  *((int *)opttable[j].var) = (int)strtol(arg+l+1,NULL,10);
-                        else
-	               	  *((int *)opttable[j].var) = (int)strtol(arg+l,NULL,10);
-	               	if (errno == ERANGE)
-	               		return (i);
-	                break;
-	            case OPTSUBOPT :
-	            	//option with more than 1 char as suboptions:
-	            	// /oail or /op
-	            	strcpy((char *)opttable[j].var, arg+l);
-	            	arg += strlen(arg)-1;
-	            	break;
-	            case OPTSTR :
-	            	if ( ((int)strlen(arg) == l) &&
-			     ( **(argv+1) != '-') )//&&
-			     //( **(argv+1) != '/') )
-			{	//copy the next arg: /s string
-			    argv++; i++;
-			    strcpy((char *)opttable[j].var,*argv);
-                            //printf("**:%s\n",opttable[j].var);
-			}
-			else
-			//copy the rest: /sstring
-			{	
-			    if ( ( *(arg+l)=='=' ) || (*(arg+l)==' ') ) // accept '=' or ' ' after a string
-			    	strcpy((char *)opttable[j].var, arg+l+1);
-			    else
-			    	strcpy((char *)opttable[j].var, arg+l);
-                            //printf("$$:%s\n",opttable[j].var);
-			}
-			arg += strlen(arg)-1;
-			break;
-		    case OPTBOOL :
-			//check a + or - after the sw: "/a-/b+"
-			if ( *(arg+l)=='-' )
-			{
-			    arg++; //advance argv after -
-			    bool_ = false;
-			}
-			else
-			    if ( *(arg+l)=='+' )
-		  	    {
-				arg++; //advance argv after -
-				bool_ = true;
-			    }
-			    else
-				bool_ = true;
-	               	*((bool *)opttable[j].var) = bool_;
-	               	break;
-	            case OPTLONG :
-	                *((long *)opttable[j].var) = strtol(arg+l,NULL,10);
-	                if (errno == ERANGE)
-	                    return (i);
-	                break;
-	        }
-	    	arg += l-1; //next arg within the same arg, "/a/b" or "/ab"
-	      	break;      //break the for
-	      }
-	    }
-	    arg++;	//try the next char
-	    if ((*arg == '-') || (*arg == '/')) arg++;
+      //options are case sensitive!!
+      int l = strlen(opttable[j].sw);
+      bool bool_ = true;
+      
+      //printf("%s\n", opttable[j].sw);
+      if (strncmp(arg,opttable[j].sw, l) == 0) //found this option
+      {
+      	char * var = NULL; //pointers to the input data for this option
+
+        if ( ((int)strlen(arg) == l) &&
+            ( (i+1 <= argc) && (**(argv+1) != '-') ) )//&&
+          //( **(argv+1) != '/') )
+        { //copy the next arg: /i int
+          argv++; i++;
+          var = *argv;
+          //printf("next arg\n");
+          //printf("**:%s\n",opttable[j].var);
         }
-        argv++;  //try the next arg, eg: "/a /b"
-    }
-    return (i);
-}
+        else
+        { //copy the rest: /iint
+          if ( ( *(arg+l)=='=' ) || (*(arg+l)==' ') ) // accept '=' or ' ' after the switch
+            var = arg + l + 1;
+          else
+            var = arg + l;
+          //printf("$$:%s\n",opttable[j].var);
+        }
+
+        // if (*var == '\0')
+        //   continue;
+        //printf("%d %s\n",i, var);
+        switch ((int)opttable[j].opttyp)
+        {
+        case OPTINT :
+          // if ( ( *(arg+l)=='=' ) || (*(arg+l)==' ') ) // accept '=' or ' ' after the switch
+          //   *((int *)opttable[j].var) = (int)strtol(arg+l+1,NULL,10);
+          // else
+          //   *((int *)opttable[j].var) = (int)strtol(arg+l,NULL,10);
+          // if (errno == ERANGE)
+          //   return (i);
+          // break;
+          if (*var != '\0')
+          {
+            *((int *)opttable[j].var) = (int)strtol(var,NULL,10);
+
+            if (errno == ERANGE)
+              return (i);
+          }
+          break;
+
+        case OPTLONG :
+          if (*var != '\0')
+          {
+            *((vegalong *)opttable[j].var) = strtol(var,NULL,10);
+            if (errno == ERANGE)
+              return (i);
+          }
+          break;
+
+          // case OPTSUBOPT :
+          //   //option with more than 1 char as suboptions:
+          //   // /oail or /op
+          //   strcpy((char *)opttable[j].var, arg+l);
+          //   arg += strlen(arg)-1;
+          //   break;
+        case OPTSTR :
+          if (*var != '\0')
+            strcpy((char *)opttable[j].var, var);
+          break;
+
+        case OPTBOOL :
+          //check a + or - after the sw: "/a- /b+"
+          if ( *var == '-' )
+            bool_ = false;
+          else
+            bool_ = true;
+
+          *((bool *)opttable[j].var) = bool_;
+          break;
+        }
+        break;      //break the for
+      } // end if (strncmp(arg,opttable[j].sw, l) == 0) 
+    } // end for (int j=0; opttable[j].sw != NULL;j++)
 
+    argv++;  //try the next arg, eg: "/a /b"
+  }
+  return (i);
 }
diff --git a/src/libvega-getopts/getopts.h b/libraries/getopts/getopts.h
similarity index 70%
rename from src/libvega-getopts/getopts.h
rename to libraries/getopts/getopts.h
index 8bf08478628b62fc5214457bdb48ce61eeaf402d..4fe008e2566751c525dd0ecea02f8ad9090a002e 100644
--- a/src/libvega-getopts/getopts.h
+++ b/libraries/getopts/getopts.h
@@ -1,19 +1,17 @@
 /* getopts.h */
-#ifndef GETOPTS_H
-#define GETOPTS_H
 
-namespace vega
-{
-#define GETOPTS
+#ifndef _GETOPTS_H_
+#define _GETOPTS_H_
+
 #define OPTINT 1
 #define OPTSTR 2
 #define OPTBOOL 3
 #define OPTLONG 4
 #define OPTSUBOPT 5
-typedef struct {char *sw;
+typedef struct {const char *sw;
                int opttyp;
                void *var;} opt_t;
 int getopts(int argc, char **argv, opt_t opttable[]);
-}
+
 #endif
 
diff --git a/src/libglslPhong/glslPhong.cpp b/libraries/glslPhong/glslPhong.cpp
similarity index 90%
rename from src/libglslPhong/glslPhong.cpp
rename to libraries/glslPhong/glslPhong.cpp
index dee1b725598dc160c2d21aa72b8cbdfb1dbbfd2c..6788635187bbd3259b00cba563ca8a48b739ad70 100644
--- a/src/libglslPhong/glslPhong.cpp
+++ b/libraries/glslPhong/glslPhong.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "glslPhong" library , Copyright (C) 2013 USC                          *
+ * "glslPhong" library , Copyright (C) 2018 USC                          *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -35,7 +39,7 @@
 #include <stdio.h>
 #include <string.h>
 
-#if defined(WIN32) || defined(__linux__)
+#if defined(_WIN32) || defined(WIN32) || defined(linux) || defined(__linux__)
   #define USE_GLEW
 #elif defined(__APPLE__)
   #include "openGL-headers.h"
@@ -74,15 +78,12 @@ char GLSLPhong::fragmentShaderStringAll [] = "varying vec3 normal, eyeVec;\n"
 "  {\n"
 "    final_color += gl_LightSource[i].ambient * gl_FrontMaterial.ambient;\n"
 "    vec3 L = normalize(lightDir[i]);\n"
-"    float lambertTerm = dot(N,L);\n"
-"    if (lambertTerm > 0.0)\n"
-"    {\n"
-"      final_color += gl_LightSource[i].diffuse * gl_FrontMaterial.diffuse * lambertTerm;\n"
-"      vec3 E = normalize(eyeVec);\n"
-"      vec3 R = reflect(-L, N);\n"
-"      float specular = pow(max(dot(R, E), 0.0), gl_FrontMaterial.shininess);\n"
-"      final_color += gl_LightSource[i].specular * gl_FrontMaterial.specular * specular;\n"
-"    }\n"
+"    float lambert = max(dot(N,L), 0.0);\n"
+"    final_color += gl_LightSource[i].diffuse * gl_FrontMaterial.diffuse * lambert;\n"
+"    vec3 E = normalize(eyeVec);\n"
+"    vec3 R = reflect(-L, N);\n"
+"    float specular = pow(max(dot(R,E), 0.0), gl_FrontMaterial.shininess);\n"
+"    final_color += gl_LightSource[i].specular * gl_FrontMaterial.specular * specular;\n"
 "  }\n"
 "  gl_FragColor = final_color;\n"
 "}\n";
diff --git a/src/libglslPhong/glslPhong.h b/libraries/glslPhong/glslPhong.h
similarity index 81%
rename from src/libglslPhong/glslPhong.h
rename to libraries/glslPhong/glslPhong.h
index f9df38f1be4bbff8fd3d6bfb2e75f59aacd0d007..9580eaf0fa63175db27e7f1bf287b11ab236035c 100644
--- a/src/libglslPhong/glslPhong.h
+++ b/libraries/glslPhong/glslPhong.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "glslPhong" library , Copyright (C) 2013 USC                          *
+ * "glslPhong" library , Copyright (C) 2018 USC                          *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/libraries/glui/glui-2.35/lgpl-2.1.txt b/libraries/glui/glui-2.35/lgpl-2.1.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4362b49151d7b34ef83b3067a8f9c9f877d72a0e
--- /dev/null
+++ b/libraries/glui/glui-2.35/lgpl-2.1.txt
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey 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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/thirdparty/glui/2.36/src/algebra3.cpp b/libraries/glui/glui-2.35/src/algebra3.cpp
similarity index 97%
rename from thirdparty/glui/2.36/src/algebra3.cpp
rename to libraries/glui/glui-2.35/src/algebra3.cpp
index 143c43fb1ae09fb8fde2155ec6f7e5b7c240aaaa..5101480e5b470786e704442241e60287fe1be088 100644
--- a/thirdparty/glui/2.36/src/algebra3.cpp
+++ b/libraries/glui/glui-2.35/src/algebra3.cpp
@@ -2,27 +2,25 @@
 
   algebra3.cpp, algebra3.h -  C++ Vector and Matrix Algebra routines
 
-  GLUI User Interface Toolkit
-  Copyright (c) 1998-2007 Paul Rademacher
+  GLUI User Interface Toolkit (LGPL)
+  Copyright (c) 1998 Paul Rademacher
 
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 */
 
diff --git a/thirdparty/glui/2.36/src/algebra3.h b/libraries/glui/glui-2.35/src/algebra3.h
similarity index 95%
rename from thirdparty/glui/2.36/src/algebra3.h
rename to libraries/glui/glui-2.35/src/algebra3.h
index 0ee5622cba59de06f7cdd7c66f02d9b496ff180a..9875b3a8c99bc4a42421c0418e4e75af7d04a906 100644
--- a/thirdparty/glui/2.36/src/algebra3.h
+++ b/libraries/glui/glui-2.35/src/algebra3.h
@@ -2,27 +2,25 @@
 
   algebra3.cpp, algebra3.h -  C++ Vector and Matrix Algebra routines
 
-  GLUI User Interface Toolkit
+  GLUI User Interface Toolkit (LGPL)
   Copyright (c) 1998 Paul Rademacher
 
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 */
 
diff --git a/thirdparty/glui/2.36/src/arcball.cpp b/libraries/glui/glui-2.35/src/arcball.cpp
similarity index 84%
rename from thirdparty/glui/2.36/src/arcball.cpp
rename to libraries/glui/glui-2.35/src/arcball.cpp
index 177c910981a049c45a455bd13ee891eaf7b45216..63c983e68ce3639c6d2ff5c01cef5d840b6da460 100644
--- a/thirdparty/glui/2.36/src/arcball.cpp
+++ b/libraries/glui/glui-2.35/src/arcball.cpp
@@ -5,7 +5,7 @@
 
           --------------------------------------------------
 
-  GLUI User Interface Toolkit
+  GLUI User Interface Toolkit (LGPL)
   Copyright (c) 1998 Paul Rademacher
      Feb 1998, Paul Rademacher (rademach@cs.unc.edu)
      Oct 2003, Nigel Stewart - GLUI Code Cleaning
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 **********************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/arcball.h b/libraries/glui/glui-2.35/src/arcball.h
similarity index 75%
rename from thirdparty/glui/2.36/src/arcball.h
rename to libraries/glui/glui-2.35/src/arcball.h
index 7382d9113d82a01bb2e10b1f3e2ab279c141bac5..18edd0598a51db9c1d4dd009420567590733b4dc 100644
--- a/thirdparty/glui/2.36/src/arcball.h
+++ b/libraries/glui/glui-2.35/src/arcball.h
@@ -2,7 +2,7 @@
 
   arcball.h
 
-  GLUI User Interface Toolkit 
+  GLUI User Interface Toolkit (LGPL)
   Copyright (c) 1998 Paul Rademacher
      Feb 1998, Paul Rademacher (rademach@cs.unc.edu)
      Oct 2003, Nigel Stewart - GLUI Code Cleaning
@@ -11,21 +11,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
  ---------------------------------------------------------------------
 
diff --git a/thirdparty/glui/2.36/src/devcpp/example1.dev b/libraries/glui/glui-2.35/src/devcpp/example1.dev
similarity index 100%
rename from thirdparty/glui/2.36/src/devcpp/example1.dev
rename to libraries/glui/glui-2.35/src/devcpp/example1.dev
diff --git a/thirdparty/glui/2.36/src/devcpp/example2.dev b/libraries/glui/glui-2.35/src/devcpp/example2.dev
similarity index 100%
rename from thirdparty/glui/2.36/src/devcpp/example2.dev
rename to libraries/glui/glui-2.35/src/devcpp/example2.dev
diff --git a/thirdparty/glui/2.36/src/devcpp/example3.dev b/libraries/glui/glui-2.35/src/devcpp/example3.dev
similarity index 100%
rename from thirdparty/glui/2.36/src/devcpp/example3.dev
rename to libraries/glui/glui-2.35/src/devcpp/example3.dev
diff --git a/thirdparty/glui/2.36/src/devcpp/example4.dev b/libraries/glui/glui-2.35/src/devcpp/example4.dev
similarity index 100%
rename from thirdparty/glui/2.36/src/devcpp/example4.dev
rename to libraries/glui/glui-2.35/src/devcpp/example4.dev
diff --git a/thirdparty/glui/2.36/src/devcpp/example5.dev b/libraries/glui/glui-2.35/src/devcpp/example5.dev
similarity index 100%
rename from thirdparty/glui/2.36/src/devcpp/example5.dev
rename to libraries/glui/glui-2.35/src/devcpp/example5.dev
diff --git a/thirdparty/glui/2.36/src/devcpp/glui.dev b/libraries/glui/glui-2.35/src/devcpp/glui.dev
similarity index 100%
rename from thirdparty/glui/2.36/src/devcpp/glui.dev
rename to libraries/glui/glui-2.35/src/devcpp/glui.dev
diff --git a/thirdparty/glui/2.36/src/doc/glui_manual.pdf b/libraries/glui/glui-2.35/src/doc/glui_manual.pdf
similarity index 100%
rename from thirdparty/glui/2.36/src/doc/glui_manual.pdf
rename to libraries/glui/glui-2.35/src/doc/glui_manual.pdf
diff --git a/thirdparty/glui/2.36/src/example/example1.cpp b/libraries/glui/glui-2.35/src/example/example1.cpp
similarity index 100%
rename from thirdparty/glui/2.36/src/example/example1.cpp
rename to libraries/glui/glui-2.35/src/example/example1.cpp
diff --git a/thirdparty/glui/2.36/src/example/example2.cpp b/libraries/glui/glui-2.35/src/example/example2.cpp
similarity index 100%
rename from thirdparty/glui/2.36/src/example/example2.cpp
rename to libraries/glui/glui-2.35/src/example/example2.cpp
diff --git a/thirdparty/glui/2.36/src/example/example3.cpp b/libraries/glui/glui-2.35/src/example/example3.cpp
similarity index 100%
rename from thirdparty/glui/2.36/src/example/example3.cpp
rename to libraries/glui/glui-2.35/src/example/example3.cpp
diff --git a/thirdparty/glui/2.36/src/example/example4.cpp b/libraries/glui/glui-2.35/src/example/example4.cpp
similarity index 100%
rename from thirdparty/glui/2.36/src/example/example4.cpp
rename to libraries/glui/glui-2.35/src/example/example4.cpp
diff --git a/thirdparty/glui/2.36/src/example/example5.cpp b/libraries/glui/glui-2.35/src/example/example5.cpp
similarity index 100%
rename from thirdparty/glui/2.36/src/example/example5.cpp
rename to libraries/glui/glui-2.35/src/example/example5.cpp
diff --git a/thirdparty/glui/2.36/src/example/example6.cpp b/libraries/glui/glui-2.35/src/example/example6.cpp
similarity index 98%
rename from thirdparty/glui/2.36/src/example/example6.cpp
rename to libraries/glui/glui-2.35/src/example/example6.cpp
index 6111f1f6ee224b0fbc12bdeb65865a19e84e82fa..1eaffce3d4dc14b1180c5120c00817c7380b0ca0 100644
--- a/thirdparty/glui/2.36/src/example/example6.cpp
+++ b/libraries/glui/glui-2.35/src/example/example6.cpp
@@ -260,9 +260,6 @@ void control_cb(int control) {
     }
   }
 }
-void textbox_cb(GLUI_Control *control) {
-    printf("Got textbox callback\n");
-}
 
 //void out_of_memory() {
 //  printf("Out of memory!\n\n");
@@ -293,7 +290,7 @@ int main(int argc, char* argv[])
   hah->set_h(180);
   new GLUI_Column(ep,false); 
 
-  moo = new GLUI_TextBox(ep,true,1,textbox_cb);
+  moo = new GLUI_TextBox(ep,true);
   moo->set_text(general);
   moo->set_h(400);
   moo->set_w(410);
diff --git a/thirdparty/glui/2.36/src/glui.cpp b/libraries/glui/glui-2.35/src/glui.cpp
similarity index 97%
rename from thirdparty/glui/2.36/src/glui.cpp
rename to libraries/glui/glui-2.35/src/glui.cpp
index 7b9bf478a5d8824749aabb50be04028eea0cf38f..683f750263e2b080c77189d2ed8844df5f9283c5 100644
--- a/thirdparty/glui/2.36/src/glui.cpp
+++ b/libraries/glui/glui-2.35/src/glui.cpp
@@ -1,6 +1,6 @@
 /****************************************************************************
   
-  GLUI User Interface Toolkit 
+  GLUI User Interface Toolkit (LGPL)
   ---------------------------
 
      glui.cpp
@@ -12,21 +12,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
 
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
 
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 #include "glui_internal_control.h"
@@ -583,10 +581,10 @@ void    GLUI_Main::display( void )
   }
 
   /*******    Draw GLUI window     ******/
-  glClearColor( bkgd_color[0] / 255.0f,
-		        bkgd_color[1] / 255.0f,
-		        bkgd_color[2] / 255.0f,
-		        1.0f );
+  glClearColor( (float) bkgd_color.r / 255.0,
+		(float) bkgd_color.g / 255.0,
+		(float) bkgd_color.b / 255.0,
+		1.0 );
   glClear( GL_COLOR_BUFFER_BIT ); /* | GL_DEPTH_BUFFER_BIT );          */
 
   set_ortho_projection();
@@ -1131,12 +1129,10 @@ GLUI_Main::GLUI_Main( void )
   curr_cursor             = GLUT_CURSOR_LEFT_ARROW;
 
   int r=200, g=200, b=200;
-  bkgd_color[0] = r;
-  bkgd_color[1] = g;
-  bkgd_color[2] = b;
-  bkgd_color_f[0] = r / 255.0f;
-  bkgd_color_f[1] = g / 255.0f;
-  bkgd_color_f[2] = b / 255.0f;
+  bkgd_color.set( r,g,b );
+  bkgd_color_f[0] = r / 255.0;
+  bkgd_color_f[1] = g / 255.0;
+  bkgd_color_f[2] = b / 255.0;
 
   /*** Create the main panel ***/
   main_panel              = new GLUI_Panel;
@@ -1152,7 +1148,7 @@ void      GLUI_Main::draw_raised_box( int x, int y, int w, int h )
   w = w+x;
   h = h+y;
 
-  glColor3ubv( bkgd_color );
+  glColor3ub( bkgd_color.r, bkgd_color.g, bkgd_color.b );
   glBegin( GL_LINE_LOOP );
   glVertex2i( x+1, y+1 );  glVertex2i( w-1, y+1 );
   glVertex2i( w-1, h-1 );  glVertex2i( x+1, h-1 );
@@ -1183,7 +1179,7 @@ void      GLUI_Main::draw_lowered_box( int x, int y, int w, int h )
   w = w+x;
   h = h+y;
 
-  glColor3ubv( bkgd_color );
+  glColor3ub( bkgd_color.r, bkgd_color.g, bkgd_color.b );
   glBegin( GL_LINE_LOOP );
   glVertex2i( x+1, y+1 );         glVertex2i( w-1, y+1 );
   glVertex2i( w-1, h-1 );     glVertex2i( x+1, h-1 );
diff --git a/thirdparty/glui/2.36/src/glui_add_controls.cpp b/libraries/glui/glui-2.35/src/glui_add_controls.cpp
similarity index 91%
rename from thirdparty/glui/2.36/src/glui_add_controls.cpp
rename to libraries/glui/glui-2.35/src/glui_add_controls.cpp
index ecf72b9a6e61061043c19222a91e5b9460c7f08d..20ed43d123516b09f66c9ce301f27a79fad05030 100644
--- a/thirdparty/glui/2.36/src/glui_add_controls.cpp
+++ b/libraries/glui/glui-2.35/src/glui_add_controls.cpp
@@ -1,6 +1,6 @@
 /****************************************************************************
 
-  GLUI User Interface Toolkit 
+  GLUI User Interface Toolkit (LGPL)
   ---------------------------
 
      glui_add_controls.cpp - Routines for adding controls to a GLUI window
@@ -16,21 +16,19 @@ that aren't used.
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_bitmap_img_data.cpp b/libraries/glui/glui-2.35/src/glui_bitmap_img_data.cpp
similarity index 99%
rename from thirdparty/glui/2.36/src/glui_bitmap_img_data.cpp
rename to libraries/glui/glui-2.35/src/glui_bitmap_img_data.cpp
index e65884097740893204755d989035726482afaf51..bfcef31fab303b4b1776404ca3dc467d68f255b0 100644
--- a/thirdparty/glui/2.36/src/glui_bitmap_img_data.cpp
+++ b/libraries/glui/glui-2.35/src/glui_bitmap_img_data.cpp
@@ -28,8 +28,7 @@ enum {
   UW, // User-interface white-- backgrounds of checkboxes and radio buttons
 };
  
- Orion Sky Lawlor, olawlor@acm.org, 2006/05/04 
- License: ZLIB
+ Orion Sky Lawlor, olawlor@acm.org, 2006/05/04 (LGPL)
 */
 
 /*----------------------- checkboxes --------------------------*/
diff --git a/thirdparty/glui/2.36/src/glui_bitmaps.cpp b/libraries/glui/glui-2.35/src/glui_bitmaps.cpp
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_bitmaps.cpp
rename to libraries/glui/glui-2.35/src/glui_bitmaps.cpp
diff --git a/thirdparty/glui/2.36/src/glui_button.cpp b/libraries/glui/glui-2.35/src/glui_button.cpp
similarity index 98%
rename from thirdparty/glui/2.36/src/glui_button.cpp
rename to libraries/glui/glui-2.35/src/glui_button.cpp
index a414cd8f161dd96a989dc00ffaeddad9ecf3ef0e..4b128cc04956d25352ec1a5fbf410ed8d79fffe1 100644
--- a/thirdparty/glui/2.36/src/glui_button.cpp
+++ b/libraries/glui/glui-2.35/src/glui_button.cpp
@@ -136,7 +136,7 @@ void     GLUI_Button::draw_text( int sunken )
 {
   int string_width;
 
-  glColor3ubv( glui->bkgd_color );
+  glColor3ub( glui->bkgd_color.r, glui->bkgd_color.g, glui->bkgd_color.b );
   glDisable( GL_CULL_FACE );
   glBegin( GL_QUADS );
   glVertex2i( 2, 2 );         glVertex2i( w-2, 2 );
diff --git a/thirdparty/glui/2.36/src/glui_checkbox.cpp b/libraries/glui/glui-2.35/src/glui_checkbox.cpp
similarity index 81%
rename from thirdparty/glui/2.36/src/glui_checkbox.cpp
rename to libraries/glui/glui-2.35/src/glui_checkbox.cpp
index 12d619e6904290b9e9e92c5d0e124ce289847a3e..ed77b3638347c16bd835d1f2c044821d1e514621 100644
--- a/thirdparty/glui/2.36/src/glui_checkbox.cpp
+++ b/libraries/glui/glui-2.35/src/glui_checkbox.cpp
@@ -1,7 +1,7 @@
 
 /****************************************************************************
   
-  GLUI User Interface Toolkit 
+  GLUI User Interface Toolkit (LGPL)
   ---------------------------
 
      glui_checkbox - GLUI_Checkbox control class
@@ -14,21 +14,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
 
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
 
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
@@ -150,7 +148,7 @@ void    GLUI_Checkbox::draw_active_area( void )
     glLineStipple( 1, 0x5555 );
     glColor3f( 0., 0., 0. );
   } else {
-    glColor3ubv( glui->bkgd_color );
+    glColor3ub( glui->bkgd_color.r, glui->bkgd_color.g, glui->bkgd_color.b );
   }
 
   glBegin( GL_LINE_LOOP );
diff --git a/thirdparty/glui/2.36/src/glui_column.cpp b/libraries/glui/glui-2.35/src/glui_column.cpp
similarity index 71%
rename from thirdparty/glui/2.36/src/glui_column.cpp
rename to libraries/glui/glui-2.35/src/glui_column.cpp
index 8932fedb2bfb8c6b98212bb80c9285bf7286538c..29901bc3d3e20cdd3f000db9cebfb690d86accf0 100644
--- a/thirdparty/glui/2.36/src/glui_column.cpp
+++ b/libraries/glui/glui-2.35/src/glui_column.cpp
@@ -12,21 +12,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_commandline.cpp b/libraries/glui/glui-2.35/src/glui_commandline.cpp
similarity index 84%
rename from thirdparty/glui/2.36/src/glui_commandline.cpp
rename to libraries/glui/glui-2.35/src/glui_commandline.cpp
index 1237fcb27a13d04bdf9a1ffbc0cdb48d875cffcd..7586598254676efd38a24870768c073c4c8b51bf 100644
--- a/thirdparty/glui/2.36/src/glui_commandline.cpp
+++ b/libraries/glui/glui-2.35/src/glui_commandline.cpp
@@ -10,21 +10,20 @@
 
   Copyright (c) 1998 Paul Rademacher, 2005 William Baxter
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA
 
   This program is -not- in the public domain.
 
diff --git a/thirdparty/glui/2.36/src/glui_control.cpp b/libraries/glui/glui-2.35/src/glui_control.cpp
similarity index 95%
rename from thirdparty/glui/2.36/src/glui_control.cpp
rename to libraries/glui/glui-2.35/src/glui_control.cpp
index 0542fdbc68ab8e311b094f5250dd87286b54fef7..80248e77b648475119a58005341e55e0c0686ba3 100644
--- a/thirdparty/glui/2.36/src/glui_control.cpp
+++ b/libraries/glui/glui-2.35/src/glui_control.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
@@ -76,7 +74,7 @@ void GLUI_Control::set_to_bkgd_color( void )
   if ( NOT glui )
     return;
 
-  glColor3ubv( glui->bkgd_color );
+  glColor3ub( glui->bkgd_color.r, glui->bkgd_color.g, glui->bkgd_color.b );
 }
 
 /******** GLUI_Control::draw_box_inwards_outline() ********/
@@ -808,6 +806,13 @@ void GLUI_Control::sync_live(int recurse, int draw_it)
         changed = true;
       }
     } 
+    else if ( live_type == GLUI_LIVE_DOUBLE ) {
+      if ( *((double*)ptr_val) != last_live_double ) {
+        set_double_val( *((double*)ptr_val) );
+        last_live_double = *((double*)ptr_val);
+        changed = true;
+      }
+    } 
     else if ( live_type == GLUI_LIVE_TEXT ) {
       if ( last_live_text.compare((const char*)ptr_val) != 0 ) {
         set_text( (char*) ptr_val );
@@ -900,6 +905,10 @@ void GLUI_Control::output_live(int update_main_gfx)
     *((float*)ptr_val) = float_val;
     last_live_float    = float_val;
   } 
+  else if ( live_type == GLUI_LIVE_DOUBLE ) {
+    *((double*)ptr_val) = double_val;
+    last_live_double    = double_val;
+  } 
   else if ( live_type == GLUI_LIVE_TEXT ) {
     strncpy( (char*) ptr_val, text.c_str(), text.length()+1);
     last_live_text =  text;
@@ -917,8 +926,6 @@ void GLUI_Control::output_live(int update_main_gfx)
       fp++;
     }
   }
-  else if ( live_type == GLUI_LIVE_DOUBLE ) {
-  }
 
   /** Update the main gfx window? **/
   if ( update_main_gfx AND this->glui != NULL ) {
@@ -967,6 +974,10 @@ void GLUI_Control::init_live()
     set_float_val( *((float*)ptr_val) );
     last_live_float = *((float*)ptr_val);
   } 
+  else if ( live_type == GLUI_LIVE_DOUBLE ) {
+    set_double_val( *((double*)ptr_val) );
+    last_live_double = *((double*)ptr_val);
+  } 
   else if ( live_type == GLUI_LIVE_TEXT ) {
     set_text( (const char*) ptr_val );
     last_live_text = (const char*) ptr_val;
diff --git a/thirdparty/glui/2.36/src/glui_edittext.cpp b/libraries/glui/glui-2.35/src/glui_edittext.cpp
similarity index 85%
rename from thirdparty/glui/2.36/src/glui_edittext.cpp
rename to libraries/glui/glui-2.35/src/glui_edittext.cpp
index feddd8e5e2ca6d45fc6c70fe84461a164227f032..d804fa60faa1bd350ef07a41b0e977f6ebe57e0b 100644
--- a/thirdparty/glui/2.36/src/glui_edittext.cpp
+++ b/libraries/glui/glui-2.35/src/glui_edittext.cpp
@@ -1,5 +1,5 @@
 /****************************************************************************
-
+  
   GLUI User Interface Toolkit
   ---------------------------
 
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied
-  warranty. In no event will the authors be held liable for any damages
-  arising from the use of this software.
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
 
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
 
-  1. The origin of this software must not be misrepresented; you must not
-  claim that you wrote the original software. If you use this software
-  in a product, an acknowledgment in the product documentation would be
-  appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-  misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
@@ -55,6 +53,9 @@ GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
   else if (data_type == GLUI_EDITTEXT_FLOAT) {
     live_type = GLUI_LIVE_FLOAT;
   }
+  else if (data_type == GLUI_EDITTEXT_DOUBLE) {
+    live_type = GLUI_LIVE_DOUBLE;
+  }
   common_construct( parent, name, data_type, live_type, live_var, id, callback );
 }
 
@@ -87,6 +88,15 @@ GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
 /****************************** GLUI_EditText::GLUI_EditText() **********/
 
 GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
+                              double *live_var,
+                              int id, GLUI_CB callback )
+{
+  common_construct( parent, name, GLUI_EDITTEXT_DOUBLE, GLUI_LIVE_DOUBLE, live_var, id, callback);
+}
+
+/****************************** GLUI_EditText::GLUI_EditText() **********/
+
+GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, 
                               char *live_var,
                               int id, GLUI_CB callback )
 {
@@ -95,7 +105,7 @@ GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
 
 /****************************** GLUI_EditText::GLUI_EditText() **********/
 
-GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
+GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name, 
                               std::string &live_var,
                               int id, GLUI_CB callback )
 {
@@ -104,19 +114,19 @@ GLUI_EditText::GLUI_EditText( GLUI_Node *parent, const char *name,
 
 /****************************** GLUI_EditText::common_construct() **********/
 
-void GLUI_EditText::common_construct( GLUI_Node *parent, const char *name,
-                                      int data_t, int live_t, void *data, int id,
+void GLUI_EditText::common_construct( GLUI_Node *parent, const char *name, 
+                                      int data_t, int live_t, void *data, int id, 
                                       GLUI_CB cb )
 {
   common_init();
   set_name( name );
-
+    
   live_type   = live_t;
   data_type   = data_t;
   ptr_val     = data;
   user_id     = id;
   callback    = cb;
-
+    
 
   if ( live_type == GLUI_LIVE_INT) {
     if ( data == NULL )
@@ -127,6 +137,12 @@ void GLUI_EditText::common_construct( GLUI_Node *parent, const char *name,
     if ( data == NULL )
       set_float_val(float_val);   /** Set to some default, in case of no live var **/
   }
+  else if ( live_type == GLUI_LIVE_DOUBLE ) {
+    num_periods = 1;
+    if ( data == NULL )
+      set_double_val(double_val);   /** Set to some default, in case of no live var **/
+  }
+
 
   parent->add_control( this );
 
@@ -141,7 +157,7 @@ int    GLUI_EditText::mouse_down_handler( int local_x, int local_y )
 
   if ( debug )    dump( stdout, "-> MOUSE DOWN" );
 
-  tmp_insertion_pt = find_insertion_pt( local_x, local_y );
+  tmp_insertion_pt = find_insertion_pt( local_x, local_y );  
   if ( tmp_insertion_pt == -1 ) {
     if ( glui )
       glui->deactivate_current_control(  );
@@ -176,23 +192,23 @@ int    GLUI_EditText::mouse_held_down_handler( int local_x, int local_y,
 {
   int tmp_pt;
 
-  if ( NOT new_inside )
+  if ( NOT new_inside ) 
     return false;
 
   if ( debug )    dump( stdout, "-> HELD DOWN" );
-
+  
   tmp_pt = find_insertion_pt( local_x, local_y );
-
+  
   if ( tmp_pt == -1 AND sel_end != 0 ) {    /* moved mouse past left edge */
     special_handler( GLUT_KEY_LEFT, GLUT_ACTIVE_SHIFT );
   }
-  else if ( tmp_pt == substring_end+1 AND sel_end != (int) text.length()) {
+  else if ( tmp_pt == substring_end+1 AND sel_end != (int) text.length()) {    
     /* moved mouse past right edge */
-    special_handler( GLUT_KEY_RIGHT, GLUT_ACTIVE_SHIFT );
+    special_handler( GLUT_KEY_RIGHT, GLUT_ACTIVE_SHIFT );    
   }
   else if ( tmp_pt != -1 AND tmp_pt != sel_end ) {
     sel_end = insertion_pt = tmp_pt;
-
+    
     update_and_draw_text();
   }
 
@@ -232,12 +248,12 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
     return true;
   }
   else if ( (key == 127 AND !ctrl_down) OR  /* FORWARD DELETE */
-            ( key == CTRL('d') AND modifiers == GLUT_ACTIVE_CTRL) )
+            ( key == CTRL('d') AND modifiers == GLUT_ACTIVE_CTRL) ) 
   {
     if ( sel_start == sel_end ) {   /* no selection */
       if ( insertion_pt < (int)text.length() ) {
         /*** See if we're deleting a period in a float data-type box ***/
-        if ( data_type == GLUI_EDITTEXT_FLOAT AND text[insertion_pt]=='.' )
+        if ( ((data_type == GLUI_EDITTEXT_FLOAT) OR (data_type == GLUI_EDITTEXT_DOUBLE)) AND text[insertion_pt]=='.' )
           num_periods--;
 
         /*** Shift over string first ***/
@@ -266,7 +282,7 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
     if ( sel_start == sel_end ) {   /* no selection */
       if ( insertion_pt > 0 ) {
         /*** See if we're deleting a period in a float data-type box ***/
-        if ( data_type == GLUI_EDITTEXT_FLOAT AND text[insertion_pt-1]=='.' )
+        if ( ((data_type == GLUI_EDITTEXT_FLOAT) OR (data_type == GLUI_EDITTEXT_DOUBLE)) AND text[insertion_pt-1]=='.' )
           num_periods--;
 
         /*** Shift over string first ***/
@@ -280,7 +296,7 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
       sel_start = sel_end = insertion_pt;
     }
   }
-  else if ( modifiers == GLUT_ACTIVE_CTRL )  /* CTRL ONLY */
+  else if ( modifiers == GLUT_ACTIVE_CTRL )  /* CTRL ONLY */ 
   {
     /* Ctrl-key bindings */
     if ( key == CTRL('a') ) {
@@ -302,7 +318,7 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
       return special_handler( GLUT_KEY_DOWN, 0 );
     }
     else if ( key == CTRL('u') ) { /* ERASE LINE */
-      insertion_pt = 0;
+      insertion_pt = 0;  
       text.erase(0,text.length());
       sel_start = sel_end = 0;
     }
@@ -321,16 +337,16 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
     }
   }
   else if ( (modifiers & GLUT_ACTIVE_CTRL) OR
-            (modifiers & GLUT_ACTIVE_ALT) )
+            (modifiers & GLUT_ACTIVE_ALT) ) 
   {
     /** ignore other keys with modifiers */
     return true;
   }
-  else { /* Regular key */
+  else { /* Regular key */    
     regular_key = true;
 
     /** Check if we only accept numbers **/
-    if (data_type == GLUI_EDITTEXT_FLOAT ) {
+    if ((data_type == GLUI_EDITTEXT_FLOAT ) OR (data_type == GLUI_EDITTEXT_DOUBLE)) {
       if ( (key < '0' OR key > '9') AND key != '.' AND key != '-' )
         return true;
 
@@ -356,7 +372,7 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
           a period.  Check whether the period is contained within
           is current selection (thus it will be safely replaced) **/
 
-          int period_found = false;
+          int period_found = false; 
           if ( sel_start != sel_end ) {
             for( i=MIN(sel_end,sel_start); i<MAX(sel_start,sel_end); i++ ) {
               /*  printf( "%c ", text[i] );              */
@@ -373,8 +389,8 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
             return true;
         }
       }
-    }
-    else if (data_type == GLUI_EDITTEXT_INT)
+    } 
+    else if (data_type == GLUI_EDITTEXT_INT)	
     {
       if ( (key < '0' OR key > '9') AND key != '-' )
         return true;
@@ -394,7 +410,7 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
       }
     }
 
-    /** This is just to get rid of warnings - the flag regular_key is
+    /** This is just to get rid of warnings - the flag regular_key is 
       set if the key was not a backspace, return, whatever.  But I
       believe if we're here, we know it was a regular key anyway */
     if ( regular_key ) {
@@ -419,10 +435,10 @@ int    GLUI_EditText::key_handler( unsigned char key,int modifiers )
   }
 
   /******** Now redraw text ***********/
-  /* Hack to prevent text box from being cleared first **/
+  /* Hack to prevent text box from being cleared first **/  
   /**  int substring_change =  update_substring_bounds();
-  draw_text_only =
-  (NOT substring_change AND NOT has_selection AND regular_key );
+  draw_text_only = 
+  (NOT substring_change AND NOT has_selection AND regular_key ); 
   */
 
   draw_text_only = false;  /** Well, hack is not yet working **/
@@ -472,6 +488,7 @@ void    GLUI_EditText::deactivate( void )
 {
   int    new_int_val;
   float  new_float_val;
+  double  new_double_val;
 
   active = false;
 
@@ -481,7 +498,7 @@ void    GLUI_EditText::deactivate( void )
   if ( debug )
     dump( stdout, "-> DISACTIVATE" );
 
-  sel_start = sel_end = insertion_pt = -1;
+  sel_start = sel_end = insertion_pt = -1; 
 
   /***** Retrieve the current value from the text *****/
   /***** The live variable will be updated by set_text() ****/
@@ -493,6 +510,14 @@ void    GLUI_EditText::deactivate( void )
 
     set_float_val( new_float_val );
   }
+  else if ( data_type == GLUI_EDITTEXT_DOUBLE ) {
+    if ( text.length() == 0 ) /* zero-length string - make it "0.0" */
+      text = "0.0";
+
+    new_double_val = atof( text.c_str() );
+
+    set_double_val( new_double_val );
+  }
   else if ( data_type == GLUI_EDITTEXT_INT ) {
     if ( text.length() == 0 ) /* zero-length string - make it "0" */
       text = "0";
@@ -501,7 +526,7 @@ void    GLUI_EditText::deactivate( void )
 
     set_int_val( new_int_val );
   }
-  else
+  else 
     if ( data_type == GLUI_EDITTEXT_TEXT ) {
       set_text(text); /* This will force callbacks and gfx refresh */
     }
@@ -514,17 +539,17 @@ void    GLUI_EditText::deactivate( void )
   /***** Now do callbacks if value changed ******/
   if ( orig_text != text ) {
     this->execute_callback();
-
+    
     if ( 0 ) {
       /* THE CODE BELOW IS FROM WHEN SPINNER ALSO MAINTAINED CALLBACKS    */
-      if ( spinner == NULL ) {   /** Are we independent of a spinner?  **/
+      if ( spinner == NULL ) {   /** Are we independent of a spinner?  **/  
         if ( callback ) {
-          callback( this );
-        }
-      }
-      else {                      /* We're attached to a spinner */
-        spinner->do_callbacks();  /* Let the spinner do the callback stuff */
-      }
+          callback( this );              
+        }              
+      }              
+      else {                      /* We're attached to a spinner */              
+        spinner->do_callbacks();  /* Let the spinner do the callback stuff */  
+      }              
     }
   }
 
@@ -545,7 +570,7 @@ void    GLUI_EditText::draw( int x, int y )
   glBegin( GL_LINES );
   glColor3f( .5, .5, .5 );
   glVertex2i( text_x_offset, 0 );     glVertex2i( w, 0 );
-  glVertex2i( text_x_offset, 0 );     glVertex2i( text_x_offset, h );
+  glVertex2i( text_x_offset, 0 );     glVertex2i( text_x_offset, h );     
 
   glColor3f( 1., 1., 1. );
   glVertex2i( text_x_offset, h );     glVertex2i( w, h );
@@ -566,7 +591,7 @@ void    GLUI_EditText::draw( int x, int y )
   /** Find where to draw the text **/
   update_substring_bounds();
   draw_text(0,0);
-
+  
   draw_insertion_pt();
 }
 
@@ -584,8 +609,8 @@ int    GLUI_EditText::update_substring_bounds( void )
   old_end = substring_end;
 
   /*** Calculate the width of the usable area of the edit box ***/
-  box_width = MAX( this->w - this->text_x_offset
-		   - 4     /*  2 * the two-line box border */
+  box_width = MAX( this->w - this->text_x_offset 
+		   - 4     /*  2 * the two-line box border */ 
 		   - 2 * GLUI_EDITTEXT_BOXINNERMARGINX, 0 );
 
   CLAMP( substring_end, 0, MAX(text_len-1,0) );
@@ -593,7 +618,7 @@ int    GLUI_EditText::update_substring_bounds( void )
 
   if ( debug )    dump( stdout, "-> UPDATE SS" );
 
-  if ( insertion_pt >= 0 AND
+  if ( insertion_pt >= 0 AND 
        insertion_pt < substring_start ) {   /* cursor moved left */
     substring_start = insertion_pt;
 
@@ -613,7 +638,7 @@ int    GLUI_EditText::update_substring_bounds( void )
       while ( substring_width( substring_start, substring_end ) > box_width )
 	substring_end--;
 
-      while(substring_end < text_len-1
+      while(substring_end < text_len-1 
             AND substring_width( substring_start, substring_end ) <= box_width)
       	substring_end++;
     }
@@ -633,7 +658,7 @@ int    GLUI_EditText::update_substring_bounds( void )
 
   if ( substring_start == old_start AND substring_end == old_end )
     return false;  /*** bounds did not change ***/
-  else
+  else 
     return true;   /*** bounds did change ***/
 }
 
@@ -643,7 +668,7 @@ int    GLUI_EditText::update_substring_bounds( void )
 void    GLUI_EditText::update_x_offsets( void )
 {
 }
-
+ 
 
 /********************************* GLUI_EditText::draw_text() ****************/
 
@@ -702,14 +727,14 @@ void    GLUI_EditText::draw_text( int x, int y )
     glVertex2i( sel_x_end, h-2 );    glVertex2i( sel_x_start, h-2 );
     glEnd();
   }
-
+   
 
   if ( sel_start == sel_end ) {   /* No current selection */
     if ( enabled )
       glColor3b( 0, 0, 0 );
     else
       glColor3b( 32, 32, 32 );
-
+      
     glRasterPos2i( text_x, 13);
     for( i=substring_start; i<=substring_end; i++ ) {
       glutBitmapCharacter( get_font(), this->text[i] );
@@ -728,12 +753,12 @@ void    GLUI_EditText::draw_text( int x, int y )
 	glRasterPos2i( x, 13);
 	glutBitmapCharacter( get_font(), this->text[i] );
       }
-
+      
       x += char_width( text[i] );
     }
   }
 
-  if ( debug )    dump( stdout, "<- DRAW_TEXT" );
+  if ( debug )    dump( stdout, "<- DRAW_TEXT" );  
 }
 
 
@@ -752,14 +777,14 @@ int  GLUI_EditText::find_insertion_pt( int x, int y )
   /* We move from right to left, looking to see if the mouse was clicked
      to the right of the ith character */
 
-  curr_x = this->x_abs + text_x_offset
+  curr_x = this->x_abs + text_x_offset 
     + substring_width( substring_start, substring_end )
     + 2                             /* The edittext box has a 2-pixel margin */
     + GLUI_EDITTEXT_BOXINNERMARGINX;   /** plus this many pixels blank space
 					 between the text and the box       **/
 
   /*** See if we clicked in an empty box ***/
-  if ( (int) text.length() == 0 )
+  if ( (int) text.length() == 0 ) 
     return 0;
 
   /** find mouse click in text **/
@@ -768,7 +793,7 @@ int  GLUI_EditText::find_insertion_pt( int x, int y )
 
     if ( x > curr_x ) {
       /*      printf( "-> %d\n", i );              */
-
+      
       return i+1;
     }
   }
@@ -780,7 +805,7 @@ int  GLUI_EditText::find_insertion_pt( int x, int y )
   if ( 0 ) {
     if ( x > (x_abs + text_x_offset + 2 ) )
       return substring_start;
-
+    
     return -1; /* Nothing found */
   }
 }
@@ -807,15 +832,15 @@ void     GLUI_EditText::draw_insertion_pt( void )
 
   /*    printf( "insertion pt: %d\n", insertion_pt );              */
 
-  curr_x = this->x_abs + text_x_offset
+  curr_x = this->x_abs + text_x_offset 
     + substring_width( substring_start, substring_end )
     + 2                             /* The edittext box has a 2-pixel margin */
     + GLUI_EDITTEXT_BOXINNERMARGINX;   /** plus this many pixels blank space
 					 between the text and the box       **/
 
   for( i=substring_end; i>=insertion_pt; i-- ) {
-    curr_x -= char_width( text[i] );
-  }
+    curr_x -= char_width( text[i] ); 
+  }  
 
   glColor3f( 0.0, 0.0, 0.0 );
   glBegin( GL_LINE_LOOP );
@@ -846,11 +871,11 @@ int  GLUI_EditText::substring_width( int start, int end )
   width = 0;
 
   for( i=start; i<=end; i++ )
-    width += char_width( text[i] );
+    width += char_width( text[i] ); 
 
   return width;
 }
-
+ 
 
 /***************************** GLUI_EditText::update_and_draw_text() ********/
 
@@ -872,11 +897,11 @@ int    GLUI_EditText::special_handler( int key,int modifiers )
 {
   if ( NOT glui )
     return false;
-
+  
   if ( debug )
-    printf( "SPECIAL:%d - mod:%d   subs:%d/%d  ins:%d  sel:%d/%d\n",
+    printf( "SPECIAL:%d - mod:%d   subs:%d/%d  ins:%d  sel:%d/%d\n", 
 	    key, modifiers, substring_start, substring_end,insertion_pt,
-	    sel_start, sel_end );
+	    sel_start, sel_end );	 
 
   if ( key == GLUT_KEY_LEFT ) {
     if ( (modifiers & GLUT_ACTIVE_CTRL) != 0 ) {
@@ -904,17 +929,17 @@ int    GLUI_EditText::special_handler( int key,int modifiers )
   /*** Update selection if shift key is down ***/
   if ( (modifiers & GLUT_ACTIVE_SHIFT ) != 0 )
     sel_end = insertion_pt;
-  else
+  else 
     sel_start = sel_end = insertion_pt;
+  
 
-
-  CLAMP( insertion_pt, 0, (int) text.length()); /* Make sure insertion_pt
+  CLAMP( insertion_pt, 0, (int) text.length()); /* Make sure insertion_pt 
 						                                      is in bounds */
-  CLAMP( sel_start, 0, (int) text.length()); /* Make sure insertion_pt
+  CLAMP( sel_start, 0, (int) text.length()); /* Make sure insertion_pt 
 						                                    is in bounds */
-  CLAMP( sel_end, 0, (int) text.length()); /* Make sure insertion_pt
+  CLAMP( sel_end, 0, (int) text.length()); /* Make sure insertion_pt 
 					                                     is in bounds */
-
+					      
   /******** Now redraw text ***********/
   if ( can_draw())
     update_and_draw_text();
@@ -933,12 +958,12 @@ int    GLUI_EditText::special_handler( int key,int modifiers )
 int    GLUI_EditText::find_word_break( int start, int direction )
 {
   int    i, j;
-  const char   *breaks = " :-.,";
+  char   *breaks = " :-.,";
   int     num_break_chars = (int)strlen(breaks), text_len = (int)text.length();
   int     new_pt;
 
   /** If we're moving left, we have to start two back, in case we're either
-  already at the beginning of a word, or on a separating token.
+  already at the beginning of a word, or on a separating token.  
   Otherwise, this function would just return the word we're already at **/
   if ( direction == -1 ) {
     start -= 2;
@@ -981,7 +1006,7 @@ void   GLUI_EditText::clear_substring( int start, int end )
   printf( "'\n" ); flushout;
   */
   /*** See if we're deleting a period in a float data-type box ***/
-  if ( data_type == GLUI_EDITTEXT_FLOAT ) {
+  if (( data_type == GLUI_EDITTEXT_FLOAT ) OR ( data_type == GLUI_EDITTEXT_DOUBLE ) ){
     for( i=start; i<end; i++ )
       if ( text[i] == '.' )
         num_periods = 0;
@@ -1010,8 +1035,9 @@ void   GLUI_EditText::update_size( void )
   text_x_offset += delta;
   /*  w += delta;              */
 
-  if ( data_type == GLUI_EDITTEXT_TEXT OR
-       data_type == GLUI_EDITTEXT_FLOAT) {
+  if ( data_type == GLUI_EDITTEXT_TEXT OR 
+       data_type == GLUI_EDITTEXT_FLOAT OR
+       data_type == GLUI_EDITTEXT_DOUBLE) {
     if ( w < text_x_offset + GLUI_EDITTEXT_MIN_TEXT_WIDTH )
       w = text_x_offset + GLUI_EDITTEXT_MIN_TEXT_WIDTH;
   }
@@ -1039,6 +1065,7 @@ void    GLUI_EditText::set_text( const char *new_text )
   /** Update the spinner, if we have one **/
   if ( spinner ) {
     spinner->float_val = this->float_val;
+    spinner->double_val = this->double_val;
     spinner->int_val   = this->int_val;
   }
 
@@ -1055,7 +1082,7 @@ void   GLUI_EditText::set_float_val( float new_val )
     /*** Clamp the new value to the existing limits ***/
 
     CLAMP( new_val, float_low, float_high );
-  }
+  } 
   else if ( has_limits == GLUI_LIMIT_WRAP ) {
     /*** Clamp the value cyclically to the limits - that is, if the
       value exceeds the max, set it the the minimum, and conversely ***/
@@ -1067,11 +1094,37 @@ void   GLUI_EditText::set_float_val( float new_val )
   }
 
   float_val = new_val;
+  double_val = (double)new_val;
   int_val   = (int) new_val;  /* Mirror the value as an int, too */
-
+  
   set_numeric_text();
 }
 
+/******************************* GLUI_EditText::set_float_val() ************/
+
+void   GLUI_EditText::set_double_val( double new_val )
+{
+  if ( has_limits == GLUI_LIMIT_CLAMP ) {
+    /*** Clamp the new value to the existing limits ***/
+
+    CLAMP( new_val, double_low, double_high );
+  } 
+  else if ( has_limits == GLUI_LIMIT_WRAP ) {
+    /*** Clamp the value cyclically to the limits - that is, if the
+      value exceeds the max, set it the the minimum, and conversely ***/
+
+    if ( new_val < double_low )
+      new_val = double_high;
+    if ( new_val > double_high )
+      new_val = double_low;
+  }
+
+  double_val = new_val;
+  float_val = (float)new_val;
+  int_val   = (int) new_val;  /* Mirror the value as an int, too */
+  
+  set_numeric_text();
+}
 
 /********************************** GLUI_EditText::set_int_val() ************/
 
@@ -1094,6 +1147,7 @@ void   GLUI_EditText::set_int_val( int new_val )
 
   int_val   = new_val;
   float_val = (float) new_val;   /* We mirror the value as a float, too */
+  double_val = (double) new_val;   /* We mirror the value as a double, too */
 
   set_numeric_text();
 }
@@ -1106,14 +1160,32 @@ void GLUI_EditText::set_float_limits( float low, float high, int limit_type )
   has_limits  = limit_type;
   float_low   = low;
   float_high  = high;
-
+  
   if ( NOT IN_BOUNDS( float_val, float_low, float_high ))
     set_float_val( float_low );
 
   int_low     = (int) float_low;
   int_high    = (int) float_high;
+  double_low = (double) float_low;
+  double_high = (double) float_high;
 }
 
+/********************************* GLUI_EditText::set_float_limits() *********/
+
+void GLUI_EditText::set_double_limits( double low, double high, int limit_type )
+{
+  has_limits  = limit_type;
+  double_low   = low;
+  double_high  = high;
+  
+  if ( NOT IN_BOUNDS( double_val, double_low, double_high ))
+    set_double_val( double_low );
+
+  int_low     = (int) double_low;
+  int_high    = (int) double_high;
+  float_low     = (float) double_low;
+  float_high    = (float) double_high;
+}
 
 /*********************************** GLUI_EditText::set_int_limits() *********/
 
@@ -1128,6 +1200,8 @@ void   GLUI_EditText::set_int_limits( int low, int high, int limit_type )
 
   float_low   = (float) int_low;
   float_high  = (float) int_high;
+  double_low   = (double) int_low;
+  double_high  = (double) int_high;
 }
 
 
@@ -1138,8 +1212,11 @@ void    GLUI_EditText::set_numeric_text( void )
   char buf_num[200];
   int  i, text_len;
 
-  if ( data_type == GLUI_EDITTEXT_FLOAT ) {
-    sprintf( buf_num, "%#g", float_val );
+  if (( data_type == GLUI_EDITTEXT_FLOAT ) OR ( data_type == GLUI_EDITTEXT_DOUBLE )){
+    if ( data_type == GLUI_EDITTEXT_FLOAT )
+      sprintf( buf_num, "%#g", float_val );
+    else
+      sprintf( buf_num, "%#g", double_val );
 
     num_periods = 0;
     text_len = (int) strlen(buf_num);
@@ -1153,7 +1230,7 @@ void    GLUI_EditText::set_numeric_text( void )
       for ( i=text_len-1; i>0; i-- ) {
         if ( buf_num[i] == '0' AND buf_num[i-1] != '.' )
           buf_num[i] = '\0';
-        else
+        else 
           break;
       }
     }
@@ -1163,7 +1240,7 @@ void    GLUI_EditText::set_numeric_text( void )
     sprintf( buf_num, "%d", int_val );
     set_text( buf_num );
   }
-
+    
 }
 
 
@@ -1171,9 +1248,9 @@ void    GLUI_EditText::set_numeric_text( void )
 
 void   GLUI_EditText::dump( FILE *out, const char *name )
 {
-  fprintf( out,
+  fprintf( out, 
            "%s (edittext@%p):  ins_pt:%d  subs:%d/%d  sel:%d/%d   len:%d\n",
-           name, this,
+           name, this, 
            insertion_pt,
            substring_start,
            substring_end,
diff --git a/thirdparty/glui/2.36/src/glui_filebrowser.cpp b/libraries/glui/glui-2.35/src/glui_filebrowser.cpp
similarity index 80%
rename from thirdparty/glui/2.36/src/glui_filebrowser.cpp
rename to libraries/glui/glui-2.35/src/glui_filebrowser.cpp
index 20980e390d940e6e2caeae889f46754ecff2deb3..d4265413ce3effa7a68abf76e05e49d4654c7934 100644
--- a/thirdparty/glui/2.36/src/glui_filebrowser.cpp
+++ b/libraries/glui/glui-2.35/src/glui_filebrowser.cpp
@@ -10,22 +10,9 @@
 
   Copyright (c) 1998 Paul Rademacher
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
-
+  This program is freely distributable without licensing fees and is
+  provided without guarantee or warrantee expressed or implied. This
+  program is -not- in the public domain.
 
 *****************************************************************************/
 
@@ -128,7 +115,7 @@ void GLUI_FileBrowser::fbreaddir(const char *d) {
       } while (FindNextFile(hFind, &FN) != 0);
       
       if (GetLastError() == ERROR_NO_MORE_FILES)
-        FindClose(hFind);
+        FindClose(&FN);
       else
         perror("fbreaddir");
     }
diff --git a/thirdparty/glui/2.36/src/glui_img_checkbox_0.c b/libraries/glui/glui-2.35/src/glui_img_checkbox_0.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_checkbox_0.c
rename to libraries/glui/glui-2.35/src/glui_img_checkbox_0.c
diff --git a/thirdparty/glui/2.36/src/glui_img_checkbox_0_dis.c b/libraries/glui/glui-2.35/src/glui_img_checkbox_0_dis.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_checkbox_0_dis.c
rename to libraries/glui/glui-2.35/src/glui_img_checkbox_0_dis.c
diff --git a/thirdparty/glui/2.36/src/glui_img_checkbox_1.c b/libraries/glui/glui-2.35/src/glui_img_checkbox_1.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_checkbox_1.c
rename to libraries/glui/glui-2.35/src/glui_img_checkbox_1.c
diff --git a/thirdparty/glui/2.36/src/glui_img_checkbox_1_dis.c b/libraries/glui/glui-2.35/src/glui_img_checkbox_1_dis.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_checkbox_1_dis.c
rename to libraries/glui/glui-2.35/src/glui_img_checkbox_1_dis.c
diff --git a/thirdparty/glui/2.36/src/glui_img_downarrow.c b/libraries/glui/glui-2.35/src/glui_img_downarrow.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_downarrow.c
rename to libraries/glui/glui-2.35/src/glui_img_downarrow.c
diff --git a/thirdparty/glui/2.36/src/glui_img_leftarrow.c b/libraries/glui/glui-2.35/src/glui_img_leftarrow.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_leftarrow.c
rename to libraries/glui/glui-2.35/src/glui_img_leftarrow.c
diff --git a/thirdparty/glui/2.36/src/glui_img_listbox_down.c b/libraries/glui/glui-2.35/src/glui_img_listbox_down.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_listbox_down.c
rename to libraries/glui/glui-2.35/src/glui_img_listbox_down.c
diff --git a/thirdparty/glui/2.36/src/glui_img_listbox_down.ppm b/libraries/glui/glui-2.35/src/glui_img_listbox_down.ppm
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_listbox_down.ppm
rename to libraries/glui/glui-2.35/src/glui_img_listbox_down.ppm
diff --git a/thirdparty/glui/2.36/src/glui_img_listbox_up.c b/libraries/glui/glui-2.35/src/glui_img_listbox_up.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_listbox_up.c
rename to libraries/glui/glui-2.35/src/glui_img_listbox_up.c
diff --git a/thirdparty/glui/2.36/src/glui_img_listbox_up.ppm b/libraries/glui/glui-2.35/src/glui_img_listbox_up.ppm
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_listbox_up.ppm
rename to libraries/glui/glui-2.35/src/glui_img_listbox_up.ppm
diff --git a/thirdparty/glui/2.36/src/glui_img_listbox_up_dis.c b/libraries/glui/glui-2.35/src/glui_img_listbox_up_dis.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_listbox_up_dis.c
rename to libraries/glui/glui-2.35/src/glui_img_listbox_up_dis.c
diff --git a/thirdparty/glui/2.36/src/glui_img_listbox_up_dis.ppm b/libraries/glui/glui-2.35/src/glui_img_listbox_up_dis.ppm
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_listbox_up_dis.ppm
rename to libraries/glui/glui-2.35/src/glui_img_listbox_up_dis.ppm
diff --git a/thirdparty/glui/2.36/src/glui_img_radiobutton_0.c b/libraries/glui/glui-2.35/src/glui_img_radiobutton_0.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_radiobutton_0.c
rename to libraries/glui/glui-2.35/src/glui_img_radiobutton_0.c
diff --git a/thirdparty/glui/2.36/src/glui_img_radiobutton_0_dis.c b/libraries/glui/glui-2.35/src/glui_img_radiobutton_0_dis.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_radiobutton_0_dis.c
rename to libraries/glui/glui-2.35/src/glui_img_radiobutton_0_dis.c
diff --git a/thirdparty/glui/2.36/src/glui_img_radiobutton_1.c b/libraries/glui/glui-2.35/src/glui_img_radiobutton_1.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_radiobutton_1.c
rename to libraries/glui/glui-2.35/src/glui_img_radiobutton_1.c
diff --git a/thirdparty/glui/2.36/src/glui_img_radiobutton_1_dis.c b/libraries/glui/glui-2.35/src/glui_img_radiobutton_1_dis.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_radiobutton_1_dis.c
rename to libraries/glui/glui-2.35/src/glui_img_radiobutton_1_dis.c
diff --git a/thirdparty/glui/2.36/src/glui_img_rightarrow.c b/libraries/glui/glui-2.35/src/glui_img_rightarrow.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_rightarrow.c
rename to libraries/glui/glui-2.35/src/glui_img_rightarrow.c
diff --git a/thirdparty/glui/2.36/src/glui_img_spindown_0.c b/libraries/glui/glui-2.35/src/glui_img_spindown_0.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_spindown_0.c
rename to libraries/glui/glui-2.35/src/glui_img_spindown_0.c
diff --git a/thirdparty/glui/2.36/src/glui_img_spindown_1.c b/libraries/glui/glui-2.35/src/glui_img_spindown_1.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_spindown_1.c
rename to libraries/glui/glui-2.35/src/glui_img_spindown_1.c
diff --git a/thirdparty/glui/2.36/src/glui_img_spindown_dis.c b/libraries/glui/glui-2.35/src/glui_img_spindown_dis.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_spindown_dis.c
rename to libraries/glui/glui-2.35/src/glui_img_spindown_dis.c
diff --git a/thirdparty/glui/2.36/src/glui_img_spinup_0.c b/libraries/glui/glui-2.35/src/glui_img_spinup_0.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_spinup_0.c
rename to libraries/glui/glui-2.35/src/glui_img_spinup_0.c
diff --git a/thirdparty/glui/2.36/src/glui_img_spinup_1.c b/libraries/glui/glui-2.35/src/glui_img_spinup_1.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_spinup_1.c
rename to libraries/glui/glui-2.35/src/glui_img_spinup_1.c
diff --git a/thirdparty/glui/2.36/src/glui_img_spinup_dis.c b/libraries/glui/glui-2.35/src/glui_img_spinup_dis.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_spinup_dis.c
rename to libraries/glui/glui-2.35/src/glui_img_spinup_dis.c
diff --git a/thirdparty/glui/2.36/src/glui_img_uparrow.c b/libraries/glui/glui-2.35/src/glui_img_uparrow.c
similarity index 100%
rename from thirdparty/glui/2.36/src/glui_img_uparrow.c
rename to libraries/glui/glui-2.35/src/glui_img_uparrow.c
diff --git a/thirdparty/glui/2.36/src/glui_internal.h b/libraries/glui/glui-2.35/src/glui_internal.h
similarity index 71%
rename from thirdparty/glui/2.36/src/glui_internal.h
rename to libraries/glui/glui-2.35/src/glui_internal.h
index 60d08e6eb47fcbe057769a9c5b30a1441ef4bbdc..24f4947989542643e904676904ba0759d222f493 100644
--- a/thirdparty/glui/2.36/src/glui_internal.h
+++ b/libraries/glui/glui-2.35/src/glui_internal.h
@@ -1,20 +1,3 @@
-/*
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source 
-*/
 #ifndef GLUI_INTERNAL_H
 #define GLUI_INTERNAL_H
 
@@ -118,22 +101,5 @@
 /**** Return the ASCII control code given the non-control ASCII character */
 #define CTRL(c) ( (c>=('a'-1)) ? (c-'a'+1) : (c-'A'+1) )
 
-/*
-typedef unsigned char Byte;
-
-#ifndef _RGBC_
-class RGBc 
-{
-    public:
-    Byte r, g, b;
-
-    void set(Byte r,Byte g,Byte b) {this->r=r;this->g=g;this->b=b;}
-
-    RGBc( void ) {}
-    RGBc( Byte r, Byte g, Byte b ) { set( r, g, b ); }
-};
-#define _RGBC_
-#endif
-*/
 
 #endif /* GLUI_INTERNAL_H */
diff --git a/thirdparty/glui/2.36/src/glui_internal_control.h b/libraries/glui/glui-2.35/src/glui_internal_control.h
similarity index 60%
rename from thirdparty/glui/2.36/src/glui_internal_control.h
rename to libraries/glui/glui-2.35/src/glui_internal_control.h
index d454d533227e1bedfae66f83bc5b4fd7752e21a5..7e363aab123cd0f66d1005e2c9450f522e154d34 100644
--- a/thirdparty/glui/2.36/src/glui_internal_control.h
+++ b/libraries/glui/glui-2.35/src/glui_internal_control.h
@@ -2,21 +2,6 @@
  Header file for use by GLUI controls.  
  Everything you need is right here.
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
 
 */
 #ifndef __GLUI_INTERNAL_CONTROL_H
diff --git a/thirdparty/glui/2.36/src/glui_list.cpp b/libraries/glui/glui-2.35/src/glui_list.cpp
similarity index 93%
rename from thirdparty/glui/2.36/src/glui_list.cpp
rename to libraries/glui/glui-2.35/src/glui_list.cpp
index 15b5501263689070b88777a71bad0ae1f0b07daf..6ecf282a08be585ee1cc77c6641eb731bb3b0411 100644
--- a/thirdparty/glui/2.36/src/glui_list.cpp
+++ b/libraries/glui/glui-2.35/src/glui_list.cpp
@@ -10,21 +10,9 @@
 
   Copyright (c) 2004 John Kew
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This program is freely distributable without licensing fees and is
+  provided without guarantee or warrantee expressed or implied. This
+  program is -not- in the public domain.
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_listbox.cpp b/libraries/glui/glui-2.35/src/glui_listbox.cpp
similarity index 92%
rename from thirdparty/glui/2.36/src/glui_listbox.cpp
rename to libraries/glui/glui-2.35/src/glui_listbox.cpp
index 36ca8818cefabfbb11afc043125940c3ec317706..d0d0dda656b40ce47b1d9becb149f310a9cc62df 100644
--- a/thirdparty/glui/2.36/src/glui_listbox.cpp
+++ b/libraries/glui/glui-2.35/src/glui_listbox.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_mouse_iaction.cpp b/libraries/glui/glui-2.35/src/glui_mouse_iaction.cpp
similarity index 86%
rename from thirdparty/glui/2.36/src/glui_mouse_iaction.cpp
rename to libraries/glui/glui-2.35/src/glui_mouse_iaction.cpp
index 00e7dc41e2091f3b42718bc4f951eec37c5f0661..0acd69f7a28dab3744bc3ecad9e3f550e9613708 100644
--- a/thirdparty/glui/2.36/src/glui_mouse_iaction.cpp
+++ b/libraries/glui/glui-2.35/src/glui_mouse_iaction.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_node.cpp b/libraries/glui/glui-2.35/src/glui_node.cpp
similarity index 87%
rename from thirdparty/glui/2.36/src/glui_node.cpp
rename to libraries/glui/glui-2.35/src/glui_node.cpp
index a7010267e843881a8401f4312d111eddcd52174a..a943332f4b6b357934251298f12f05938a9e6453 100644
--- a/thirdparty/glui/2.36/src/glui_node.cpp
+++ b/libraries/glui/glui-2.35/src/glui_node.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_panel.cpp b/libraries/glui/glui-2.35/src/glui_panel.cpp
similarity index 82%
rename from thirdparty/glui/2.36/src/glui_panel.cpp
rename to libraries/glui/glui-2.35/src/glui_panel.cpp
index 5c3b116cb8013665eb2f726910afe3de389a34d8..3aa61ae8cf0e1c87cf353a039e07ba75daf79a00 100644
--- a/thirdparty/glui/2.36/src/glui_panel.cpp
+++ b/libraries/glui/glui-2.35/src/glui_panel.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
@@ -126,7 +124,7 @@ void    GLUI_Panel::draw( int x, int y )
       str_width = string_width(name);
 
       if ( glui )
-	glColor3ubv(glui->bkgd_color);
+	glColor3ub(glui->bkgd_color.r,glui->bkgd_color.g,glui->bkgd_color.b);
       glDisable( GL_CULL_FACE );
       glBegin( GL_QUADS );
       glVertex2i( left-3, 0 );               glVertex2i( left+str_width+3, 0 );
diff --git a/thirdparty/glui/2.36/src/glui_radio.cpp b/libraries/glui/glui-2.35/src/glui_radio.cpp
similarity index 90%
rename from thirdparty/glui/2.36/src/glui_radio.cpp
rename to libraries/glui/glui-2.35/src/glui_radio.cpp
index 4b81f3f94d7c21990e72a776fe85ff9210653d59..aef16685904b75a05b7f8f3e29c62aa996b7fa00 100644
--- a/thirdparty/glui/2.36/src/glui_radio.cpp
+++ b/libraries/glui/glui-2.35/src/glui_radio.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
@@ -337,7 +335,7 @@ void    GLUI_RadioButton::draw_active_area( void )
     glLineStipple( 1, 0x5555 );
     glColor3f( 0., 0., 0. );
   } else {
-    glColor3ubv( glui->bkgd_color );
+    glColor3ub( glui->bkgd_color.r, glui->bkgd_color.g, glui->bkgd_color.b );
   }
 
   glBegin( GL_LINE_LOOP );
diff --git a/thirdparty/glui/2.36/src/glui_rollout.cpp b/libraries/glui/glui-2.35/src/glui_rollout.cpp
similarity index 86%
rename from thirdparty/glui/2.36/src/glui_rollout.cpp
rename to libraries/glui/glui-2.35/src/glui_rollout.cpp
index 559d2bfb599bc43398f1602b5c0ecc79bd7bc438..d29e00641a1b02c669b481f79fa59a8798a053f3 100644
--- a/thirdparty/glui/2.36/src/glui_rollout.cpp
+++ b/libraries/glui/glui-2.35/src/glui_rollout.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
@@ -188,7 +186,7 @@ void   GLUI_Rollout::draw( int x, int y )
   glui->draw_raised_box( left, top, w-left*2, 16 );
 
   if ( glui )
-    glColor3ubv(glui->bkgd_color);
+    glColor3ub(glui->bkgd_color.r,glui->bkgd_color.g,glui->bkgd_color.b);
   glDisable( GL_CULL_FACE );
   glBegin( GL_QUADS );
   glVertex2i( left+1, top+1 );      glVertex2i( right-1, top+1 );
diff --git a/thirdparty/glui/2.36/src/glui_rotation.cpp b/libraries/glui/glui-2.35/src/glui_rotation.cpp
similarity index 93%
rename from thirdparty/glui/2.36/src/glui_rotation.cpp
rename to libraries/glui/glui-2.35/src/glui_rotation.cpp
index b367ac8144de7d5d2a50c4ca113aca45612ac590..b02d743556e7442d3b9ccfe23eae67803962afef 100644
--- a/thirdparty/glui/2.36/src/glui_rotation.cpp
+++ b/libraries/glui/glui-2.35/src/glui_rotation.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_scrollbar.cpp b/libraries/glui/glui-2.35/src/glui_scrollbar.cpp
similarity index 96%
rename from thirdparty/glui/2.36/src/glui_scrollbar.cpp
rename to libraries/glui/glui-2.35/src/glui_scrollbar.cpp
index 1a0e6a2991469ed13beea248bca92674b97cf9aa..f0fb137026ed70e1008b38fa149e7cac5558a5d3 100644
--- a/thirdparty/glui/2.36/src/glui_scrollbar.cpp
+++ b/libraries/glui/glui-2.35/src/glui_scrollbar.cpp
@@ -9,21 +9,9 @@
 
   Copyright (c) 2004 John Kew, 1998 Paul Rademacher
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This program is freely distributable without licensing fees and is
+  provided without guarantee or warrantee expressed or implied. This
+  program is -not- in the public domain.
 
 *****************************************************************************/
 
@@ -334,7 +322,7 @@ void GLUI_Scrollbar::draw_scroll_arrow(int arrowtype, int x, int y)
       return; /* tri is NULL */
   }
 
-  glColor3ubv(glui->bkgd_color);
+  glColor3ubv(&glui->bkgd_color.r);
   glRecti(x,y,x+GLUI_SCROLL_ARROW_WIDTH,y+GLUI_SCROLL_ARROW_HEIGHT);
   if (!offset) {
     glui->draw_raised_box(x,y+1,GLUI_SCROLL_ARROW_WIDTH-1,GLUI_SCROLL_ARROW_HEIGHT-1);
@@ -426,7 +414,7 @@ void GLUI_Scrollbar::draw_scroll() {
 void GLUI_Scrollbar::draw_scroll_box(int x, int y, int w, int h)
 {
   if (!enabled) return;
-  glColor3ubv(glui->bkgd_color);
+  glColor3ubv(&glui->bkgd_color.r);
   glRecti(x,y,x+w,y+h);
   glui->draw_raised_box(x,y, w-1, h-1);
   
diff --git a/thirdparty/glui/2.36/src/glui_separator.cpp b/libraries/glui/glui-2.35/src/glui_separator.cpp
similarity index 64%
rename from thirdparty/glui/2.36/src/glui_separator.cpp
rename to libraries/glui/glui-2.35/src/glui_separator.cpp
index 9a1c1ed770aa2d50b0908764cd020717d777335e..87907d8a38512954823a1b1afb1f9223ef1bb2ba 100644
--- a/thirdparty/glui/2.36/src/glui_separator.cpp
+++ b/libraries/glui/glui-2.35/src/glui_separator.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_spinner.cpp b/libraries/glui/glui-2.35/src/glui_spinner.cpp
similarity index 81%
rename from thirdparty/glui/2.36/src/glui_spinner.cpp
rename to libraries/glui/glui-2.35/src/glui_spinner.cpp
index 2b51917e0fa3483a02b7f50f037856ed7342604b..817cb3775bcaf6ce48ab17d1dda06de14bbdd2fd 100644
--- a/thirdparty/glui/2.36/src/glui_spinner.cpp
+++ b/libraries/glui/glui-2.35/src/glui_spinner.cpp
@@ -10,7 +10,7 @@
      spinner does not explicitly keep track of the current value - this is all
         handled by the underlying edittext control
         -> thus, spinner->sync_live() has no meaning, nor spinner->output_live
-	-> BUT, edittext will alter this spinner's float_val and int_val,
+	-> BUT, edittext will alter this spinner's double_val, float_val and int_val,
 	   so that spinner->get/set will work
 
 
@@ -25,21 +25,19 @@ FIXME: there's a heck of a lot of duplication between this and glui_scrollbar.cp
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
 
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
 
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
@@ -54,6 +52,25 @@ FIXME: there's a heck of a lot of duplication between this and glui_scrollbar.cp
 #define  GLUI_SPINNER_CALLBACK_INTERVAL    1
 
  
+/****************************** spinner_edittext_callback() ******************/
+/*   This function is not used anymore.  It has been replaced by directly    */
+/*   Including an optional pointer to a spinner from an edittext box         */
+
+void  spinner_edittext_callback( int id )
+{
+  GLUI_Spinner *spinner;
+
+  putchar( '.' ); flushout;
+  
+  spinner = (GLUI_Spinner*) id;
+
+  if ( NOT spinner )
+    return;
+
+  spinner->do_callbacks();
+}
+
+
 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
 
 GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name, 
@@ -80,6 +97,14 @@ GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name,
 
 /****************************** GLUI_Spinner::GLUI_Spinner() ****************/
 
+GLUI_Spinner::GLUI_Spinner( GLUI_Node* parent, const char *name, 
+             double *live_var, int id, GLUI_CB callback )
+{
+  common_construct(parent, name, GLUI_SPINNER_DOUBLE, live_var, id, callback);
+}
+
+/****************************** GLUI_Spinner::GLUI_Spinner() ****************/
+
 GLUI_Spinner::GLUI_Spinner( GLUI_Node *parent, const char *name, 
                             int data_t, void *live_var,
                             int id, GLUI_CB callback )
@@ -105,6 +130,9 @@ void GLUI_Spinner::common_construct( GLUI_Node* parent, const char *name,
   else if ( data_t == GLUI_SPINNER_FLOAT ) {
     text_type = GLUI_EDITTEXT_FLOAT;
   }
+  else if ( data_t == GLUI_SPINNER_DOUBLE ) {
+    text_type = GLUI_EDITTEXT_DOUBLE;
+  }
   else {
     assert(0); /* Did not pass in a valid data type */
   }
@@ -150,9 +178,9 @@ int    GLUI_Spinner::mouse_down_handler( int local_x, int local_y )
     go ahead and increment by one for int spinners ***/
   if ( data_type == GLUI_SPINNER_INT ) {
     if ( state == GLUI_SPINNER_STATE_UP )
-      edittext->set_float_val( edittext->float_val + 1.0 );
+      edittext->set_double_val( edittext->float_val + 1.0 );
     else if ( state == GLUI_SPINNER_STATE_DOWN )
-      edittext->set_float_val( edittext->float_val - .9 );
+      edittext->set_double_val( edittext->float_val - .9 );
   }
   
   do_click();  
@@ -293,7 +321,7 @@ void    GLUI_Spinner::draw( int x, int y )
     glLineStipple( 1, 0x5555 );
   }
   else {
-    glColor3ubv( glui->bkgd_color );
+    glColor3ub( glui->bkgd_color.r,glui->bkgd_color.g,glui->bkgd_color.b );
   } 
 
   glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
@@ -346,6 +374,16 @@ void   GLUI_Spinner::set_float_val( float new_val )
   edittext->set_float_val( new_val );
 }
 
+/******************************* GLUI_Spinner::set_double_val() ************/
+
+void   GLUI_Spinner::set_double_val( double new_val )
+{ 
+  if ( NOT edittext )
+    return;
+
+  edittext->set_double_val( new_val );
+}
+
 
 /********************************** GLUI_Spinner::set_int_val() ************/
 
@@ -415,10 +453,10 @@ void    GLUI_Spinner::do_click( void )
       modifier_factor = .01f;
   }
 
-  if ( this->data_type == GLUI_SPINNER_FLOAT OR 1) {
+  if ( this->data_type == GLUI_SPINNER_DOUBLE OR 1) {
     incr = growth * direction * modifier_factor * user_speed;
-    edittext->set_float_val( edittext->float_val + incr );
-    /** Remember, edittext mirrors the float and int values ***/
+    edittext->set_double_val( edittext->double_val + incr );
+    /** Remember, edittext mirrors the double, float and int values ***/
   }
 
   /*** Now update live variable and do callback.  We don't want
@@ -449,10 +487,10 @@ void    GLUI_Spinner::do_drag( int x, int y )
   /*  delta_x = x - last_x;              */
   delta_y = -(y - last_y);
  
-  if ( this->data_type == GLUI_SPINNER_FLOAT OR 1 ) {
+  if ( this->data_type == GLUI_SPINNER_DOUBLE OR 1 ) {
     incr = growth * delta_y * modifier_factor * user_speed;
-    edittext->set_float_val( edittext->float_val + incr );
-    /** Remember, edittext mirrors the float and int values ***/
+    edittext->set_double_val( edittext->double_val + incr );
+    /** Remember, edittext mirrors the double, float and int values ***/
   }
 
   last_x = x;
@@ -499,6 +537,7 @@ void    GLUI_Spinner::do_callbacks( void )
   if ( NOT edittext )
     return;
   this->float_val = edittext->float_val;
+  this->double_val = edittext->double_val;
   this->int_val   = edittext->int_val;
   /*    *******************************************/
 
@@ -510,12 +549,17 @@ void    GLUI_Spinner::do_callbacks( void )
     if ( data_type == GLUI_SPINNER_FLOAT AND float_val == last_float_val ) {
       return;
     }
+
+    if ( data_type == GLUI_SPINNER_DOUBLE AND double_val == last_double_val ) {
+      return;
+    }
   }
   
   this->execute_callback();
 
   last_int_val   = int_val;
   last_float_val = float_val;
+  last_double_val = double_val;
   first_callback = false;
 }
 
@@ -530,6 +574,15 @@ void GLUI_Spinner::set_float_limits( float low, float high, int limit_type )
   edittext->set_float_limits( low, high, limit_type );
 }
 
+/********************************* GLUI_Spinner::set_double_limits() *********/
+
+void GLUI_Spinner::set_double_limits( double low, double high, int limit_type )
+{
+  if ( NOT edittext ) 
+    return;
+
+  edittext->set_double_limits( low, high, limit_type );
+}
 
 /*********************************** GLUI_Spinner::set_int_limits() *********/
 
@@ -546,11 +599,13 @@ void   GLUI_Spinner::set_int_limits( int low, int high, int limit_type )
 
 void    GLUI_Spinner::reset_growth( void )
 {
-  float lo, hi;
+  double lo, hi;
 
   if ( edittext->has_limits == GLUI_LIMIT_NONE ) {
     if ( data_type == GLUI_SPINNER_FLOAT )
       growth = sqrt(ABS(edittext->float_val)) * .05f;
+    else if ( data_type == GLUI_SPINNER_DOUBLE )
+      growth = sqrt(ABS(edittext->double_val)) * .05;
     else if ( data_type == GLUI_SPINNER_INT )
       growth = .4f; 
   }
@@ -560,16 +615,21 @@ void    GLUI_Spinner::reset_growth( void )
       hi = edittext->float_high;
       growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS;
     }
+    else if ( data_type == GLUI_SPINNER_DOUBLE ) {
+      lo = edittext->double_low;
+      hi = edittext->double_high;
+      growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS;
+    }
     else if ( data_type == GLUI_SPINNER_INT ) {
-      lo = (float) edittext->int_low;
-      hi = (float) edittext->int_high;
+      lo = (double) edittext->int_low;
+      hi = (double) edittext->int_high;
       
       growth = (hi-lo) / GLUI_SPINNER_GROWTH_STEPS;
     }
   }
 
-  if ( growth == 0.0f )
-    growth = .001f;
+  if ( growth == 0.0 )
+    growth = .001;
 }
 
 
@@ -577,15 +637,19 @@ void    GLUI_Spinner::reset_growth( void )
 
 void    GLUI_Spinner::increase_growth( void )
 {
-  float hi = 0.0,lo = 0.0;
+  double hi = 0.0,lo = 0.0;
 
   if ( data_type == GLUI_SPINNER_FLOAT ) {
     lo = edittext->float_low;
     hi = edittext->float_high;
   }
+  else if ( data_type == GLUI_SPINNER_DOUBLE ) {
+    lo = edittext->double_low;
+    hi = edittext->double_high;
+  }
   else if ( data_type == GLUI_SPINNER_INT ) {
-    lo = (float) edittext->int_low;
-    hi = (float) edittext->int_high;
+    lo = (double) edittext->int_low;
+    hi = (double) edittext->int_high;
   }
  
   if ( growth < (hi-lo) / GLUI_SPINNER_MIN_GROWTH_STEPS )
@@ -616,6 +680,15 @@ float    GLUI_Spinner::get_float_val( void )
     return 0.0f; 
 }
 
+/********************************** GLUI_Spinner:get_float_val() *************/
+
+double    GLUI_Spinner::get_double_val( void )
+{
+  if (edittext) 
+    return edittext->double_val; 
+  else 
+    return 0.0; 
+}
 
 /********************************** GLUI_Spinner:get_int_val() *************/
 
diff --git a/thirdparty/glui/2.36/src/glui_statictext.cpp b/libraries/glui/glui-2.35/src/glui_statictext.cpp
similarity index 69%
rename from thirdparty/glui/2.36/src/glui_statictext.cpp
rename to libraries/glui/glui-2.35/src/glui_statictext.cpp
index 8d6bebe141655040cf6b56fd8ec63e1099e15c82..21ffa13ad1c0a2d5ba2e2228eacb7bd41c5dd2a8 100644
--- a/thirdparty/glui/2.36/src/glui_statictext.cpp
+++ b/libraries/glui/glui-2.35/src/glui_statictext.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_string.cpp b/libraries/glui/glui-2.35/src/glui_string.cpp
similarity index 52%
rename from thirdparty/glui/2.36/src/glui_string.cpp
rename to libraries/glui/glui-2.35/src/glui_string.cpp
index 4e590072860996e7d492c287f004a78110123299..fbf88ae59da01637c8ae390f097af1849f4d3aad 100644
--- a/thirdparty/glui/2.36/src/glui_string.cpp
+++ b/libraries/glui/glui-2.35/src/glui_string.cpp
@@ -10,21 +10,22 @@
 
   Copyright (c) 1998 Paul Rademacher (this file, Bill Baxter 2005)
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as
+  published by the Free Software Foundation; either version 2.1 of the
+  License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA
+
+  This program is -not- in the public domain.
 
 *****************************************************************************/
 
@@ -45,7 +46,7 @@ GLUI_String& glui_format_str(GLUI_String& str, const char* fmt, ...)
   va_list arg;
   while (1) {
     va_start(arg, fmt);
-    int ret = vsnprintf(buf,bufsz-1,fmt,arg);
+    int ret = vsnprintf(buf,299,fmt,arg);
     va_end(arg);
     if (ret>=0) {
       break;
diff --git a/thirdparty/glui/2.36/src/glui_textbox.cpp b/libraries/glui/glui-2.35/src/glui_textbox.cpp
similarity index 97%
rename from thirdparty/glui/2.36/src/glui_textbox.cpp
rename to libraries/glui/glui-2.35/src/glui_textbox.cpp
index 70f8d9af1299b524d07b0a3022233d15ca1e6378..bb3df5b75cb70121eac434d51e4026aba2e532bd 100644
--- a/thirdparty/glui/2.36/src/glui_textbox.cpp
+++ b/libraries/glui/glui-2.35/src/glui_textbox.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
@@ -321,7 +319,7 @@ int    GLUI_TextBox::key_handler( unsigned char key,int modifiers )
 void GLUI_TextBox::enable( void )
 {
   GLUI_Control::enable();
-  if (scrollbar) scrollbar->enable();
+  scrollbar->enable();
 }
 
 /****************************** GLUI_TextBox::disable() **********/
@@ -329,7 +327,7 @@ void GLUI_TextBox::enable( void )
 void GLUI_TextBox::disable( void )
 {
   GLUI_Control::disable();
-  if (scrollbar) scrollbar->disable();
+  scrollbar->disable();
 }
 
 /****************************** GLUI_TextBox::activate() **********/
@@ -340,11 +338,11 @@ void    GLUI_TextBox::activate( int how )
     dump( stdout, "-> ACTIVATE" );
   active = true;
 
-  orig_text = text;
-
   if ( how == GLUI_ACTIVATE_MOUSE )
     return;  /* Don't select everything if activated with mouse */
 
+  orig_text = text;
+
   sel_start    = 0;
   sel_end      = text.length();
   insertion_pt = 0;
diff --git a/thirdparty/glui/2.36/src/glui_translation.cpp b/libraries/glui/glui-2.35/src/glui_translation.cpp
similarity index 94%
rename from thirdparty/glui/2.36/src/glui_translation.cpp
rename to libraries/glui/glui-2.35/src/glui_translation.cpp
index f262809d5ce5b78f9390857cc0d56557100e134f..43e82d87ad90918a45f6ce19281a536d497c5584 100644
--- a/thirdparty/glui/2.36/src/glui_translation.cpp
+++ b/libraries/glui/glui-2.35/src/glui_translation.cpp
@@ -13,21 +13,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/glui_tree.cpp b/libraries/glui/glui-2.35/src/glui_tree.cpp
similarity index 88%
rename from thirdparty/glui/2.36/src/glui_tree.cpp
rename to libraries/glui/glui-2.35/src/glui_tree.cpp
index 75735b72420e31a1b4fabaed2eb5c84b4019e3dd..f253fa4f9c3fda803ffa87eaeaec59542292d247 100644
--- a/thirdparty/glui/2.36/src/glui_tree.cpp
+++ b/libraries/glui/glui-2.35/src/glui_tree.cpp
@@ -10,19 +10,9 @@
 
   Copyright (c) 1998 Paul Rademacher
 
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  This program is freely distributable without licensing fees and is
+  provided without guarantee or warrantee expressed or implied. This
+  program is -not- in the public domain.
 
 *****************************************************************************/
 
@@ -178,7 +168,7 @@ void   GLUI_Tree::draw( int x, int y )
   glui->draw_raised_box( left, top, 16, 16 );
 
   if ( glui )
-    glColor3ubv(glui->bkgd_color);
+    glColor3ub(glui->bkgd_color.r,glui->bkgd_color.g,glui->bkgd_color.b);
   glDisable( GL_CULL_FACE );
   glBegin( GL_QUADS );
   glVertex2i( left+17, top+1 );      glVertex2i( right-1, top+1 );
diff --git a/thirdparty/glui/2.36/src/glui_treepanel.cpp b/libraries/glui/glui-2.35/src/glui_treepanel.cpp
similarity index 92%
rename from thirdparty/glui/2.36/src/glui_treepanel.cpp
rename to libraries/glui/glui-2.35/src/glui_treepanel.cpp
index 4cbf8c1954fbfe799fd39b0ff1105c1b630c542c..fc3fd4a3518d27f1440643694f84842c6f3c2bf3 100644
--- a/thirdparty/glui/2.36/src/glui_treepanel.cpp
+++ b/libraries/glui/glui-2.35/src/glui_treepanel.cpp
@@ -1,25 +1,6 @@
-/*
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
-*/
-
 #include "GL/glui.h"
 
 
-
 /****************************** GLUI_TreePanel::GLUI_TreePanel() *********/
 
 GLUI_TreePanel::GLUI_TreePanel(GLUI_Node *parent, const char *name, 
diff --git a/libraries/glui/glui-2.35/src/glui_window.cpp b/libraries/glui/glui-2.35/src/glui_window.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..02c2d0d2612e4b98d121eccac883b1b0c6a107bf
--- /dev/null
+++ b/libraries/glui/glui-2.35/src/glui_window.cpp
@@ -0,0 +1,44 @@
+/*
+
+  glui_window.cpp - GLUI_Button control class
+
+  GLUI User Interface Toolkit (LGPL)
+  Copyright (c) 1998 Paul Rademacher
+
+  WWW:    http://sourceforge.net/projects/glui/
+  Forums: http://sourceforge.net/forum/?group_id=92496
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+#include "GL/glui.h"
+#include "glui_internal.h"
+
+GLUI_Glut_Window::GLUI_Glut_Window()
+:   GLUI_Node(),
+
+	glut_window_id(0),
+	glut_keyboard_CB(NULL),
+	glut_special_CB(NULL),
+	glut_reshape_CB(NULL),
+	glut_passive_motion_CB(NULL),
+	glut_mouse_CB(NULL),
+	glut_visibility_CB(NULL),
+	glut_motion_CB(NULL),
+	glut_display_CB(NULL),
+	glut_entry_CB(NULL)
+{
+}
diff --git a/thirdparty/glui/2.36/src/include/GL/glui.h b/libraries/glui/glui-2.35/src/include/GL/glui.h
similarity index 91%
rename from thirdparty/glui/2.36/src/include/GL/glui.h
rename to libraries/glui/glui-2.35/src/include/GL/glui.h
index 9b879d7f90edc4f2edd094f91509e77f9a7cb1bf..32df6fda381aacea71ef2a79b8263735e51ffd33 100644
--- a/thirdparty/glui/2.36/src/include/GL/glui.h
+++ b/libraries/glui/glui-2.35/src/include/GL/glui.h
@@ -1,9 +1,9 @@
 /****************************************************************************
 
-  GLUI User Interface Toolkit
-  ---------------------------
+  GLUI User Interface Toolkit (LGPL)
+  ----------------------------------
 
-     glui.h - Main (and only) external header for
+     glui.h - Main (and only) external header for 
         GLUI User Interface Toolkit
 
           --------------------------------------------------
@@ -13,49 +13,40 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied
-  warranty. In no event will the authors be held liable for any damages
-  arising from the use of this software.
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
 
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
 
-  1. The origin of this software must not be misrepresented; you must not
-  claim that you wrote the original software. If you use this software
-  in a product, an acknowledgment in the product documentation would be
-  appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-  misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
 #ifndef GLUI_GLUI_H
 #define GLUI_GLUI_H
 
-// Having stdlib here first fixes some 'exit() redefined' errors on MSVC.NET
-// that come from old GLUT headers.
-#if defined(WIN32)
-  #include <windows.h>
-#endif
-#include <cstdlib>
-
 #if defined(GLUI_FREEGLUT)
 
   // FreeGLUT does not yet work perfectly with GLUI
   //  - use at your own risk.
-
+  
   #include <GL/freeglut.h>
 
 #elif defined(GLUI_OPENGLUT)
 
   // OpenGLUT does not yet work properly with GLUI
   //  - use at your own risk.
-
+  
   #include <GL/openglut.h>
 
-#else
+#else 
 
   #ifdef __APPLE__
   #include <GLUT/glut.h>
@@ -65,58 +56,52 @@
 
 #endif
 
+#include <cstdlib>
 #include <cstdio>
 #include <cstring>
 #include <string>
 #include <vector>
 
-/* GLUI API shared library export/import declarations. */
+#define GLUI_VERSION 2.3f    /********** Current version **********/
+
 #if defined(_WIN32)
-# ifdef GLUI_BUILDING_LIB
-#  ifdef GLUIDLL
-#   define GLUIAPI __declspec(dllexport)
-#  else
-#   define GLUIAPI
-#  endif
-# else
-#  ifdef GLUIDLL
-#   define GLUIAPI __declspec(dllimport)
-#  else
-#   define GLUIAPI
-#  endif
-# endif
-#else
-#define GLUIAPI
+#if !defined(GLUI_NO_LIB_PRAGMA)
+#pragma comment(lib, "glui32.lib")  // Link automatically with GLUI library
+#endif
 #endif
 
+/********** Do some basic defines *******/
 
-#define GLUI_VERSION 2.36f    /********** Current version **********/
-
-#if defined(_WIN32)
-#  if !defined(GLUI_NO_LIB_PRAGMA) && !defined(GLUI_BUILDING_LIB)
-// Link automatically with GLUI library
-#    if defined GLUIDLL  // define this when using glui dynamic library
-#      pragma comment(lib, "glui32dll.lib")
-#    else
-#      pragma comment(lib, "glui32.lib")
-#    endif
-#  endif
+#ifndef Byte
+#define Byte unsigned char
 #endif
 
+#ifndef _RGBC_
+class RGBc {
+public:
+  Byte r, g, b;
+    
+  void set(Byte r,Byte g,Byte b) {this->r=r;this->g=g;this->b=b;}
+    
+  RGBc( void ) {}
+  RGBc( Byte r, Byte g, Byte b ) { set( r, g, b ); }
+};
+#define _RGBC_
+#endif
 
 /********** List of GLUT callbacks ********/
 
-enum GLUI_Glut_CB_Types
-{
+enum GLUI_Glut_CB_Types 
+{ 
     GLUI_GLUT_RESHAPE,
     GLUI_GLUT_KEYBOARD,
     GLUI_GLUT_DISPLAY,
     GLUI_GLUT_MOUSE,
     GLUI_GLUT_MOTION,
     GLUI_GLUT_SPECIAL,
-    GLUI_GLUT_PASSIVE_MOTION,
+    GLUI_GLUT_PASSIVE_MOTION,  
     GLUI_GLUT_ENTRY,
-    GLUI_GLUT_VISIBILITY
+    GLUI_GLUT_VISIBILITY  
 };
 
 /********* Constants for window placement **********/
@@ -130,7 +115,7 @@ enum GLUI_Glut_CB_Types
 #define GLUI_STATICTEXT_SIZE           13
 #define GLUI_SEPARATOR_HEIGHT           8
 #define GLUI_DEFAULT_CONTROL_WIDTH    100
-#define GLUI_DEFAULT_CONTROL_HEIGHT    13
+#define GLUI_DEFAULT_CONTROL_HEIGHT    13 
 #define GLUI_EDITTEXT_BOXINNERMARGINX   3
 #define GLUI_EDITTEXT_HEIGHT           20
 #define GLUI_EDITTEXT_WIDTH           130
@@ -213,7 +198,7 @@ enum GLUI_Glut_CB_Types
 #define GLUI_TREEPANEL_DISPLAY_HIERARCHY     32 // display some sort of hierachy in the tree node title
 #define GLUI_TREEPANEL_HIERARCHY_NUMERICDOT  64 // display hierarchy in 1.3.2 (etc... ) format
 #define GLUI_TREEPANEL_HIERARCHY_LEVEL_ONLY 128 // display hierarchy as only the level depth
-
+ 
 /******************* GLUI Scrollbar Defaults - JVK ***************************/
 #define  GLUI_SCROLL_ARROW_WIDTH     16
 #define  GLUI_SCROLL_ARROW_HEIGHT    16
@@ -229,7 +214,7 @@ enum GLUI_Glut_CB_Types
 #define  GLUI_SCROLL_HORIZONTAL       1
 
 
-/** Size of the character width hash table for faster lookups.
+/** Size of the character width hash table for faster lookups. 
   Make sure to keep this a power of two to avoid the slow divide.
   This is also a speed/memory tradeoff; 128 is enough for low ASCII.
 */
@@ -237,7 +222,7 @@ enum GLUI_Glut_CB_Types
 
 /**********  Translation codes  **********/
 
-enum TranslationCodes
+enum TranslationCodes  
 {
     GLUI_TRANSLATION_MOUSE_NONE = 0,
     GLUI_TRANSLATION_MOUSE_UP,
@@ -253,7 +238,7 @@ enum TranslationCodes
 /************ A string type for us to use **********/
 
 typedef std::string GLUI_String;
-GLUIAPI GLUI_String& glui_format_str(GLUI_String &str, const char* fmt, ...);
+GLUI_String& glui_format_str(GLUI_String &str, const char* fmt, ...);
 
 /********* Pre-declare classes as needed *********/
 
@@ -284,12 +269,14 @@ class Arcball;
 #define GLUI_EDITTEXT_TEXT             1
 #define GLUI_EDITTEXT_INT              2
 #define GLUI_EDITTEXT_FLOAT            3
+#define GLUI_EDITTEXT_DOUBLE           4
 #define GLUI_SPINNER_INT               GLUI_EDITTEXT_INT
 #define GLUI_SPINNER_FLOAT             GLUI_EDITTEXT_FLOAT
+#define GLUI_SPINNER_DOUBLE            GLUI_EDITTEXT_DOUBLE
 #define GLUI_SCROLL_INT                GLUI_EDITTEXT_INT
 #define GLUI_SCROLL_FLOAT              GLUI_EDITTEXT_FLOAT
 // This is only for deprecated interface
-#define GLUI_EDITTEXT_STRING           4
+#define GLUI_EDITTEXT_STRING           5
 
 /*** Definition of callbacks ***/
 typedef void (*GLUI_Update_CB) (int id);
@@ -301,12 +288,12 @@ typedef void (*Int4_CB)        (int, int, int, int);
 
 /************************************************************/
 /**
- Callback Adapter Class
+ Callback Adapter Class                            
     Allows us to support different types of callbacks;
     like a GLUI_Update_CB function pointer--which takes an int;
     and a GLUI_Control_CB function pointer--which takes a GUI_Control object.
 */
-class GLUIAPI GLUI_CB
+class GLUI_CB
 {
 public:
   GLUI_CB() : idCB(0),objCB(0) {}
@@ -335,11 +322,11 @@ class GLUI_Control;
  GLUI_Node is a node in a sort of tree of GLUI controls.
  Each GLUI_Node has a list of siblings (in a circular list)
  and a linked list of children.
-
+ 
  Everything onscreen is a GLUI_Node--windows, buttons, etc.
  The nodes are traversed for event processing, sizing, redraws, etc.
 */
-class GLUIAPI GLUI_Node
+class GLUI_Node 
 {
     friend class GLUI_Tree;     /* JVK */
     friend class GLUI_Rollout;
@@ -385,7 +372,7 @@ protected:
 /*                                                          */
 /************************************************************/
 
-enum GLUI_StdBitmaps_Codes
+enum GLUI_StdBitmaps_Codes 
 {
     GLUI_STDBITMAP_CHECKBOX_OFF = 0,
     GLUI_STDBITMAP_CHECKBOX_ON,
@@ -422,7 +409,7 @@ enum GLUI_StdBitmaps_Codes
  to represent small textures like checkboxes, arrows, etc.
  via the GLUI_StdBitmaps class.
 */
-class GLUIAPI GLUI_Bitmap
+class GLUI_Bitmap 
 {
     friend class GLUI_StdBitmaps;
 
@@ -432,7 +419,7 @@ public:
 
     /** Create bitmap from greyscale byte image */
     void init_grey(unsigned char *array);
-
+    
     /** Create bitmap from color int image */
     void init(int *array);
 
@@ -450,13 +437,13 @@ private:
 /************************************************************/
 
 /**
- Keeps an array of GLUI_Bitmap objects to represent all the
+ Keeps an array of GLUI_Bitmap objects to represent all the 
  images used in the UI: checkboxes, arrows, etc.
 */
-class GLUIAPI GLUI_StdBitmaps
+class GLUI_StdBitmaps
 {
 public:
-    GLUI_StdBitmaps();
+    GLUI_StdBitmaps(); 
     ~GLUI_StdBitmaps();
 
     /** Return the width (in pixels) of the n'th standard bitmap. */
@@ -465,7 +452,7 @@ public:
     int  height(int n) const;
 
     /** Draw the n'th standard bitmap (one of the enums
-       listed in GLUI_StdBitmaps_Codes) at pixel corner (x,y).
+       listed in GLUI_StdBitmaps_Codes) at pixel corner (x,y). 
     */
     void draw(int n, int x, int y) const;
 
@@ -483,11 +470,11 @@ private:
  The master manages our interaction with GLUT.
  There's only one GLUI_Master_Object.
 */
-class GLUIAPI GLUI_Master_Object
+class GLUI_Master_Object 
 {
 
     friend void glui_idle_func();
-
+  
 public:
 
     GLUI_Master_Object();
@@ -538,10 +525,10 @@ public:
     void set_glutMenuStatusFunc(Int3_CB f)            {glutMenuStatusFunc(f);}
     void set_glutMenuStateFunc(Int1_CB f)              {glutMenuStateFunc(f);}
     void set_glutButtonBoxFunc(Int2_CB f)              {glutButtonBoxFunc(f);}
-    void set_glutDialsFunc(Int2_CB f)                      {glutDialsFunc(f);}
-
+    void set_glutDialsFunc(Int2_CB f)                      {glutDialsFunc(f);}  
+  
 
-    GLUI          *create_glui( const char *name, long flags=0, int x=-1, int y=-1 );
+    GLUI          *create_glui( const char *name, long flags=0, int x=-1, int y=-1 ); 
     GLUI          *create_glui_subwindow( int parent_window, long flags=0 );
     GLUI          *find_glui_by_window_id( int window_id );
     void           get_viewport_area( int *x, int *y, int *w, int *h );
@@ -565,7 +552,7 @@ private:
 /**
  This is the only GLUI_Master_Object in existence.
 */
-extern GLUIAPI GLUI_Master_Object GLUI_Master;
+extern GLUI_Master_Object GLUI_Master;
 
 /************************************************************/
 /*                                                          */
@@ -577,11 +564,11 @@ extern GLUIAPI GLUI_Master_Object GLUI_Master;
  A top-level window.  The GLUI_Master GLUT callback can route events
  to the callbacks in this class, for arbitrary use by external users.
  (see GLUI_Master_Object::set_glutKeyboardFunc).
-
+ 
  This entire approach seems to be superceded by the "subwindow" flavor
  of GLUI.
 */
-class GLUIAPI GLUI_Glut_Window : public GLUI_Node
+class GLUI_Glut_Window : public GLUI_Node 
 {
 public:
     GLUI_Glut_Window();
@@ -607,10 +594,10 @@ public:
 /************************************************************/
 
 /**
-  A GLUI_Main handles GLUT events for one window, routing them to the
-  appropriate controls.  The central user-visible "GLUI" class
+  A GLUI_Main handles GLUT events for one window, routing them to the 
+  appropriate controls.  The central user-visible "GLUI" class 
   inherits from this class; users should not allocate GLUT_Main objects.
-
+  
   There's a separate GLUI_Main object for:
   	- Each top-level window with GUI stuff in it.
 	- Each "subwindow" of another top-level window.
@@ -619,7 +606,7 @@ public:
   A better name for this class might be "GLUI_Environment";
   this class provides the window-level context for every control.
 */
-class GLUIAPI GLUI_Main : public GLUI_Node
+class GLUI_Main : public GLUI_Node 
 {
     /********** Friend classes *************/
 
@@ -663,7 +650,7 @@ protected:
     buffer_mode_t buffer_mode; ///< Current drawing mode
     int           curr_cursor;
     int           w, h;
-    long          flags;
+    long          flags; 
     bool          closing;
     int           parent_window;
     int           glui_id;
@@ -709,7 +696,7 @@ protected:
 public:
     GLUI_StdBitmaps  std_bitmaps;
     GLUI_String      window_name;
-    unsigned char    bkgd_color[3];
+    RGBc             bkgd_color;
     float            bkgd_color_f[3];
 
     void            *font;
@@ -719,34 +706,34 @@ public:
     void         activate_control( GLUI_Control *control, int how );
     void         align_controls( GLUI_Control *control );
     void         deactivate_current_control( void );
-
+    
     /** Draw a 3D-look pushed-out box around this rectangle */
     void         draw_raised_box( int x, int y, int w, int h );
     /** Draw a 3D-look pushed-in box around this rectangle */
     void         draw_lowered_box( int x, int y, int w, int h );
-
+    
     /** Return true if this control should redraw itself immediately (front buffer);
        Or queue up a redraw and return false if it shouldn't (back buffer).
     */
     bool         should_redraw_now(GLUI_Control *ctl);
-
-    /** Switch to the appropriate draw buffer now.  Returns the old draw buffer.
+    
+    /** Switch to the appropriate draw buffer now.  Returns the old draw buffer. 
        This routine should probably only be called from inside the GLUI_DrawingSentinal,
        in glui_internal_control.h
     */
     int          set_current_draw_buffer();
     /** Go back to using this draw buffer.  Undoes set_current_draw_buffer. */
     void         restore_draw_buffer( int buffer_state );
-
+    
     /** Pack, resize the window, and redraw all the controls. */
     void         refresh();
-
+    
     /** Redraw the main graphics window */
     void         post_update_main_gfx();
-
+  
     /** Recompute the sizes and positions of all controls */
     void         pack_controls();
-
+    
     void         close_internal();
     void         check_subwindow_position();
     void         set_ortho_projection();
@@ -765,13 +752,13 @@ public:
  checkboxes, labels, edit boxes, scrollbars, etc.
  Most of the work of this class is in routing events,
  like keystrokes, mouseclicks, redraws, and sizing events.
-
- Yes, this is a huge and hideous class.  It needs to be
+ 
+ Yes, this is a huge and hideous class.  It needs to be 
  split up into simpler subobjects.  None of the data members
  should be directly accessed by users (they should be protected,
  not public); only subclasses.
 */
-class GLUIAPI GLUI_Control : public GLUI_Node
+class GLUI_Control : public GLUI_Node 
 {
 public:
 
@@ -780,9 +767,9 @@ public:
     int             x_abs, y_abs;
     int             x_off, y_off_top, y_off_bot; /* INNER margins, by which child
                                                     controls are indented */
-    int             contain_x, contain_y;
+    int             contain_x, contain_y; 
     int             contain_w, contain_h;
-    /* if this is a container control (e.g.,
+    /* if this is a container control (e.g., 
        radiogroup or panel) this indicated dimensions
        of inner area in which controls reside */
 
@@ -791,29 +778,31 @@ public:
     bool            active;       ///< If true, we've got the focus
     bool            can_activate; ///< If false, remove from tab order.
     bool            spacebar_mouse_click; ///< Spacebar simulates click.
-
+    
 /** Callbacks */
     long            user_id;  ///< Integer to pass to callback function.
     GLUI_CB         callback; ///< User callback function, or NULL.
 
 /** Variable value storage */
     float           float_val;        /**< Our float value */
+    double          double_val;       /**< Our double value */
     int             int_val;          /**< Our integer value */
     float           float_array_val[GLUI_DEF_MAX_ARRAY];
     int             float_array_size;
     GLUI_String     text;       /**< The text inside this control */
-
+    
 /** "Live variable" updating */
     void           *ptr_val;          /**< A pointer to the user's live variable value */
     int             live_type;
     bool            live_inited;
     /* These variables store the last value that live variable was known to have. */
-    int             last_live_int;
+    int             last_live_int;  
     float           last_live_float;
+    double          last_live_double;
     GLUI_String     last_live_text;
     float           last_live_float_array[GLUI_DEF_MAX_ARRAY];
-
-/** Properties of our control */
+    
+/** Properties of our control */    
     GLUI           *glui;       /**< Our containing event handler (NEVER NULL during event processing!) */
     bool            is_container;  /**< Is this a container class (e.g., panel) */
     int             alignment;
@@ -830,10 +819,12 @@ public:
     virtual void   set_name( const char *string );
     virtual void   set_int_val( int new_int )         { int_val = new_int; output_live(true); }
     virtual void   set_float_val( float new_float )   { float_val = new_float; output_live(true); }
+    virtual void   set_double_val( double new_double )   { double_val = new_double; output_live(true); }
     virtual void   set_ptr_val( void *new_ptr )       { ptr_val = new_ptr; output_live(true); }
     virtual void   set_float_array_val( float *array_ptr );
 
     virtual float  get_float_val( void )              { return float_val; }
+    virtual double  get_double_val( void )              { return double_val; }
     virtual int    get_int_val( void )                { return int_val; }
     virtual void   get_float_array_val( float *array_ptr );
     virtual int    get_id( void ) const { return user_id; }
@@ -849,7 +840,7 @@ public:
     virtual void idle( void )            { }
     virtual int  mouse_over( int state, int x, int y ) { return false; }
 
-    virtual void enable( void );
+    virtual void enable( void ); 
     virtual void disable( void );
     virtual void activate( int how )     { active = true; }
     virtual void deactivate( void )     { active = false; }
@@ -862,42 +853,42 @@ public:
     int          can_draw( void ) { return (glui != NULL && hidden == false); }
 
     /** Redraw this control.
-       In single-buffering mode (drawing to GL_FRONT), this is just
+       In single-buffering mode (drawing to GL_FRONT), this is just 
            a call to translate_and_draw_front (after a can_draw() check).
-       In double-buffering mode (drawing to GL_BACK), this queues up
+       In double-buffering mode (drawing to GL_BACK), this queues up 
           a redraw and returns false, since you shouldn't draw yet.
     */
     void          redraw(void);
-
+    
     /** Redraw everybody in our window. */
     void         redraw_window(void);
 
     virtual void align( void );
     void         pack( int x, int y );    /* Recalculate positions and offsets */
-    void         pack_old( int x, int y );
+    void         pack_old( int x, int y );    
     void         draw_recursive( int x, int y );
     int          set_to_glut_window( void );
     void         restore_window( int orig );
     void         translate_and_draw_front( void );
-    void         translate_to_origin( void )
+    void         translate_to_origin( void ) 
     {glTranslatef((float)x_abs+.5,(float)y_abs+.5,0.0);}
     virtual void draw( int x, int y )=0;
     void         set_font( void *new_font );
     void        *get_font( void );
     int          string_width( const char *text );
-    int          string_width( const GLUI_String &str )
+    int          string_width( const GLUI_String &str ) 
     { return string_width(str.c_str()); }
     int          char_width( char c );
 
     void         draw_name( int x, int y );
-    void         draw_box_inwards_outline( int x_min, int x_max,
+    void         draw_box_inwards_outline( int x_min, int x_max, 
                                            int y_min, int y_max );
     void         draw_box( int x_min, int x_max, int y_min, int y_max,
                            float r, float g, float b );
     void         draw_bkgd_box( int x_min, int x_max, int y_min, int y_max );
     void         draw_emboss_box( int x_min, int x_max,int y_min,int y_max);
     void         draw_string( const char *text );
-    void         draw_string( const GLUI_String &s )
+    void         draw_string( const GLUI_String &s ) 
     { draw_string(s.c_str()); }
     void         draw_char( char c );
     void         draw_active_box( int x_min, int x_max, int y_min, int y_max );
@@ -911,13 +902,13 @@ public:
     void         output_live( int update_main_gfx );        /** Writes live variable **/
     virtual void set_text( const char *t )   {}
     void         execute_callback( void );
-    void         get_this_column_dims( int *col_x, int *col_y,
-                                       int *col_w, int *col_h,
+    void         get_this_column_dims( int *col_x, int *col_y, 
+                                       int *col_w, int *col_h, 
                                        int *col_x_off, int *col_y_off );
     virtual bool needs_idle( void ) const;
     virtual bool wants_tabs() const      { return false; }
 
-    GLUI_Control(void)
+    GLUI_Control(void) 
     {
         x_off          = GLUI_XOFF;
         y_off_top      = GLUI_YOFF;
@@ -931,7 +922,9 @@ public:
         float_array_size = 0;
         glui_format_str(name, "Control: %p", this);
         float_val      = 0.0;
+        double_val     = 0.0;
         last_live_float = 0.0;
+        last_live_double = 0.0;
         ptr_val        = NULL;
         glui           = NULL;
         w              = GLUI_DEFAULT_CONTROL_WIDTH;
@@ -944,7 +937,7 @@ public:
         spacebar_mouse_click = true;    /* Does spacebar simulate a mouse click? */
         live_type      = GLUI_LIVE_NONE;
         text = "";
-        last_live_text = "";
+        last_live_text == "";
         live_inited    = false;
         collapsible    = false;
         is_open        = true;
@@ -964,11 +957,11 @@ public:
 /*                                                          */
 /************************************************************/
 /**
-  An onscreen, clickable button--an outlined label that
+  An onscreen, clickable button--an outlined label that 
   can be clicked.  When clicked, a button
   calls its GLUI_CB callback with its ID.
 */
-class GLUIAPI GLUI_Button : public GLUI_Control
+class GLUI_Button : public GLUI_Control
 {
 public:
     bool currently_inside;
@@ -986,13 +979,13 @@ public:
 
 /**
  Create a new button.
-
+ 
   @param parent The panel our object is inside; or the main GLUI object.
   @param name The text inside the button.
   @param id Optional ID number, to pass to the optional callback function.
   @param callback Optional callback function, taking either the int ID or control.
 */
-    GLUI_Button( GLUI_Node *parent, const char *name,
+    GLUI_Button( GLUI_Node *parent, const char *name, 
                  int id=-1, GLUI_CB cb=GLUI_CB() );
     GLUI_Button( void ) { common_init(); };
 
@@ -1017,7 +1010,7 @@ protected:
  A checkbox, which can be checked on or off.  Can be linked
  to an int value, which gets 1 for on and 0 for off.
 */
-class GLUIAPI GLUI_Checkbox : public GLUI_Control
+class GLUI_Checkbox : public GLUI_Control
 {
 public:
     int  orig_value;
@@ -1039,10 +1032,10 @@ public:
 
 /**
  Create a new checkbox object.
-
+ 
   @param parent The panel our object is inside; or the main GLUI object.
   @param name Label next to our checkbox.
-  @param value_ptr Optional integer value to attach to this checkbox.  When the
+  @param value_ptr Optional integer value to attach to this checkbox.  When the 
      checkbox is checked or unchecked, *value_ptr will also be changed. ("Live Vars").
   @param id Optional ID number, to pass to the optional callback function.
   @param callback Optional callback function, taking either the int ID or control.
@@ -1073,7 +1066,7 @@ protected:
  A GLUI_Column object separates all previous controls
  from subsequent controls with a vertical bar.
 */
-class GLUIAPI GLUI_Column : public GLUI_Control
+class GLUI_Column : public GLUI_Control
 {
 public:
     void draw( int x, int y );
@@ -1081,7 +1074,7 @@ public:
 /**
  Create a new column, which separates the previous controls
  from subsequent controls.
-
+ 
   @param parent The panel our object is inside; or the main GLUI object.
   @param draw_bar If true, draw a visible bar between new and old controls.
 */
@@ -1107,20 +1100,20 @@ protected:
 /**
  A GLUI_Panel contains a group of related controls.
 */
-class GLUIAPI GLUI_Panel : public GLUI_Control
+class GLUI_Panel : public GLUI_Control
 {
 public:
 
 /**
  Create a new panel.  A panel groups together a set of related controls.
-
+ 
   @param parent The outer panel our panel is inside; or the main GLUI object.
   @param name The string name at the top of our panel.
   @param type Optional style to display the panel with--GLUI_PANEL_EMBOSSED by default.
       GLUI_PANEL_RAISED causes the panel to appear higher than the surroundings.
       GLUI_PANEL_NONE causes the panel's outline to be invisible.
 */
-    GLUI_Panel( GLUI_Node *parent, const char *name,
+    GLUI_Panel( GLUI_Node *parent, const char *name, 
                 int type=GLUI_PANEL_EMBOSSED );
     GLUI_Panel() { common_init(); }
 
@@ -1136,7 +1129,7 @@ protected:
         h            = GLUI_DEFAULT_CONTROL_HEIGHT + 7;
         int_val      = GLUI_PANEL_EMBOSSED;
         alignment    = GLUI_ALIGN_CENTER;
-        is_container = true;
+        is_container = true; 
         can_activate = false;
         name="";
     };
@@ -1151,12 +1144,12 @@ protected:
 /**
  A list of files the user can select from.
 */
-class GLUIAPI GLUI_FileBrowser : public GLUI_Panel
+class GLUI_FileBrowser : public GLUI_Panel
 {
 public:
 /**
  Create a new list of files the user can select from.
-
+ 
   @param parent The panel our object is inside; or the main GLUI object.
   @param name Prompt to give to the user at the top of the file browser.
   @param frame_type Optional style to display the panel with--GLUI_PANEL_EMBOSSED by default.
@@ -1165,7 +1158,7 @@ public:
   @param id Optional ID number, to pass to the optional callback function.
   @param callback Optional callback function, taking either the int ID or control.
 */
-    GLUI_FileBrowser( GLUI_Node *parent,
+    GLUI_FileBrowser( GLUI_Node *parent, 
                       const char *name,
                       int frame_type = GLUI_PANEL_EMBOSSED,
                       int user_id = -1,
@@ -1183,13 +1176,13 @@ public:
     void set_allow_change_dir(int c) { allow_change_dir = c; }
 
 protected:
-    void common_init()
+    void common_init() 
     {
         w            = GLUI_DEFAULT_CONTROL_WIDTH;
         h            = GLUI_DEFAULT_CONTROL_HEIGHT;
         int_val      = GLUI_PANEL_EMBOSSED;
         alignment    = GLUI_ALIGN_CENTER;
-        is_container = true;
+        is_container = true; 
         can_activate = false;
         allow_change_dir = true;
         last_item    = -1;
@@ -1215,14 +1208,14 @@ private:
  A rollout contains a set of controls,
  like a panel, but can be collapsed to just the name.
 */
-class GLUIAPI GLUI_Rollout : public GLUI_Panel
+class GLUI_Rollout : public GLUI_Panel
 {
 public:
 
 /**
  Create a new rollout.  A rollout contains a set of controls,
  like a panel, but can be collapsed to just the name.
-
+ 
   @param parent The panel our object is inside; or the main GLUI object.
   @param name String to show at the top of the rollout.
   @param open Optional boolean.  If true (the default), the rollout's controls are displayed.
@@ -1231,11 +1224,11 @@ public:
       GLUI_PANEL_RAISED causes the panel to appear higher than the surroundings.
       GLUI_PANEL_NONE causes the panel's outline to be invisible.
 */
-    GLUI_Rollout( GLUI_Node *parent, const char *name, int open=true,
+    GLUI_Rollout( GLUI_Node *parent, const char *name, int open=true, 
                   int type=GLUI_PANEL_EMBOSSED );
     GLUI_Rollout( void ) { common_init(); }
-
-
+    
+    
     bool        currently_inside, initially_inside;
     GLUI_Button  button;
 
@@ -1244,8 +1237,8 @@ public:
     int mouse_down_handler( int local_x, int local_y );
     int mouse_up_handler( int local_x, int local_y, bool inside );
     int  mouse_held_down_handler( int local_x, int local_y, bool inside );
-
-    void  open( void );
+        
+    void  open( void ); 
     void  close( void );
 
     void update_size( void );
@@ -1273,10 +1266,10 @@ protected:
 /**
   One collapsible entry in a GLUI_TreePanel.
 */
-class GLUIAPI GLUI_Tree : public GLUI_Panel
+class GLUI_Tree : public GLUI_Panel
 {
 public:
-    GLUI_Tree(GLUI_Node *parent, const char *name,
+    GLUI_Tree(GLUI_Node *parent, const char *name, 
               int open=false, int inset=0);
 
 private:
@@ -1298,7 +1291,7 @@ public:
     bool        currently_inside, initially_inside;
     GLUI_Button  button;
     GLUI_String  level_name; // level name, eg: 1.1.2, III, or 3
-    GLUI_TreePanel *panel;
+    GLUI_TreePanel *panel; 
 
     void draw( int x, int y );
     void draw_pressed( void );
@@ -1306,7 +1299,7 @@ public:
     int mouse_up_handler( int local_x, int local_y, bool inside );
     int  mouse_held_down_handler( int local_x, int local_y, bool inside );
     void set_column(GLUI_Column *c) { column = c; }
-    void  open( void );
+    void  open( void ); 
     void  close( void );
 
     /*   void set_name( const char *text )   { panel.set_name( text ); }; */
@@ -1319,14 +1312,14 @@ public:
     int get_level() { return level; }
     int get_child_number() { return child_number; }
     void enable_bar() { if (column) { column->int_val = 1;  set_color(red, green, blue); } }
-    void disable_bar() { if (column) { column->int_val = 0;  } }
-    void set_child_number(int c) { child_number = c; }
-    void set_level_color(float r, float g, float b) {
+    void disable_bar() { if (column) { column->int_val = 0;  } } 
+    void set_child_number(int c) { child_number = c; } 
+    void set_level_color(float r, float g, float b) { 
         lred = r;
         lgreen = g;
         lblue  = b;
     }
-    void set_color(float r, float g, float b) {
+    void set_color(float r, float g, float b) { 
         red = r;
         green = g;
         blue  = b;
@@ -1356,7 +1349,7 @@ protected:
         name             = "";
         level_name       = "";
         level            = 0;
-
+    
     };
 };
 
@@ -1370,10 +1363,10 @@ protected:
 /**
   Manages, maintains, and formats a tree of GLUI_Tree objects.
   These are shown in a heirarchical, collapsible display.
-
+  
   FIXME: There's an infinite loop in the traversal code (OSL 2006/06)
 */
-class GLUIAPI GLUI_TreePanel : public GLUI_Panel
+class GLUI_TreePanel : public GLUI_Panel 
 {
 public:
     GLUI_TreePanel(GLUI_Node *parent, const char *name,
@@ -1390,14 +1383,14 @@ public:
     float lblue;
     int root_children;
     /* These variables allow the tree panel to traverse the tree
-       using only two function calls. (Well, four, if you count
+       using only two function calls. (Well, four, if you count 
        going in reverse */
 
     GLUI_Tree    *curr_branch; /* Current Branch */
     GLUI_Panel *curr_root;   /* Current Root */
 
 public:
-    void            set_color(float r, float g, float b);
+    void            set_color(float r, float g, float b); 
     void            set_level_color(float r, float g, float b);
     void            set_format(int f) { format = f; }
 
@@ -1421,7 +1414,7 @@ public:
 
 protected:
     int uniqueID( void ) { next_id++; return next_id - 1; }
-    void common_init()
+    void common_init() 
     {
         GLUI_Panel();
         next_id = 0;
@@ -1445,9 +1438,9 @@ class GLUI_Translation;
 
 /**
  The main user-visible interface object to GLUI.
-
+ 
 */
-class GLUIAPI GLUI : public GLUI_Main
+class GLUI : public GLUI_Main 
 {
 public:
 /** DEPRECATED interface for creating new GLUI objects */
@@ -1459,11 +1452,11 @@ public:
     void  add_separator( void );
     void  add_separator_to_panel( GLUI_Panel *panel );
 
-    GLUI_RadioGroup
+    GLUI_RadioGroup 
     *add_radiogroup( int *live_var=NULL,
                      int user_id=-1,GLUI_CB callback=GLUI_CB());
 
-    GLUI_RadioGroup
+    GLUI_RadioGroup 
     *add_radiogroup_to_panel(  GLUI_Panel *panel,
                                int *live_var=NULL,
                                int user_id=-1, GLUI_CB callback=GLUI_CB() );
@@ -1482,50 +1475,50 @@ public:
     GLUI_Rotation *add_rotation_to_panel( GLUI_Panel *panel,
                                           const char *name, float *live_var=NULL,
                                           int id=-1, GLUI_CB callback=GLUI_CB());
-
+  
     GLUI_Translation *add_translation( const char *name,
                                        int trans_type, float *live_var=NULL,
                                        int id=-1, GLUI_CB callback=GLUI_CB()	);
-    GLUI_Translation *add_translation_to_panel(
-        GLUI_Panel *panel, const char *name,
+    GLUI_Translation *add_translation_to_panel( 
+        GLUI_Panel *panel, const char *name, 
         int trans_type, float *live_var=NULL,
         int id=-1, GLUI_CB callback=GLUI_CB());
-
-    GLUI_Checkbox  *add_checkbox( const char *name,
+  
+    GLUI_Checkbox  *add_checkbox( const char *name, 
                                   int *live_var=NULL,
                                   int id=-1, GLUI_CB callback=GLUI_CB());
-    GLUI_Checkbox  *add_checkbox_to_panel( GLUI_Panel *panel, const char *name,
-                                           int *live_var=NULL, int id=-1,
+    GLUI_Checkbox  *add_checkbox_to_panel( GLUI_Panel *panel, const char *name, 
+                                           int *live_var=NULL, int id=-1, 
                                            GLUI_CB callback=GLUI_CB());
 
-    GLUI_Button  *add_button( const char *name, int id=-1,
+    GLUI_Button  *add_button( const char *name, int id=-1, 
                               GLUI_CB callback=GLUI_CB());
-    GLUI_Button  *add_button_to_panel( GLUI_Panel *panel, const char *name,
+    GLUI_Button  *add_button_to_panel( GLUI_Panel *panel, const char *name, 
                                        int id=-1, GLUI_CB callback=GLUI_CB() );
 
     GLUI_StaticText  *add_statictext( const char *name );
     GLUI_StaticText  *add_statictext_to_panel( GLUI_Panel *panel, const char *name );
 
-    GLUI_EditText  *add_edittext( const char *name,
+    GLUI_EditText  *add_edittext( const char *name, 
                                   int data_type=GLUI_EDITTEXT_TEXT,
                                   void*live_var=NULL,
                                   int id=-1, GLUI_CB callback=GLUI_CB()	);
-    GLUI_EditText  *add_edittext_to_panel( GLUI_Panel *panel,
+    GLUI_EditText  *add_edittext_to_panel( GLUI_Panel *panel, 
                                            const char *name,
                                            int data_type=GLUI_EDITTEXT_TEXT,
-                                           void *live_var=NULL, int id=-1,
+                                           void *live_var=NULL, int id=-1, 
                                            GLUI_CB callback=GLUI_CB() );
-    GLUI_EditText  *add_edittext( const char *name, GLUI_String& live_var,
+    GLUI_EditText  *add_edittext( const char *name, GLUI_String& live_var, 
                                   int id=-1, GLUI_CB callback=GLUI_CB()	);
-    GLUI_EditText  *add_edittext_to_panel( GLUI_Panel *panel, const char *name,
+    GLUI_EditText  *add_edittext_to_panel( GLUI_Panel *panel, const char *name, 
                                            GLUI_String& live_var, int id=-1,
                                            GLUI_CB callback=GLUI_CB() );
 
-    GLUI_Spinner  *add_spinner( const char *name,
+    GLUI_Spinner  *add_spinner( const char *name, 
                                 int data_type=GLUI_SPINNER_INT,
                                 void *live_var=NULL,
                                 int id=-1, GLUI_CB callback=GLUI_CB() );
-    GLUI_Spinner  *add_spinner_to_panel( GLUI_Panel *panel,
+    GLUI_Spinner  *add_spinner_to_panel( GLUI_Panel *panel, 
                                          const char *name,
                                          int data_type=GLUI_SPINNER_INT,
                                          void *live_var=NULL,
@@ -1533,13 +1526,13 @@ public:
                                          GLUI_CB callback=GLUI_CB() );
 
     GLUI_Panel     *add_panel( const char *name, int type=GLUI_PANEL_EMBOSSED );
-    GLUI_Panel     *add_panel_to_panel( GLUI_Panel *panel, const char *name,
+    GLUI_Panel     *add_panel_to_panel( GLUI_Panel *panel, const char *name, 
                                         int type=GLUI_PANEL_EMBOSSED );
 
 
     GLUI_Rollout   *add_rollout( const char *name, int open=true,
                                  int type=GLUI_PANEL_EMBOSSED);
-    GLUI_Rollout   *add_rollout_to_panel( GLUI_Panel *panel, const char *name,
+    GLUI_Rollout   *add_rollout_to_panel( GLUI_Panel *panel, const char *name, 
                                           int open=true,
                                           int type=GLUI_PANEL_EMBOSSED);
 
@@ -1591,7 +1584,7 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_EditText : public GLUI_Control
+class GLUI_EditText : public GLUI_Control
 {
 public:
     int                 has_limits;
@@ -1601,11 +1594,12 @@ public:
     int                 title_x_offset;
     int                 text_x_offset;
     int                 substring_start; /*substring that gets displayed in box*/
-    int                 substring_end;
+    int                 substring_end;  
     int                 sel_start, sel_end;  /* current selection */
     int                 num_periods;
     int                 last_insertion_pt;
     float               float_low, float_high;
+    double              double_low, double_high;
     int                 int_low, int_high;
     GLUI_Spinner       *spinner;
     int                 debug;
@@ -1638,8 +1632,10 @@ public:
     void update_size( void );
 
     void set_float_limits( float low,float high,int limit_type=GLUI_LIMIT_CLAMP);
+    void set_double_limits( double low,double high,int limit_type=GLUI_LIMIT_CLAMP);
     void set_int_limits( int low, int high, int limit_type=GLUI_LIMIT_CLAMP );
     void set_float_val( float new_val );
+    void set_double_val( double new_val );
     void set_int_val( int new_val );
     void set_text( const char *text );
     void set_text( const GLUI_String &s) { set_text(s.c_str()); }
@@ -1659,12 +1655,16 @@ public:
     GLUI_EditText( GLUI_Node *parent, const char *name,
                    float *live_var,
                    int id=-1, GLUI_CB callback=GLUI_CB() );
-    // Constructor, char* live variable
+    // Constructor, double live variable
     GLUI_EditText( GLUI_Node *parent, const char *name,
+                   double *live_var,
+                   int id=-1, GLUI_CB callback=GLUI_CB() );
+    // Constructor, char* live variable
+    GLUI_EditText( GLUI_Node *parent, const char *name, 
                    char *live_var,
                    int id=-1, GLUI_CB callback=GLUI_CB() );
     // Constructor, std::string live variable
-    GLUI_EditText( GLUI_Node *parent, const char *name,
+    GLUI_EditText( GLUI_Node *parent, const char *name, 
                    std::string &live_var,
                    int id=-1, GLUI_CB callback=GLUI_CB() );
 
@@ -1698,7 +1698,7 @@ protected:
         debug                 = false;
         draw_text_only        = false;
     }
-    void common_construct( GLUI_Node *parent, const char *name,
+    void common_construct( GLUI_Node *parent, const char *name, 
                            int data_type, int live_type, void *live_var,
                            int id, GLUI_CB callback );
 };
@@ -1709,19 +1709,12 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_CommandLine : public GLUI_EditText
+class GLUI_CommandLine : public GLUI_EditText
 {
 public:
     typedef GLUI_EditText Super;
 
     enum { HIST_SIZE = 100 };
-
-	#if _MSC_VER && (_MSC_VER < 1800)
-    // Explicit template instantiation needed for dll
-    template class GLUIAPI std::allocator<GLUI_String>;
-    template class GLUIAPI std::vector<GLUI_String, std::allocator<GLUI_String> >;
-    #endif
-
     std::vector<GLUI_String> hist_list;
     int  curr_hist;
     int  oldest_hist;
@@ -1758,7 +1751,6 @@ protected:
         newest_hist = 0;
         commit_flag = false;
     }
-
 };
 
 /************************************************************/
@@ -1767,14 +1759,14 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_RadioGroup : public GLUI_Control
+class GLUI_RadioGroup : public GLUI_Control
 {
 public:
     int  num_buttons;
 
     void draw( int x, int y );
     void set_name( const char *text );
-    void set_int_val( int int_val );
+    void set_int_val( int int_val ); 
     void set_selected( int int_val );
 
     void draw_group( int translate );
@@ -1804,7 +1796,7 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_RadioButton : public GLUI_Control
+class GLUI_RadioButton : public GLUI_Control
 {
 public:
     int orig_value;
@@ -1845,7 +1837,7 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_Separator : public GLUI_Control
+class GLUI_Separator : public GLUI_Control
 {
 public:
     void draw( int x, int y );
@@ -1877,21 +1869,24 @@ protected:
 /*               Spinner class (container)                  */
 /*                                                          */
 /************************************************************/
-
-class GLUIAPI GLUI_Spinner : public GLUI_Control
+ 
+class GLUI_Spinner : public GLUI_Control
 {
 public:
     // Constructor, no live var
-    GLUI_Spinner( GLUI_Node* parent, const char *name,
+    GLUI_Spinner( GLUI_Node* parent, const char *name, 
                   int data_type=GLUI_SPINNER_INT, int id=-1, GLUI_CB callback=GLUI_CB() );
     // Constructor, int live var
-    GLUI_Spinner( GLUI_Node* parent, const char *name,
+    GLUI_Spinner( GLUI_Node* parent, const char *name, 
                   int *live_var, int id=-1, GLUI_CB callback=GLUI_CB() );
     // Constructor, float live var
-    GLUI_Spinner( GLUI_Node* parent, const char *name,
+    GLUI_Spinner( GLUI_Node* parent, const char *name, 
                   float *live_var, int id=-1, GLUI_CB callback=GLUI_CB() );
+    // Constructor, double live var
+    GLUI_Spinner( GLUI_Node* parent, const char *name, 
+                  double *live_var, int id=-1, GLUI_CB callback=GLUI_CB() );
     // Deprecated constructor
-    GLUI_Spinner( GLUI_Node* parent, const char *name,
+    GLUI_Spinner( GLUI_Node* parent, const char *name, 
                   int data_type,
                   void *live_var,
                   int id=-1, GLUI_CB callback=GLUI_CB() );
@@ -1906,6 +1901,7 @@ public:
     int           callback_count;
     int           last_int_val;
     float         last_float_val;
+    double        last_double_val;
     int           first_callback;
     float         user_speed;
 
@@ -1925,6 +1921,7 @@ public:
     void update_size( void );
 
     void set_float_limits( float low,float high,int limit_type=GLUI_LIMIT_CLAMP);
+    void set_double_limits( double low,double high,int limit_type=GLUI_LIMIT_CLAMP);
     void set_int_limits( int low, int high,int limit_type=GLUI_LIMIT_CLAMP);
     int  find_arrow( int local_x, int local_y );
     void do_drag( int x, int y );
@@ -1936,8 +1933,10 @@ public:
     const char *get_text( void );
 
     void set_float_val( float new_val );
+    void set_double_val( double new_val );
     void set_int_val( int new_val );
     float  get_float_val( void );
+    double get_double_val( void );
     int    get_int_val( void );
     void increase_growth( void );
     void reset_growth( void );
@@ -1960,7 +1959,7 @@ protected:
         first_callback = true;
         user_speed   = 1.0;
     }
-    void common_construct( GLUI_Node* parent, const char *name,
+    void common_construct( GLUI_Node* parent, const char *name, 
                            int data_type, void *live_var,
                            int id, GLUI_CB callback );
 };
@@ -1971,7 +1970,7 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_StaticText : public GLUI_Control
+class GLUI_StaticText : public GLUI_Control
 {
 public:
     void set_text( const char *text );
@@ -1997,7 +1996,7 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_TextBox : public GLUI_Control
+class GLUI_TextBox : public GLUI_Control
 {
 public:
     /* GLUI Textbox - JVK */
@@ -2010,7 +2009,7 @@ public:
     GLUI_String         orig_text;
     int                 insertion_pt;
     int                 substring_start; /*substring that gets displayed in box*/
-    int                 substring_end;
+    int                 substring_end;  
     int                 sel_start, sel_end;  /* current selection */
     int                 last_insertion_pt;
     int                 debug;
@@ -2030,7 +2029,7 @@ public:
     int  mouse_held_down_handler( int local_x, int local_y, bool inside );
     int  key_handler( unsigned char key,int modifiers );
     int  special_handler( int key,int modifiers );
-
+  
     void activate( int how );
     void deactivate( void );
 
@@ -2090,8 +2089,8 @@ protected:
         draw_text_only        = false;
     }
     void common_construct(
-        GLUI_Node *parent, GLUI_String *live_var,
-        bool scroll, int id, GLUI_CB callback);
+        GLUI_Node *parent, GLUI_String *live_var, 
+        bool scroll, int id, GLUI_CB callback); 
 };
 
 /************************************************************/
@@ -2100,7 +2099,7 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_List_Item : public GLUI_Node
+class GLUI_List_Item : public GLUI_Node 
 {
 public:
     GLUI_String text;
@@ -2113,18 +2112,18 @@ public:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_List : public GLUI_Control
+class GLUI_List : public GLUI_Control
 {
 public:
     /* GLUI List - JVK */
     GLUI_List( GLUI_Node *parent, bool scroll = false,
                int id=-1, GLUI_CB callback=GLUI_CB() );
-               /*, GLUI_Control *object = NULL
+               /*, GLUI_Control *object = NULL 
                ,GLUI_InterObject_CB obj_cb = NULL);*/
 
     GLUI_List( GLUI_Node *parent,
-               GLUI_String& live_var, bool scroll = false,
-               int id=-1,
+               GLUI_String& live_var, bool scroll = false, 
+               int id=-1, 
                GLUI_CB callback=GLUI_CB()
                /*,GLUI_Control *object = NULL */
                /*,GLUI_InterObject_CB obj_cb = NULL*/);
@@ -2150,7 +2149,7 @@ public:
     int  mouse_held_down_handler( int local_x, int local_y, bool inside );
     int  key_handler( unsigned char key,int modifiers );
     int  special_handler( int key,int modifiers );
-
+  
     void activate( int how );
     void deactivate( void );
 
@@ -2219,16 +2218,16 @@ protected:
 /*               Scrollbar class - JVK                      */
 /*                                                          */
 /************************************************************/
-
-class GLUIAPI GLUI_Scrollbar : public GLUI_Control
+ 
+class GLUI_Scrollbar : public GLUI_Control
 {
 public:
     // Constructor, no live var
     GLUI_Scrollbar( GLUI_Node *parent,
-                    const char *name,
+                    const char *name, 
                     int horz_vert=GLUI_SCROLL_HORIZONTAL,
                     int data_type=GLUI_SCROLL_INT,
-                    int id=-1, GLUI_CB callback=GLUI_CB()
+                    int id=-1, GLUI_CB callback=GLUI_CB() 
                     /*,GLUI_Control *object = NULL*/
                     /*,GLUI_InterObject_CB obj_cb = NULL*/
                     );
@@ -2236,7 +2235,7 @@ public:
     // Constructor, int live var
     GLUI_Scrollbar( GLUI_Node *parent, const char *name, int horz_vert,
                     int *live_var,
-                    int id=-1, GLUI_CB callback=GLUI_CB()
+                    int id=-1, GLUI_CB callback=GLUI_CB() 
                     /*,GLUI_Control *object = NULL*/
                     /*,GLUI_InterObject_CB obj_cb = NULL*/
                     );
@@ -2270,7 +2269,7 @@ public:
     int track_length;
 
 
-    /* Rather than directly access an Editbox or Textbox for
+    /* Rather than directly access an Editbox or Textbox for 
        changing variables, a pointer to some object is defined
        along with a static callback in the form func(void *, int) -
        the int is the new value, the void * must be cast to that
@@ -2284,7 +2283,7 @@ public:
     int  mouse_held_down_handler( int local_x, int local_y, bool inside );
     int  key_handler( unsigned char key,int modifiers );
     int  special_handler( int key,int modifiers );
-
+  
     void draw( int x, int y );
     void draw_pressed( void );
     void draw_unpressed( void );
@@ -2315,7 +2314,7 @@ protected:
     void common_init ( void );
     void common_construct(
         GLUI_Node *parent,
-        const char *name,
+        const char *name, 
         int horz_vert,
         int data_type, void* live_var,
         int id, GLUI_CB callback
@@ -2333,14 +2332,14 @@ protected:
 /*                                                          */
 /************************************************************/
 
-class GLUIAPI GLUI_Listbox_Item : public GLUI_Node
+class GLUI_Listbox_Item : public GLUI_Node 
 {
 public:
     GLUI_String text;
     int         id;
 };
 
-class GLUIAPI GLUI_Listbox : public GLUI_Control
+class GLUI_Listbox : public GLUI_Control
 {
 public:
     GLUI_String       curr_text;
@@ -2374,7 +2373,7 @@ public:
 
     GLUI_Listbox_Item *get_item_ptr( const char *text );
     GLUI_Listbox_Item *get_item_ptr( int id );
-
+  
 
     GLUI_Listbox( GLUI_Node *parent,
                   const char *name, int *live_var=NULL,
@@ -2410,7 +2409,7 @@ protected:
 /**
   This is the superclass of translation and rotation widgets.
 */
-class GLUIAPI GLUI_Mouse_Interaction : public GLUI_Control
+class GLUI_Mouse_Interaction : public GLUI_Control
 {
 public:
     /*int  get_main_area_size( void ) { return MIN( h-18,  */
@@ -2434,7 +2433,7 @@ public:
     virtual void iaction_draw_active_area_ortho( void )=0;
     virtual void iaction_dump( FILE *output )=0;
     virtual void iaction_init( void ) = 0;
-
+  
     GLUI_Mouse_Interaction( void ) {
         glui_format_str( name, "Mouse_Interaction: %p", this );
         w              = GLUI_MOUSE_INTERACTION_WIDTH;
@@ -2456,14 +2455,14 @@ public:
   An onscreen rotation controller--allows the user to interact with
   a 3D rotation via a spaceball-like interface.
 */
-class GLUIAPI GLUI_Rotation : public GLUI_Mouse_Interaction
+class GLUI_Rotation : public GLUI_Mouse_Interaction
 {
 public:
     Arcball        *ball;
     GLUquadricObj *quadObj;
     bool           can_spin, spinning;
     float          damping;
-
+  
     int  iaction_mouse_down_handler( int local_x, int local_y );
     int  iaction_mouse_up_handler( int local_x, int local_y, bool inside );
     int  iaction_mouse_held_down_handler( int local_x, int local_y, bool inside );
@@ -2511,7 +2510,7 @@ protected:
   An onscreen translation controller--allows the user to interact with
   a 3D translation.
 */
-class GLUIAPI GLUI_Translation : public GLUI_Mouse_Interaction
+class GLUI_Translation : public GLUI_Mouse_Interaction
 {
 public:
     int trans_type;  /* Is this an XY or a Z controller? */
@@ -2535,7 +2534,7 @@ public:
 
     void setup_texture( void );
     void setup_lights( void );
-    void draw_2d_arrow( int radius, int filled, int orientation );
+    void draw_2d_arrow( int radius, int filled, int orientation ); 
     void draw_2d_x_arrows( int radius );
     void draw_2d_y_arrows( int radius );
     void draw_2d_z_arrows( int radius );
@@ -2587,7 +2586,7 @@ void _glutBitmapString( void *font, const char *s );
 /********** Our own callbacks for glut *********/
 /* These are the callbacks that we pass to glut.  They take
    some action if necessary, then (possibly) call the user-level
-   glut callbacks.
+   glut callbacks.  
 */
 
 void glui_display_func( void );
diff --git a/thirdparty/glui/2.36/src/msvc/example1.dsp b/libraries/glui/glui-2.35/src/msvc/example1.dsp
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/example1.dsp
rename to libraries/glui/glui-2.35/src/msvc/example1.dsp
diff --git a/thirdparty/glui/2.36/src/msvc/example2.dsp b/libraries/glui/glui-2.35/src/msvc/example2.dsp
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/example2.dsp
rename to libraries/glui/glui-2.35/src/msvc/example2.dsp
diff --git a/thirdparty/glui/2.36/src/msvc/example3.dsp b/libraries/glui/glui-2.35/src/msvc/example3.dsp
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/example3.dsp
rename to libraries/glui/glui-2.35/src/msvc/example3.dsp
diff --git a/thirdparty/glui/2.36/src/msvc/example4.dsp b/libraries/glui/glui-2.35/src/msvc/example4.dsp
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/example4.dsp
rename to libraries/glui/glui-2.35/src/msvc/example4.dsp
diff --git a/thirdparty/glui/2.36/src/msvc/example5.dsp b/libraries/glui/glui-2.35/src/msvc/example5.dsp
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/example5.dsp
rename to libraries/glui/glui-2.35/src/msvc/example5.dsp
diff --git a/thirdparty/glui/2.36/src/msvc/example6.dsp b/libraries/glui/glui-2.35/src/msvc/example6.dsp
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/example6.dsp
rename to libraries/glui/glui-2.35/src/msvc/example6.dsp
diff --git a/thirdparty/glui/2.36/src/msvc/glui.dsp b/libraries/glui/glui-2.35/src/msvc/glui.dsp
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/glui.dsp
rename to libraries/glui/glui-2.35/src/msvc/glui.dsp
diff --git a/thirdparty/glui/2.36/src/msvc/glui.dsw b/libraries/glui/glui-2.35/src/msvc/glui.dsw
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/glui.dsw
rename to libraries/glui/glui-2.35/src/msvc/glui.dsw
diff --git a/libraries/glui/glui-2.35/src/msvc/glui.sln b/libraries/glui/glui-2.35/src/msvc/glui.sln
new file mode 100644
index 0000000000000000000000000000000000000000..0055d72d469aeb3427c5af8151abfec85800e844
--- /dev/null
+++ b/libraries/glui/glui-2.35/src/msvc/glui.sln
@@ -0,0 +1,25 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_glui library", "glui.vcxproj", "{304505FF-4A2A-471E-B163-17446985C98E}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Debug|x64 = Debug|x64
+		Release|Win32 = Release|Win32
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{304505FF-4A2A-471E-B163-17446985C98E}.Debug|Win32.ActiveCfg = Debug|x64
+		{304505FF-4A2A-471E-B163-17446985C98E}.Debug|x64.ActiveCfg = Debug|x64
+		{304505FF-4A2A-471E-B163-17446985C98E}.Debug|x64.Build.0 = Debug|x64
+		{304505FF-4A2A-471E-B163-17446985C98E}.Release|Win32.ActiveCfg = Release|x64
+		{304505FF-4A2A-471E-B163-17446985C98E}.Release|x64.ActiveCfg = Release|x64
+		{304505FF-4A2A-471E-B163-17446985C98E}.Release|x64.Build.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/thirdparty/glui/2.36/src/msvc/ppm2array.dsp b/libraries/glui/glui-2.35/src/msvc/ppm2array.dsp
similarity index 100%
rename from thirdparty/glui/2.36/src/msvc/ppm2array.dsp
rename to libraries/glui/glui-2.35/src/msvc/ppm2array.dsp
diff --git a/thirdparty/glui/2.36/src/quaternion.cpp b/libraries/glui/glui-2.35/src/quaternion.cpp
similarity index 85%
rename from thirdparty/glui/2.36/src/quaternion.cpp
rename to libraries/glui/glui-2.35/src/quaternion.cpp
index 993adc1d8ec06bcd88f7ce60adbe529d608cfbbc..048890111d9f63b842c796838a4896f992cd761e 100644
--- a/thirdparty/glui/2.36/src/quaternion.cpp
+++ b/libraries/glui/glui-2.35/src/quaternion.cpp
@@ -4,27 +4,25 @@
 
   -------------------------------------------------------------------
 
-  GLUI User Interface Toolkit 
+  GLUI User Interface Toolkit (LGPL)
   Copyright (c) 1998 Paul Rademacher
 
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 ************************************************************************
 
diff --git a/thirdparty/glui/2.36/src/quaternion.h b/libraries/glui/glui-2.35/src/quaternion.h
similarity index 82%
rename from thirdparty/glui/2.36/src/quaternion.h
rename to libraries/glui/glui-2.35/src/quaternion.h
index 6d03984fd10e9a587c6de84e951d2d6b1ab6ee73..f58a7445e5273c34a5f4934fd50a56f0e7f5b3e1 100644
--- a/thirdparty/glui/2.36/src/quaternion.h
+++ b/libraries/glui/glui-2.35/src/quaternion.h
@@ -2,7 +2,7 @@
 
   quaternion.h - A quaternion class
 
-  GLUI User Interface Toolkit 
+  GLUI User Interface Toolkit (LGPL)
   Copyright (c) 1998 Paul Rademacher
 
   ---------------------------------------------------------------------
@@ -10,21 +10,19 @@
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 *****************************************************************************/
 
diff --git a/thirdparty/glui/2.36/src/readme.txt b/libraries/glui/glui-2.35/src/readme.txt
similarity index 79%
rename from thirdparty/glui/2.36/src/readme.txt
rename to libraries/glui/glui-2.35/src/readme.txt
index c2ec379400238139232f1ebb1560b84b38f8f628..6208e3f273bacf87ee4d66d85a10d3bf94aceaae 100644
--- a/thirdparty/glui/2.36/src/readme.txt
+++ b/libraries/glui/glui-2.35/src/readme.txt
@@ -1,63 +1,23 @@
-Welcome to the GLUI User Interface Library
+Welcome to the GLUI User Interface Library, v2.3!
+March 22, 2005
 -------------------------------------------------
 
 This distribution contains the latest community-maintained fork of the
-GLUI Library, now under the ZLIB license.  It is based on the GLUI
-v2.1 beta version from Paul Rademacher
-(http://www.cs.unc.edu/~rademach/glui/) plus the compatibility changes 
-made by Nigel Stewart in his "GLUI v2.2"
-(http://www.nigels.com/glt/glui).
-
+GLUI Library.  It is based on the GLUI v2.1 beta version from Paul
+Rademacher (http://www.cs.unc.edu/~rademach/glui/) plus the
+compatibility changes made by Nigel Stewart in his "GLUI v2.2" 
+(http://www.nigels.com/glt/glui) In accordance with the LGPL under
+which the library is released (according to Paul's web page at least),
+these changes are available to everyone in the community.
 
 WARNING: This version (2.3) introduces some incompatible changes with
 previous versions!!
 
-----------------------------------
-CHANGES:
-Version 2.36, November 4, 2007
-
-- License changed to the more permissive ZLIB license 
-  with the blessing of Paul Rademacher, Nigel Stewart, Bill Baxter, 
-  John Kew, Orion Sky Lawlor and all other developers.
-
-----------------------------------
 CHANGES:
-Version 2.35, July 7, 2006
-
-- Applied patch [950354] "Good Idle For Spinners" written by Alain
-  Durat.  This makes it so GLUI doesn't suck up 100% of your CPU time
-  when nothing is happening.
-
-Many changes submitted by Orion Sky Lawlor.
-- Comments: I've added doxygen comments to all of glui.h.  Use "make
-  docs" to generate Doxygen in doc/html/index.html.
-- Bug fix: rollout actually resizes properly when opened (bin/example5
-  demonstrated this bug). 
-- Bug fix: scroll bars have a (saner) time-based speed limit.  This is
-  visible in scrolling around in bin/example6.
-- Appearance: double-buffering can be turned on globally, eliminating
-  flicker when, e.g., resizing bin/example5 window.  This is on by
-  default; turn double-buffering off in GLUI::init or set
-  glui->buffer_mode to GLUI::buffer_front.
-- Appearance: the texture used in the GLUI_Rotation
-  control sphere is now mipmapped, which is much smoother than
-  nearest-neighbor.  It's also a stored (glGenTextures) texture, which
-  is much much faster. 
-- Optimization: I moved GLUI_Node::add_child_to_control to a more
-  linker-friendly location.  Now non-deprecated executables are up to
-  100KB smaller on disk.
-- Optimization: glui_img's are now 1 byte per pixel instead of 3 ints
-  per pixel.  This saves 50KB+ space on disk in the library and each
-  executable. 
-
-
 
 ----------------------------------
-CHANGES:
-Version 2.3, March 22, 2005
-
 - GLUI_String is now a std::string
-  This is the main source of most incompatibilities, but I felt it was
+  This is the main source of most incopatibilities, but I felt it was
   a necessary change, because the previous usage of a fixed-sized
   buffer was just too unsafe.  I myself was bitten a few times passing
   a char* buffer of insufficient size into GLUI as a live variable.
diff --git a/thirdparty/glui/2.36/src/tools/ppm.cpp b/libraries/glui/glui-2.35/src/tools/ppm.cpp
similarity index 100%
rename from thirdparty/glui/2.36/src/tools/ppm.cpp
rename to libraries/glui/glui-2.35/src/tools/ppm.cpp
diff --git a/thirdparty/glui/2.36/src/tools/ppm.h b/libraries/glui/glui-2.35/src/tools/ppm.h
similarity index 100%
rename from thirdparty/glui/2.36/src/tools/ppm.h
rename to libraries/glui/glui-2.35/src/tools/ppm.h
diff --git a/thirdparty/glui/2.36/src/tools/ppm2array.cpp b/libraries/glui/glui-2.35/src/tools/ppm2array.cpp
similarity index 100%
rename from thirdparty/glui/2.36/src/tools/ppm2array.cpp
rename to libraries/glui/glui-2.35/src/tools/ppm2array.cpp
diff --git a/thirdparty/glui/2.36/src/viewmodel.cpp b/libraries/glui/glui-2.35/src/viewmodel.cpp
similarity index 87%
rename from thirdparty/glui/2.36/src/viewmodel.cpp
rename to libraries/glui/glui-2.35/src/viewmodel.cpp
index 602a540642a6efb0a8b00b0a5c1ff656a4ed1fb8..f51be27ded2c19614367dda1dc9b991c6b492b2c 100644
--- a/thirdparty/glui/2.36/src/viewmodel.cpp
+++ b/libraries/glui/glui-2.35/src/viewmodel.cpp
@@ -2,27 +2,25 @@
 
   viewmodel.cpp
 
-  GLUI User Interface Toolkit 
+  GLUI User Interface Toolkit (LGPL)
   Copyright (c) 1998 Paul Rademacher
 
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 */
 
diff --git a/thirdparty/glui/2.36/src/viewmodel.h b/libraries/glui/glui-2.35/src/viewmodel.h
similarity index 91%
rename from thirdparty/glui/2.36/src/viewmodel.h
rename to libraries/glui/glui-2.35/src/viewmodel.h
index 4a3d5a2b01c5eef460d24af7b7e47a8b6a7693c4..c62932cfe8d466f47dd22a2dae5b8a7def0c7338 100644
--- a/thirdparty/glui/2.36/src/viewmodel.h
+++ b/libraries/glui/glui-2.35/src/viewmodel.h
@@ -2,27 +2,25 @@
 
   ViewModel.h
 
-  GLUI User Interface Toolkit 
+  GLUI User Interface Toolkit (LGPL)
   Copyright (c) 1998 Paul Rademacher
 
   WWW:    http://sourceforge.net/projects/glui/
   Forums: http://sourceforge.net/forum/?group_id=92496
 
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 */
 
diff --git a/thirdparty/glui/2.36/www/image1.png b/libraries/glui/glui-2.35/www/image1.png
similarity index 100%
rename from thirdparty/glui/2.36/www/image1.png
rename to libraries/glui/glui-2.35/www/image1.png
diff --git a/thirdparty/glui/2.36/www/index.html b/libraries/glui/glui-2.35/www/index.html
similarity index 82%
rename from thirdparty/glui/2.36/www/index.html
rename to libraries/glui/glui-2.35/www/index.html
index 4ade537fdf1fff366c8f6e0b90d9001e6dee0363..ef5febf096c23635dc0687ca756d241a0a4d8054 100644
--- a/thirdparty/glui/2.36/www/index.html
+++ b/libraries/glui/glui-2.35/www/index.html
@@ -3,7 +3,7 @@
 <head>
   <meta http-equiv="Content-Style-Type" content="text/css">
   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-  <meta name="Author" content="Nigel Stewart,  Paul Rademacher, Bill Baxter">
+  <meta name="Author" content="Nigel Stewart,  Paul Rademacher">
   <meta name="KeyWords" content="GLUI, OpenGL, GLUT, C++, User Interface">
   <title>GLUI User Interface Library</title>
 </head>
@@ -15,10 +15,9 @@
 <td width="100%">
 <p align="center">
 <a href="http://glui.sourceforge.net"><img src="title.jpg" alt="GLUI User Interface Library" width="391" height="40" border="0"></a><br><br>
-<b>Version 2.35</b><br><br>
+<b>Version 2.2</b><br><br>
 Written by <a HREF="http://www.cs.unc.edu/~rademach">Paul Rademacher</a><br>
 Maintained by <a href="http://www.nigels.com/">Nigel Stewart</a>
-and <a href="http://www.billbaxter.com/">Bill Baxter</a>
 </p>
 </td>
 
@@ -37,6 +36,7 @@ and <a href="http://www.billbaxter.com/">Bill Baxter</a>
 <a href="http://cvs.sourceforge.net/viewcvs.py/glui/glui/src/">CVS</a><br/>
 <a href="http://sourceforge.net/project/showfiles.php?group_id=92496">Files</a><br/>
 <br/>
+Mirrors<br/><a href="http://sf.gds.tuwien.ac.at/g/gl/glui/">[at]</a><br/>
 
 </font>
 <br/>
@@ -52,22 +52,33 @@ and <a href="http://www.billbaxter.com/">Bill Baxter</a>
 
 <h3>Status</h3>
 
-<p>
-<b>July 2006</b><br>
-After about a year of GLUI 2.3 sitting around in CVS, finally a new
-official release!  We've skipped right past 2.3 and gone straight to
-GLUI 2.35 (<a HREF="http://sourceforge.net/project/shownotes.php?release_id=430115&amp;group_id=92496">release notes</a>).<br> 
-GLUI 2.35 includes not only the never-released changes
-made for 2.3, but also several tasty new improvements submitted by
-Alain Daurat and Orion Sky Lawlor. 
-</p>
-
 <p>
 <b>March 2005</b><br>
 GLUI website migration to SourceForge.<br>
 Some new features are being merged into CVS.<br>
 </p>
 
+<p>
+<b>October 2003</b><br>
+GLUI is being migrated to SourceForge.<br>
+Source code is now available via <a href="https://sourceforge.net/cvs/?group_id=92496">CVS</a>.<br>
+<a href="https://sourceforge.net/forum/forum.php?forum_id=319502">Help</a>,
+<a href="https://sourceforge.net/forum/forum.php?forum_id=319503">developer</a>,
+and
+<a href="https://sourceforge.net/forum/forum.php?forum_id=319501">open</a>
+discussion forums have been established.<br>
+<a href="https://sourceforge.net/tracker/?group_id=92496&amp;atid=600952">Bug Tracking</a>
+is also available.
+</p>
+
+<p>
+<b>25th November 2002</b><br>
+Version 2.2 is available.<br>
+This is an update for gcc 3.2.<br>
+There are no feature enhancements or incompatibilities with 2.1.<br>
+<a href="#download">Download</a> GLUI 2.2
+</p>
+
 <hr>
 
 <h3>What is GLUI?</h3>
@@ -140,23 +151,10 @@ of OpenGL, Microsoft's, or Mesa's.
 GLUI subwindows docked inside the main graphics window.</p>
 <p align="center"><img src="screen3.png" alt="X-Windows GLUI" width="585" height="312" border="0"></p>
 
-<p>This screenshot (from Windows) shows the new widgets in GLUI 2.3, including
-scrollbars, a list widget, an area text widget, and a simple file browser.</p>
-<p align="center"><img src="screen4.png" alt="GLUI 2.3 Widgets" width="648" height="506" border="0"></p>
-
 <hr>
 
 <h3><A NAME="download">Download GLUI</a></h3>
 
-<p>Version 2.35</p>
-
-	<ul>
-		<li>Source: <a href="http://prdownloads.sourceforge.net/glui/glui_2.35.tgz?download">glui-2.35.tgz</a></li>
-		<li>Source: <a href="http://prdownloads.sourceforge.net/glui/glui-2.35.zip?download">glui-2.35.zip</a></li>
-		<li>OS X:   <a href="http://lukecyca.com/wp-content/uploads/2007/02/GLUIFramework.pkg.tgz">GLUIFramework.pkg.tgz</a> 
-			    (<a href="http://lukecyca.com/2007/02/14/glui-23-framework-for-mac-os-x/">Packaged by Luke Cyca</a>)</li>
-	</ul>
-
 <p>Version 2.2</p>
 
 	<ul>
@@ -202,6 +200,7 @@ GLUI is licensed under <a HREF="http://www.gnu.org/copyleft/lesser.html">LGPL</a
 
 <ul>
 <li>Paul Rademacher's <a href="http://www.cs.unc.edu/~rademach/glui/">original GLUI page</a>.</li>
+<li>European <a href="http://gd.tuwien.ac.at/hci/glui/">GLUI mirror</a> page.</li>
 <li>GLUI <a href="http://freshmeat.net/projects/glui/">freshmeat</a> page.</li>
 <li><a href="http://www.opengl.org">OpenGL.org</a></li>
 </ul>
diff --git a/thirdparty/glui/2.36/www/screen2.png b/libraries/glui/glui-2.35/www/screen2.png
similarity index 100%
rename from thirdparty/glui/2.36/www/screen2.png
rename to libraries/glui/glui-2.35/www/screen2.png
diff --git a/thirdparty/glui/2.36/www/screen3.png b/libraries/glui/glui-2.35/www/screen3.png
similarity index 100%
rename from thirdparty/glui/2.36/www/screen3.png
rename to libraries/glui/glui-2.35/www/screen3.png
diff --git a/thirdparty/glui/2.36/www/title.jpg b/libraries/glui/glui-2.35/www/title.jpg
similarity index 100%
rename from thirdparty/glui/2.36/www/title.jpg
rename to libraries/glui/glui-2.35/www/title.jpg
diff --git a/thirdparty/glui/2.36/www/valid-html401.png b/libraries/glui/glui-2.35/www/valid-html401.png
similarity index 100%
rename from thirdparty/glui/2.36/www/valid-html401.png
rename to libraries/glui/glui-2.35/www/valid-html401.png
diff --git a/thirdparty/glui/readme.txt b/libraries/glui/readme.txt
similarity index 100%
rename from thirdparty/glui/readme.txt
rename to libraries/glui/readme.txt
diff --git a/libraries/graph/graph.cpp b/libraries/graph/graph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cd10753d7891598285464ab98887c6d88a7daf25
--- /dev/null
+++ b/libraries/graph/graph.cpp
@@ -0,0 +1,614 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "graph" library , Copyright (C) 2018 USC                              *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <algorithm>
+#include "matrixIO.h"
+#include "graph.h"
+using namespace std;
+
+Graph::Graph() 
+{ 
+  numVertices = 0; 
+  numEdges = 0; 
+}
+
+Graph::Graph(const Graph & graph) 
+{
+  (*this) = graph;
+}
+
+Graph & Graph::operator=(const Graph & graph) 
+{
+  numVertices = graph.numVertices;
+  numEdges = graph.numEdges;
+  edges = graph.edges;
+  vertexNeighbors = graph.vertexNeighbors;
+  vertexNeighborsVector = graph.vertexNeighborsVector;
+  return *this;
+}
+
+Graph::~Graph()
+{
+}
+
+Graph::Graph(int numVertices_, int numEdges_, const int * edges_, int sortEdgeVertices): numVertices(numVertices_), numEdges(numEdges_)
+{
+  //printf("num vertices: %d\n", numVertices);
+  for(int i=0; i<numEdges; i++)
+  {
+    //printf("Edge: %d %d\n", edges_[2*i+0], edges_[2*i+1]);
+    const int * edge = edges_ + 2*i;
+    if (sortEdgeVertices && (edge[0] > edge[1])) // keep the two indices in each edge sorted
+      edges.insert(make_pair(edge[1], edge[0])); 
+    else
+      edges.insert(make_pair(edge[0], edge[1])); 
+  }
+
+  BuildVertexNeighbors();
+}
+
+Graph::Graph(const char * filename, int sortEdgeVertices)
+{
+  FILE * fin;
+  OpenFile_(filename, &fin, "r");
+  int code = fscanf(fin, "%d %d\n", &numVertices, &numEdges);
+  if (code != 2)
+    throw 1;
+
+  //printf("num vertices: %d\n", numVertices);
+  for(int i=0; i<numEdges; i++)
+  {
+    int vtxA, vtxB;
+    int code = fscanf(fin, "%d %d\n", &vtxA, &vtxB);
+    if (code != 2)
+      throw 2;
+
+    //printf("Edge: %d %d\n", vtxA, vtxB);
+    // keep the two indices in each edge sorted
+    if (sortEdgeVertices && (vtxA > vtxB))
+      edges.insert(make_pair(vtxB, vtxA));
+    else
+      edges.insert(make_pair(vtxA, vtxB));
+  }
+
+  fclose(fin);
+
+  BuildVertexNeighbors();
+}
+
+Graph::Graph(const SparseMatrix * matrix)
+{
+  numVertices = matrix->GetNumRows();
+  for(int row = 0; row < matrix->GetNumRows(); row++)
+  {
+    int rowLen = matrix->GetRowLength(row);
+    for(int j = 0; j < rowLen; j++)
+    {
+      int col = matrix->GetColumnIndex(row, j);
+      if (row < col)
+        edges.insert(make_pair(row, col));
+      else
+        edges.insert(make_pair(col, row));
+    }
+  }
+  numEdges = edges.size();
+
+  BuildVertexNeighbors();
+}
+
+void Graph::Save(const char * filename) const
+{
+  FILE * fout;
+  OpenFile_(filename, &fout, "w");
+  fprintf(fout, "%d %d\n", numVertices, numEdges);
+
+  for(set<pair<int, int> > :: iterator iter = edges.begin(); iter != edges.end(); iter++)
+  {
+    int vtxA = iter->first;
+    int vtxB = iter->second;
+    fprintf(fout, "%d %d\n", vtxA, vtxB);
+  }
+
+  fclose(fout);
+}
+
+void Graph::BuildVertexNeighbors()
+{
+  vertexNeighbors.assign(numVertices, map<int, int>());
+
+  for(set<pair<int,int> > :: iterator iter = edges.begin(); iter != edges.end(); iter++)
+  {
+    vertexNeighbors[iter->first].insert(make_pair(iter->second,0)); 
+    vertexNeighbors[iter->second].insert(make_pair(iter->first,0)); 
+  }
+
+  // number the neighbors
+  for(int i=0; i<numVertices; i++)
+  {
+    int count = 0;
+    for(map<int,int> :: iterator iter = vertexNeighbors[i].begin(); iter != vertexNeighbors[i].end(); iter++)
+    {
+      iter->second = count;
+      count++;
+    }
+  }
+
+  BuildVertexNeighborsVector();
+}
+
+void Graph::BuildVertexNeighborsVector()
+{
+  vertexNeighborsVector.assign(numVertices, vector<int>());
+
+  // create a copy of the data (in a vector), so that can access ith element fast
+  for(int i=0; i<numVertices; i++)
+    for(map<int,int> :: iterator iter = vertexNeighbors[i].begin(); iter != vertexNeighbors[i].end(); iter++)
+      vertexNeighborsVector[i].push_back(iter->first);
+}
+
+int Graph::GetMaxDegree() const
+{
+  int maxDegree = 0;
+  for(int vtx=0; vtx<numVertices; vtx++)
+    if ((int)vertexNeighbors[vtx].size() > maxDegree)
+      maxDegree = vertexNeighbors[vtx].size();
+  return maxDegree;
+}
+
+int Graph::GetMinDegree() const
+{
+  int minDegree = INT_MAX;
+  for(int vtx=0; vtx<numVertices; vtx++)
+    if ((int)vertexNeighbors[vtx].size() < minDegree)
+      minDegree = vertexNeighbors[vtx].size();
+  return minDegree;
+}
+
+double Graph::GetAvgDegree() const
+{
+  double avgDegree = 0;
+  for(int vtx=0; vtx<numVertices; vtx++)
+    avgDegree += vertexNeighbors[vtx].size();
+  return avgDegree / numVertices;
+}
+
+double Graph::GetStdevDegree() const
+{
+  double avgDegree_ = GetAvgDegree();
+  double std = 0;
+  for(int vtx=0; vtx<numVertices; vtx++)
+    std += (vertexNeighbors[vtx].size() - avgDegree_) * (vertexNeighbors[vtx].size() - avgDegree_);
+  return sqrt(std / numVertices);
+}
+
+int Graph::IsNeighbor(int vtx1, int vtx2) const
+{
+  map<int,int> :: const_iterator iter = vertexNeighbors[vtx1].find(vtx2);
+  if (iter == vertexNeighbors[vtx1].end())
+    return 0;
+  else
+    return iter->second + 1;
+}
+
+std::map<int,int> Graph::GetNeighborhoodWithDistance(int vertex, int neighborhoodSize)
+{
+  set<int> seed = {vertex};
+  return GetNeighborhoodWithDistance(seed, neighborhoodSize);
+}
+
+std::map<int,int> Graph::GetNeighborhoodWithDistance(const std::set<int> & seedVertices, int neighborhoodSize)
+{
+  map<int, int> foundVertices;
+  vector<int> lastLayerVertices;;
+  for(int vtx : seedVertices)
+  {
+    foundVertices.emplace(vtx, 0);
+    lastLayerVertices.push_back(vtx);
+  }
+
+  vector<int> newAffectedVertices;
+  for(int i=1; i<=neighborhoodSize; i++)
+  {
+    for(size_t j = 0; j < lastLayerVertices.size(); j++)
+    {
+      // traverse all neighbors and check if they were already previously inserted
+      int vtx = lastLayerVertices[j];
+      int deg = GetNumNeighbors(vtx);
+      for(int k=0; k<deg; k++)
+      {
+        int vtxNeighbor = GetNeighbor(vtx, k);
+        if (foundVertices.find(vtxNeighbor) == foundVertices.end())
+        {
+          // discovered new vertex
+          newAffectedVertices.push_back(vtxNeighbor);
+          foundVertices.emplace(vtxNeighbor, i);
+        }
+      }
+    }
+
+    lastLayerVertices.swap(newAffectedVertices);
+    newAffectedVertices.clear();
+  }
+  return foundVertices;
+}
+
+void Graph::ExpandNeighbors()
+{
+  // over all edges:
+  // insert neigbors of every vtx into the edges
+
+  set<pair<int, int> > expandedEdges = edges;
+
+  for(set<pair<int, int> > :: iterator iter = edges.begin(); iter != edges.end(); iter++)
+  {
+    int vtxA = iter->first; 
+    int vtxB = iter->second; 
+
+    // connect all neighbors of A to B
+    for(map<int,int> :: iterator mapIter = vertexNeighbors[vtxA].begin(); mapIter != vertexNeighbors[vtxA].end(); mapIter++)
+    {
+      if (vtxB < mapIter->first)
+        expandedEdges.insert(make_pair(vtxB, mapIter->first)); 
+    }
+
+    // connect all neigbhors of B to A
+    for(map<int,int> :: iterator mapIter = vertexNeighbors[vtxB].begin(); mapIter != vertexNeighbors[vtxB].end(); mapIter++)
+      if (vtxA < mapIter->first)
+        expandedEdges.insert(make_pair(vtxA, mapIter->first)); 
+  }
+ 
+  edges = expandedEdges;
+  numEdges = edges.size();
+  BuildVertexNeighbors();
+}
+
+void Graph::PrintInfo() const
+{
+  printf("Graph vertices: %d\n", numVertices);
+  printf("Graph edges: %d\n", numEdges);
+  printf("Graph min degree: %d\n", GetMinDegree());
+  printf("Graph max degree: %d\n", GetMaxDegree());
+  printf("Graph avg degree: %G\n", GetAvgDegree());
+  printf("Graph degree stdev: %G\n", GetStdevDegree());
+}
+
+void Graph::GetLaplacian(SparseMatrix ** L, int scaleRows) const
+{
+  SparseMatrixOutline outline(3*numVertices);
+  for(int i=0; i<numVertices; i++)
+  {
+    int numNeighbors = (int)vertexNeighborsVector[i].size();
+    if (numNeighbors == 0)
+      continue;
+
+    for(int k=0; k<3; k++)
+      outline.AddEntry(3 * i + k, 3 * i + k, (scaleRows != 0) ? 1.0 : numNeighbors);
+
+    double weight;
+    if (scaleRows != 0)
+      weight = -1.0 / numNeighbors;
+    else
+      weight = -1.0;
+
+    for(int j=0; j<numNeighbors; j++)
+      for(int k=0; k<3; k++)
+        outline.AddEntry(3 * i + k, 3 * vertexNeighborsVector[i][j] + k, weight);
+  }
+
+  *L = new SparseMatrix(&outline);
+}
+
+Graph * Graph::CartesianProduct(Graph & graph2) const
+{
+  int numProductVertices = numVertices * graph2.numVertices;
+  int numProductEdges = numEdges * graph2.numVertices + numVertices * graph2.numEdges;
+  int * productEdges = (int*) malloc (sizeof(int) * 2 * numProductEdges);
+ 
+  printf("Num space-time graph vertices: %d\n", numProductVertices);
+  printf("Num space-time graph edges: %d\n", numProductEdges);
+
+  int edge = 0;
+  for(int j=0; j<graph2.numVertices; j++)
+  {
+    for(int i=0; i<numVertices; i++)
+    {
+      // connect every vertex of graph1 to its neighbors
+      //std::vector< std::vector<int> > vertexNeighborsVector;
+      for(int k=0; k<(int)vertexNeighborsVector[i].size(); k++)
+      {  
+        if (i > vertexNeighborsVector[i][k])
+        {
+          productEdges[2*edge+0] = GetCartesianProductVertexIndex(i, j);
+          productEdges[2*edge+1] = GetCartesianProductVertexIndex(vertexNeighborsVector[i][k], j);
+          edge++;
+        }
+      }
+      // connect every vertex of graph2 to its neighbors
+      for(int k=0; k<(int)(graph2.vertexNeighborsVector[j].size()); k++)
+      {  
+        if (j > graph2.vertexNeighborsVector[j][k])
+        {
+          productEdges[2*edge+0] = GetCartesianProductVertexIndex(i, j);
+          productEdges[2*edge+1] = GetCartesianProductVertexIndex(i, graph2.vertexNeighborsVector[j][k]);
+          edge++;
+        }
+      }
+    }
+  }
+
+  Graph * graph = new Graph(numProductVertices, numProductEdges, productEdges);
+  free(productEdges);
+  return graph;
+}
+
+// cluster given vertices into connected components
+void Graph::Cluster(const set<int> & vertices, vector<set<int> > & clusters) const
+{
+  clusters.clear();
+
+  set<int> remainingVertices = vertices;
+
+  while(remainingVertices.size() > 0)
+  {
+    int seed = *remainingVertices.begin();
+    clusters.resize(clusters.size() + 1);
+    set<int> & curCluster = clusters.back();
+
+    vector<int> oldFront, front;
+
+    curCluster.insert(seed);
+    remainingVertices.erase(seed);
+    oldFront.push_back(seed);
+
+    while (oldFront.size() > 0)
+    {
+      // create the front
+      front.clear();
+      for(size_t i = 0; i < oldFront.size(); i++)
+      {
+        int node = oldFront[i];
+        for(size_t j = 0; j < vertexNeighborsVector[node].size(); j++)
+        {
+          int neighbor = vertexNeighborsVector[node][j];
+          if (remainingVertices.find(neighbor) != remainingVertices.end())
+          {
+            front.push_back(neighbor);
+            remainingVertices.erase(neighbor);
+            curCluster.insert(neighbor);
+          }
+        }
+      }
+      oldFront = front;
+    }
+  }
+}
+
+int Graph::GetCartesianProductVertexIndex(int vertex1, int vertex2) const
+{
+  return vertex2 * numVertices + vertex1;
+}
+
+void Graph::GetCartesianProductVertexIndexComponents(int productVertex, int * vertex1, int * vertex2) const
+{
+  *vertex2 = productVertex / numVertices;
+  *vertex1 = productVertex % numVertices;
+}
+
+void Graph::ShortestDistance(const std::set<int> & seedVertices, std::vector<int> & distances) const
+{
+  distances.assign(numVertices, INT_MAX);
+  for(set<int> :: const_iterator iter = seedVertices.begin(); iter != seedVertices.end(); iter++)
+    distances[*iter] = 0;
+
+  int distance = 0;
+  vector<int> oldFront, front;
+  oldFront.reserve(numVertices);
+  front.reserve(numVertices);
+  oldFront.insert(oldFront.end(), seedVertices.begin(), seedVertices.end());
+  while (oldFront.size() > 0)
+  {
+    distance++;
+
+    // create the front
+    front.clear();
+    for(size_t i = 0; i < oldFront.size(); i++)
+    {
+      int node = oldFront[i];
+      for(size_t j = 0; j < vertexNeighborsVector[node].size(); j++)
+      {
+        int neighbor = vertexNeighborsVector[node][j];
+        if (distances[neighbor] == INT_MAX)
+        {
+          front.push_back(neighbor);
+          distances[neighbor] = distance;
+        }
+      }
+    }
+
+    oldFront = front; 
+  }
+}
+
+bool Graph::FindShortestPath(const std::set<int> & seedVertices, const std::set<int> & destinationVertices, std::vector<int> * path) const
+{
+  if (path)
+    path->clear();
+  int distance = 0;
+  vector<int> oldFront, front;
+  oldFront.insert(oldFront.end(), seedVertices.begin(), seedVertices.end());
+  map<int, int> parentNode;
+  for(set<int>::const_iterator it = seedVertices.begin(); it != seedVertices.end(); it++)
+  {
+    parentNode[*it] = -1;
+    if (destinationVertices.find(*it) != destinationVertices.end()) // one of the start locations is the destination
+    {
+      if (path)
+        path->push_back(*it);
+      return true;
+    }
+  }
+
+  while (oldFront.size() > 0)
+  {
+    distance++;
+
+    // create the front
+    front.clear();
+    for(size_t i = 0; i < oldFront.size(); i++)
+    {
+      int node = oldFront[i];
+      for(size_t j = 0; j < vertexNeighborsVector[node].size(); j++)
+      {
+        int neighbor = vertexNeighborsVector[node][j];
+        if (parentNode.find(neighbor) == parentNode.end())
+        {
+          front.push_back(neighbor);
+          parentNode.insert(pair<int, int>(neighbor, node));
+          if (destinationVertices.find(neighbor) != destinationVertices.end()) // we reach the destination
+          {
+            if (path)
+            {
+              int parent = neighbor, curNode = 0;
+              do
+              {
+                curNode = parent;
+                path->push_back(curNode); // push nodes into path in a reverse order
+                parent = parentNode[curNode];
+              } while(parent >= 0);
+              reverse(path->begin(), path->end()); // recover the correct order
+            }
+            return true;
+          }
+        }
+      }
+    }
+
+    oldFront = front;
+  }
+
+  return false;
+}
+
+bool Graph::FindLocalShortestPath(const std::set<int> & localVertices, const std::set<int> & seedVertices, const std::set<int> & destinationVertices, std::vector<int> * path) const
+{
+  if (path)
+    path->clear();
+  int distance = 0;
+  vector<int> oldFront, front;
+  map<int, int> parentNode;
+
+  for(set<int>::const_iterator it = seedVertices.begin(); it != seedVertices.end(); it++)
+  {
+    if (localVertices.find(*it) == localVertices.end())
+      continue; // the seed vertex is not inside localVertices
+    oldFront.push_back(*it);
+    parentNode[*it] = -1;
+    if (destinationVertices.find(*it) != destinationVertices.end()) // one of the start locations is the destination
+    {
+      if (path)
+        path->push_back(*it);
+      return true;
+    }
+  }
+
+  while (oldFront.size() > 0)
+  {
+    distance++;
+
+    // create the front
+    front.clear();
+    for(size_t i = 0; i < oldFront.size(); i++)
+    {
+      int node = oldFront[i];
+      for(size_t j = 0; j < vertexNeighborsVector[node].size(); j++)
+      {
+        int neighbor = vertexNeighborsVector[node][j];
+        if (localVertices.find(neighbor) == localVertices.end())
+          continue;
+        if (parentNode.find(neighbor) == parentNode.end())
+        {
+          front.push_back(neighbor);
+          parentNode.insert(pair<int, int>(neighbor, node));
+          if (destinationVertices.find(neighbor) != destinationVertices.end()) // we reach the destination
+          {
+            if (path)
+            {
+              int parent = neighbor, curNode = 0;
+              do
+              {
+                curNode = parent;
+                path->push_back(curNode); // push nodes into path in a reverse order
+                parent = parentNode[curNode];
+              } while(parent >= 0);
+              reverse(path->begin(), path->end()); // recover the correct order
+            }
+            return true;
+          }
+        }
+      }
+    }
+
+    oldFront = front;
+  }
+
+  return false;
+}
+
+
+void Graph::GetConnectedComponent(int vtx, std::set<int> &connectedVertices) const
+{
+  std::vector<int> Q;
+  std::set<int> visited;
+  size_t start = 0;
+
+  Q.push_back(vtx);
+  visited.insert(vtx);
+
+  while (Q.size() - start > 0) {
+    int v = Q[start];
+    start++;
+
+    for (int i = 0; i < (int)vertexNeighborsVector[v].size(); i++) {
+      int c = vertexNeighborsVector[v][i];
+      if (visited.find(c) == visited.end()) {
+        Q.push_back(c);
+        visited.insert(c);
+      }
+    }
+  }
+
+  connectedVertices = visited;
+}
diff --git a/libraries/graph/graph.h b/libraries/graph/graph.h
new file mode 100644
index 0000000000000000000000000000000000000000..f1ece40c4ddf78b02cb9b9bec9c4ef75f7c52e5c
--- /dev/null
+++ b/libraries/graph/graph.h
@@ -0,0 +1,153 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "graph" library , Copyright (C) 2018 USC                              *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _GRAPH_H_
+#define _GRAPH_H_
+
+#include <vector>
+#include <set>
+#include <map>
+#include "sparseMatrix.h"
+
+/*
+  A class to store an undirected graph (nodes connected with edges).
+*/
+
+class Graph
+{
+public:
+
+  Graph(); 
+  // if sortEdgeVertices=1, each edge (v0, v1) in the file will be sorted to ensure v0 < v1 
+  // before added into internal data
+  Graph(const char * filename, int sortEdgeVertices=1); // load graph from file
+  // each edge is given by two integers; length of "edges" should be 2xnumEdges
+  // if sortEdgeVertices=1, each edge (v0, v1) in edges will be sorted to ensure v0 < v1 
+  // before added into internal data
+  Graph(int numVertices, int numEdges, const int * edges, int sortEdgeVertices=1);
+
+  // convert a matrix into a graph; numVertices = matrix->GetNumRows()
+  // two vtx (v0, v1) share an edge if they have sparse entries at matrix(v0,v1) or matrix(v1,v0)
+  Graph(const SparseMatrix * matrix);
+
+  Graph(const Graph & graph);
+  Graph & operator=(const Graph & graph);
+  virtual ~Graph();
+
+  void Save(const char * filename) const; // save graph to file
+
+  int GetNumVertices() const;
+  int GetNumEdges() const;
+
+  int GetNumNeighbors(int vertex) const;
+  int GetNeighbor(int vertex, int i) const;
+  // return 0 if vtx1 and vtx2 are not neighbors
+  // return neighbor index + 1 (range: [1, #neighbor]) otherwise
+  int IsNeighbor(int vtx1, int vtx2) const;
+  // neighborhoodSize >= 0
+  // find all vertices whose distance to vertex <= neighborhoodSize
+  // return their vertex ID and distance
+  std::map<int,int> GetNeighborhoodWithDistance(int vertex, int neighborhoodSize);
+  std::map<int,int> GetNeighborhoodWithDistance(const std::set<int> & seedVertices, int neighborhoodSize);
+
+  int GetMinDegree() const;
+  int GetMaxDegree() const;
+  double GetAvgDegree() const;
+  double GetStdevDegree() const;
+
+  void ExpandNeighbors(); // connects every node to all the neighbors of every neighbor
+  void PrintInfo() const;
+
+  // if scaleRows == 1, each row will be scaled to sum to one
+  void GetLaplacian(SparseMatrix ** L, int scaleRows=0) const;
+
+  // returns the Cartesian graph product of "this" and graph2
+  Graph * CartesianProduct(Graph & graph2) const;
+  // return the index of vertex (vertex1, vertex2) in the cartesian product of "this" with another graph (which is not needed explicitly)
+  int GetCartesianProductVertexIndex(int vertex1, int vertex2) const;
+  // converts in the opposite direction
+  void GetCartesianProductVertexIndexComponents(int productVertex, int * vertex1, int * vertex2) const;
+
+  // clusters given vertices into connected components
+  // previous data stored in clusters will be cleared
+  void Cluster(const std::set<int> & vertices, std::vector<std::set<int> > & clusters) const;
+
+  // computes the shortest distance from the given seed vertices (distance of zero) to all the graph vertices
+  // input: seed vertices
+  // output: distance to the set of seed vertices, for each mesh vertex
+  void ShortestDistance(const std::set<int> & seedVertices, std::vector<int> & distances) const;
+
+  // computes one of the shortest path from seedVertices to one of the destinationVertices
+  // return whether the path exists.
+  // if path != NULL, previous data stored in path will be cleared. On return, it stores the path from
+  // one of the seed vertices to one of the destination vertices, both ends included
+  bool FindShortestPath(const std::set<int> & seedVertices, const std::set<int> & destinationVertices, std::vector<int> * path = NULL) const;
+
+  // find shortest path only in the local vertices set
+  bool FindLocalShortestPath(const std::set<int> & localVertices, const std::set<int> & seedVertices, const std::set<int> & destinationVertices, std::vector<int> * path = NULL) const;
+
+  // find the connected component of vtx
+  void GetConnectedComponent(int vtx, std::set<int> & connectedVertices) const;
+
+protected:
+  int numVertices, numEdges; // num vertices, num edges
+  std::set< std::pair<int, int> > edges;
+  // for each vtx, mapping: vtx index of neighbor [0, numVertices) -> neighbor index [0, #neighbor)
+  std::vector< std::map<int, int> > vertexNeighbors;
+  std::vector< std::vector<int> > vertexNeighborsVector;
+
+  void BuildVertexNeighbors();
+  void BuildVertexNeighborsVector();
+};
+
+inline int Graph::GetNumVertices() const
+{
+  return numVertices;
+}
+
+inline int Graph::GetNumEdges() const
+{
+  return numEdges;
+}
+
+inline int Graph::GetNumNeighbors(int vertex) const
+{
+  return (int) vertexNeighborsVector[vertex].size();
+}
+
+inline int Graph::GetNeighbor(int vertex, int i) const
+{
+  return vertexNeighborsVector[vertex][i];
+}
+
+#endif
+
diff --git a/src/libhashTable/hashTable.cpp b/libraries/hashTable/hashTable.cpp
similarity index 64%
rename from src/libhashTable/hashTable.cpp
rename to libraries/hashTable/hashTable.cpp
index 7116dd33f8c52bd9a9617a52cd01d950bcaf291e..96ccdd8e544df6c3f0a45cc117df4db76c24bb53 100644
--- a/src/libhashTable/hashTable.cpp
+++ b/libraries/hashTable/hashTable.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "hashTable" library , Copyright (C) 2007 CMU                          *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 #include <set>
 using namespace std;
 
-namespace vega
-{
 template<class Data>
 HashTable<Data>::HashTable(unsigned int suggestedSize)
 {
@@ -161,42 +163,7 @@ void HashTable<Data>::printInfo()
 }
 
 // set<unsigned int>
-template HashTable<set<unsigned int> >::HashTable(unsigned int suggestedSize);
-template bool HashTable<set<unsigned int> >::isPrime(unsigned int n);
-template void HashTable<set<unsigned int> >::clear();
-template void HashTable<set<unsigned int> >::insert(unsigned int key, set<unsigned int>  & data);
-template bool HashTable<set<unsigned int> >::contains(unsigned int key, set<unsigned int> & data);
-template void HashTable<set<unsigned int> >::erase(unsigned int key);
-template set<unsigned int> * HashTable<set<unsigned int> >::find(unsigned int key);
-template void HashTable<set<unsigned int> >::printInfo();
-
-// set<int>
-template HashTable<set<int> >::HashTable(unsigned int suggestedSize);
-template bool HashTable<set<int> >::isPrime(unsigned int n);
-template void HashTable<set<int> >::clear();
-template void HashTable<set<int> >::insert(unsigned int key, set<int>  & data);
-template bool HashTable<set<int> >::contains(unsigned int key, set<int> & data);
-template void HashTable<set<int> >::erase(unsigned int key);
-template set<int> * HashTable<set<int> >::find(unsigned int key);
-template void HashTable<set<int> >::printInfo();
-
-// unsigned int
-template HashTable<unsigned int>::HashTable(unsigned int suggestedSize);
-template bool HashTable<unsigned int>::isPrime(unsigned int n);
-template void HashTable<unsigned int>::clear();
-template void HashTable<unsigned int>::insert(unsigned int key, unsigned int & data);
-template bool HashTable<unsigned int>::contains(unsigned int key, unsigned int & data);
-template void HashTable<unsigned int>::erase(unsigned int key);
-template unsigned int * HashTable<unsigned int>::find(unsigned int key);
-template void HashTable<unsigned int>::printInfo();
-
-// int
-template HashTable<int>::HashTable(unsigned int suggestedSize);
-template bool HashTable<int>::isPrime(unsigned int n);
-template void HashTable<int>::clear();
-template void HashTable<int>::insert(unsigned int key, int & data);
-template bool HashTable<int>::contains(unsigned int key, int & data);
-template void HashTable<int>::erase(unsigned int key);
-template int * HashTable<int>::find(unsigned int key);
-template void HashTable<int>::printInfo();
-}
+template class HashTable<set<unsigned int>>;
+template class HashTable<set<int>>;
+template class HashTable<unsigned int>;
+template class HashTable<int>;
diff --git a/src/libhashTable/hashTable.h b/libraries/hashTable/hashTable.h
similarity index 85%
rename from src/libhashTable/hashTable.h
rename to libraries/hashTable/hashTable.h
index b9b861db15830d5f1de33f00b19bfa12b6c22f86..67d2dbc5481dbca527117ef82950846ddd0cc39e 100644
--- a/src/libhashTable/hashTable.h
+++ b/libraries/hashTable/hashTable.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "hashTable" library , Copyright (C) 2007 CMU                          *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -41,8 +45,6 @@
 #include <vector>
 #include <list>
 
-namespace vega
-{
 template<class Data>
 class HashTable
 {
@@ -80,6 +82,6 @@ inline unsigned int HashTable<Data>::hashFunction(unsigned int key)
 {
   return (key*(key+3) % p);
 }
-}
+
 #endif
 
diff --git a/src/libimageIO/imageFormats.h b/libraries/imageIO/imageFormats.h
similarity index 77%
rename from src/libimageIO/imageFormats.h
rename to libraries/imageIO/imageFormats.h
index 558a4dec82e8e0e7fb0c8f2704c8890c6bd7621a..9b2ddb0a19146d9b85c74be2cbe9742aa90842c1 100644
--- a/src/libimageIO/imageFormats.h
+++ b/libraries/imageIO/imageFormats.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
  * http://www.jernejbarbic.com/code                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libimageIO/imageIO.cpp b/libraries/imageIO/imageIO.cpp
similarity index 95%
rename from src/libimageIO/imageIO.cpp
rename to libraries/imageIO/imageIO.cpp
index de08a1440afcb9b5f86b172f0b06e562404de5cb..0dec2287d7054d276150d6d2aa41c721c54031a8 100644
--- a/src/libimageIO/imageIO.cpp
+++ b/libraries/imageIO/imageIO.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Yili Zhao, Jernej Barbic                                 *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -39,7 +43,7 @@
 #include <string.h>
 #include <stdlib.h>
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #pragma warning(disable : 4996)
 #endif
 
@@ -64,9 +68,8 @@ extern "C"
 #endif
 
 #include "imageIO.h"
+#include "vegalong.h"
 
-namespace vega
-{
 #define BITS_PER_CHANNEL_8 8
 #define BITS_PER_CHANNEL_16 16
 #define IMAGE_IO_RGB 3
@@ -113,7 +116,7 @@ ImageIO::errorType ImageIO::loadPPM(const char * filename)
 
   char buf[4096];
   char * result = fgets(buf, 4096, file);
-  result = result; // to avoid a compiler warning
+  result = result + 1; // to avoid a compiler warning
   if(strncmp(buf, "P6", 2))
   {
     printf("Error in loadPPM: File is not a raw RGB ppm.\n");
@@ -126,7 +129,8 @@ ImageIO::errorType ImageIO::loadPPM(const char * filename)
   int maxval;
   while(i < 3)
   {
-    result = fgets(buf, 4096, file);
+    char * dummy = fgets(buf, 4096, file);
+    dummy = dummy + 1; // to suppress compiler warning
     if(buf[0] == '#') // ignore comments
       continue;
     if(i == 0)
@@ -140,12 +144,13 @@ ImageIO::errorType ImageIO::loadPPM(const char * filename)
   bytesPerPixel = 3;
 
   // read the pixels
-  free(pixels);
+  if(ownPixels)
+    free(pixels);
   pixels = (unsigned char*) malloc (sizeof(unsigned char) * 3 * width * height);
+  ownPixels = 1;
   if(fread(pixels, sizeof(unsigned char), 3 * width * height, file) < 3 * width * height)
   {
     printf("Error in loadPPM: Error reading ppm image from %s.\n", filename);
-    free(pixels);
     fclose(file);
     return IO_ERROR;
   }
@@ -182,6 +187,8 @@ ImageIO::errorType ImageIO::savePPM(const char * filename)
     if (fwrite(&pixelsNoAlphaChannel[pos], sizeof(unsigned char), 3 * width, file) != 3 * width)
     {
       printf("Error in savePPM: Error while saving ppm image to %s.\n", filename);
+      if (bytesPerPixel == 4)
+        free(pixelsNoAlphaChannel);
       fclose(file);
       return IO_ERROR;
     }
@@ -247,17 +254,18 @@ ImageIO::errorType ImageIO::loadTGA(const char * filename)
     return INVALID_FILE_FORMAT;
   }
 
-  long int imageSize = width * height * bytesPerPixel;
+  vegalong imageSize = width * height * bytesPerPixel;
 
   // allocate memory for image data
-  free(pixels);
+  if(ownPixels)
+    free(pixels);
   pixels = (unsigned char*) malloc (sizeof(unsigned char) * imageSize);
+  ownPixels = 1;
 
   // read image data
   if ( (int)fread(pixels, sizeof(unsigned char), imageSize, file) < imageSize)
   {
     printf("Error in loadTGA: Error reading tga image from %s.\n", filename);
-    free(pixels);
     fclose(file);
     return IO_ERROR;
   }
@@ -393,12 +401,14 @@ ImageIO::errorType ImageIO::loadJPEG(const char * filename)
   width = jpgPicturePtr->image_width;
   height = jpgPicturePtr->image_height;
 
-  // CAREFULL we must be!! the bytesPerPixel information is not in the jpeg header.
-  // It is only available after calling jpeg_start_decompress()
+  // Careful: the bytesPerPixel information is not available in the jpeg header.
+  // It is only available after calling jpeg_start_decompress().
   bytesPerPixel = jpgPicturePtr->output_components;
- 
-  free(pixels);
+
+  if(ownPixels)
+    free(pixels);
   pixels = (unsigned char *) malloc(sizeof(unsigned char) * width * height * bytesPerPixel);
+  ownPixels = 1;
 //  printf("Width = %d; Height = %d, bytesPerPixel = %d\n", width, height, bytesPerPixel);
 //  fflush(NULL);
 
@@ -410,7 +420,6 @@ ImageIO::errorType ImageIO::loadJPEG(const char * filename)
     if (jpeg_read_scanlines(jpgPicturePtr, (JSAMPARRAY)rowPtr, maxNumLines) != maxNumLines)
     {
       printf("Error in loadJPEG: Error reading jpg image from %s.\n", filename);
-      free(pixels);
       jpeg_destroy_decompress(jpgPicturePtr);
       fclose(file);
       return IO_ERROR;
@@ -554,7 +563,10 @@ ImageIO::errorType ImageIO::loadTIFF(const char * filename)
     return IO_ERROR;
   }
 
+  if(ownPixels)
+    free(pixels);
   pixels = (unsigned char*) malloc (sizeof(unsigned char) * width * height * bytesPerPixel);
+  ownPixels = 1;
 
   // write tiff_pixels into the pixels array
   int counter = 0;
@@ -804,8 +816,7 @@ ImageIO::errorType ImageIO::loadPNG(const char * filename)
   default:
     printf("Error in loadPNG: image transformation failed.\n");
     fclose(file);
-    exit(0);  // comment this out after debugging, use return IO_ERROR
-    //return (IO_ERROR);
+    return (IO_ERROR);
     break;
   }
 
@@ -814,12 +825,14 @@ ImageIO::errorType ImageIO::loadPNG(const char * filename)
   {
     printf("Error in loadPNG: the number of bytes per row, which is %lu, does not match bytesPerPixel * width, which is %d.\n", png_get_rowbytes(png_ptr, info_ptr), bytesPerPixel * width);  
     fclose(file);
-    exit(0); // comment this out after debugging, use return IO_ERROR
     return (IO_ERROR);
   }
 
   unsigned int bytesPerRow = bytesPerPixel * width;
+  if(ownPixels)
+    free(pixels);
   pixels = (unsigned char *) malloc (sizeof(unsigned char) * bytesPerRow * height);
+  ownPixels = 1;
 
   png_bytep * row_pointers = (png_bytep*) malloc (sizeof(png_bytep) * height);
   for(unsigned int row = 0; row < height; row++)
@@ -1030,4 +1043,3 @@ void ImageIO::flipVertically()
   free(rowBuffer);
 }
 
-}
diff --git a/src/libimageIO/imageIO.h b/libraries/imageIO/imageIO.h
similarity index 84%
rename from src/libimageIO/imageIO.h
rename to libraries/imageIO/imageIO.h
index 81c119e44777e895ab6ffac8359973ac02ff1616..5f1db5f1f988e2e7e9bfece09f991ad4b59fd8c5 100644
--- a/src/libimageIO/imageIO.h
+++ b/libraries/imageIO/imageIO.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Yili Zhao, Jernej Barbic                                 *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -38,8 +42,6 @@
 #ifndef _IMAGEIO_H_
 #define _IMAGEIO_H_
 
-namespace vega
-{
 class ImageIO
 {
 public:
@@ -56,6 +58,7 @@ public:
   inline unsigned int getHeight() { return height; }
   inline unsigned int getBytesPerPixel() { return bytesPerPixel; }
   inline unsigned char * getPixels() { return pixels; }
+  inline unsigned char getPixel(int x, int y, int channel) { return pixels[(y * width + x) * bytesPerPixel + channel]; }
 
   // error codes
   typedef enum { OK, INVALID_FILE_FORMAT, IO_ERROR, MEMORY_ERROR, OTHER_ERROR } errorType;
@@ -103,6 +106,6 @@ protected:
   errorType loadNONE(const char * filename);
   errorType saveNONE(const char * filename);
 };
-}
+
 #endif
 
diff --git a/libraries/immersionMesher/immersionGraphNode.cpp b/libraries/immersionMesher/immersionGraphNode.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..78d1733c68d7e9fc3f96a4d010b8d2879421a2c3
--- /dev/null
+++ b/libraries/immersionMesher/immersionGraphNode.cpp
@@ -0,0 +1,166 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "immersionMesher" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "immersionGraphNode.h"
+
+#include <cassert>
+using namespace std;
+
+ostream & operator << (ostream & o, ImmOwnership os)
+{
+  switch (os)
+  {
+  case OWNED:
+    cout << "OWN"; break;
+  case DECLINED:
+    cout << "DEC"; break;
+  case UNDECIDED:
+    cout << "UND"; break;
+  }
+  return o;
+}
+
+ImmersionGraphNode::ImmersionGraphNode(int cellID, int nodeID, const vector<map<int, bool>> & cellPatches) : nodeID(nodeID), cellID(cellID)
+{
+  for(auto p : cellPatches[cellID])
+  {
+    int patchID = p.first;
+    nbrs[patchID] = -1;
+    patchOwnership[patchID] = (p.second ? UNDECIDED : DECLINED);
+  }
+}
+
+bool ImmersionGraphNode::hasNbrAtPatch(int patchID) const
+{
+  auto it = nbrs.find(patchID);
+  assert(it != nbrs.end());
+  return (it->second >= 0);
+}
+
+int ImmersionGraphNode::getNbrIDAtPatch(int patchID) const
+{
+  auto it = nbrs.find(patchID);
+  assert(it != nbrs.end());
+  return it->second;
+}
+
+void ImmersionGraphNode::setNbrIDAtPatch(int patchID, int nbrID)
+{
+  assert(nbrs.find(patchID) != nbrs.end());
+  assert(nbrs[patchID] == -1);
+  nbrs[patchID] = nbrID;
+}
+
+bool ImmersionGraphNode::hasNbrAtPatchAndNotNode(int patchID, int nodeID) const
+{
+  auto it = nbrs.find(patchID);
+  assert(it != nbrs.end());
+  return (it->second >= 0 && it->second != nodeID);
+}
+
+ImmOwnership ImmersionGraphNode::getPatchOwnership(int patchID) const
+{
+  assert(nbrs.find(patchID) != nbrs.end());
+  auto it = patchOwnership.find(patchID);
+  assert(it != patchOwnership.end());
+  return patchOwnership.find(patchID)->second;
+}
+
+void ImmersionGraphNode::setPatchOwnership(int patchID, ImmOwnership o)
+{
+  assert(o != UNDECIDED);
+  assert(nbrs.find(patchID) != nbrs.end());
+  assert(patchOwnership[patchID] == UNDECIDED || patchOwnership[patchID] == o);
+  patchOwnership[patchID] = o;
+}
+
+// cellPatchBouNbrs: cellID -> patchID -> bouID -> <nbr bouID, nbr patchID >
+bool ImmersionGraphNode::checkPatchValid(const vector<map<int, map<int , pair<int, int>>>> & cellPatchBouNbrs, bool verbose) const
+{
+  for(auto p : patchOwnership) // for each patch
+  {
+    int patchID = p.first;
+    ImmOwnership o = p.second;
+
+    auto patchNbrIt = cellPatchBouNbrs[cellID].find(patchID);
+    if (patchNbrIt == cellPatchBouNbrs[cellID].end()) continue; // if this cell has only one B-patch, then this B-patch has no geo nbrs
+    const auto & nbrPatchMap = patchNbrIt->second; // bouID -> <nbr bouID, nbr patchID >
+    for(auto p2: nbrPatchMap)
+    {
+      int nbrPatchID = p2.second.second;
+      if (nbrPatchID == patchID) continue; // if patch self-intersect
+      if (neighboringOwernshipValid(o, getPatchOwnership(nbrPatchID)) == false)
+      {
+        if (verbose)
+        {
+          cout << "checkPatchValid fails on patch " << patchID << " at a node " << nodeID << " at cell " << cellID << endl;
+          cout << "nbr patch " << nbrPatchID << endl;
+          print();
+        }
+        return false;
+      }
+    }
+  }
+  if (verbose)
+  {
+    cout << "checkPatchValid finishes" << endl;
+  }
+  return true;
+}
+
+void ImmersionGraphNode::print() const
+{
+  cout << "node " << nodeID << " at cell " << cellID << " ";
+  for(auto p : nbrs)
+  {
+    cout << p.first << "->(" << getPatchOwnership(p.first) << " " << p.second << ") ";
+  }
+  cout << endl;
+}
+
+void printNodes(const vector<ImmersionGraphNode> & nodes)
+{
+  for(auto & node : nodes)
+  {
+    node.print();
+  }
+}
+
+bool ImmersionGraphNode::operator == (const ImmersionGraphNode & node2) const
+{
+  if (nodeID != node2.nodeID) return false;
+  if (cellID != node2.cellID) return false;
+  if (nbrs != node2.nbrs) return false;
+  if (patchOwnership != node2.patchOwnership) return false;
+  return true;
+}
+
diff --git a/libraries/immersionMesher/immersionGraphNode.h b/libraries/immersionMesher/immersionGraphNode.h
new file mode 100644
index 0000000000000000000000000000000000000000..305f2a58b28f31f9cb6b09e1cea4bfbac5bcecd5
--- /dev/null
+++ b/libraries/immersionMesher/immersionGraphNode.h
@@ -0,0 +1,132 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "immersionMesher" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef IMMERSIONGRAPHNODE_H
+#define IMMERSIONGRAPHNODE_H
+
+#include <iostream>
+#include <vector>
+#include <map>
+#include <set>
+#include <tuple>
+
+/*
+  Implements a node in our immersion graph.
+*/
+
+// The ownership of a node to a B-patch.
+enum ImmOwnership
+{
+  OWNED,
+  DECLINED,
+  UNDECIDED
+};
+
+std::ostream & operator << (std::ostream & o, ImmOwnership os);
+
+// whether the owerships of the two geometric neighboring B-patches on a node is compatible by Rule 5
+inline bool neighboringOwernshipValid(ImmOwnership o1, ImmOwnership o2)
+{
+  if (o1 == OWNED && o2 == OWNED) return false;
+  return true;
+}
+
+class ImmersionGraphNode
+{
+public:
+  // cellPatches is the var cellPatches stored in class ImmersionMeshing,
+  // it defines a mapping: cellID -> patchID -> whether this patch points outward for this cell
+  // the constructor initializes nbrs to be nbrs[patchID] = -1, where -1 means no neighbors
+  // patchOwnership is initialized to: patchOwnership[patchID] = DECLINED, if the orientation of the B-patch and the node don't agree
+  //                                                             UNDECIDED, otherwise
+  ImmersionGraphNode(int cellID, int nodeID, const std::vector<std::map<int, bool>> & cellPatches);
+
+  int getNodeID() const { return nodeID; }
+  int getCellID() const { return cellID; }
+  const std::map<int, int> & getNbrs() const { return nbrs; }
+  const std::map<int, ImmOwnership> & getOwnerships() const { return patchOwnership; }
+
+  // whether the node has connected to another node across patchID
+  bool hasNbrAtPatch(int patchID) const;
+
+  // get the node ID of the neighboring node across patchID
+  // return -1 if no neighboring node connected
+  int getNbrIDAtPatch(int patchID) const;
+
+  // set a neighboring node with nbrID to this node across patchID
+  void setNbrIDAtPatch(int patchID, int nbrID);
+
+  // return true if and only if there is a neighboring node across patchID but this neighbor is not nodeID
+  bool hasNbrAtPatchAndNotNode(int patchID, int nodeID) const;
+
+  // get the ownership at patchID
+  ImmOwnership getPatchOwnership(int patchID) const;
+
+  // set the ownership at patchID to o
+  void setPatchOwnership(int patchID, ImmOwnership o);
+
+  // integrity check using Rule 5 on the node's B-patches
+  // input cellPatchBouNbrs is a member var of class ImmersionMeshing,
+  // it defines a mapping: cellID -> patchID -> bouID -> <nbr bouID, nbr patchID >
+  bool checkPatchValid(const std::vector<std::map<int, std::map<int , std::pair<int, int>>>> & cellPatchBouNbrs, bool verbose = false) const;
+
+  // print patch ownership and neighboring nodes
+  void print() const;
+
+  bool operator == (const ImmersionGraphNode & node2) const;
+  bool operator != (const ImmersionGraphNode & node2) const { return ! (*this == node2); }
+
+protected:
+  int nodeID = -1;
+  int cellID = -1; // the cell this node belongs to
+  std::map<int, int> nbrs;                    // patchID -> nbr nodeID, in the constructor, we initialize: nbrs[patchID] = -1
+  std::map<int, ImmOwnership> patchOwnership; // patchID -> ownership
+};
+
+// print a vector of nodes for debugging
+void printNodes(const std::vector<ImmersionGraphNode> & nodes);
+
+// When ambiguity shows up during the search for an immersion,
+// we store relevant data onto a stack and pick a potential direction to continue searching.
+// This structure is used to hold the data of our immersion algorithm.
+struct ImmStackEntry
+{
+  std::vector<ImmersionGraphNode> nodes;               // the nodes vector, storing all the nodes
+  std::vector<int> patchOwnerID;            // patchID -> nodeID which owns this patch, -1 if no node owns it
+  std::vector<std::set<int>> usedNodes;     // cellID -> nodeIDs, stores the current nodes that are visited by the search algorithm
+  std::vector<std::set<int>> unusedNodes;   // cellID -> nodeIDs, stores those nodes not visited yet
+  std::vector<int> nodeOrder;               // record the order of nodes added into the graph, used only for illustrating the process of the algorithm
+  std::set<std::tuple<int,int,int>> triedDirections; // the search direction already tried at the state when this data is pushed to the stack
+};
+
+#endif
+
diff --git a/libraries/immersionMesher/immersionMesher.h b/libraries/immersionMesher/immersionMesher.h
new file mode 100644
index 0000000000000000000000000000000000000000..4c66986b2882f9cb3c9d2e0c3dcb9176686f6a19
--- /dev/null
+++ b/libraries/immersionMesher/immersionMesher.h
@@ -0,0 +1,239 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "immersionMesher" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef IMMERSIONMESHER_H
+#define IMMERSIONMESHER_H
+
+#include "triKey.h"
+#include "tetMeshGeo.h"
+#include "triMeshGeo.h"
+#include "triMeshNeighbor.h"
+#include "barycentricCoordinates.h"
+#include "immersionGraphNode.h"
+#include "iglRemeshSelfIntersection.h"
+#include "profiler.h"
+#include <iostream>
+#include <vector>
+#include <map>
+#include <array>
+
+/*
+  This class implements:
+
+  Yijing Li, Jernej Barbic: Immersion of Self-Intersecting Solids and Surfaces,
+  ACM Transactions on Graphics 37(4) (SIGGRAPH 2018), Vancouver, Canada, 2018.
+
+  It can compute a tet mesh with properly duplicated tets to embed
+  a self-intersecting triangle mesh.
+*/
+
+class ImmersionMesher
+{
+public:
+
+  ImmersionMesher();
+
+  // Set verbosity.
+  void setVerbose(bool verbose) { this->verbose = verbose; }
+
+  // Use CSG to generate virtual tets, as opposed to the virtual tets algorithm of our paper (default: false, i.e., use our paper).
+  void useCSGForVirtualTets(bool useCSGInVT) { this->useCSGInVT = useCSGInVT; }
+
+  // The main routine. It computes an ``unglued'' tet mesh by properly duplicating the tets of the input tet mesh, and embeds
+  // the input triangle mesh into it (please see paper).
+  // Input:
+  //   triangle mesh (manifold and potentially self-intersecting)
+  //   tetMesh covering the space occupied by the triangle mesh
+  // Output:
+  //   Note: Each output is an std::vector because there may be multiple tet mesh outputs, due to ambiguities (please see paper).
+  //   outputTetMeshes: output tet meshes
+  //   embeddingWeights: barycentric coordinates of input triangles embedded into each output tet mesh
+  //   cellMeshes (optional; for visualization purposes): The triangle mesh of each cell. Each std::vector element is the triangle mesh of one cell.
+  //   cellMeshWeights (optional; for visualization purposes): Barycentric coordinates to embed cellMeshes into the output tet mesh. Each std::vector element gives the barycentric coordinates of the embedding of the triangle mesh of one cell into the output tet mesh.
+  void run(const TriMeshGeo & triMesh,
+           const TetMeshGeo & tetMesh,
+           std::vector<TetMeshGeo> & outputTetMeshes,
+           std::vector<BarycentricCoordinates> & embeddingWeights,
+           std::vector<std::vector<TriMeshGeo>> * cellMeshes = nullptr,
+           std::vector<std::vector<BarycentricCoordinates>> * cellMeshWeights = nullptr);
+
+  // Self-intersection divides the space into several R^3 cells (see paper).
+  int getNumCells() const { return numCells; }
+  // Patches are subsets of the input triangle surface that form the cells' boundary (see paper).
+  int getNumPatches() const { return numPatches; }
+  // Arcs are common boundaries between patches (see paper).
+  int getNumArcs() const { return numArcs; }
+
+  // To obtain profiling data after calling "run".
+  Profiler & getProfiler() { return profiler; }
+
+  // The mesh obtained after the input mesh self-cuts.
+  const TriMeshRef getCutMesh() const { return selfCutMesh.cutMesh; }
+
+  // patchID -> triangles in cutVtxIDs of this patch
+  const std::vector<std::vector<Vec3i>> & getPatchTriangles() const { return patchTris; }
+
+  // patchID -> nbring bouID -> topological nbring patchID sharing the bou
+  const std::vector<std::map<int, int>> & getPatchNbrPatches() const { return patchBouNbrs; }
+
+  // arcID -> nbring patchIDs
+  const std::vector<std::set<int>> & getArcNbrPatches() const { return arcNbrPatches; }
+
+  // cellID -> triangles in cutVtxIDs of this cell,
+  // triangle orientations are adjusted so that their normals are pointing outward from the cell
+  const std::vector<std::vector<Vec3i>> & getCellTriangles() const { return cellTris; }
+
+  // arcID -> stitchID UEdgeKeys on the arc
+  const std::vector<std::set<UEdgeKey>> & getArcs() const { return arcs; }
+
+  // stitchID -> position in world space of this stitchID
+  const std::vector<Vec3d> & getStitchPositions() const { return stitchPositions; }
+
+protected:
+
+  void buildBasicData();
+  void prepareDataForSolve();
+  void buildCellSurfaceMeshes();
+  void runNodeSearchMethod(std::vector<std::vector<ImmersionGraphNode>> &);
+  void buildCellTetMeshes();
+  bool tryUpdate(ImmStackEntry * entry, int nodeA, bool nodeAlreadyChagned, int patchIDToOwn = -1, int patchIDToDecline = -1);
+  bool tryGlobalUpdate(ImmStackEntry * entry);
+  bool tryConnect(ImmStackEntry * entry, int nodeA, int nodeB, int patchID);
+  int addSeed(ImmStackEntry * entry);
+  std::tuple<int,int,int> getIncompletePatchHeuristically(const ImmStackEntry * entry,
+      int & numOpenPatchChecked, bool & hasAmbiguityInSearch, std::set<std::tuple<int,int,int>> * triedDir = nullptr);
+  std::tuple<int,int,int> getAvailableUndecidedPatch(const ImmStackEntry * entry, std::set<std::tuple<int,int,int>> * triedDir = nullptr);
+  void printGraphInfo(const ImmStackEntry * curEntry) const;
+  void produceFinalTetMesh(std::vector<ImmersionGraphNode> & cellNodes, int graphID,
+      TetMeshGeo & outputTetMesh, BarycentricCoordinates & outputInterpWeight,
+      std::vector<TriMeshGeo> * allCellMesh, std::vector<BarycentricCoordinates> * allCellInerpWeight);
+
+  // The following three function implement the three stages of our work. They must be called one after another.
+  // Call this function first.
+  void buildTopologyData(const TriMeshGeo & triMesh, const TetMeshGeo & tetMesh);
+  // Then, call this function.
+  void runImmersionAlgorithm();
+  // Finally, call this function, which gives you the results.
+  void generateImmersedTetMesh(std::vector<TetMeshGeo> & tetMeshes, std::vector<BarycentricCoordinates> & embeddingWeights,
+      std::vector<std::vector<TriMeshGeo>> * allCellMeshes = nullptr, std::vector<std::vector<BarycentricCoordinates>> * allCellMeshWeights = nullptr);
+
+  // If two triangles intersect in a normal way (no degeneracy),
+  // in world space where the triangles intersect, there is one line segement l_w in R^3 where the intersection happens.
+  // In abstract manifold space, there are two line segements l_t{0,1} that correspond to the intersection on the two triangles, respectively.
+  // When mapping the texture space to the world space, the two line segments l_t{0,1} are mapped to the same l_w.
+  // On the entire mesh, those l_w form curves where self-intersection happens.
+  // These curves are the boundaries of the patches. We call them arcs.
+  // "Bous" are the arcs represented in the abstract manifold space (see paper).
+  // In most cases two bous are mapped to one arc, when mapping from abstract manifold space to world space.
+  int getNumBous() const { return numBous; }
+  // Note that after cutting by self-intersection, the input triangle mesh becomes a new mesh called 'cutMesh'.
+  // The vertices (named by cutVtxID) of the cutMesh are built in such a way that all cutVtx triangles do not intersect with
+  // each other except on the edges.
+  // Also, if two bous l_t0 and l_t1 correspond to the same arc, the cutVtxIDs on l_t0 and l_t1 are DIFFERENT.
+  // In this way, we are able to differentiate between l_t0 and l_t1.
+  // To visualize arcs, we have another vertex ID called 'stitchID', where two cutVtxIDs that correspond to the same point on an arc
+  // have the same stitchID. In this way, we can count and visualize arcs.
+
+  // bouID -> cutVtxID UEdgeKeys on the bou
+  const std::vector<std::set<UEdgeKey>> & getBous() const { return bous; }
+
+  Profiler profiler;
+
+  TetMeshGeo inputTetMeshGeo;
+  TriMeshGeo inputTriMeshGeo;
+
+  bool cutMeshSaved = false;
+  bool useCSGInVT = false;
+  bool verbose = false;
+
+  // output
+  iglInterface::SelfCutMeshData selfCutMesh;
+
+  int numCells = 0;
+  int numPatches = 0;
+  int numArcs = 0;
+  int numBous = 0;
+
+  std::vector<Vec3d> stitchPositions; // stitchID -> pos
+  TriMeshNeighbor cutMeshNbr;
+  std::vector<std::vector<Vec3i>> patchTris; // patchID -> cut triangles in cutVtxIDs of this patch
+  std::vector<std::vector<int>> patchTriIDs; // patchID -> cutTriIDs of the patch
+  std::map<UTriKey, int> cutMeshTriKeyIDs;   // cut UTriKey -> cutTriID of the patch
+
+  std::vector<std::vector<Vec3i>> cellTris; // cellID -> ourward cut triangles in cutVtxIDs of this cell
+  std::vector<std::map<int, bool>> cellPatches; // cellID -> patchID -> whether this patch points outward for this cell
+  std::vector<int> cellWindingNumbers; // cellID -> windingNumber
+  std::vector<std::map<int, int>> cellNeighborsAtPatch; // cellID -> patchID -> nbr cellID
+
+  std::vector<std::map<int, int>> patchBouNbrs; // patchID -> bouID -> topological nbring patchID sharing the bou
+  std::vector<std::set<UEdgeKey>> bous; // bouID -> cutVtxID UEdgeKeys on the bou
+  std::vector<std::set<int>> bouVertices;    // bouID -> vtx on the bou
+  std::vector<std::array<int,2>> bouPatchIDs; // bouID -> two patchIDs
+
+  std::vector<std::set<UEdgeKey>> arcs; // arcID -> stitchID UEdgeKeys on the arc
+  std::vector<std::set<int>> arc2Bous; // arcID -> bouID
+  std::vector<int> bou2Arcs; // bouID -> arcID
+
+  std::vector<std::set<int>> patchNbrArcs; // patchID -> nbring arcIDs
+  std::vector<std::set<int>> arcNbrPatches; // arcID -> nbring patchIDs;
+  std::vector<std::map<int, std::set<int>>> patchNbrArcBous; // patchID -> arcID as part of the patch boundary -> bouID as the patch boundary
+
+  // On a cell, boundary patches (B-patches) form a closed "manifold" shape.
+  // we wish to find which B-patch neighbors to which B-patch on a cell, so we use the following two vars:
+  // cellID -> patchID -> bouID -> <nbr bouID, nbr patchID >
+  std::vector<std::map<int, std::map<int , std::pair<int, int>>>> cellPatchBouNbrs;
+  // When two B-patches neighbors on a cell, they share at least one arc.
+  // on this arc, the bou vertices from each patch corresponds to each other:
+  // cellID -> <unordered bouID pair> -> vector of vtxIDs on both bous that are correspondent
+  std::vector<std::map<UEdgeKey, std::vector<std::pair<int, int>>>> cellBouCorrespondence;
+
+  std::vector<std::map<int, int>> cellPatchStart; // cellID -> patchID -> the start of the triangles from this patch in this cell mesh (cellTriID)
+  std::vector<TriMeshGeo> manifoldCellMesh; // cellID -> the manifold cell surface mesh
+  std::vector<std::vector<std::set<int>>> manifoldCellMeshOriVtxIDs; // cellID -> mnCellVtxID -> cutVtxID
+  std::vector<std::map<int,std::map<int, int>>> manifoldCellMeshPatchOri2NewVtxIDMap; // cellID -> patchID -> cutVtxID -> mnCellVtxID
+  std::vector<std::vector<int>> manifoldCellOriTriIDs; // cellID -> cellTriID -> cutTriID
+  std::vector<std::vector<Vec3i>> manifoldCellOriTris; // cellID -> cellTriID -> cut triangles in cutVtxIDs (this could be removed)
+  std::vector<std::map<int, std::pair<int, bool>>> manifoldCellOri2NewTriIDMap; // cellID -> cutTriID -> <cellTriID, orientation>
+
+  std::vector<TetMeshGeo> cellTetMeshes;
+  std::vector<BarycentricCoordinates> cellTetMeshInterps; // embed: cellTetMeshes -> manifoldCellMesh
+  std::vector<std::vector<std::vector<int>>> cellTetMeshTri2TetIDs; // cellID -> cellTriID -> cellTetIDs intersecting the triangle
+  std::vector<std::vector<int>> cellTetMeshOriTetIDs;               // cellID -> cellTetID -> inputTetID
+  // When creating a manifold cell mesh, we need to know which part in the final cell mesh is from which patch.
+
+  std::vector<std::set<int>> cellID2NodeIDs; // cellID -> nodeIDs of this cellID
+  std::vector<std::vector<ImmersionGraphNode>> computedCellNodes;
+  bool debugImmersion = false;
+};
+
+#endif
+
diff --git a/libraries/immersionMesher/immersionMesherBuildGraph.cpp b/libraries/immersionMesher/immersionMesherBuildGraph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fd4af24d9c043ec22fe76c048d756edde4ce476d
--- /dev/null
+++ b/libraries/immersionMesher/immersionMesherBuildGraph.cpp
@@ -0,0 +1,1067 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "immersionMesher" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "containerHelper.h"
+#include "basicAlgorithms.h"
+#include "tetKey.h"
+#include "vec4d.h"
+#include "listIO.h"
+#include <cassert>
+#include "immersionMesher.h"
+#include "immersionGraphNode.h"
+using namespace std;
+
+// update patch owership and connection of nodeA based on some prior changes, according to several rules
+// optional input: patchIDToAdd: >=0 perform an action of letting nodeA own this patch first
+//                               -1  no such action
+bool ImmersionMesher::tryUpdate(ImmStackEntry * entry, int nodeA, bool nodeHasChanged, int patchIDToOwn, int patchIDToDecline)
+{
+  vector<ImmersionGraphNode> & nodes = entry->nodes;
+  vector<int> & patchOwnerID = entry->patchOwnerID;
+  vector<set<int>> & usedNodes = entry->usedNodes;
+  const auto & cellIDsAtPatch = selfCutMesh.cellIDsAtPatch;
+
+  // newlyOwnedPatchIDs stores the patchIDs that will be owned in this function or from input parameter patchIDToOwn
+  // newlyDeclinedPatchIDs stores the patchIDs that will be declined in this function or from input parameter patchIDToDecline
+  // the usage of these two vars is to propogate update to other nodes who has the same patches
+  vector<int> newlyOwnedPatchIDs, newlyDeclinedPatchIDs;
+  if (debugImmersion) cout << "updating node " << nodeA << endl;
+
+  // use Rule 5 to check whether B-patches are compatible on nodeA
+  if (nodes[nodeA].checkPatchValid(cellPatchBouNbrs) == false)
+  {
+    cout << "Error: node is broken before calling tryUpdate" << endl;
+    return false;
+  }
+
+  if (patchIDToOwn >= 0)  // peform the action of nodeA owning patchID
+  {
+    if (nodes[nodeA].getPatchOwnership(patchIDToOwn) != UNDECIDED) return false;
+    assert(patchOwnerID[patchIDToOwn] < 0);
+    nodeHasChanged = true;
+    nodes[nodeA].setPatchOwnership(patchIDToOwn, OWNED);
+    patchOwnerID[patchIDToOwn] = nodeA;
+    newlyOwnedPatchIDs.push_back(patchIDToOwn);
+  }
+
+  if (patchIDToDecline >= 0)
+  {
+    if (nodes[nodeA].getPatchOwnership(patchIDToDecline) == OWNED) return false;
+    nodes[nodeA].setPatchOwnership(patchIDToDecline, DECLINED);
+    nodeHasChanged = true;
+    newlyDeclinedPatchIDs.push_back(patchIDToDecline);
+  }
+
+  int cellA = nodes[nodeA].getCellID();
+
+  // apply Rule 4 to update patch ownerships on node A
+  for(auto p : nodes[nodeA].getNbrs()) // for all nbring node
+  {
+    int patchID = p.first;
+    int nodeB = p.second;
+    if (nodeB < 0) continue; // nodeB < 0 means there is no neighboring node across patchID
+    int cellB = nodes[nodeB].getCellID();
+    const set<int> & arcsAroundPatchID = patchNbrArcs[patchID];
+
+    for(auto p2 : nodes[nodeB].getOwnerships())
+    {
+      int patchB = p2.first;
+      if (patchB == patchID) continue; // nodeA is connected to B across patchID, nothing can be done to patchID now
+      ImmOwnership ownershipB = p2.second;
+      if (ownershipB == UNDECIDED) continue;
+
+      // Rule 4 only applies on patches sharing the arc with patchID
+      // this condition helps on the case of self-touching cells
+      if (intersect(patchNbrArcs[patchB], arcsAroundPatchID) == false) continue;
+
+      for(auto q : patchBouNbrs[patchB]) // for each topological neighbor of patchB
+      {
+        int bouID = q.first;
+        int arcID = bou2Arcs[bouID];
+        int topoNbrPatchA = q.second; // get one topological neighbor of patchB
+        // skip if topoNbrPatchA is not a B-patch of cellA
+        if (cellPatches[cellA].find(topoNbrPatchA) == cellPatches[cellA].end()) continue;
+
+        // Rule 4 only applies on patches sharing the arc with patchID
+        // this condition helps on the case of self-touching cells
+        if (arcsAroundPatchID.find(arcID) == arcsAroundPatchID.end()) continue;
+
+        if (topoNbrPatchA == patchID) continue; // nodeA is connected to B across patchID, nothing can be done to patchID now
+
+        ImmOwnership existingA = nodes[nodeA].getPatchOwnership(topoNbrPatchA);
+
+        if (existingA == ownershipB) continue;
+
+        // now the two owernships are different on node A and B
+        if (existingA != UNDECIDED) // Rule 4 does not hold here!
+        {
+          if (debugImmersion)
+          {
+            cout << "update fail because nodeA has patch not compatible with a nbr node " << nodeB << " from cell " << cellB << endl;
+            cout << "patch on A " << topoNbrPatchA << " " << existingA << endl;
+            cout << "patch on B " << patchB << " " << ownershipB << endl;
+          }
+          return false;
+        }
+        // then existingA must be UNDECIDE, apply Rule 4 to update patch ownership on A
+
+        if (debugImmersion)
+        {
+          cout << "set patch " << topoNbrPatchA << " on node " << nodeA << " to " << ownershipB <<
+              " because nbring node " << nodeB << " has a topo nbr patch " << patchB << endl;
+        }
+
+        nodeHasChanged = true;
+        nodes[nodeA].setPatchOwnership(topoNbrPatchA, ownershipB);
+        if (ownershipB == OWNED)
+        {
+          patchOwnerID[topoNbrPatchA] = nodeA;
+          newlyOwnedPatchIDs.push_back(topoNbrPatchA);
+        }
+        else
+        {
+          assert(ownershipB == DECLINED);
+          newlyDeclinedPatchIDs.push_back(topoNbrPatchA);
+        }
+      }
+    }
+  } // end apply Rule 4 to update patch ownerships on node A
+
+  if (debugImmersion) cout << "after patch pass, checking whether patches are OK" << endl;
+
+  // check whether patches are OK on nodeA
+  if (nodes[nodeA].checkPatchValid(cellPatchBouNbrs, debugImmersion) == false) return false;
+
+  if (debugImmersion)  cout << "checking patches passed" << endl;
+
+  // Apply Rule 5, go through all UNDECIDED patches, decline them if necessary.
+  for(auto p : nodes[nodeA].getOwnerships())
+  {
+    int patchID = p.first;
+    ImmOwnership o = p.second;
+    if (o != UNDECIDED) continue; // we visit all UNDECIDED patch in this loop
+
+    // if this UNDECIDED patch has been owned by others, then we decline it
+    if (patchOwnerID[patchID] >= 0)
+    {
+      assert(patchOwnerID[patchID] != nodeA);
+      if (debugImmersion)
+      {
+        cout << "set patch " << patchID << " on node " << nodeA << " to DECLINED "
+            "because this patch has been owned by others" << endl;
+      }
+      nodes[nodeA].setPatchOwnership(patchID, DECLINED);
+      nodeHasChanged = true;
+      // here we don't store this patchID into newlyDeclinedPatchIDs because
+      // we want to use newlyDeclinedPatchIDs to propogate update to other nodes
+      // by checking whether all nodes have declined this patchID except one node
+      // in this case, this node will own this patch
+      // since here in this if branch the ower of patchID is already known,
+      // we cannot do any propogation on it, so we don't store this patch to newlyDeclinedPatchIDs
+      continue;
+    }
+
+    // if this UNDECIDED patch has a geometric patch nbr on the node that is owned
+    // then we decline the patch (Rule 5)
+    bool hasNbrOwned = false;
+    for(auto p2: cellPatchBouNbrs[cellA][patchID])
+    {
+      int nbrPatchID = p2.second.second;
+      if (nbrPatchID == patchID) continue; // if patch self-intersect
+      if (nodes[nodeA].getPatchOwnership(nbrPatchID) == OWNED)
+      {
+        hasNbrOwned = true;
+        break;
+      }
+    }
+    if (hasNbrOwned)
+    {
+      nodeHasChanged = true;
+      nodes[nodeA].setPatchOwnership(patchID, DECLINED);
+      newlyDeclinedPatchIDs.push_back(patchID);
+    }
+  } // end apply Rule 5, go through all UNDECIDED patches, decline them if necessar
+
+  if (debugImmersion)  cout << "declining patches finished" << endl;
+
+  // check whether patches are OK
+  if (nodes[nodeA].checkPatchValid(cellPatchBouNbrs) == false) return false;
+
+  vector<int> otherChangedNodes; // nodes other than nodeA that are changed in the following update
+  // apply Rule 7 to decline patches on other nodes
+  for(int ownedPatchID : newlyOwnedPatchIDs)
+  {
+    int cellID = cellIDsAtPatch[ownedPatchID].second; // get the cellID whose orientation agrees with the patch
+    assert(cellID == cellA);
+    for(int node = 0; node < sizei(nodes); node++)
+    {
+      if (node == nodeA) continue;
+      if (nodes[node].getCellID() != cellID) continue;
+      auto o = nodes[node].getPatchOwnership(ownedPatchID);
+      if (o == OWNED) return false; // this patch is owned by both node A and another node, which by Rule 7, is not an immersion
+      if (o == DECLINED) continue;
+      // now o can only be UNDECIDED, we will assign this to be DECLINED according to Rule 7
+      nodes[node].setPatchOwnership(ownedPatchID, DECLINED);
+
+      if (debugImmersion)
+      {
+        cout << "Since patch " << ownedPatchID << " is owned by " << nodeA << ", this node " << node <<
+        " has to DECLINE it" << endl;
+      }
+
+      if (setFind(usedNodes[cellID], node)) // we leave unusedNodes because they are not connected yet
+        otherChangedNodes.push_back(node);
+    }
+  }
+
+  for(int declinedPatchID : newlyDeclinedPatchIDs)
+  {
+    if (patchOwnerID[declinedPatchID] >= 0) continue; // already have an owner node, skip
+    if (cellIDsAtPatch[declinedPatchID].second != cellA) continue;
+    int numNodesUndecided = 0;
+    int targetNode = -1;
+    for(int nodeID : cellID2NodeIDs[cellA])
+    {
+      if (nodes[nodeID].getPatchOwnership(declinedPatchID) == UNDECIDED)
+      {
+        numNodesUndecided++;
+        targetNode = nodeID;
+      }
+    }
+//
+//    cout << "checking declined patchID " << numNodesUndecided << endl;
+//    cout << "Cell " << cellA << endl;
+//    cout << streamRange(cellID2NodeIDs[cellA]) << endl;
+    if (numNodesUndecided == 0)
+    {
+//      cout << "target patch  " << declinedPatchID << endl;
+//      printNodes(nodes);
+      return false; // all nodes decline this patch, not an immersion
+    }
+    if (numNodesUndecided == 1)
+    {
+      if (setNotFind(usedNodes[cellA],targetNode)) // if this target node is in unused node
+      {
+        usedNodes[cellA].insert(targetNode);
+        entry->unusedNodes[cellA].erase(targetNode);
+        entry->nodeOrder.push_back(targetNode);
+      }
+      bool success = tryUpdate(entry, targetNode, true, declinedPatchID);
+      if (success == false) return false;
+    }
+  }
+
+  if (nodeHasChanged) // since patches have been changed on A, we should propogate update to nbring nodes, according to Rule 4
+  {
+    for(auto p : nodes[nodeA].getNbrs())
+    {
+      int nbrNodeID = p.second;
+      if (nbrNodeID < 0) continue; // if not connected across p, skip
+      bool success = tryUpdate(entry, nbrNodeID, false);
+      if (success == false) return false;
+    }
+  }
+
+  for(int node : otherChangedNodes)
+  {
+    bool success = tryUpdate(entry, node, true); // propogate update()
+    if (success == false) return false;
+  }
+
+  if (debugImmersion) { cout << "updating node " << nodeA << " end" << endl; }
+  return true;
+}
+
+// Use Rule 4 is to connect nodes if their patches are topological neighbors
+// Use Rule 6 to connect nodes around an arc
+bool ImmersionMesher::tryGlobalUpdate(ImmStackEntry * entry)
+{
+  vector<ImmersionGraphNode> & nodes = entry->nodes;
+  vector<int> & patchOwnerID = entry->patchOwnerID;
+  const vector<set<int>> & usedNodes = entry->usedNodes;
+  const auto & cellIDsAtPatch = selfCutMesh.cellIDsAtPatch;
+  // try to find any unconnected node pairs that can be connect via Rule 4 and connect them
+  for(int patchID = 0; patchID < numPatches; patchID++) // for each owned patch
+  {
+    int nodeID = patchOwnerID[patchID];
+    if (nodeID < 0) continue; // if no node owns patchID, continue
+    assert(nodeID >= 0 && nodeID < sizei(nodes));
+    int cellID = nodes[nodeID].getCellID();
+    for(auto p : patchBouNbrs[patchID]) // for each topological nbr of patchID
+    {
+      int bouID = p.first;
+      int topoNbrPatchID = p.second;
+      if (topoNbrPatchID < patchID) continue; // we only visit each unordered pair once
+      int nbrNodeID = patchOwnerID[topoNbrPatchID];
+      if (nbrNodeID < 0) continue; // skip if no node owns topoNbrNodeID
+      int nbrCellID = nodes[nbrNodeID].getCellID();
+      if (cellID == nbrCellID) return false; // two nodes from one cell should not own a topo-nbr patch pair
+      assert(cellID != nbrCellID);
+
+      // There should be a third patch between the two nodes which own patchID and topoNbrPatchID.
+      // By Rule 4, the two nodes connect to each other when the third patch shares the same arc with the other two patches
+      // Here we are checking this: geoNbrPatchID is the third patch.
+      UEdgeKey cellIDKey(cellID, nbrCellID);
+      // cellPatchBouNbrs: cellID -> patchID -> bouID -> <nbr bouID, nbr patchID>
+      auto p2 = cellPatchBouNbrs[cellID][patchID][bouID];
+      int geoNbrPatchID = p2.second;
+      int geoNbrBou = p2.first;
+      assert(bou2Arcs[bouID] == bou2Arcs[geoNbrBou]); // assert bouID and geoNbrBou are on the same arc
+      assert(mapFind(cellPatchBouNbrs[nbrCellID][topoNbrPatchID], bouID));
+      auto p3 = cellPatchBouNbrs[nbrCellID][topoNbrPatchID][bouID];
+      if (p3.second != geoNbrPatchID) return false;
+      if (p3.first != geoNbrBou) return false;
+
+      auto cellIDs = cellIDsAtPatch[geoNbrPatchID];
+      UEdgeKey foundCellIDKey(cellIDs.first, cellIDs.second);
+      if (cellIDKey != foundCellIDKey) continue;
+      int thisNbrID = nodes[nodeID].getNbrIDAtPatch(geoNbrPatchID); // we get the node connected to nodeID across geoNbrPatchID
+      if (thisNbrID == nbrNodeID) continue; // the two already connected
+      if (thisNbrID >= 0) return false; // another node connected to nodeID, Rule 4 violated, return false
+
+      if (debugImmersion)
+      { cout << "patch pair " << patchID << " " << topoNbrPatchID <<
+          " found a patch leading to connection: " << nodeID << " " <<
+          nbrNodeID << " patch " << geoNbrPatchID << endl;
+      }
+      bool success = tryConnect(entry, nodeID, nbrNodeID, geoNbrPatchID);
+      if (debugImmersion) { cout << "connection " << (success ? "finished" : "failed") << endl; }
+      if (success == false) return false;
+    }
+  }
+
+  // try to apply Rule 6:
+  //    d    q   e
+  //         |
+  // ---p--------r-----
+  //         |              p, q, r, s: patches
+  //    d    s   f          c, d, e, f: cells
+  for(int arcID = 0; arcID < numArcs; arcID++)
+  {
+    assert(arc2Bous[arcID].size() == 2);
+    vector<int> bous(arc2Bous[arcID].begin(), arc2Bous[arcID].end());
+
+    // get patchIDs p, q, r, s, which all neighbors arcID
+    int p = bouPatchIDs[bous[0]][0];
+    int q = bouPatchIDs[bous[1]][0];
+    int r = bouPatchIDs[bous[0]][1];
+    int s = bouPatchIDs[bous[1]][1];
+
+    // get the cellIDs c, d, e, f around the arcs
+    int c = cellIDsAtPatch[p].first;
+    int d = cellIDsAtPatch[p].second;
+    int f = cellIDsAtPatch[r].first;
+    int e = cellIDsAtPatch[r].second;
+    if (c == 0 || d == 0 || f == 0 || e == 0) continue;
+    if (cellWindingNumbers[c] == 0 || cellWindingNumbers[d] == 0 || cellWindingNumbers[e] == 0 || cellWindingNumbers[f] == 0)
+      continue;  // there is an air cell among them, skip
+
+    // cellIDsAtPatch: patchID -> <cellID on front side of patch, cellID on back side of patch>
+    // adjust c,d,e,f so that they match the picture above
+    assert(c != f);
+    if ((cellIDsAtPatch[s].first != c && cellIDsAtPatch[s].second != c) ||
+        (cellIDsAtPatch[s].first != f && cellIDsAtPatch[s].second != f))
+    {
+      swap(c, d);
+      swap(e, f);
+    }
+
+    for(int nodeC : usedNodes[c]) // for each node C on cell c,
+    {
+      int nodeD = nodes[nodeC].getNbrIDAtPatch(p);
+      int nodeF = nodes[nodeC].getNbrIDAtPatch(s);
+      // first, let's check whether Rule 6 can be used to connect E and F across r, or connect D and E across q
+      // connect on r or q
+      if (nodeD >= 0 && nodeF >= 0)
+      {
+        int nodeE = nodes[nodeD].getNbrIDAtPatch(q);
+        if (nodeE >= 0)
+        { // check if we can connect E and F across r
+          if(nodes[nodeE].getPatchOwnership(r) == DECLINED && nodes[nodeF].getPatchOwnership(r) == DECLINED
+              && nodes[nodeE].getNbrIDAtPatch(r) != nodeF)
+          {
+            // if node E/F already connected to another node not F/E, then Rule 6 is broken
+            if (nodes[nodeE].hasNbrAtPatchAndNotNode(r, nodeF)) return false;
+            if (nodes[nodeF].hasNbrAtPatchAndNotNode(r, nodeE)) return false;
+            if (tryConnect(entry, nodeE, nodeF, r) == false) return false;
+            // we return true here because tryConnect will call this tryGlobalUpdate() again recursively
+            // so when tryConnect returns, there should be no more arcs to apply Rule 6 to, so we can safely return true
+            return true;
+          }
+        }
+        else
+        { // check if we can connect D and E across q
+          nodeE = nodes[nodeF].getNbrIDAtPatch(r);
+          if (nodeE >= 0 && nodes[nodeE].getPatchOwnership(q) == DECLINED && nodes[nodeD].getPatchOwnership(q) == DECLINED)
+            // we don't need to check nodes[nodeE].getNbrIDAtPatch(q) != nodeD here because if this condition is true, we
+            // will enter the branch above instead
+          {
+            if (nodes[nodeE].hasNbrAtPatchAndNotNode(q, nodeD)) return false;
+            if (nodes[nodeD].hasNbrAtPatchAndNotNode(q, nodeE)) return false;
+            if (tryConnect(entry, nodeE, nodeD, q) == false) return false;
+            return true;
+          }
+        }
+      }
+      else if (nodeF < 0 && nodeD >= 0)
+      { // check if we can connect C and F across s
+        int nodeE = nodes[nodeD].getNbrIDAtPatch(q);
+        if (nodeE >= 0)
+        {
+          nodeF = nodes[nodeE].getNbrIDAtPatch(r);
+          if (nodeF >= 0 && nodes[nodeF].getPatchOwnership(s) == DECLINED && nodes[nodeC].getPatchOwnership(s) == DECLINED)
+            // we don't need to check nodes[nodeF].getNbrIDAtPatch(s) != nodeC here because if this condition is true, we
+            // will enter the branch: if (nodeD >= 0 && nodeF >= 0)
+          {
+            if (nodes[nodeC].hasNbrAtPatchAndNotNode(s, nodeF)) return false;
+            if (nodes[nodeF].hasNbrAtPatchAndNotNode(s, nodeC)) return false;
+            if (tryConnect(entry, nodeC, nodeF, s) == false) return false;
+            return true;
+          }
+        }
+      }
+      else if (nodeD < 0 && nodeF >= 0)
+      { // check if we can connect C and D across p
+        int nodeE = nodes[nodeF].getNbrIDAtPatch(r);
+        if (nodeE >=0)
+        {
+          nodeD = nodes[nodeE].getNbrIDAtPatch(q);
+          if (nodeD >= 0 && nodes[nodeD].getPatchOwnership(p) == DECLINED && nodes[nodeC].getPatchOwnership(p) == DECLINED)
+          {
+            if (nodes[nodeC].hasNbrAtPatchAndNotNode(p, nodeD)) return false;
+            if (nodes[nodeD].hasNbrAtPatchAndNotNode(p, nodeC)) return false;
+            if (tryConnect(entry, nodeC, nodeD, p) == false) return false;
+            return true;
+          }
+        }
+      }
+    } // end for nodeC
+  }
+
+  return true;
+}
+
+// connect node A to nodeB across patchID
+// return whether it succeeds
+bool ImmersionMesher::tryConnect(ImmStackEntry * entry, int nodeA, int nodeB, int patchID)
+{
+  vector<ImmersionGraphNode> & nodes = entry->nodes;
+
+  if (debugImmersion)
+  {
+    cout << "node A and B: " << nodeA << " " << nodeB << " connecting"<< endl;
+    nodes[nodeA].print();
+    nodes[nodeB].print();
+    cout << "patch " << patchID << endl;
+  }
+
+  nodes[nodeA].setNbrIDAtPatch(patchID, nodeB);
+  nodes[nodeB].setNbrIDAtPatch(patchID, nodeA);
+
+  // check ownership of patchID for node A and B
+  // by rule 2, they should both decline this patch in order to connect to each other
+  auto oA = nodes[nodeA].getPatchOwnership(patchID);
+  if(oA == OWNED) return false;
+  nodes[nodeA].setPatchOwnership(patchID, DECLINED);
+  auto oB = nodes[nodeB].getPatchOwnership(patchID);
+  if(oB == OWNED) return false;
+  nodes[nodeB].setPatchOwnership(patchID, DECLINED);
+
+  if (debugImmersion) { cout << "updating nodeA..." << endl; }
+
+  if (tryUpdate(entry, nodeA, true, -1, patchID) == false) return false;
+
+  if (debugImmersion) { cout << "updating nodeB..." << endl;}
+
+  if (tryUpdate(entry, nodeB, true, -1, patchID) == false) return false;
+
+  if (debugImmersion) { cout << "finish two nodes update" << endl; }
+
+  if (tryGlobalUpdate(entry) == false) return false;
+
+  return true;
+}
+
+// find a suitable seed node from unused nodes to begin the search
+// return -1 if no nodes found, otherwise return seed nodeID
+int ImmersionMesher::addSeed(ImmStackEntry * entry)
+{
+  vector<ImmersionGraphNode> & nodes = entry->nodes;
+  vector<int> & patchOwnerID = entry->patchOwnerID;
+  vector<set<int>> & usedNodes = entry->usedNodes;
+  vector<set<int>> & unusedNodes = entry->unusedNodes;
+
+  int numNodes = nodes.size();
+  // find a seed
+  for(int nodeID = 0; nodeID < numNodes; nodeID++) // for each unused node
+  {
+    int cellID = nodes[nodeID].getCellID();
+    if (setFind(usedNodes[cellID], nodeID)) continue; // if the node is used, then skip
+    for(auto p : cellPatches[cellID])
+    {
+      int patchID = p.first;
+      // to find a useful seed, we should find a node which can own a B-patch
+      // since new nodes are initialized to have patch owernships either DECLINED or UNDECIDED
+      // if we meet a node with a UNDECIDED patch, we will set this patch to OWNED to
+      // make this node a seed
+      if (nodes[nodeID].getPatchOwnership(patchID) == UNDECIDED) // find a node with a UNDECIDED patch
+      {
+        assert(patchOwnerID[patchID] == -1);
+        usedNodes[cellID].insert(nodeID);                // add this node to usedNodes
+        unusedNodes[cellID].erase(nodeID);               // remove this node from unusedNodes
+
+        // update patch owernships to reflect owning patchID
+        bool success = tryUpdate(entry, nodeID, true, patchID);
+        if (success == false)
+        {
+          cout << "Error, update fail on the seed node!" << endl;
+          return -1;
+        }
+        entry->nodeOrder.push_back(nodeID);
+        return nodeID;
+      }
+    }
+  }
+  return -1;
+}
+
+// find an incomplete patch
+// incomplete patch is defined as a declined patch of a node with no neighboring node connected across the patch
+// if there is an incomplete patch, we can try to connect a node across this patch to advance our search for immersion
+// return: a tuple of node A, node B and patch p, where A can connect to B across p
+//         numIncompletePatchesVisited: #incomplete patches visited in this function
+//         hasAmbiguityInSearch: whether all incomplete patches have ambiguity on how to connect another node
+// if triedDir == nullptr, then the code uses a heuristic to only advance at the incomplete patch where
+// there is no ambigutiy on witch node to connect to.
+// if triedDir != nullptr, it stores the search direction we already tried,
+// therefore, we shall avoid those search directions, then we just return the first valid search direction
+// that has not appeared in triedDir. Note in this case the heuristic does not apply
+// Here search direction is defined as a potential action, either as a tuple of <A, B, p> which meanings node A can
+// connect to B across patch p
+tuple<int,int,int> ImmersionMesher::getIncompletePatchHeuristically(const ImmStackEntry * entry,
+    int & numIncompletePatchesVisited, bool & hasAmbiguityInSearch, set<tuple<int,int,int>> * triedDir)
+{
+  const vector<ImmersionGraphNode> & nodes = entry->nodes;
+  const vector<set<int>> & usedNodes = entry->usedNodes;
+  const vector<set<int>> & unusedNodes = entry->unusedNodes;
+  const int numNodes = nodes.size();
+
+  bool debugOuterLoop = false;
+  numIncompletePatchesVisited = 0;
+  hasAmbiguityInSearch = false;
+
+  for(int nodeID = 0; nodeID < numNodes; nodeID++) // for each used node
+  {
+    int cellID = nodes[nodeID].getCellID();
+    if (setNotFind(usedNodes[cellID], nodeID)) continue;
+    for(auto p : cellPatches[cellID]) // check each B-patch of the node
+    {
+//        cout << "check open patch at node " << nodeID << " cell " << cellID << " patch " << p.first << endl;
+      int patchID = p.first;
+      // we look for incomplete patch
+      if (nodes[nodeID].getPatchOwnership(patchID) != DECLINED || nodes[nodeID].hasNbrAtPatch(patchID)) continue;
+
+      numIncompletePatchesVisited++;
+      // found an incomplete patch:
+      // check whether we can connect a new node to it
+      assert(cellNeighborsAtPatch[cellID].find(patchID) != cellNeighborsAtPatch[cellID].end());
+      int nbrCellID = cellNeighborsAtPatch[cellID][patchID]; // get the neighboring cell ID across this patch
+      if (cellWindingNumbers[nbrCellID] == 0) continue; // if nbrCellID is the outer cell (empty space), skip
+
+      bool ambiguityFound = false;
+      int targetNbrNodeID = -1; // the target nodeID we should use to connect at the incomplete patch
+      // first, we go through all used nodes on this nbrCellID to check whether it can be connected to nodeID
+      // without violating any rules
+      for(int nbrNodeID : usedNodes[nbrCellID])
+      {
+        assert(nbrCellID == nodes[nbrNodeID].getCellID());
+        // if nbrNodeID already connects to sth. acorss patchID, then skip (Rule 1)
+        if (nodes[nbrNodeID].getNbrIDAtPatch(patchID) >= 0) continue;
+
+        auto tmpEntry = *entry;
+
+        if (debugOuterLoop)
+        {
+          cout << "check open patch at node " << nodeID << " cell " << cellID << " patch " << patchID << " to node " << nbrNodeID <<
+              " cell " << nbrCellID << endl;
+        }
+        // connect nodeID and nbrNodeID across patchID
+        bool success = tryConnect(&tmpEntry, nodeID, nbrNodeID, patchID);
+        if (debugOuterLoop)
+        {
+          cout << "result is " << success << endl;
+        }
+        if (success)
+        {
+          if (triedDir)  // we should avoid the tried direction
+          {
+            tuple<int,int,int> dir(nodeID, nbrNodeID, patchID);
+            if (setFind(*triedDir, dir)) continue; // this connection was in tried direction
+            return dir;
+          }
+          if (targetNbrNodeID < 0)
+          {
+            targetNbrNodeID = nbrNodeID; // store the nbrNodeID
+          }
+          else // targetNbrNodeID >= 0, we already found one valid nbrNodeID at this incomplete patch, ambiguity!
+          {
+            ambiguityFound = true;
+            break; // break the search on nbrNodeID across the patchID
+          }
+        }
+      } // end searching for nbrNodeID across the patchID
+
+      if (triedDir) // we tried all the used nodes at this nbrCellID, but we cannot connect them
+      {             // either because the search direction is in triedDir, or the connection is not valid (i.e., breaks rules)
+        if(unusedNodes[nbrCellID].size() > 0) // we can still try connecting to an unused node
+        {
+          int nbrNodeID = *unusedNodes[nbrCellID].begin();
+          tuple<int,int,int> dir(nodeID, nbrNodeID, patchID);
+          if (setFind(*triedDir, dir)) continue;
+          return dir;
+        }
+        continue;
+      }
+
+      // if there is ambiguity on which used node to connect, or ambiguity on whether to connect to one used node or
+      // to an unused node, then we apply the heuristic and try the next incomplete patch
+      if (ambiguityFound || (targetNbrNodeID >= 0 && unusedNodes[nbrCellID].size() > 0))
+      {
+        if (verbose)
+          cout << "---------------------------- AMB ---------------------------" << endl;
+        hasAmbiguityInSearch = true;
+        continue; // let 's try the next imcomplete Patch
+      }
+
+      if (targetNbrNodeID >= 0) // we find one used no to connect to
+      {
+        return tuple<int,int,int>(nodeID, targetNbrNodeID, patchID);
+      }
+      else // targetNbrNodeID < 0 but unusedNodes[nbrCellID].size() > 0,
+      {    // we can connect to one unused node
+        int nbrNodeID = *unusedNodes[nbrCellID].begin();
+        return tuple<int,int,int>(nodeID, nbrNodeID, patchID);
+      }
+    } // for all patch on node
+  } // for all node
+
+  return tuple<int,int,int>(-1,-1,-1);
+}
+
+// find one B-patch that is UNDECIDED, skip search directions stored in triedDir
+// the format of tuples in the triedDir is: <node A, node B, patch p>
+// if node B >= 0, this search direction represents a connection from A to B across p
+// else, this search direction represents A owning p
+// return the tuple representing a valid search direction
+// no heuristic applied here, unlike the function getIncompletePatchHeuristically
+tuple<int,int,int> ImmersionMesher::getAvailableUndecidedPatch(const ImmStackEntry * entry, set<tuple<int,int,int>> * triedDir)
+{
+  const vector<ImmersionGraphNode> & nodes = entry->nodes;
+  const vector<set<int>> & usedNodes = entry->usedNodes;
+
+  bool debugOuterLoop = false;
+  const int numNodes = nodes.size();
+
+  for(int nodeID = 0; nodeID < numNodes; nodeID++) // for all used nodes
+  {
+    int cellID = nodes[nodeID].getCellID();
+    if (setNotFind(usedNodes[cellID], nodeID)) continue;
+    for(auto p : cellPatches[cellID])
+    {
+//        cout << "check undecided patch at node " << nodeID << " cell " << cellID << " patch " << p.first << endl;
+      int patchID = p.first;
+      // we are seraching for a UNDECIDED B-patch
+      if (nodes[nodeID].getPatchOwnership(patchID) != UNDECIDED) continue;
+
+      // found one!
+      // check whether this node can own this patch
+      {
+        tuple<int,int,int> dir(nodeID, -1, patchID);
+        if (setNotFind(*triedDir, dir)) // this direction not tried yet
+        {
+          auto tmpEntry = *entry;
+
+          assert(tmpEntry.patchOwnerID[patchID] < 0);
+          bool success = tryUpdate(&tmpEntry, nodeID, true, patchID);
+          success = success && tryGlobalUpdate(&tmpEntry);
+          // if we can successfully own this patch, then we have found a good search direction
+          if (success) { return dir; }
+        }
+      }
+
+      // check whether we can connect another node to this patch
+      assert(cellNeighborsAtPatch[cellID].find(patchID) != cellNeighborsAtPatch[cellID].end());
+      int nbrCellID = cellNeighborsAtPatch[cellID][patchID];
+      if (cellWindingNumbers[nbrCellID] == 0) continue; // if nbr cell is the outer space, skip
+
+      for(int nbrNodeID : usedNodes[nbrCellID])
+      {
+        assert(nbrCellID == nodes[nbrNodeID].getCellID());
+        if (nodes[nbrNodeID].getNbrIDAtPatch(patchID) >= 0) continue; // if nbrNodeID has connected to sth. already across patchID
+        auto tmpEntry = *entry;
+
+        if (debugOuterLoop)
+        {
+          cout << "check open patch at node " << nodeID << " cell " << cellID << " patch " << patchID << " to node " << nbrNodeID <<
+              " cell " << nbrCellID << endl;
+        }
+        tuple<int,int,int> dir(nodeID, nbrNodeID, patchID);
+        if (setFind(*triedDir, dir)) continue;
+
+        bool success = tryConnect(&tmpEntry, nodeID, nbrNodeID, patchID);
+        if (debugOuterLoop) { cout << "result is " << success << endl; }
+        if (success) { return dir; }
+      }
+    } // for all patch on node
+  } // for all node
+
+  return tuple<int,int,int>(-1,-1,-1);
+}
+
+void ImmersionMesher::printGraphInfo(const ImmStackEntry * curEntry) const
+{
+  cout << "==================================" << endl;
+  printNodes(curEntry->nodes);
+  cout << "used nodes: ";
+  for(int cellID = 1; cellID < sizei(curEntry->usedNodes); cellID++)
+  {
+    cout << cellID << "->" << streamRange(curEntry->usedNodes[cellID]) << " ";
+  }
+  cout << endl;
+  cout << "unused nodes: ";
+  int numUnused = 0;
+  for(int cellID = 1; cellID < sizei(curEntry->unusedNodes); cellID++) { numUnused += curEntry->usedNodes[cellID].size(); }
+  cout << numUnused << ", ";
+  for(int cellID = 1; cellID < sizei(curEntry->unusedNodes); cellID++)
+  {
+    cout << cellID << "->" << streamRange(curEntry->usedNodes[cellID]) << " ";
+  }
+  cout << endl;
+  cout << "==================================" << endl;
+}
+
+void ImmersionMesher::runNodeSearchMethod(vector<vector<ImmersionGraphNode>> & graphs)
+{
+//  verbose = true;
+  cout << "=======================================================" << endl;
+  cout << "          Start to building immersion graphs" << endl;
+  cout << "=======================================================" << endl;
+  vector<ImmStackEntry> searchStack; // define a stack
+  // When ambiguity shows up during the search for an immersion,
+  // we store relevant data onto a stack and pick a potential direction to continue searching
+  searchStack.emplace_back(); // first, push a stack entry representing the currect search
+  ImmStackEntry * curEntry = &searchStack[0];
+
+  curEntry->patchOwnerID.resize(numPatches, -1);
+
+  // initialize usedNodes, unusedNodes, nodes
+  cout << "Winding number for each cell: ";
+
+  for(int cellID = 1; cellID < numCells; cellID++) // we skip cellID == 0, because cellID 0 is the outer cell, representing the empty space
+  {
+    cout << "(cell " << cellID << ": " << cellWindingNumbers[cellID] << ") ";
+    if (cellWindingNumbers[cellID] < 0) // winding number < 0 on this cell, there is inversion, not valid input
+    {
+      cout << "Error: winding number is negative! Immersion not possible!" << endl;
+      return;
+    }
+
+    // create some nodes at this cell. the number of the nodes is the cell's winding number
+    for(int i = 0; i < cellWindingNumbers[cellID]; i++)
+    {
+      int nodeID = curEntry->nodes.size();
+      curEntry->nodes.emplace_back(cellID, nodeID, cellPatches);
+    }
+  }
+  cout << endl;
+
+  curEntry->usedNodes.resize(numCells);
+  curEntry->unusedNodes.resize(numCells);
+  for(int nodeID = 0; nodeID < sizei(curEntry->nodes); nodeID++)
+  {
+    int cellID = curEntry->nodes[nodeID].getCellID();
+    curEntry->unusedNodes[cellID].insert(nodeID);
+  }
+  cellID2NodeIDs = curEntry->unusedNodes; // initialize cellID2NodeIDs
+
+  const int numNodes = curEntry->nodes.size();
+  cout << "We have " << numNodes << " nodes to connect in the graph." << endl;
+
+  // pick a seed to start
+  int seedNode = addSeed(curEntry);
+  if (seedNode == -1)
+  {
+    cout << "Error: cannot find a seed to start! Input not immersion!" << endl;
+    return;
+  }
+
+  // pop the search stack, return true if success
+  auto processRetreat = [&]() -> bool
+  {
+    if (searchStack.size() > 1)
+    {
+      cout << "Retreat to the last fork" << endl;
+      searchStack.pop_back();
+      curEntry = &(searchStack.back());
+
+      cout << "Now the current graph is: " << endl;
+      printGraphInfo(curEntry);
+      return true;
+    }
+    else
+    {
+      cout << "=======================================================" << endl;
+      cout << "         Nothing left in stack, search over" << endl;
+      cout << "=======================================================" << endl;
+      return false;
+    }
+  };
+
+//  bool debugOuterLoop = false;
+  bool hasAmbiguityInSearch = false;
+  for(int iter = 0; ; iter++)
+  {
+    if (verbose)
+      cout << "****************iter: " << iter << endl;
+//    char tmp = 0;
+//    cin >> tmp;
+//    cout << "input  tmp is " << tmp << endl;
+
+    bool hasFinished = true; // whether the search for immersion is finished
+    hasAmbiguityInSearch = false;
+
+    // if there is a patch not owned by any node, the search is not done yet
+    if(find(curEntry->patchOwnerID.begin(), curEntry->patchOwnerID.end(), -1) != curEntry->patchOwnerID.end())
+      hasFinished = false;
+
+    int numIncompletePatchesVisited = 0;
+    // try to find one incomplete patch and a node to connect across the patch
+    // incomplete patch is defined as a declined patch of a node with no neighboring node connected across the patch
+    // if there is an incomplete patch, we can try to connect a node across this patch to advance our search for immersion
+    // the function getIncompletePatchHeuristically retruns a tuple of node A, node B and patch p, where A can connect to B across p
+    //         numIncompletePatchesVisited: #incomplete patches visited in this function
+    //         hasAmbiguityInSearch: whether all incomplete patches have ambiguity on how to connect another node
+    // Here triedDir == nullptr, the code uses a heuristic to only advance at the incomplete patch where
+    // there is no ambigutiy on witch node to connect to.
+    // If all incomplete patches have ambiguity, the funtion returns <-1,-1,-1>, representing failure in using the heuristic
+    tuple<int,int,int> incompletePatchResult = getIncompletePatchHeuristically(curEntry, numIncompletePatchesVisited, hasAmbiguityInSearch);
+    if (numIncompletePatchesVisited > 0) { hasFinished = false; }
+
+    if (hasFinished)
+    {
+      cout << "=======================================================" << endl;
+      cout << "            SUCCESS in finding an immersion" << endl;
+      cout << "=======================================================" << endl;
+      int graphID = graphs.size();
+      graphs.push_back(curEntry->nodes);
+      if (verbose)
+        ListIO::save(("nodeOrder" + to_string(graphID) + ".txt").c_str(), curEntry->nodeOrder, 0);
+
+      if (processRetreat()) { continue; }
+      else break;
+    }
+
+    if (get<0>(incompletePatchResult) >= 0) // we have found an incomplete B-patch and it is unambiguous to connect
+    {
+      int nodeID = get<0>(incompletePatchResult);
+      int nbrNodeID = get<1>(incompletePatchResult);
+      int nbrCellID = curEntry->nodes[nbrNodeID].getCellID();
+      int patchID = get<2>(incompletePatchResult);
+      if (verbose)
+      {
+        cout << "connect node " << nodeID << " cell " << curEntry->nodes[nodeID].getCellID() << " patch " << patchID <<
+            " to node " << nbrNodeID << " cell " << curEntry->nodes[nbrNodeID].getCellID() << endl;
+      }
+
+      // if the node to connect to, nbrNodeID is in unusedNodes, then we move it from unusedNodes to usedNodes
+      if (setFind(curEntry->unusedNodes[nbrCellID], nbrNodeID))
+      {
+        // grab an unused node
+        curEntry->unusedNodes[nbrCellID].erase(nbrNodeID);
+        curEntry->usedNodes[nbrCellID].insert(nbrNodeID);
+        curEntry->nodeOrder.push_back(nbrNodeID);
+        if (verbose)
+          cout << "grab an unused node" << endl;
+      }
+
+      bool success = tryConnect(curEntry, nodeID, nbrNodeID, patchID);
+      if (success)
+      {
+        // successfully connected to nbrNodeID
+        if (verbose)
+        {
+          cout << "connect node " << nodeID << " cell " << curEntry->nodes[nodeID].getCellID() << " patch " << patchID <<
+              " to node " << nbrNodeID << " cell " << curEntry->nodes[nbrNodeID].getCellID() << endl;
+          printGraphInfo(curEntry);
+        }
+        continue; // go the next iteration
+      }
+
+      // fail to connect
+      cout << "Failed to make this connection!" << endl;
+      // If nbrNodeID is in usedNodes in the last iteration, then getIncompletePatchHeuristically() already called
+      // tryConnect() to determine that connecting nbrNodeID is fine.
+      // So we enter this "else" branch only when nbrNodeID has just been added into usedNodes in this iteration.
+      // This means the existing graph failed to add an unused node.
+      // Then, the existing graph must have been broken, we should stop this search direction and go the previous one
+      // on the stack
+      if (processRetreat()) { continue; } // if we succeed in retreating, then go to the next iteration
+      else break;                         // else, nothing left in the stack, break the search
+    } // end if (get<0>(incompletePatchResult) >= 0)
+
+    // else, no available unabmbiguous, incomplete patch to connect, and the algorithm has not finished
+
+    // if no ambiguity on incomplete B-patches, and we have found at least one incomplete B-patch
+    // this then means that on that incomplete B-patch, no nodes can connect to it!
+    // then clearly this current search direction is broken, we have to retreat
+    if (hasAmbiguityInSearch == false && numIncompletePatchesVisited > 0)
+    {
+      if (processRetreat()) { continue; } // if we succeed in retreating, then go to the next iteration
+      else break;                         // else, nothing left in the stack, break the search
+    }
+
+    // then it means we either:
+    // a) have another connected component to discover, which we will find by adding a new seed on the component
+    // or
+    // b) have ambiguity on which node to own/connect to an undecided B-patch, which we will determine by branching and searching
+    // or
+    // c) have ambiguity on which node to connect to an incomplete B-patch, which we will determine by branching and searching
+    if (numIncompletePatchesVisited == 0)
+    {
+      cout << "finished one connected component, now goes to the next" << endl;
+      // first we try case (a), searching for a new connected component in the graph
+      ImmStackEntry tmpEntry = *curEntry;
+      int newSeed = addSeed(&tmpEntry);
+      if (newSeed >= 0) // found one
+      {
+        *curEntry = tmpEntry;
+        continue;
+      }
+    }
+
+    // if we have ambiguity during the search,
+    // or if no incomplete patches found and no new connected componet can be found
+    // then we save current state in the stack and branch the search direction
+    cout << "=======================================================" << endl;
+    cout << " Save current state on stack and begin a new direction" << endl;
+    cout << "=======================================================" << endl;
+    if (hasAmbiguityInSearch)
+      cout << "Because ambiguity found in the search" << endl;
+    else
+    {
+      cout << "Because num incomplete patches visited is 0" << endl;
+    }
+
+    { // push the entry to the stack
+      // actually in our implementation, the current active state is always in the var searchStack,
+      // so technically it's not "pushing" but "copying"...  but you got the idea
+      ImmStackEntry newEntry = *curEntry; // copy the current state
+      searchStack.emplace_back(move(newEntry));
+      curEntry = &(searchStack.back());
+    }
+
+    // tried direction saves which branching direction we have tried at this point
+    set<tuple<int,int,int>> & triedDir = searchStack[searchStack.size()-2].triedDirections;
+    cout << "#Tried directions: " << triedDir.size() << endl;
+    tuple<int,int,int> result(-1,-1,-1);
+    if (numIncompletePatchesVisited == 0)
+    {
+      // we are in case (b)
+      cout << "All left are undecided patches, try one" << endl;
+      result = getAvailableUndecidedPatch(curEntry, &triedDir);
+    }
+    else
+    {
+      // case (c)
+      result = getIncompletePatchHeuristically(curEntry, numIncompletePatchesVisited, hasAmbiguityInSearch, &triedDir);
+    }
+
+    int nodeID = get<0>(result);
+    int nbrNodeID = get<1>(result);
+    int nbrCellID = curEntry->nodes[nbrNodeID].getCellID();
+    int patchID = get<2>(result);
+    if (nodeID >= 0) // we find a possible search direction
+    {
+      triedDir.insert(result); // save this direction to triedDir so that we won't enter again
+
+      if (nbrNodeID >= 0) // the action of the direction is to connect to nbrNodeID
+      {
+        if (setFind(curEntry->unusedNodes[nbrCellID], nbrNodeID)) // is nbrNodeID is in unusedNodes, move it to usedNodes first
+        {
+          // grab an unused node
+          curEntry->unusedNodes[nbrCellID].erase(nbrNodeID);
+          curEntry->usedNodes[nbrCellID].insert(nbrNodeID);
+          curEntry->nodeOrder.push_back(nbrNodeID);
+          if (verbose)
+            cout << "grab an unused node" << endl;
+        }
+
+        bool success = tryConnect(curEntry, nodeID, nbrNodeID, patchID);
+        if (success == false)
+        {
+          cout << "Fail to connect to a possible search direction" << endl;
+          if (processRetreat()) continue;
+          else break;
+        }
+
+        if (verbose)
+        {
+          cout << "connect node " << nodeID << " cell " << curEntry->nodes[nodeID].getCellID() << " patch " << patchID <<
+              " to node " << nbrNodeID << " cell " << curEntry->nodes[nbrNodeID].getCellID() << endl;
+          printGraphInfo(curEntry);
+        }
+      }
+      else // action is to own a patch
+      {
+        bool success = tryUpdate(curEntry, nodeID, true, patchID);
+        success = success && tryGlobalUpdate(curEntry);
+        assert(success); // this action has been tried in getAvailableUndecidedPatch(), so it must be success
+        if (verbose)
+        {
+          cout << "assign node " << nodeID << " cell " << curEntry->nodes[nodeID].getCellID() << " an owned patch " << patchID << endl;
+          printGraphInfo(curEntry);
+        }
+      }
+      continue; // finish the action, go to the next iteration
+    } // end if (nodeID >= 0) // we find a possible search direction
+    else // no possible direction can be found on this state
+    {
+      cout << "Finish searching available directions at this fork, have to retreat twice" << endl;
+      if (processRetreat() && processRetreat()) continue; // we retreat twice because no more possible direction
+      else break;
+    }
+  } // end algorithm loop
+
+  cout << "Finished building immersion graph." << endl;
+  if (graphs.size() == 0) { cout << "Found no immersion!" << endl; }
+  else { cout << "Found " << graphs.size() << " possible immersion graph" << (graphs.size() == 1 ? "" : "s") << "." << endl; }
+}
diff --git a/libraries/immersionMesher/immersionMesherCellComplex.cpp b/libraries/immersionMesher/immersionMesherCellComplex.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..df34f517d189c62ada242fd2a47d6b97601a6bbd
--- /dev/null
+++ b/libraries/immersionMesher/immersionMesherCellComplex.cpp
@@ -0,0 +1,913 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "immersionMesher" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "immersionMesher.h"
+#include "predicates.h"
+#include "basicAlgorithms.h"
+#include "containerHelper.h"
+#include "performanceCounter.h"
+#include "geometryQuery.h"
+#include <iostream>
+#include <fstream>
+using namespace std;
+
+ImmersionMesher::ImmersionMesher()
+{
+}
+
+void ImmersionMesher::run(
+           const TriMeshGeo & triMesh,
+           const TetMeshGeo & tetMesh,
+           std::vector<TetMeshGeo> & tetMeshes,
+           std::vector<BarycentricCoordinates> & embeddingWeights,
+           std::vector<std::vector<TriMeshGeo>> * allCellMeshes,
+           std::vector<std::vector<BarycentricCoordinates>> * allCellMeshWeights)
+{
+  buildTopologyData(triMesh, tetMesh);
+  runImmersionAlgorithm();
+  generateImmersedTetMesh(tetMeshes, embeddingWeights, allCellMeshes, allCellMeshWeights);
+}
+
+void ImmersionMesher::buildTopologyData(const TriMeshGeo & triMesh, const TetMeshGeo & tetMesh)
+{
+  bool tmpDumpInfo = verbose;
+  bool tmpCSG = useCSGInVT;
+  *this = ImmersionMesher(); // clear all internal data
+  verbose = tmpDumpInfo;
+  useCSGInVT = tmpCSG;
+
+  inputTetMeshGeo = tetMesh;
+  inputTriMeshGeo = triMesh;
+  cout << "Input mesh for ImmersionMesher:, #v: " << inputTriMeshGeo.numVertices() << " #t: " << inputTriMeshGeo.numTriangles() << "." << endl;
+  for(int triID = 0; triID < inputTriMeshGeo.numTriangles(); triID++)
+  {
+    if (isTriangleDegenerate(inputTriMeshGeo.pos(triID, 0),inputTriMeshGeo.pos(triID, 1),inputTriMeshGeo.pos(triID, 2)))
+    {
+      cout << "Error: input triMesh has degenerate triangles" << endl;
+      throw 1;
+    }
+  }
+  if (areTrianglesManifold(inputTriMeshGeo.triangles()) == false)
+  {
+    cout << "Error: Input triMesh is not manifold" << endl;
+    throw 1;
+  }
+  {
+    set<Vec3d> inputPosSet;
+    for(Vec3d p : inputTriMeshGeo.positions()) inputPosSet.insert(p);
+    if (sizei(inputPosSet) < inputTriMeshGeo.numVertices())
+    {
+      cout << "Error: input triMesh has duplicating positions: #ori vtx " << inputTriMeshGeo.numVertices() << " #unique vtx " << sizei(inputPosSet) << endl;
+      throw 1;
+    }
+    set<UTriKey> inputUTriKeys;
+    for(Vec3i tri : inputTriMeshGeo.triangles()) inputUTriKeys.emplace(tri);
+    if(sizei(inputUTriKeys) < inputTriMeshGeo.numTriangles())
+    {
+      cout << "Error: input triMesh has duplicating faces: #ori tri: " << inputTriMeshGeo.numTriangles() << " #unique tri: " << inputUTriKeys.size() << endl;
+      throw 1;
+    }
+  }
+
+  ProfilerSection basicDataSec(&profiler, "buildCells&Patches");
+  basicDataSec.start();
+  buildBasicData();
+  basicDataSec.stop();
+
+  ProfilerSection moreDataSec(&profiler, "moreDataPreparation");
+  moreDataSec.start();
+  prepareDataForSolve();
+  moreDataSec.stop();
+}
+
+void ImmersionMesher::runImmersionAlgorithm()
+{
+  PerformanceCounter graphTime;
+  computedCellNodes.clear();
+  ProfilerSection searchSec(&profiler, "nodeSearch");
+  searchSec.start();
+  graphTime.StartCounter();
+  runNodeSearchMethod(computedCellNodes);
+  graphTime.StopCounter();
+  cout << "Time to build immersion graph: " << graphTime.GetElapsedTime() << "s." << endl;
+  searchSec.stop();
+}
+
+void ImmersionMesher::buildBasicData()
+{
+  ProfilerExtraSection iglRemeshSection(&profiler, "iglRemeshOnInput");
+  iglRemeshSection.start();
+  cout << "Run igl::remeshSelfIntersection..." << endl;
+  selfCutMesh = iglInterface::remeshSelfIntersection(inputTriMeshGeo, false);
+  iglRemeshSection.stop();
+
+  assert(sizei(selfCutMesh.cutPosExact) == selfCutMesh.cutMesh.numVertices());
+
+//  fixCutMeshConnection(selfCutMesh);
+  const auto & cutMesh = selfCutMesh.cutMesh;
+  cout << "Finished remeshing, the result cut mesh: #v " << cutMesh.numVertices() << " #t: " << cutMesh.numTriangles() << "." << endl;
+
+  if (verbose)
+  {
+    cutMesh.save("cutMesh.obj");
+    cutMeshSaved = true;
+  }
+
+  const auto & vtxStitchIDs = selfCutMesh.vtxStitchIDs; // cutVtxID -> stitchID
+  assert(vtxStitchIDs.size() == (size_t)cutMesh.numVertices());
+  for(int i = 0; i < cutMesh.numVertices(); i++) // build stitchPositions: stitchID -> position
+  {
+    int stID = vtxStitchIDs[i];
+    if (stitchPositions.size() <= (size_t)stID) stitchPositions.resize(stID+1);
+    stitchPositions[stID] = cutMesh.pos(i);
+  }
+
+  if (areTrianglesManifold(cutMesh.triangles()) == false)
+  {
+    cout << "Error: cut mesh is not manifold!!!!" << endl;
+    cutMesh.save("cutMesh.obj");
+    cutMeshSaved = true;
+    exit(1);
+  }
+
+  cutMeshNbr = TriMeshNeighbor(cutMesh);
+  auto cutBou = cutMeshNbr.findBoundaryTriangles(cutMesh.triangles());
+  if (cutBou.size() > 0)
+  {
+    cout << "Error: cut mesh has boundary" << endl;
+    for(auto p : cutBou) { cout << "(" << p.first << ", " << p.second << ") "; }
+    cout << endl;
+    exit(1);
+  }
+
+  const auto & triPatchIDs = selfCutMesh.triPatchIDs;       // triID -> patchID it belongs to
+  const auto & cellIDsAtPatch = selfCutMesh.cellIDsAtPatch; // patchID -> <cellID on front side of patch, cellID on back side of patch>
+  numPatches = cellIDsAtPatch.size();
+
+  // build patchTris patchTriIDs cutMeshTriKeyIDs
+  // patchTris: patchID -> cut triangles (Vec3i) of the patch
+  // patchTriIDs: patchID -> cutTriIDs of the patch
+  // cutMeshTriKeyIDs: cut UTriKey -> cutTriID
+  for(int triID = 0; triID < cutMesh.numTriangles(); triID++)
+  {
+    int patchID = triPatchIDs[triID];
+    if (sizei(patchTris) <= patchID) { patchTris.resize(patchID+1); patchTriIDs.resize(patchID+1); }
+    patchTris[patchID].push_back(cutMesh.tri(triID));
+    patchTriIDs[patchID].push_back(triID);
+    cutMeshTriKeyIDs[cutMesh.tri(triID)] = triID;
+  }
+
+  if (verbose)
+  {
+    ObjMesh patchMesh;
+    patchMesh.addVertexPositions(cutMesh.positions());
+    for(int patchID = 0; patchID < numPatches; patchID++)
+    {
+      ObjMesh::Group g("p" + to_string(patchID));
+      for(Vec3i tri : patchTris[patchID])
+      {
+        ObjMesh::Face f(tri);
+        g.addFace(move(f));
+      }
+      patchMesh.addGroup(move(g));
+    }
+    patchMesh.save("patchMesh.obj");
+  }
+
+  // build cellTris, cellPatches, cellWindingNumbers and cellNeighobrsAtPatch
+  for(size_t patchID = 0; patchID < cellIDsAtPatch.size(); patchID++)
+  {
+    int outCellID = cellIDsAtPatch[patchID].first;
+    int inCellID = cellIDsAtPatch[patchID].second;
+
+    int maxCellID = max(inCellID, outCellID);
+    if (sizei(cellTris) <= maxCellID)
+    {
+      cellTris.resize(maxCellID+1);
+      cellPatches.resize(maxCellID+1);
+      cellWindingNumbers.resize(maxCellID+1, -1);
+      cellNeighborsAtPatch.resize(maxCellID+1);
+    }
+    int triID = patchTriIDs[patchID][0]; // get the first triangle at patchID
+
+    int wnOut = selfCutMesh.windAroundTri[triID].first; // windingNumber outside triID
+    int wnIn = selfCutMesh.windAroundTri[triID].second; // windingNumber inside triID
+    for(int tID : patchTriIDs[patchID])
+    { // check all triangles on this patch has the same inner/outer winding numbers as the first one
+      assert(selfCutMesh.windAroundTri[tID] == make_pair(wnOut, wnIn));
+    }
+    // cellWindingNumber is initialized to be -1
+    // if they are not initialized, then their value must match the current value
+    if (cellWindingNumbers[outCellID] != -1) assert(cellWindingNumbers[outCellID] == wnOut);
+    if (cellWindingNumbers[inCellID] != -1) assert(cellWindingNumbers[inCellID] == wnIn);
+    cellWindingNumbers[outCellID] = wnOut;
+    cellWindingNumbers[inCellID] = wnIn;
+
+    cellNeighborsAtPatch[inCellID][patchID] = outCellID;
+    cellNeighborsAtPatch[outCellID][patchID] = inCellID;
+
+    for(auto t : patchTris[patchID])
+    {
+      cellTris[inCellID].push_back(t);
+      Vec3i tr(t[0], t[2], t[1]);        // get the reversed triangle
+      cellTris[outCellID].push_back(tr); // add it to the outer cell
+      cellPatches[inCellID][patchID] = true;
+      cellPatches[outCellID][patchID] =  false;
+    }
+  }
+  numCells = cellTris.size();
+  // libigl selfIntersection code assumes the first cell is always the outer cell (empty space) and its winding number is 0
+  assert(cellWindingNumbers[0] == 0);
+
+  cout << "Found #cells = " << numCells << " and #Patches = " << numPatches << "." << endl;
+  if (verbose)
+  {
+    ofstream fout("cellPatches.txt");
+    for(int cellID = 0; cellID < numCells; cellID++)
+    {
+      fout << cellID << ": ";
+      for(auto p : cellPatches[cellID]) { fout << "p" << p.first << ", "; }
+      fout << endl;
+    }
+    fout.close();
+
+    fout.open("cellWN.txt");
+    for(int cellID = 0; cellID < numCells; cellID++)
+      fout << cellID << ": " << cellWindingNumbers[cellID] << endl;
+    fout.close();
+
+    for(int i = 0; i < numPatches; i++)
+    {
+      TriMeshRef mesh(cutMesh.positions(), patchTris[i]);
+      auto newMesh = removeIsolatedVertices(mesh);
+      newMesh.save("patch" + to_string(i) + ".obj");
+    }
+  }
+}
+
+
+void ImmersionMesher::prepareDataForSolve()
+{
+  const auto & cutMesh = selfCutMesh.cutMesh;
+  const auto & vtxStitchIDs = selfCutMesh.vtxStitchIDs;
+
+  // build patchMeshRef and patchTriNbr
+  // patchMeshRef: patchID -> patch mesh (cutVtxPos, patchTris[patchID])
+  // patchTriNbr:  patchID -> TriangleNeighbor for the patch
+  vector<TriMeshRef> patchMeshRef(numPatches);
+  vector<TriangleNeighbor> patchTriNbr(patchTris.size());
+  for(size_t patchID = 0; patchID < patchTris.size(); patchID++)
+  {
+    // cout << "patchID " << patchID << endl;
+    patchMeshRef[patchID] = TriMeshRef(cutMesh.positions(), patchTris[patchID]);
+    const auto & patchTri = patchTris[patchID];
+    if (areTrianglesEdgeManifold(patchTri) == false)
+    {
+      cout << "Error: patch " << patchID << " is not edge manifold" << endl;
+      throw 1;
+    }
+    else
+    {
+      if (areTrianglesManifold(patchTri) == false)
+      {
+        cout << "Warning: patch " << patchID << " is not vtx manifold" << endl;
+      }
+    }
+//    assert(areTrianglesManifold(patchTri) == true);
+    patchTriNbr[patchID] = TriangleNeighbor(patchTri);
+  }
+
+
+  // ===========================================================
+  //               Compute data about bous
+  // ===========================================================
+
+  {
+    map<UEdgeKey, set<pair<int, int>>> uedgesPatch; // uedge -> the <patchID, loopID>s this uedge belongs to
+    for(int patchID = 0; patchID < numPatches; patchID++)
+    {
+      const auto & tris = patchTris[patchID];
+      auto loops = patchTriNbr[patchID].findBoundaryLoops(tris); // get the boundary loops for each patch
+      for(size_t loopID = 0; loopID < loops.size(); loopID++)    // for each loop
+      {
+        const auto & loop = loops[loopID];
+        for(size_t i = 0; i < loop.size(); i++)
+        {
+          UEdgeKey ue(loop[i], loop[(i+1)%loop.size()]);
+          uedgesPatch[ue].emplace(patchID, loopID);
+        }
+      }
+    }
+    // each boundary uedge should be shared by two and only two <patch, loop> pairs
+    for(const auto & p : uedgesPatch)
+    {
+      if (p.second.size() != 2) // this uedge is not shared by exactly two <patch, loop> pairs, sth. wrong
+      { // print debugging information
+        cout << "Error: one uedge has " << p.second.size() << " patches around" << endl;
+        cout << "patchIDs this uedge lies: ";
+        for(auto & p2 : p.second) { cout << p2.first << " "; }
+        cout << endl;
+
+        UEdgeKey key = p.first; // uedge key
+        cout << "UEdge is " << key << endl;
+        cout << "Search for all patches to find this key: " << endl;
+        for(int pID = 0; pID < numPatches; pID++)
+        {
+          bool found = false;
+          const auto & tris = patchTris[pID];
+
+          for(Vec3i t : tris)
+          {
+            for(int i = 0; i < 3; i++)
+            {
+              UEdgeKey e(t[i], t[(i+1)%3]);
+              if (e == key)
+              {
+                cout << "Find this key on patch " << pID << " tri " << t << endl;
+                found = true;
+              }
+            }
+          }
+          if (found)
+          {
+            cout << "now lets see the boundary loops of this patch: " << endl;
+            auto loops = patchTriNbr[pID].findBoundaryLoops(tris);
+            for(size_t loopID = 0; loopID < loops.size(); loopID++)
+            {
+              const auto & loop = loops[loopID];
+              cout << "loop " << loopID << ": ";
+              for(auto l : loop) cout << l << " ";
+              cout << endl;
+            }
+          }
+        }
+        throw 1;
+      }
+    }
+
+    map<set<pair<int,int>>, set<UEdgeKey>> patch2bous; // <patchID,loopID> pairs -> uedges on the pairs
+    for(const auto & p : uedgesPatch)
+    {
+      patch2bous[p.second].insert(p.first);
+    }
+
+    // build patchBouNbrs, bous, bouVertices, bouPatchIDs
+    patchBouNbrs.resize(numPatches);
+    for(const auto & p : patch2bous)
+    {
+      // we need to find the connected components from uedgeSet
+      // each of the CC is a bou
+      map<int, bool> allVtxVisited;
+      map<int, set<UEdgeKey>> allVtxNbr; // vtx -> nbr uedges
+      for(auto ue : p.second) // for all uedges on the pairs
+      {
+        allVtxVisited[ue[0]] = false;
+        allVtxVisited[ue[1]] = false;
+        allVtxNbr[ue[0]].insert(ue);
+        allVtxNbr[ue[1]].insert(ue);
+      }
+
+      for(auto & allVtxVisitedPair : allVtxVisited)
+      {
+        // if this vtx (allVtxVisitedPair,first) is visited, continue
+        if (allVtxVisitedPair.second == true) { continue; }
+        // else, label this vtx as visited
+        allVtxVisitedPair.second = true;
+        set<int> bouVtx = { allVtxVisitedPair.first }; // assign seed vtx
+        // use BFS to find all the other vtx in the same connected component on uedges as the seed
+        // also find the edges BFS visited and stored in set<UEdgeKey> bouEdges
+        set<UEdgeKey> bouEdges;
+        set<int> candidates = bouVtx, nextCandidates; // buffers for BFS
+        while(candidates.size() > 0)
+        {
+          for(int vtx : candidates)
+          {
+            for(const UEdgeKey & nbrVtxPair : allVtxNbr[vtx])
+            {
+              bouEdges.insert(nbrVtxPair); // store the edge we visited
+              for(int i = 0; i < 2; i++)
+              {
+                int nbrVtx = nbrVtxPair[i];
+                if (allVtxVisited[nbrVtx] == true) { continue; }
+                bouVtx.insert(nbrVtx); // bouVtx stores the vtx visited on the connected component on uedges
+                nextCandidates.insert(nbrVtx);
+                allVtxVisited[nbrVtx] = true;
+              }
+            }
+          }
+          candidates.swap(nextCandidates);
+          nextCandidates.clear();
+        }
+
+        // record this bou
+        int bouID = bous.size();
+        bous.emplace_back(move(bouEdges));
+        bouVertices.emplace_back(move(bouVtx));
+
+
+        vector<pair<int, int>> pIDs(p.first.begin(), p.first.end()); // <patchID,loopID> pairs
+        // get the two patchIDs to build patchBouNbrs:
+        int pID0 = pIDs[0].first, pID1 = pIDs[1].first;
+        if (pID0 > pID1) swap(pID0, pID1);
+        patchBouNbrs[pID0][bouID] = pID1;
+        patchBouNbrs[pID1][bouID] = pID0;
+        bouPatchIDs.emplace_back(std::array<int,2>({pID0, pID1}));
+      } // end for one BFS
+    } // end for <patchID, loopID> pairs
+    numBous = bous.size();
+  }
+
+  // ===========================================================
+  //               Now Bous have been computed
+  //               Lets build arcs
+  // ===========================================================
+
+  // build arcs from bous
+  std::vector<std::map<UEdgeKey, UEdgeKey>> arcEdge2bouEdge(numBous); // bouID -> arcEdgeKey -> bouEdgeKey
+  map<set<UEdgeKey>, set<int>> arcEdges2Bous; // arcEdgeKey group representing one arc -> bouIDs lie on this arc
+  for(int bouID = 0; bouID < numBous; bouID++)
+  {
+    set<UEdgeKey> stueSet; // stores UEdgeKeys in stitchIDs, which are actually arcEdgeKeys
+    for(auto ue : bous[bouID])
+    {
+      UEdgeKey stue (vtxStitchIDs[ue[0]], vtxStitchIDs[ue[1]]); // stitchID UEdge
+      stueSet.insert(stue);
+      if(arcEdge2bouEdge[bouID].find(stue) != arcEdge2bouEdge[bouID].end()) // if this stUEdge has been visited on this bouID
+      {
+        cout << "Error: one bou has edges collide with each other, bou " << bouID <<
+            " edge is " << ue << " and " << arcEdge2bouEdge[bouID][stue] <<  " st edge " << stue << endl;
+        if (cutMeshSaved == false && verbose) { cutMesh.save("cutMesh.obj"); cutMeshSaved = true; }
+        cout << "saving this bou: bouID " << bouID << endl;
+        for(auto ue: bous[bouID]) { cout << ue[0] << " " << ue[1] << " "; }
+        cout << endl;
+        cout << "Done" << endl;
+        throw 1;
+      }
+      arcEdge2bouEdge[bouID][stue] = ue;
+    }
+    arcEdges2Bous[stueSet].insert(bouID);
+  }
+
+  // build arc2Bous, bou2Arcs and arcs
+  numArcs = arcEdges2Bous.size();
+  cout << "Found #pre-arcs = " << numBous << " and #arcs = " << numArcs << "." << endl;
+  
+  arc2Bous.resize(numArcs);
+  bou2Arcs.resize(numBous, -1);
+  for(const auto & p : arcEdges2Bous)
+  {
+    if (p.second.size() != 2) // exactly two bous lie on one arc
+    {
+      cout << "Error: one arc has " << p.second.size() << " corresponding bous" << endl;
+      throw 1;
+    }
+    int arcID = arcs.size();
+    arcs.push_back(p.first); // push the set of arcEdgeKeys into arcs
+    for(int bouID : p.second) // for the bous lie on this arc
+    {
+      arc2Bous[arcID].insert(bouID);
+      assert(bou2Arcs[bouID] < 0 || bou2Arcs[bouID] == arcID);
+      bou2Arcs[bouID] = arcID;
+    }
+  }
+  // check all bous have corresponding arcs
+  assert(find(bou2Arcs.begin(), bou2Arcs.end(), -1) == bou2Arcs.end());
+  if (verbose)
+  {
+    for(int arcID = 0; arcID < numArcs; arcID++)
+      cout << "arcID " << arcID << " has bou: " << streamRange(arc2Bous[arcID]) << endl;
+  }
+
+  // build patchNbrArcs, arcNbrPatches, patchNbrArcBous
+  patchNbrArcs.resize(numPatches);
+  arcNbrPatches.resize(numArcs);
+  patchNbrArcBous.resize(numPatches);
+  for(int patchID = 0; patchID < numPatches; patchID++)
+  {
+    for(const auto & p : patchBouNbrs[patchID])
+    {
+      int bouID = p.first;
+      int arcID = bou2Arcs[bouID];
+      patchNbrArcs[patchID].insert(arcID);
+      arcNbrPatches[arcID].insert(patchID);
+      patchNbrArcBous[patchID][arcID].insert(bouID);
+    }
+  }
+
+  if (verbose)
+  {
+    for(int patchID = 0; patchID < numPatches; patchID++)
+    {
+      cout << "patch " << patchID << " GeoNbr: ";
+      for(auto nbr : patchBouNbrs[patchID])
+      {
+        cout << "(" << nbr.first << ", " << nbr.second << ") ";
+      }
+      cout << endl;
+      for(auto p : patchNbrArcBous[patchID])
+      {
+        if (p.second.size() > 1)
+          cout << "Note! patch " << patchID << " at arc " << p.first << " has " << p.second.size() << " bous!" << endl;
+      }
+    }
+    for(int bouID = 0; bouID < numBous; bouID++)
+    {
+      string filename = "bou" + to_string(bouID) + ".txt";
+      ofstream fout(filename.c_str());
+      for(auto edge : bous[bouID])
+        fout << edge[0] << " " << edge[1] << endl;
+      fout.close();
+    }
+  }
+
+
+  // ===========================================================
+  //      Finally, build B-patch neigboring data on a cell
+  // ===========================================================
+
+  // build cellPatchNeighbors, cellBouCorrespondence
+  cellPatchBouNbrs.resize(numCells);
+  cellBouCorrespondence.resize(numCells);
+  for(int cellID = 1; cellID < numCells; cellID++)
+  {
+    if (verbose)
+      cout << "Build cellPatchBouNbrs: " << cellID << endl;
+    // build cell patch neighbors for pID0 and pID1. The bou of pID0 at this arc is b0. The bou of pID1 at the arc is b1.
+    auto buildCellPatchBouNbr = [&](int pID0, int pID1, int b0, int b1)
+    {
+      assert(b0 != b1);
+      assert(bou2Arcs[b0] == bou2Arcs[b1]); // b0 and b1 must lie on the same arc
+
+      if (verbose)
+        cout << "add cell " << cellID << " patch nbr: " << pID0 << '-' << b0 << " " << pID1 << '-' << b1 << endl;
+      // patchBouNbrs: patchID -> nbring bouID -> topological nbring patchID sharing the bou
+      assert(mapFind(patchBouNbrs[pID0], b0));
+      assert(mapFind(patchBouNbrs[pID1], b1));
+      cellPatchBouNbrs[cellID][pID0][b0] = pair<int, int>(b1, pID1);
+      cellPatchBouNbrs[cellID][pID1][b1] = pair<int, int>(b0, pID0);
+
+      UEdgeKey bouPair(b0, b1);
+      // the two bous lie on the same arc, so their #vertices should be the same
+      assert(bouVertices[b0].size() == bouVertices[b1].size());
+      if (mapFind(cellBouCorrespondence[cellID], bouPair)) return; // if this bou pair has been built, then return
+
+      // now let's build the correspondence on b0 and b1
+      vector<pair<int,int>> vtxCor;
+      map<int, int> stvtx2b0vtx; // stitchID -> cutVtxID in b0
+      for(int vID : bouVertices[b0])
+        stvtx2b0vtx[vtxStitchIDs[vID]] = vID;
+      if(stvtx2b0vtx.size() == bouVertices[b0].size()) // one to one mapping of stitchID and b0VtxID
+      {
+        for(int vID : bouVertices[b1])
+        {
+          int stID = vtxStitchIDs[vID];
+          assert(mapFind(stvtx2b0vtx, stID));
+          vtxCor.emplace_back(stvtx2b0vtx[stID], vID);
+        }
+      }
+      else // more than one v0 vtx match one stID (stID = stitchID)
+      {    // this can happen when the bou is a ring shape, then the start and end vtx of the ring have the same stID
+        map<int, set<int>> stvtx2b0vtxSet; // stitchID -> set of b0VtxIDs
+        for(int vID : bouVertices[b0])
+          stvtx2b0vtxSet[vtxStitchIDs[vID]].insert(vID);
+
+        if (verbose)
+        {
+          int nonSingleCount = 0;
+          for(const auto & p : stvtx2b0vtxSet)
+            if (p.second.size() > 1)
+            {
+              assert(p.second.size() == 2);
+              nonSingleCount++;
+            }
+          cout << "found non-trivial boundary at b0 : " << b0 << " with nonSingleCount: " << nonSingleCount << endl;
+        }
+
+        // first pass, find correspondance on those unambiguous vertices
+        for(int vID : bouVertices[b1])
+        {
+          int stID = vtxStitchIDs[vID];
+          assert(mapFind(stvtx2b0vtxSet, stID));
+          if (stvtx2b0vtxSet[stID].size() == 1) // the stID corresponds to one bou0 vtx, then no ambiguity
+            vtxCor.emplace_back(stvtx2b0vtx[stID], vID);
+        }
+        // second pass, we use the neighboring information on the vertices to determine the matches
+        // if b0a, b0b, b1a, b1b all have the same stID, then
+        // we use the topological neighboring vtx of b0a to find whether it should match b1a or b1b
+        // since no way that the neighboring vtx of b0a: b0c( and b0d, if any) are ambiguous as well,
+        // otherwise, two UEdgeKeys on bou0 map to the same arcEdgeKey, which is forbiden and checked beforehand
+        for(int b1vID : bouVertices[b1])
+        {
+          int stID = vtxStitchIDs[b1vID];
+          // in this loop, we deal with those b1vID with ambiguious correspondence
+          if (stvtx2b0vtxSet[stID].size() == 1) continue;
+
+          map<int, int> targetCount; // candidate of b0vtx that maps to b1vID -> confidence count
+          // find its neighbor
+          auto b1vtxNbr = cutMeshNbr.getVtxNearbyVertices(b1vID, cutMesh);
+          for(int nv : b1vtxNbr)  // visit b1 nbrs
+          {
+            if (setNotFind(bouVertices[b1], nv)) continue;
+            // for a neighboring b1 vtx: nv
+            int nbrstID = vtxStitchIDs[nv]; // nv's stID
+            if (mapNotFind(stvtx2b0vtxSet, nbrstID)) continue;
+            if (stvtx2b0vtxSet[nbrstID].size() > 1) continue;
+            if(mapNotFind(stvtx2b0vtx, nbrstID)) continue;
+            int nbrb0v = stvtx2b0vtx[nbrstID]; // the b0 vtx mapping to this nv
+            for(int b0Cand : stvtx2b0vtxSet[stID]) // b0 vtx that map to the b1 vtx with stID
+            {
+              // if b0Cand is a neighbor of nbrb0v, then we treat it as a candidate for mapping to b1vID
+              if (cutMeshNbr.areVerticesNeighbors(nbrb0v, b0Cand)
+                  && setFind(bous[b0], UEdgeKey(nbrb0v, b0Cand)))
+              {
+                if (mapFind(targetCount, b0Cand)) targetCount[b0Cand]++;
+                else targetCount[b0Cand] = 1;
+              }
+            }
+          }
+          assert(targetCount.size() == 1); // should only have one candidate
+          int b0v = targetCount.begin()->first;
+          vtxCor.emplace_back(b0v, b1vID);
+        }
+      }
+
+      if (bouPair[0] == b1)  // switch order in pairs
+      {
+        for(auto & p : vtxCor) swap(p.first, p.second);
+      }
+      cellBouCorrespondence[cellID][bouPair] = move(vtxCor); // finally, build the correspondence on b0 and b1
+    };
+
+    // first, build arc2patches: arcID -> <patchIDs, bouIDs> on this cell
+    // here we define <patchID, bouID> as "patch end"
+    // around one arc, there are 4 and only 4 patch ends
+    map<int, set<pair<int, int>>> arc2patches;
+    for(auto p : cellPatches[cellID]) // patchID -> whether this patch points outward for this cell
+    {
+      int patchID = p.first;
+      for(const auto & patchBou : patchBouNbrs[patchID])
+      {
+        int bouID = patchBou.first;
+        int arcID = bou2Arcs[bouID];
+        arc2patches[arcID].emplace(patchID, bouID);
+      }
+    }
+
+    if (verbose)
+      cout << arc2patches.size() << " arcs in cell " << cellID << endl;
+
+    // then, try to build the B-patch correspondence around each arcID
+    for(const auto & p : arc2patches)
+    {
+      int arcID = p.first;
+      const auto & patchIDs = p.second;
+      if(verbose)
+      {
+        cout << "in cell " << cellID <<", " << patchIDs.size() << " patch ends" << endl;
+        for(auto p : patchIDs) cout << p.first << '-' << p.second << " ";
+        cout << endl;
+      }
+      if (patchIDs.size() != 2 && patchIDs.size() != 4) // around one arc there can only be 2 or 4 patch ends
+      {
+        cout << "Error: in cell " << cellID << " an arc " << arcID << " has strange patch-bou pairs: ";
+        for(auto pb : patchIDs) cout << "(" << pb.first << " " << pb.second << ") ";
+        cout << endl;
+        throw 1;
+      }
+      if (patchIDs.size() == 2) // simple case
+      {
+        auto it = patchIDs.begin();
+        int pID0 = it->first, b0 = it->second;
+        it++;
+        int pID1 = it->first, b1 = it->second;
+        // normal situation
+        if (b0 == b1)
+        {
+          cout << "Error: in cell " << cellID << " patch " << pID0 << " and " << pID1 << " has an arc " << arcID;
+          cout <<" but the bou is the same: " << b0 << endl;
+        }
+        assert(b0 != b1);
+        buildCellPatchBouNbr(pID0, pID1, b0, b1);
+      }
+      else if (patchIDs.size() == 4) // difficult case, happens on the "self-touching" cell
+      {
+        // we first get one patch end: pID0, bou0
+        auto patchIt = patchIDs.begin();
+        int pID0 = patchIt->first, bou0 = patchIt->second;
+        patchIt++;
+        // then we try to find which of the rest three patch end should match <pID0, bou0>
+
+        auto getArcEdgeLen2 = [&](UEdgeKey edge)
+        {
+          return len2(stitchPositions[edge[0]] - stitchPositions[edge[1]]);
+        };
+        // to improve robustness, we find the UEdge with the longest length
+        auto maxLenEdge = *maxFunctionValue(arcs[arcID].begin(), arcs[arcID].end(), getArcEdgeLen2);
+
+        auto bouEdge0 = arcEdge2bouEdge[bou0][maxLenEdge]; // the longest bou edge
+        assert(bouEdge0[0] >= 0 && bouEdge0[1] >= 0);
+        // get the triangleID on the patch pID0 at bouEdge0
+        int triID0 = patchTriNbr[pID0].getTriangleAtEdge({bouEdge0[0], bouEdge0[1]});
+        if (triID0 < 0) triID0 = patchTriNbr[pID0].getTriangleAtEdge({bouEdge0[1], bouEdge0[0]});
+        assert(triID0 >= 0);
+        Vec3i t0 = patchTris[pID0][triID0];
+        int t0vtxID = getTriangleVertexOppositeEdge(t0, bouEdge0);
+        assert(t0vtxID >= 0);
+        OEdgeKey triEdge0 = getTriangleOEdge(t0, bouEdge0); // get the OEdgeKey on the triangle triID0
+        assert(triEdge0[0] >= 0 && triEdge0[1] >= 0);
+        OEdgeKey stTriEdge0(vtxStitchIDs[triEdge0[0]], vtxStitchIDs[triEdge0[1]]); // get the OEdgeKey in stitchID
+
+        // get the exact pos of the triangle triID0
+        const Vec3ER & t0Pos0 = selfCutMesh.cutPosExact[t0[0]];
+        const Vec3ER & t0Pos1 = selfCutMesh.cutPosExact[t0[1]];
+        const Vec3ER & t0Pos2 = selfCutMesh.cutPosExact[t0[2]];
+        Vec3ER t0ScaledNormal = cross(t0Pos1 - t0Pos0, t0Pos2 - t0Pos0);
+
+        Vec3d t0normal = patchMeshRef[pID0].computeTriangleNormal(triID0);
+        if (t0normal.hasNaN()) // improve the normal of triID0 with exact computation
+        {
+          for(int i = 0; i < 3; i++)
+            t0normal[i] = ER_toDouble(t0ScaledNormal[i]);
+          assert(t0normal.hasNaN() == false);
+        }
+
+        if (cellPatches[cellID][pID0] == false) // if the triangle normal orientation of the patchID0 is different from the cell
+        {
+          t0ScaledNormal = (-1) * t0ScaledNormal; // then we reverse relevant data
+          t0normal *= (-1);
+          stTriEdge0.reverse();
+        }
+
+        if (verbose)
+        {
+          cout << "bouEdge0 " << bouEdge0 << endl;
+          cout << "triEdge0 " << triEdge0 << endl;
+          cout << "stTriEdge0 " << OEdgeKey(vtxStitchIDs[triEdge0[0]], vtxStitchIDs[triEdge0[1]]) << endl;
+          cout << "ori " << cellPatches[cellID][pID0] << " " << stTriEdge0 << endl;
+        }
+
+        const Vec3ER & t0OpPos = selfCutMesh.cutPosExact[t0vtxID];
+        const Vec3ER & edgePos = selfCutMesh.cutPosExact[bouEdge0[0]];
+        const Vec3ER t0Vec = t0OpPos - edgePos; // a vector from one vtx on the edgeKey to the opposite vtx on triangle triID0
+        double closestAngle = 2*M_PI;      // store the dihedral angle between the patch end <pID0,bou0> and the final found patch end at the edgeKey
+        auto closestIt = patchIDs.begin(); // the iterator to point to the final patch end we will find
+        for(; patchIt != patchIDs.end(); patchIt++) // go through the other three patch ends
+        {
+          int pID1 = patchIt->first;
+          int bou1 = patchIt->second;
+          auto bouEdge1 = arcEdge2bouEdge[bou1][maxLenEdge];
+
+          assert(bouEdge1[0] >= 0 && bouEdge1[1] >= 0);
+          // get the triangle sharing the same longest edgeKey
+          int triID1 = patchTriNbr[pID1].getTriangleAtEdge({bouEdge1[0], bouEdge1[1]});
+          if (triID1 < 0) triID1 = patchTriNbr[pID1].getTriangleAtEdge({bouEdge1[1], bouEdge1[0]});
+          assert(triID1 >= 0);
+          Vec3i t1 = patchTris[pID1][triID1];
+          OEdgeKey triEdge1 = getTriangleOEdge(t1, bouEdge1);
+          OEdgeKey stTriEdge1(vtxStitchIDs[triEdge1[0]], vtxStitchIDs[triEdge1[1]]);
+          if (cellPatches[cellID][pID1] == false)
+          {
+            stTriEdge1.reverse();
+          }
+
+          if (verbose)
+          {
+            cout << pID1 << "-" << bou1 << " bouEdge1 " << bouEdge1 << endl;
+            cout << "triEdge1 " << triEdge1 << endl;
+            cout << "stTriEdge " << OEdgeKey(vtxStitchIDs[triEdge1[0]], vtxStitchIDs[triEdge1[1]]) << endl;
+            cout << "ori " << cellPatches[cellID][pID1] << " " << stTriEdge1 << endl;
+          }
+
+          // we check whether this patch end can form a locally manifold shape with <pID0, bou0> on this cell
+          // if the orientation of the stitchID edgeKey is the same, then the orientation will not be consistent
+          // if this patch end is mapped to <pID0, bou0>, so we skip this patch end
+          if (stTriEdge0 == stTriEdge1) // same orientation on the edge key
+          {
+            if (verbose)
+            {
+              cout << "skip " << pID1 << "-" << bou1 << endl;
+            }
+            continue;
+          }
+
+          const Vec3ER & t1Pos0 = selfCutMesh.cutPosExact[t1[0]];
+          const Vec3ER & t1Pos1 = selfCutMesh.cutPosExact[t1[1]];
+          const Vec3ER & t1Pos2 = selfCutMesh.cutPosExact[t1[2]];
+          Vec3ER t1ScaledNormal = cross(t1Pos1 - t1Pos0, t1Pos2 - t1Pos0);
+          Vec3d t1normal = patchMeshRef[pID1].computeTriangleNormal(triID1);
+          if (cellPatches[cellID][pID1] == false) // again, reverse normals if patch orientation not agree with the cell
+          {
+            t1ScaledNormal = (-1) * t1ScaledNormal;
+            t1normal *= (-1);
+          }
+
+          // compute the dihedral angle between the two triangle t0 and t1 from the two patch ends
+          double angle = getVectorAngle(t0normal, t1normal);
+
+          if (isnan(angle))
+          {
+            cout << "Error: angle is NAN: at cell" << cellID;
+            for(auto p : patchIDs)
+              cout << " patch " << p.first << " bou " << p.second;
+            cout << endl;
+            assert(isnan(angle) == false);
+          }
+
+          // the actual dihedral angle can be angle or 2*M_PI-angle
+          // we choose the correct one from the two based on the computation of the following code
+          if (ER_sign(dot(t0Vec, t1ScaledNormal)) > 0)
+          {
+            angle = 2*M_PI - angle;
+          }
+
+          if (verbose)
+          {
+            cout << "at cell " << cellID << " find an angle: patch " << pID0 << "-" << bou0 << " and " << pID1 << "-" << bou1 <<
+                " is " << angle * 180.0 / M_PI << endl;
+          }
+
+          // store the patch end with the smallest dihedral angle
+          if (angle < closestAngle)
+          {
+            closestIt = patchIt;
+            closestAngle = angle;
+          }
+        } // end of patchIt
+
+        if (closestAngle >= 2*M_PI) // fail to find a valid patch end to match <pID0, bou0>
+        {
+          cout << "Error at 4 patch case: " << cellID << " pID0 " << pID0 << " the rest: ";
+          for(auto p : patchIDs)
+            cout << p.first << " ";
+          cout << endl;
+          throw 1;
+        }
+
+        assert(closestAngle < 2 * M_PI);
+        int pID1 = closestIt->first;
+        int bou1 = closestIt->second;
+        if (verbose)
+          cout << "the closest patch to " << pID0 << "-" << bou0 << " is " << pID1 << "-" << bou1 << endl;
+
+        buildCellPatchBouNbr(pID0, pID1, bou0, bou1); // build the mapping
+
+        // now, build the mapping for the rest two patch endings
+        vector<pair<int,int>> restPatches;
+        for(auto p : patchIDs)
+        {
+          if (p != make_pair(pID0, bou0) && p != make_pair(pID1, bou1))
+            restPatches.push_back(p);
+        }
+        assert(restPatches.size() == 2);
+        buildCellPatchBouNbr(restPatches[0].first, restPatches[1].first, restPatches[0].second, restPatches[1].second);
+      }
+    } // end loop on arcID
+
+    // we comment out the check below because if there can be a cell with only one B-patch and this B-patch will have no geometric nbrs
+    // so the size of cellPatchBouNbrs[cellID] will be zero, while cellPatches[cellID] is one
+    // assert(cellPatchBouNbrs[cellID].size() == cellPatches[cellID].size());
+    for(const auto & patchPair : cellPatchBouNbrs[cellID])
+    {
+      int patchID = patchPair.first;
+      assert(patchPair.second.size() == patchBouNbrs[patchID].size());
+      for(const auto & p2 : patchPair.second)
+      {
+        int bouID = p2.first;
+        int nbrBouID = p2.second.first;
+        int nbrPatchID = p2.second.second;
+        assert(patchBouNbrs[nbrPatchID].find(nbrBouID) != patchBouNbrs[nbrPatchID].end());
+        assert(patchBouNbrs[patchID].find(bouID) != patchBouNbrs[patchID].end());
+      }
+    }
+  } // end loop on cellID
+}
+
diff --git a/libraries/immersionMesher/immersionMesherGenerateTetMesh.cpp b/libraries/immersionMesher/immersionMesherGenerateTetMesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ce7b288dab8bcb76a088f8c32dc651a68a74a03a
--- /dev/null
+++ b/libraries/immersionMesher/immersionMesherGenerateTetMesh.cpp
@@ -0,0 +1,1176 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "immersionMesher" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "disjointSet.h"
+#include "basicAlgorithms.h"
+#include "containerHelper.h"
+#include "tetKey.h"
+#include "vec4d.h"
+#include "triMeshGeo.h"
+#include "virtualTets.h"
+#include "virtualTets-via-csg.h"
+#include "immersionMesher.h"
+#include "windingNumberTree.h"
+#include "labelOuterTets.h"
+using namespace std;
+
+namespace
+{
+
+struct PointInTet
+{
+  Vec4i tetVtxIndices{-1};
+  Vec4d weights{0.0};
+  int tetID = -1;
+};
+
+PointInTet getPointInTet(const BarycentricCoordinates & bc, int pointID)
+{
+  PointInTet pit;
+  assert(pointID >= 0 && pointID < (int)bc.getNumLocations());
+  pit.tetID = bc.getEmbeddingElement(pointID);
+  assert(pit.tetID >= 0);
+  pit.tetVtxIndices = bc.getEmbeddingVertexIndices(pointID);
+  pit.weights = bc.getEmbeddingWeights(pointID);
+  return pit;
+}
+
+}
+
+void ImmersionMesher::buildCellSurfaceMeshes()
+{
+  const auto & cutMesh = selfCutMesh.cutMesh;
+
+  // first, prepare buffers
+  cellPatchStart.resize(numCells);
+  manifoldCellMesh.resize(numCells);
+  manifoldCellMeshOriVtxIDs.resize(numCells);
+  manifoldCellMeshPatchOri2NewVtxIDMap.resize(numCells);
+  manifoldCellOriTriIDs.resize(numCells);
+  manifoldCellOriTris.resize(numCells);
+  manifoldCellOri2NewTriIDMap.resize(numCells);
+
+  for(int cellID = 1; cellID < numCells; cellID++)
+  {
+    vector<Vec3i> cellTris; // records the triangles of the cell. the normal of each cell triangle points outward (relative to the cell)
+                            // cell triangle vtx are in cutVtxIDs
+    vector<int> patchIDs; // B-patches of the cell
+    map<int, int> patchTriIDStart; // patchID -> cell triID start in cellTris
+    map<int, int> patchTriIDEnd; // patchID -> cell triID end in cellTris
+    map<int, pair<int, bool>> cutTriID2CellTriIDMap; // cutTriID -> <cellTriID, orientation>
+    vector<int> cellTri2cutTriIDs; // cell tri ID -> cutTriID
+
+    cellPatchStart[cellID].clear();
+    for(auto p : cellPatches[cellID])
+    {
+      int patchID = p.first;
+      patchIDs.push_back(patchID);
+      patchTriIDStart[patchID] = (cellTris.size());
+      cellPatchStart[cellID][patchID] = cellTris.size();
+      int IDStart = cellTris.size();
+      if (p.second) // triangle orientation agrees with the cell, a.k.a, triangle normals point outward
+      {
+        cellTris.insert(cellTris.end(), patchTris[patchID].begin(), patchTris[patchID].end());
+      }
+      else
+      {
+        for(Vec3i t : patchTris[patchID])
+          cellTris.emplace_back(t[0], t[2], t[1]); // reverse orientation
+      }
+      for(size_t i = 0; i < patchTris[patchID].size(); i++)
+      {
+        int cutTriID = patchTriIDs[patchID][i];
+        cutTriID2CellTriIDMap[cutTriID] = make_pair(IDStart+i, p.second);
+        cellTri2cutTriIDs.push_back(cutTriID);
+      }
+      patchTriIDEnd[patchID] = (cellTris.size());
+    }
+
+    // to create a manifold surface mesh for the cell, we should merge vertices on arcs shared by B-patches
+
+    // we properly connect triangles by first separate all triangles, assuming each triangle has three unique
+    // vtx, with IDs to be called unqID (unique ID), for cellTriID, its vtx have IDs: 3*cellTriID+1, ... , 3*cellTriID+3
+    // each triangle has three unqID that are not shared by any other triangles
+    // later we will merge them according to the triangle connection
+
+    // we use DisjointSet to merge unqIDs
+    DisjointSet dset(cellTris.size() * 3);
+
+    // we first merge unqIDs if they are from the same cutVtxID on the same patch
+    for(size_t lpID = 0; lpID < patchIDs.size(); lpID++) // local patch ID
+    {
+      int patchID = patchIDs[lpID];
+      // first, let's reconnect triangles that are in the same patch
+      map<int, set<int>> oldVtxID2unqID;
+      for(int cellTriID = patchTriIDStart[patchID]; cellTriID < patchTriIDEnd[patchID]; cellTriID++)
+      {
+        // this cellTriID is the ID of triangles in cellTris
+        for(int i = 0; i < 3; i++)
+        {
+          int unqID = cellTriID * 3 + i;
+          int cutVtxID = cellTris[cellTriID][i]; // old cut vtx ID
+          oldVtxID2unqID[cutVtxID].insert(unqID);
+        }
+      }
+      // merge unqIDs that belong to the same oldVtxID in this patch
+      for(const auto & p : oldVtxID2unqID) { dset.unionRange(p.second); }
+    }
+
+    // now let's merge unqiue IDs based on B-patch geometric neighbor information,
+    // which means, if two B-patches are geometric neighbors along a particular arc on the cell,
+    // then the vertex pairs from both patches on the arc should be merged
+    // cellPatchBouNbrs: cellID -> patchID -> bouID -> <nbr bouID, nbr patchID >
+    for(const auto & p : cellPatchBouNbrs[cellID])
+    {
+      int patchID = p.first;
+      for(const auto & p2 : p.second) // p.second: bouID -> <nbr bouID, nbr patchID >
+      {
+        int bouID = p2.first;
+        int nbrBouID = p2.second.first;
+        int nbrPatchID = p2.second.second;
+        // cout << cellID << " " << patchID << " " << bouID << " " << nbrBouID << " " << nbrPatchID << endl;
+        assert(mapFind(patchBouNbrs[nbrPatchID],nbrBouID));
+        assert(mapFind(patchBouNbrs[patchID],bouID));
+        if (patchID > nbrPatchID) continue; // visit each patchID pair only once
+
+        // we first build a map from cutVtxIDs on the bouID from patchID to unqID
+        map<int, int> bouVtxID2unqID; // cutVtxOnBouID -> unqID
+        for(int cellTriID = patchTriIDStart[patchID]; cellTriID < patchTriIDEnd[patchID]; cellTriID++)
+        {
+          for(int i = 0; i < 3; i++)
+          {
+            int unqID = cellTriID * 3 + i;
+            int cutVtxID = cellTris[cellTriID][i]; // old vtx ID
+            if (bouVertices[bouID].find(cutVtxID) == bouVertices[bouID].end()) continue;
+            // in this loop, bouVtxID2unqID[cutVtxID] may be assigned more than once
+            // which means, one cutVtxID on the bou corresponds to more than one unqID
+            // but this is fine, because in the previous code we already merge those unqIDs on the same patch
+            // so those unqIDs are already in the same set in DisjointSet
+            bouVtxID2unqID[cutVtxID] = unqID;
+          }
+        }
+        assert(bouVtxID2unqID.size() == bouVertices[bouID].size());
+
+        // then we build a map from cutVtxIDs on the nbrBouID from nbrPatchID to unqID
+        map<int, int> nbrBouVtxID2unqID; // cutVtxOnNbrBouID -> unqID
+        for(int cellTriID = patchTriIDStart[nbrPatchID]; cellTriID < patchTriIDEnd[nbrPatchID]; cellTriID++)
+        {
+          for(int i = 0; i < 3; i++)
+          {
+            int unqID = cellTriID * 3 + i;
+            int cutVtxID = cellTris[cellTriID][i]; // old vtx ID
+            if (bouVertices[nbrBouID].find(cutVtxID) == bouVertices[nbrBouID].end()) continue;
+            nbrBouVtxID2unqID[cutVtxID] = unqID; // we only need to record one unqID since they are already in the same disjoint set
+          }
+        }
+        assert(nbrBouVtxID2unqID.size() == bouVertices[nbrBouID].size());
+
+        // finally, we use unqID to merge cutVtxIDs on bouID and nbrBouID
+        UEdgeKey bouEdge(bouID, nbrBouID);
+        for(auto p : cellBouCorrespondence[cellID][bouEdge]) // for each vtx pair on the bou-pair
+        {
+          int bouVtx = p.first, nbrBouVtx = p.second;
+          if (bouEdge[0] == nbrBouID)
+          {
+            swap(bouVtx, nbrBouVtx); // make sure bouVtx is from bouID and nbrBouVtx is from nbrBouID
+          }
+          assert(setFind(bouVertices[bouID], bouVtx));
+          assert(setFind(bouVertices[nbrBouID], nbrBouVtx));
+          assert(mapFind(bouVtxID2unqID,bouVtx));
+          assert(mapFind(nbrBouVtxID2unqID,nbrBouVtx));
+          int unqID = bouVtxID2unqID[bouVtx];
+          int nbrUnqID = nbrBouVtxID2unqID[nbrBouVtx];
+          assert(cellTris[unqID/3][unqID%3] == bouVtx);
+          assert(cellTris[nbrUnqID/3][nbrUnqID%3] == nbrBouVtx);
+          dset.unionSet(unqID, nbrUnqID); // merge unqID and nbrUnqID belonging to the same cutVtxID on the arc
+        }
+      } // end each neighboring patch
+    } // end each patch on the cell
+
+    // now we have finished merging unqIDs
+    auto unqID2mergedID = dset.createOldToNewIDMapping(); // unqID -> mergedID
+    // the mergedID will become the ID for the final manifold cell mesh vertices
+
+    // now we build newPos, mergedID2cutVtxIDs and manifoldCellTris
+    vector<set<int>> mergedID2cutVtxIDs; // mergedID -> original IDs in cutMesh
+    vector<Vec3d> mergedPos;
+    vector<Vec3i> mergedCellTris(cellTris.size());
+    for(size_t cellTriID = 0; cellTriID < cellTris.size(); cellTriID++)
+    {
+      for(int i = 0; i < 3; i++)
+      {
+        int unqID = cellTriID * 3 + i;
+        int mergedID = unqID2mergedID[unqID];
+
+        if (mergedID >= (int)mergedPos.size())
+        {
+          mergedPos.resize(mergedID + 1);
+          mergedID2cutVtxIDs.resize(mergedID + 1);
+        }
+        mergedPos[mergedID] = cutMesh.pos(cellTris[cellTriID][i]);
+        mergedID2cutVtxIDs[mergedID].insert(cellTris[cellTriID][i]);
+        mergedCellTris[cellTriID][i] = mergedID;
+      }
+    }
+
+    // next, build a mapping: patchCutVtx2MergedVtxID: patchID -> cutVtxID -> mergedVtxID
+    map<int, map<int,int>> patchCutVtx2MergedVtxID;
+    for(size_t lpID = 0; lpID < cellPatches[cellID].size(); lpID++) // for each local patch ID
+    {
+      int patchID = patchIDs[lpID];
+      // first, let's reconnect triangles that are in the same patch
+      for(int cellTriID = patchTriIDStart[patchID]; cellTriID < patchTriIDEnd[patchID]; cellTriID++)
+      {
+        for(int i = 0; i < 3; i++)
+        {
+          int mergedID = mergedCellTris[cellTriID][i];
+          int cutVtxID = cellTris[cellTriID][i];
+          patchCutVtx2MergedVtxID[patchID][cutVtxID] = mergedID;
+        }
+      }
+    }
+
+    TriMeshGeo cellMesh(move(mergedPos), move(mergedCellTris));
+
+    if (verbose)
+      cellMesh.save("cell" + to_string(cellID) + ".obj");
+
+    if (areTrianglesManifold(cellMesh.triangles()) == false) // if the cell mesh is not manifold
+    {
+      // Ideally, if the input is not degenerate, all cell meshes should be manifold.
+      unordered_map<OEdgeKey, int> oedgeTri; 
+      bool isEdgeManifold = getOEdgeTriMap(cellMesh.triangles(), oedgeTri); // then check if it is edge-manifold
+      if (isEdgeManifold == false)
+      {
+        cout << "Error: created cell mesh for cellID " << cellID << " is not edge-manifold" << endl;
+        throw 1;
+      }
+
+      // if it is edge-manifold, then we can still fix this!
+      map<int, int> fixedNewVtxID2mergedVtxID;
+      fixNonManifoldVerticesOnEdgeManifoldTriangles(cellMesh, oedgeTri, &fixedNewVtxID2mergedVtxID);
+      // We fix this mesh by duplicating the non-manifold vtx to make entire mesh manifold.
+      // Now we have more vertices, we should modify mergedID2cutVtxIDs as well.
+      mergedID2cutVtxIDs.resize(cellMesh.numVertices());
+      for(auto p : fixedNewVtxID2mergedVtxID)
+      {
+        int newVtxID = p.first, oriMergedVtxID = p.second;
+        mergedID2cutVtxIDs[newVtxID] = mergedID2cutVtxIDs[oriMergedVtxID];
+      }
+      // cellMesh.save("cell" + to_string(cellID) + ".obj");
+      // exit(1);
+    }
+
+    manifoldCellMesh[cellID] = (move(cellMesh));
+    manifoldCellMeshOriVtxIDs[cellID] = (move(mergedID2cutVtxIDs));
+    manifoldCellMeshPatchOri2NewVtxIDMap[cellID] = (move(patchCutVtx2MergedVtxID));
+    manifoldCellOriTriIDs[cellID] = move(cellTri2cutTriIDs);
+    manifoldCellOriTris[cellID] = move(cellTris);
+    manifoldCellOri2NewTriIDMap[cellID] = move(cutTriID2CellTriIDMap);
+  }
+}
+
+// build a tet mesh for each cell, using virtual tets algorithm
+void ImmersionMesher::buildCellTetMeshes()
+{
+  const auto & cutMesh = selfCutMesh.cutMesh;
+
+  ProfilerSection cellSurfaceMeshSec(&profiler, "buildCellSurfaceMesh");
+  ProfilerSection entireCuttingSec(&profiler, "entireCutting");
+  ProfilerSection searchTetsSec(&profiler, "searchTetsInEachCell");
+  ProfilerSection virtualTetsSec(&profiler, "prepareVirtualTets");
+  ProfilerExtraSection tetTriInterExtraSec(&profiler, "tetTriIntersecting");
+
+  // first, let's build a manifold surface mesh for each cell, which will be used
+  // as input to the virtual tets algorithm
+  cout << "Building manifold cell surface mesh..." << endl;
+
+  cellSurfaceMeshSec.start();
+  buildCellSurfaceMeshes();
+  cellSurfaceMeshSec.stop();
+
+  cellTetMeshInterps.resize(numCells);
+  cellTetMeshTri2TetIDs.resize(numCells);
+  cellTetMeshes.resize(numCells);
+  cellTetMeshOriTetIDs.resize(numCells);
+
+  // build tet vs tri cutting
+  // cout << "cutting on the entire mesh" << endl;
+  assert(sizei(selfCutMesh.cutPosExact) == cutMesh.numVertices());
+  entireCuttingSec.start();
+  TetTriMeshCutting entireCutting(inputTetMeshGeo, cutMesh);
+  cout << "Computing tet-triangle intersection..." << endl;
+
+  // compute the mapping: tetID -> triangleIDs intersect with the tet
+  tetTriInterExtraSec.start();
+  entireCutting.computeIntersectingTriTets();
+  tetTriInterExtraSec.stop();
+
+  // note: that in other places of the code, we use "cut trinagle or cutTri(ID)" to refer to the triangles in the cutMesh
+  // from the libigl::selfIntersection code, where cells and patches are created
+  // but here in this function we also do some cutting (tet cut tri), so to avoid confusion,
+  // from now on in this function we still use cut triangle to refer to the original cut tri,
+  // and we use ecTri (entire cutting tri) to refer to the triangles after the cut from tet tets, and
+  // ecVtx to refer to the vertices after the tet cut
+
+  // cutTriID -> the ecVtxIDs inside cutTriID due to entireCutting
+  vector<vector<int>> cutTri2innerECVtxIDs(cutMesh.numTriangles());
+  // UEdgeKey in cutVtxIDs -> tet ecVtxIDs on the interior of this edge
+  map<UEdgeKey, vector<int>> cutEdge2ECVtxIDs;
+  vector<Vec3ER> tetPosER;
+  if (useCSGInVT)  // The CSG method does not need cut, but it needs exact tet positions
+  {
+    for(int vtxID = 0; vtxID < inputTetMeshGeo.numVertices(); vtxID++)
+    {
+      Vec3d p = inputTetMeshGeo.pos(vtxID);
+      Vec3ER pER(p[0], p[1], p[2]);
+      tetPosER.emplace_back(move(pER));
+    }
+  }
+  else // we perform Sutherland-Hodgman tet-triangle clipping algorithm
+  {
+    cout << "Performing Sutherland-Hodgman tet-triangle clipping algorithm..." << endl;
+    entireCutting.computeCutTriangles(&selfCutMesh.cutPosExact);
+    cout << "Finished clipping." << endl;
+
+    // we build cutTri2innerECVtxIDs and cutEdge2ECVtxIDs
+
+    // for each new vtx generated by the tet cut:
+    for(size_t ecVtxID = cutMesh.numVertices(); ecVtxID < entireCutting.cutTriPositions().size(); ecVtxID++) // ecVtx: entire cutting vtx
+    {
+      const auto & f = entireCutting.cutVertexFeatures()[ecVtxID]; // get the feature the vtx lies on
+
+      if (f.isInsideTri()) // ecVtxID is on the interior of a cut triangle
+      {
+        assert(mapFind(cutMeshTriKeyIDs, f.triFeature));
+        cutTri2innerECVtxIDs[cutMeshTriKeyIDs[f.triFeature]].push_back(ecVtxID);
+      }
+      else if (f.isTriEdge())
+      {
+        cutEdge2ECVtxIDs[f.getTriEdge()].push_back(ecVtxID);
+      }
+    }
+  }
+  entireCuttingSec.stop();
+
+  // for each cell, let's build the tet mesh serving as the input to the virtual tets algorithm
+  // we call this tet mesh, bvTetMesh (before virtual tet mesh)
+  searchTetsSec.start();
+
+  // remove the outer tets (tets embedding empty space outside the cutTriMesh)
+  function<bool(int tetID)> isTetBou1 = [&](int tetID) ->bool { return entireCutting.getTrianglesIntersectingTet(tetID).size() > 0; };
+  function<bool(int tetID)> isTetBou2 = [&](int tetID) ->bool { return entireCutting.getCutTriGroup(tetID).tri.size() > 0; };
+
+  WindingNumberTree wnTree; // we use winding number to check inside/outside of the cutMesh
+  wnTree.build(cutMesh);
+  auto isTetOuter = [&](int tetID)
+  {
+    double wn = wnTree.windingNumber(cutMesh, inputTetMeshGeo.ref().computeTetCenter(tetID));
+    return wn < 0.5;
+  };
+
+  TetNeighbor tetNeighbor(inputTetMeshGeo.tets());
+  // inputTetOutside: inputTetID -> whether tet is outside of cutMesh
+  auto inputTetOutside = labelOuterTets(inputTetMeshGeo, tetNeighbor, (useCSGInVT ? isTetBou1 : isTetBou2), isTetOuter);
+
+  // prepare buffers
+  vector<TetMeshGeo> cellBVTetMesh(numCells); // cellID -> bvTetMesh for the virtual tets algorithm on this cell
+  vector<vector<int>> cellBVInputTetIDs(numCells);  // input tet IDs for each cell's bvTetMesh: cellID -> bvTetID -> inputTetIDs
+  vector<vector<int>> cellBVTetVtxID2InputTetVtxID(numCells);
+  vector<map<int,int>> cellInputTetVtxID2BVTetVtxID(numCells);
+
+  // now, let's find all the tets that belong to each bvTetMesh
+
+  int numTets = inputTetMeshGeo.numTets();
+
+  vector<vector<int>> cutTri2cutTets(cutMesh.numTriangles()); // cutTriID -> tetIDs that intersect the triangle
+  vector<set<int>> tetCells(numTets); // tetID -> cells need this tetID for BVTetMesh
+
+  const int TET_OUTER = -100; // tet outside cutMesh
+  const int TET_CUR = INT_MAX;
+  const int TET_UNINIT = -1;
+  const int TET_INTER_TRI = 0; // tet intersecting cutTris
+  vector<int> tetLabel(numTets, TET_UNINIT);
+
+  // first, build cutTri2cutTets and set TET_OUTER and TET_INTET_TRI to tetLabel
+  for(int tetID = 0; tetID < numTets; tetID++)
+  {
+    vector<int> cutTriIDs;
+    if (useCSGInVT)
+    {
+      cutTriIDs = entireCutting.getTrianglesIntersectingTet(tetID);
+    }
+    else
+    {
+      cutTriIDs = entireCutting.getCutTriGroup(tetID).oriID;
+      sortAndDeduplicate(cutTriIDs);
+    }
+
+    for(int cutTriID : cutTriIDs) { cutTri2cutTets[cutTriID].push_back(tetID); }
+    if (inputTetOutside[tetID]) { tetLabel[tetID] = TET_OUTER; }
+    else if (cutTriIDs.size() > 0) { tetLabel[tetID] = TET_INTER_TRI; }
+  }
+
+  // then, put tets intersecting the surface cell meshes into cellBVInputTetIDs, and tetCells
+  for(int cellID = 1; cellID < numCells; cellID++)
+  {
+    for(int cutTriID : manifoldCellOriTriIDs[cellID])
+    {
+      for(int tetID : cutTri2cutTets[cutTriID])
+      {
+        cellBVInputTetIDs[cellID].push_back(tetID);
+        tetCells[tetID].insert(cellID);
+      }
+    }
+  }
+
+  vector<WindingNumberTree> cellWinTrees(numCells); // build winding number tree for each cell mesh
+  vector<bool> cellWinTreesBuilt(numCells, false);
+
+  // we then go to every tet not labeld yet (which are not outer tets and does not intersect cutTris),
+  // determing which cell they belong to
+  // for those tets, each of them belongs to one and only one cell
+  for(int tetID = 0; tetID < numTets; tetID++)
+  {
+    if (tetLabel[tetID] != TET_UNINIT) continue; // skip if has a label already
+    // we don't query one tet to all cell meshes every time, which is too costly
+    // instead, we use a flooding method, a basic implementaion is: once a seed tet is checked against all cell meshes
+    // and found which cell it belongs to, we flood-fill its neighboring non-labeled tets to belong to that cell
+    // then, we have a better method to reduce inside/outside queries: we don't need to check a seed tet against
+    // all cell meshes, we only check those cell meshes whose triangles intersect a tet neighboring the group of
+    // tets found by this flood-fill
+    int seed = tetID;
+    vector<int> ffCand = {seed}; // ffCand: flood-fill candidates
+    tetLabel[seed] = TET_CUR;
+    int ffBegin = 0, ffEnd = 1;
+    int targetCellID = -1; // which cell this seed belongs to
+    set<int> candCellIDs;
+    while(ffBegin < ffEnd)
+    {
+      for(int i = ffBegin; i < ffEnd; i++)
+      {
+        int ffTetID = ffCand[i];
+        for(int ffnbrTetID: tetNeighbor.getTetNeighbors(ffTetID))
+        {
+          if (ffnbrTetID < 0) continue;
+
+          if(targetCellID < 0 && tetLabel[ffnbrTetID] == TET_INTER_TRI)
+          {
+            assert(tetCells[ffnbrTetID].size() > 0);
+            const set<int> & rest = tetCells[ffnbrTetID]; // for each cell whose triangles intersect this nbrTet
+            if (rest.size() == 1) // this can only be the cell this seed tet belongs to
+            {
+              // we found this cell
+              int cellID = *rest.begin();
+              targetCellID = cellID;
+            }
+            else
+            {
+              candCellIDs.insert(rest.begin(), rest.end());
+            }
+          }
+
+          if (tetLabel[ffnbrTetID] != TET_UNINIT) { continue; }
+          tetLabel[ffnbrTetID] = TET_CUR;
+          ffCand.push_back(ffnbrTetID);
+        }
+      }
+      ffBegin = ffEnd;
+      ffEnd = ffCand.size();
+    }
+    // end of floodFill
+    // now ffCand stores all the tets found by this flood-fill
+
+    if(targetCellID < 0) // let's find out which cell this seed tet belongs to
+    {
+      assert(candCellIDs.size() > 0);
+      for(int cellID : candCellIDs)
+      {
+        if (cellWinTreesBuilt[cellID] == false)
+        {
+          cellWinTreesBuilt[cellID] = true;
+          cellWinTrees[cellID].build(manifoldCellMesh[cellID]);
+        }
+        double wn = cellWinTrees[cellID].windingNumber(manifoldCellMesh[cellID], inputTetMeshGeo.ref().computeTetCenter(seed));
+        if (wn > 0.5) // seed is inside, found the cell!
+        {
+          targetCellID = cellID;
+          break;
+        }
+      }
+      if (targetCellID < 0)
+      {
+        cout << "error on tet " << seed << ", vtx: " << inputTetMeshGeo.tet(seed) << endl;
+        cout << "cand cells are " << streamRange(candCellIDs) << endl;
+        for(int cellID : candCellIDs)
+        {
+          double wn = cellWinTrees[cellID].windingNumber(manifoldCellMesh[cellID], inputTetMeshGeo.ref().computeTetCenter(seed));
+          cout << "cell " << cellID << " wn is " << wn << endl;
+        }
+      }
+    }
+    assert(targetCellID >= 0);
+
+    for(int fftetID : ffCand)
+    {
+      // since cellID == 0 is the outer cell (empty space) that we will never used to run virtual tets algorithm
+      // we don't need to offset targetCellID to assign to tetLabel
+      tetLabel[fftetID] = targetCellID;
+      cellBVInputTetIDs[targetCellID].push_back(fftetID);
+    }
+  } // end for tetID, end of building tetLabel and cellBVInputTetIDs
+
+  // create cellBVTetMesh
+  for(int cellID = 1; cellID < numCells; cellID++)
+  {
+    sortAndDeduplicate(cellBVInputTetIDs[cellID]);
+
+    cellBVTetMesh[cellID] = getSubTetMesh(inputTetMeshGeo, cellBVInputTetIDs[cellID], &cellBVTetVtxID2InputTetVtxID[cellID],
+        &cellInputTetVtxID2BVTetVtxID[cellID]);
+
+    if (verbose)
+    {
+      cout << "tet at cell " << cellID << " #tet: " << cellBVInputTetIDs[cellID].size() << endl;
+      string filename = "cellBVTetMesh" + to_string(cellID) + ".veg";
+      cellBVTetMesh[cellID].save(filename);
+    }
+  }
+
+  searchTetsSec.stop();
+
+  // now let's build the tet-cut-tri data for virtual tets algorithm
+  for(int cellID = 1; cellID < numCells; cellID++)
+  {
+    virtualTetsSec.start();
+    const TriMeshGeo & cellMesh = manifoldCellMesh[cellID];
+    const vector<set<int>> & cellMeshVtxID2CutVtxIDs = manifoldCellMeshOriVtxIDs[cellID];
+    const auto & cellTri2cutTriIDs = manifoldCellOriTriIDs[cellID];
+    const auto & cellCutTris = manifoldCellOriTris[cellID];
+    const auto & cutTri2CellTriIDMap = manifoldCellOri2NewTriIDMap[cellID];
+    cout << "Using virtual tets method to create tet mesh for cell mesh ID " << cellID << "." << endl;
+    vector<Vec3ER> triPosER; // build exact positions for the manifold cell mesh
+    for(int i = 0; i < cellMesh.numVertices(); i++)
+    {
+      triPosER.emplace_back(selfCutMesh.cutPosExact[*cellMeshVtxID2CutVtxIDs[i].begin()]);
+    }
+
+    // the vars below are the output of virtual tets algorithm
+    TetMeshGeo cellTetMesh;                               // output virtualized cell tet mesh
+    BarycentricCoordinates bc;                            // barycentric coord for embedding cellMesh into cellTetMesh
+    vector<vector<int>> tetTris;                          // virtualized cellTetMesh tetID -> triangleIDs that this tet embeds
+    vector<vector<int>> triTets(cellMesh.numTriangles()); // cellTriID -> virtualized tetIDs that intersect this tri
+    vector<int> tetMeshOriTetID;                          // cellTetID -> bvTetID
+
+    if (useCSGInVT)
+    {
+      TetTriIntersectingData data; // build this data:
+
+      for(int bvTetVtxID = 0; bvTetVtxID < cellBVTetMesh[cellID].numVertices(); bvTetVtxID++)
+        data.tetPosER.push_back(tetPosER[cellBVTetVtxID2InputTetVtxID[cellID][bvTetVtxID]]);
+
+      // for each tet in the BVTetMesh for this cell
+      for(int bvTetID = 0; bvTetID < cellBVTetMesh[cellID].numTets(); bvTetID++)
+      {
+        vector<int> group; // store cellTriIDs intersecting this tet
+        // we have entireCutting, which stores the intersection between inputTetIDs and cutTriIDs
+        // we need to transfer between bvTetIDs and inputTetIDs, and between cutTriIDs and cellTriIDs
+        int inputTetID = cellBVInputTetIDs[cellID][bvTetID];
+        const auto & cutTriGroup = entireCutting.getTrianglesIntersectingTet(inputTetID);
+        for(size_t j = 0; j < cutTriGroup.size(); j++)
+        {
+          int cutTriID = cutTriGroup[j];
+          if (mapNotFind(cutTri2CellTriIDMap, cutTriID)) continue;
+          auto it = cutTri2CellTriIDMap.find(cutTriID);
+          int cellTriID = it->second.first;
+          group.push_back(cellTriID);
+        }
+        data.triInTet.emplace_back(move(group));
+      }
+
+      virtualTetsSec.stop();
+      cellTetMesh = createVirtualTetsMeshViaCSG(cellBVTetMesh[cellID], cellMesh, triPosER, data, &bc, nullptr, &tetMeshOriTetID, &tetTris,
+          verbose, &profiler);
+    }
+    else
+    {
+      TetTriCuttingData cellCutting;
+
+      // The code below in this context has very complicated logic.
+      // So we will go through the meshes and indices we used in this algorithm to make sure people don't get confused
+      // when reading the following lengthy code:
+      //
+      // the input to the WHOLE class is:
+      //   inputTriMesh and inputTetMesh, they have indices defined as: inputTriVtxID, inputTriID, inputTetVtxID, inputTetID
+      // after we call libigl::remeshSelfIntersection, inputTriMesh is cut by itself to become: cutMesh, which has indices: cutVtxID, cutTriID
+      // note that all the inputTriVtxIDs are inherited by cutMesh, so the first #inputTriVtxID cutVtx are exactly the inputTriVtx with same IDs
+      // the new cutVtx created are on the self-intersection cuts. Since two triangles intersect to form a cut,
+      // we can create two groups of cutVtx on the two triangles, respectively, to represent this cut.
+      // But since they intersect, the two groups also co-position in space.
+      // we have another index: st(Vtx)ID, stitched vtx (named from libigl interface) to represent the unique spatial location
+      // of cutVtx. Therefore, an "arc" is always expressed in stVtxIDs, where the "bou"s are represented in cutVtxIDs
+      // so that we know which two bous lie on the same arc.
+      //
+      // We also created (manifold) cell meshes: cellMeshes, whose IDs are: cellVtxID and cellTriID
+      // cellVtx are subset of cutVtx and cellTris are subset of cutTris and we have mapping between the IDs
+      // note that more than one cutVtx can map to the same cellVtx, because cellMesh is created by merging bous on its B-patches
+      // so those cutVtx on the shared bous may get merged and mapped to the same cellVtx
+      // Things get complicated on a self-touching cell, where the cell "touches" itself at a particular arc (or more arcs).
+      // At that arc, originally there are two bous which means two groups of cutVtxIDs. After building the manifold cell mesh,
+      // there are also two groups of cellVtxIDs on that arc, but they are not the copy of the old groups.
+      // An illustration is as follows:
+      //            b3                             c3
+      //            |                               |
+      //  b0------b1-------b2            c0------c1 c4-------c2
+      //            b4                           |
+      //            |                            |
+      //            b5                           c5
+      // on the self-touching arc on the cutMesh (left), b1 and b4 are on the same location, mapping to the same stVtxID.
+      // on the cell mesh (right), we have two copies of the vtx with same stVtxIDs, c1 and c4, but they connect to
+      // neighboring vtx in a different pattern than on the cutMesh.
+      // luckly the cellTri mapping is simpler, each cellTri maps to one and only one cutTri
+      // however, note that some cellTris have opposite orientations as the corresponding cellTris, because we want cellMesh to
+      // be consistently oriented. That's why we also need to store a bool variable in manifoldCellOri2NewTriIDMap.
+
+      // then, we do a tet vs tri cut between inputTetMesh and cutMesh, the result is: ecMesh (entire(Tet)CuttingMesh)
+      // the IDs of ecMesh are: ecVtxID and ecTriID
+      // note that all the cutVtxIDs are inherited by ecMesh, so the first #cutVtxID ecVtx are exactly the cutVtx with same IDs
+
+      // finally, we will create a ccMesh (cell(Tet)CuttingMesh), the IDs of which are: ccVtxID and ccTriID
+      // and a bvTetMesh (beforeVirtual(Tets)TetMesh), whose IDs are bvVtxID and bvTetID
+      // bvTetMesh is just a subset of inputTetMesh, serving as the input for the virtual tet algorithm
+      // ccMesh is the result of the tet vs tri cut between bvTetMesh and cellMesh
+      // note that all the cellVtxIDs are inherited by ccMesh, so the first #cellVtxID ccVtx are exactly the cellVtx with same IDs
+
+      // well, that's the thing...
+      // I know an easier implementation is just to run the tet vs tri cutting algorithm for each (cellMesh, bvTetMesh) pair,
+      // but it requires more exact arithmetic operations because a patch shared by two cells will be cut twice
+      // to avoid this redundancy, we come at this lengthy and complicated algorithm, which firt do tet vs tri cut on inputTetMesh and cutMesh,
+      // then use this cuttind data to construct cellCutting, the data for cellMesh vs bvTetMesh.
+
+      // OK, let's look into what cellCutting is made of:
+      // vector<Vec3d> cutVtxPos;              // ccVtx pos
+      // vector<Vec3ER> cutVtxPosER;           // exact ccVtx pos
+      // vector<TetTriCutFeature> features;    // cut feature for each ccVtx, made by triFeature in cellTriIDs and tetFeature in bvTetIDs
+      // vector<Vec3ER> tetPosER;              // exact bvTetVtx pos
+      // vector<CutTriGroup> cutTriGroups;     // for each bvTet, stores the ccTris indexed in ccVtxIDs and the cellTriIDs those ccTris are from
+
+      // the following code will build the above data
+
+      // add cell mesh vtx positions and features to cellCutting
+      // we first add those vertices of the cell surface mesh
+      for(int i = 0; i < cellMesh.numVertices(); i++)
+      {
+        int cutVtxID = *cellMeshVtxID2CutVtxIDs[i].begin();
+        cellCutting.cutVtxPos.push_back(cutMesh.pos(cutVtxID));
+        cellCutting.cutVtxPosER.push_back(selfCutMesh.cutPosExact[cutVtxID]);
+        const auto & f = entireCutting.cutVertexFeatures()[cutVtxID]; // get feature
+        // the feature is for the vtx that belongs to the vtx group of the ecMesh which is inherited from cutMesh
+        assert(f.isTriVertex()); // f.triFeature must be (-1,-1, cutVtxID), we should convert it into cellVtxIDs
+        UTriKey newTriKey(-1,-1, i);
+        // we should also convert from the inputTetIDs used in f.tetFeature into bvTetIDs
+        // but we will do this later, so for now let's assign cellCutting.features in this way:
+        cellCutting.features.emplace_back(newTriKey, f.tetFeature);
+      }
+
+      // next, let's add those vertices generated by cutting of the tets to cellMesh
+      // those vertices are found in entireCutting, we should convert their indices before adding them to cellCutting
+      map<TetTriCutFeature, int> edgeFeatureMap; // <triKey in cellVtxIDs, tetKey in cutVtxIDs> -> ecVtxID
+      // This edgeFeatureMap is used because we are adding those vertices generated by tet cuts on ecMesh to cellCutting
+      // and we are searching for those vertices by looping over each cell triangle.
+      // Since some cell edges are shared by two cell triangles, to avoid adding those vertices on the cell edge interior
+      // multiple times, we have to use this edgeFeatureMap
+
+      vector<map<int,int>> cellTriEC2CC(cellMesh.numTriangles()); // cellTriID -> ecVtxID -> ccVtxID, ccVtxID: cell cutting vtx
+      // We also build this cellTriEC2CC for creating a local map: ecVtxID -> ccVtxID on each cellTri
+      for(int cellTriID = 0; cellTriID < cellMesh.numTriangles(); cellTriID++) // for each cellMesh triangles
+      {
+        const auto & cutTri = cellCutTris[cellTriID];
+        for(int i = 0; i < 3; i++)
+        {
+          int cellVtxID = cellMesh.triVtxID(cellTriID, i);
+          int cutVtxID = cutTri[i];
+          cellTriEC2CC[cellTriID][cutVtxID] = cellVtxID;
+        }
+
+        // On ecMesh, some vertices are inherited from cutMesh. The part of them which belongs to this ceMesh is added to entireCutting
+        // in the last loop. The other part is the new vertices generated by the tet cuts.
+        // In the following loop, we will add those new vertices that are on the interior of a cutTriangle in entireCutting
+        int cutTriID = cellTri2cutTriIDs[cellTriID];
+        for(int ecVtxID : cutTri2innerECVtxIDs[cutTriID]) // the ecVtxIDs on the interior of cutTriID
+        {
+          int ccVtxID = cellCutting.cutVtxPos.size(); // create a new ccVtxID to represent this ecVtxID
+          cellCutting.cutVtxPos.push_back(entireCutting.cutTriPositions()[ecVtxID]);
+          cellCutting.cutVtxPosER.push_back(entireCutting.cutTriPositionsER()[ecVtxID]);
+          const auto & f = entireCutting.cutVertexFeatures()[ecVtxID]; // get feature
+          // we will convert the triFeature in f from cutVtxID to cellVtxID
+          // again we leave the conversion of tetFeature for now
+          cellCutting.features.emplace_back(cellMesh.tri(cellTriID), f.tetFeature);
+          assert(mapNotFind(cellTriEC2CC[cellTriID], ecVtxID));
+          cellTriEC2CC[cellTriID][ecVtxID] = ccVtxID;
+        }
+
+        // In the following loop, we will add those new vertices that are on the edge interior of a cutTriangle in entireCutting
+        for(int i = 0; i < 3; i++)
+        {
+          int cutVtx0 = cutTri[i], cutVtx1 = cutTri[(i+1)%3]; // give the edge in cutVtxIDs
+          UEdgeKey cutEdge(cutVtx0, cutVtx1);
+          if (mapNotFind(cutEdge2ECVtxIDs, cutEdge)) continue;
+          for(int ecVtxID : cutEdge2ECVtxIDs[cutEdge]) // the ecVtxIDs on the edge interior
+          {
+            const auto & f = entireCutting.cutVertexFeatures()[ecVtxID]; // get feature
+            assert(f.isTriEdge());
+            UTriKey newTriKey(-1, cellMesh.triVtxID(cellTriID, i), cellMesh.triVtxID(cellTriID, (i+1)%3)); // convert from cutVtxID to cellVtxID
+            TetTriCutFeature nf(newTriKey, f.tetFeature);
+            // again leave tetFeature for now
+            if (mapFind(edgeFeatureMap, nf)) // if the feature has been visited before, then we won't create a new ccVtx for it
+            {
+              // note that here is the usage of edgeFeatureMap
+              // we don't use ecVtx as the key for this mapping because, on the cell mesh,
+              // more than one cutVtx will map to one cellVtx (due to merging of bous among cel B-patches),
+              // so, more than one ecVtx will map to one ccVtx, therefore, ecVtx shouldn't be the key
+              cellTriEC2CC[cellTriID][ecVtxID] = edgeFeatureMap[nf];
+              continue;
+            }
+
+            // otherwise, this is a new ccVtx
+            int ccVtxID = cellCutting.cutVtxPos.size();
+            cellCutting.cutVtxPos.push_back(entireCutting.cutTriPositions()[ecVtxID]);
+            cellCutting.cutVtxPosER.push_back(entireCutting.cutTriPositionsER()[ecVtxID]);
+            cellCutting.features.emplace_back(nf);
+            edgeFeatureMap[nf] = ccVtxID;
+            assert(mapNotFind(cellTriEC2CC[cellTriID], ecVtxID));
+            cellTriEC2CC[cellTriID][ecVtxID] = ccVtxID;
+          }
+        }
+      }
+
+      // now we finished building cutVtxPos, cutVtxPosER and features (minus tetFeature indices)
+      // we will convert inputTetIDs to bvTetIDs in this loop
+      for(auto & f : cellCutting.features)
+      {
+        int t[4];
+        for(int i = 0; i < 4; i++)
+        {
+          if (f.tetFeature[i] < 0) t[i] = -1;
+          else t[i] = cellInputTetVtxID2BVTetVtxID[cellID][f.tetFeature[i]];
+        }
+        f = TetTriCutFeature(f.triFeature, UTetKey(t));
+      }
+
+      // next, build cellCutting.cutTriGroups
+      // we will convert CutTriGrop.oriID from cutTriID to cellTriID, and
+      // convert CutTriGroup.tri from ecVtxID to ccVtxID
+      for(int bvTetID = 0; bvTetID < cellBVTetMesh[cellID].numTets(); bvTetID++)
+      {
+        int inputTetID = cellBVInputTetIDs[cellID][bvTetID];
+        TetTriMeshCutting::CutTriGroup group; // store the group in cellCutting
+        const auto & oldGroup = entireCutting.getCutTriGroup(inputTetID);
+        for(const auto & p : oldGroup.cutTriIDsOnFace)
+        {
+          const UTriKey & inputTriKey = p.first;
+          int t[3];
+          for(int i = 0; i < 3; i++)
+            t[i] = cellInputTetVtxID2BVTetVtxID[cellID][inputTriKey[i]]; // convert from inputTetVtxID to BVTetVtxID
+          // p.second stores the groupTriIDs of the triangles exactly on the tet face (the UTriKey)
+          // this groupTriIDs are for indices of oldGroup.tri, so it is the same for group
+          group.cutTriIDsOnFace.emplace(UTriKey(t), p.second);
+        }
+        for(size_t j = 0; j < oldGroup.tri.size(); j++)
+        {
+          int cutTriID = oldGroup.oriID[j];
+          // one input tet may embed cutTris from more than one cell
+          // so we skip if this cutTriID is not in cellMesh
+          if (mapNotFind(cutTri2CellTriIDMap, cutTriID)) continue;
+          auto it = cutTri2CellTriIDMap.find(cutTriID);
+          int cellTriID = it->second.first;
+          group.oriID.push_back(cellTriID);
+          Vec3i ccTri;
+          for(int k = 0; k < 3; k++)
+          {
+            // here we use cellTriEC2CC to find the mapping: ecVtxID -> ccVtxID
+            // the reason why we build cellTriEC2CC as a per-cellTri mapping, instead of a cell-level ecVtxID->ccVtxID mapping,
+            // is because that on a self-touching cell, along that self-touching arc, the mapping between
+            // ecVtxID and ccVtxID is very complicated (see illustration above before building cellCutting).
+            // So we use a per-cellTri mapping to be safe.
+            int ecVtxID = oldGroup.tri[j][k];
+            assert(mapFind(cellTriEC2CC[cellTriID], ecVtxID));
+            int ccVtxID = cellTriEC2CC[cellTriID][ecVtxID];
+            ccTri[k] = ccVtxID;
+          }
+          if (it->second.second == false) // this cell triangle has opposite orientation from the cutTri
+            swap(ccTri[1], ccTri[2]);
+          group.tri.push_back(ccTri);
+        }
+        cellCutting.cutTriGroups.emplace_back(move(group));
+      }
+
+      // finally, we build tetPosER
+      for(int bvTetVtxID = 0; bvTetVtxID < cellBVTetMesh[cellID].numVertices(); bvTetVtxID++)
+        cellCutting.tetPosER.push_back(entireCutting.tetPositionsER()[cellBVTetVtxID2InputTetVtxID[cellID][bvTetVtxID]]);
+
+      virtualTetsSec.stop();
+      // calling virtual tets algorithm
+      cellTetMesh = createVirtualTetsMesh(cellBVTetMesh[cellID], cellMesh, cellCutting, &bc, nullptr,
+          &tetMeshOriTetID, &tetTris, false, verbose, &profiler);
+    }
+
+    assert(tetTris.size() == tetMeshOriTetID.size());
+    for(size_t tetID = 0; tetID < tetTris.size(); tetID++)
+    {
+      for(int triID : tetTris[tetID]) { triTets[triID].push_back(tetID); }
+    }
+
+    if(verbose)
+    {
+      string filename = "cellTetMesh" + to_string(cellID) + ".veg";
+      cellTetMesh.save(filename.c_str());
+    }
+
+    cellTetMeshInterps[cellID] = (move(bc));
+    cellTetMeshTri2TetIDs[cellID] = move(triTets);
+    cellTetMeshes[cellID] = move(cellTetMesh);
+    for(int & oldID : tetMeshOriTetID)          // here we use celTetID to refer to virtualized tet ID
+      oldID = cellBVInputTetIDs[cellID][oldID]; // convert the mapping from  cellTetID -> bvTetID to cellTetID -> inputTetID
+    cellTetMeshOriTetIDs[cellID] = (move(tetMeshOriTetID));
+  } // end for cellID
+}
+
+void ImmersionMesher::produceFinalTetMesh(vector<ImmersionGraphNode> & cellNodes, int graphID,
+    TetMeshGeo & outputTetMesh, BarycentricCoordinates & outputInterpWeight,
+    std::vector<TriMeshGeo> * allCellMesh, std::vector<BarycentricCoordinates> * allCellInerpWeight)
+{
+  // first, creates a tet mesh without stitching cell tetMeshes
+  // This means, this tet mesh is only the union of the tet meshes from each nodes, which don't connect any one
+  // we call this tet mesh: allTetMesh, with ID: allTetVtxID and allTetID
+  vector<Vec4i> allTets;            // allTetID -> tets in allTetVtxIDs
+  vector<Vec3d> allTetVertices;     // allTetVtxID -> positions
+  vector<int> nodeTetStart;         // nodeID -> allTetID start in allTets
+  vector<int> allTetID2InputTetID;  // allTetID -> inputTetID
+
+  // in this loop, we build the above vars
+  for(size_t nodeID = 0; nodeID < cellNodes.size(); nodeID++)
+  {
+    const ImmersionGraphNode & node = cellNodes[nodeID];
+    int cellID = node.getCellID();
+    nodeTetStart.push_back(allTets.size());
+
+    const auto & cellTetMesh = cellTetMeshes[cellID]; // virtualized tet mesh for cellID
+    int tetVtxIDStart = allTetVertices.size();
+
+    for(int i = 0; i < cellTetMesh.numVertices(); i++)
+    {
+      allTetVertices.push_back(cellTetMesh.pos(i));
+    }
+    assert(cellTetMeshOriTetIDs[cellID].size() == (size_t)cellTetMesh.numTets());
+    for(int i = 0; i < cellTetMesh.numTets(); i++)
+    {
+      Vec4i vec;
+      for(int j = 0; j < 4; j++)
+      {
+        vec[j] = cellTetMesh.tetVtxID(i, j) + tetVtxIDStart;
+      }
+      allTets.push_back(vec);
+      allTetID2InputTetID.push_back(cellTetMeshOriTetIDs[cellID][i]);
+    }
+  }
+
+  // now let's do merging!
+  // we build a disjoint set structure on allTet vertices, and another one on allTets
+  DisjointSet allTetVtxDSet(allTetVertices.size()), allTetsDSet(allTets.size());
+
+  // now merge tets that share the patches connecting two copies
+  for(int nodeID = 0; nodeID < sizei(cellNodes); nodeID++)
+  {
+    int tetStart = nodeTetStart[nodeID];
+    ImmersionGraphNode & node = cellNodes[nodeID];
+    int cellID = node.getCellID();
+    for(auto nbr : node.getNbrs())
+    {
+      int patchID = nbr.first;
+      int nbrNodeID = nbr.second;
+      if (nbrNodeID < 0) continue;
+      if (nodeID > nbrNodeID) continue; // we visit unordered each node pair only once
+
+      ImmersionGraphNode & nbrNode = cellNodes[nbrNodeID];
+      int nbrTetStart = nodeTetStart[nbrNodeID];
+      int nbrCellID = nbrNode.getCellID();
+      assert(mapFind(cellPatches[cellID],patchID));
+      assert(mapFind(cellPatches[nbrCellID], patchID));
+      //      map<int, int> patchIDVtx2CellTetID;
+      for(size_t i = 0; i < patchTris[patchID].size(); i++) // for each triangle on the patchID shared by nodeID and nbrNodeID
+      {
+        int cellMeshTriID = cellPatchStart[cellID][patchID] + i; // from cutTri to cellTriID
+        auto cellTetMeshIDs = cellTetMeshTri2TetIDs[cellID][cellMeshTriID]; // all the cellTetIDs intersecting this triangle
+        int nbrCellMeshTriID = cellPatchStart[nbrCellID][patchID] + i;
+        auto nbrCellTetMeshIDs = cellTetMeshTri2TetIDs[nbrCellID][nbrCellMeshTriID];
+
+        // we don't assume cellTetMeshIDs and nbrCellTetMeshIDs having the same size because
+        // on degenerate tet vs tri cases where one tri only touches at a tet, this tet may be counted only once
+        // in cellTetMeshIDs and nbrCellTetMeshIDs
+        // assert(cellTetMeshIDs.size() == nbrCellTetMeshIDs.size());
+        map<int, int> localIDPair; // inputTetID -> cellTetID
+        for(int cellTetID : cellTetMeshIDs)
+        {
+          int inputTetID = cellTetMeshOriTetIDs[cellID][cellTetID];
+          localIDPair[inputTetID] = cellTetID;
+        }
+        for(int nbrCellTetID : nbrCellTetMeshIDs)
+        {
+          int nbrInputTetID = cellTetMeshOriTetIDs[nbrCellID][nbrCellTetID];
+          if (mapNotFind(localIDPair, nbrInputTetID)) continue;
+//          assert(localIDPair.find(oldID) != localIDPair.end());
+          int allTetID = localIDPair[nbrInputTetID] + tetStart; // the tetID in allTetMesh
+          int nbrAllTetID = nbrCellTetID + nbrTetStart;    // the nbr tetID in allTetMesh
+
+          allTetsDSet.unionSet(allTetID, nbrAllTetID); // merge the two allTetIDs
+          for(int k = 0; k < 4; k++)
+          {
+            int tetVtx = allTets[allTetID][k];
+            int nbrTetVtx = allTets[nbrAllTetID][k];
+            assert(allTetVertices[tetVtx] == allTetVertices[nbrTetVtx]); // two tet vtx should be on the same pos
+            allTetVtxDSet.unionSet(tetVtx, nbrTetVtx); // merge the two allTetVtxIDs
+          }
+        }
+      } // end for each shared patch
+    } // end nbr in node
+  } // end loop over node
+
+  // now tets are merged, we will create this merged final tet mesh
+
+  auto allTetVtxID2Final = allTetVtxDSet.createOldToNewIDMapping(); // allTetVtxID -> finalTetVtxID
+  int numFinalTetVtx = 1 + *max_element(allTetVtxID2Final.begin(), allTetVtxID2Final.end());
+
+  // build final tet vertex positions
+  vector<Vec3d> finalTetVertices(numFinalTetVtx);
+  for(size_t i = 0; i < allTetVertices.size(); i++)
+  {
+    int newID = allTetVtxID2Final[i];
+    finalTetVertices[newID] = allTetVertices[i];
+  }
+
+  map<UTetKey, vector<int>> finalTetVtxIndex2allTetIDs; // finalTetKey -> allTetIDs
+  for(size_t i = 0; i < allTets.size(); i++)
+  {
+    Vec4i ni;
+    for(int j = 0; j < 4; j++)
+    {
+      ni[j] = allTetVtxID2Final[allTets[i][j]];
+      assert(ni[j] >= 0);
+    }
+    UTetKey key(ni); // the tet key in finalTetVtxIDs
+    assert(key.isValidTet());
+    finalTetVtxIndex2allTetIDs[key].push_back(i);
+  }
+
+  // we use disjoint set to merge tets to get the final tets
+  for(const auto & p : finalTetVtxIndex2allTetIDs)
+  {
+    if (p.second.size() > 1)
+    {
+      allTetsDSet.unionRange(p.second);
+    }
+  }
+
+  auto allTetID2Final = allTetsDSet.createOldToNewIDMapping();
+  int numFinalTets = 1 + *max_element(allTetID2Final.begin(), allTetID2Final.end());
+
+  // build the finalTets
+  vector<Vec4i> finalTetVtxIndices(numFinalTets);
+  for(size_t i = 0; i < allTets.size(); i++)
+  {
+    Vec4i ni;
+    for(int j = 0; j < 4; j++)
+    {
+      ni[j] = allTetVtxID2Final[allTets[i][j]];
+      assert(ni[j] >= 0);
+    }
+    int newID = allTetID2Final[i];
+    finalTetVtxIndices[newID] = ni;
+  }
+
+  // ========================================================================
+  // Then we will try to find the correct embedding for inputTriMesh as well
+  // ========================================================================
+  int numInputTriVtx = inputTriMeshGeo.numVertices();
+  vector<bool> patchUsed(numPatches, false); // whether all patch will be visited, used as integrity check
+  vector<bool> embeddedVtxVisited(numInputTriVtx, false); // whether all embedding inputTriVtx are visited, used as integrity check
+  vector<Vec4i> finalEmbeddingTetVtxID(numInputTriVtx);
+  vector<Vec4d> finalEmbeddingWeights(numInputTriVtx);
+
+  for(size_t nodeID = 0; nodeID < cellNodes.size(); nodeID++) // for each node
+  {
+    ImmersionGraphNode & node = cellNodes[nodeID];
+    int cellID = node.getCellID();
+    int tetStart = nodeTetStart[nodeID];
+
+    for(auto cellPatchOri : cellPatches[cellID])  // find which patch this node owns
+    {
+      int patchID = cellPatchOri.first;
+      if (node.getPatchOwnership(patchID) != OWNED) continue;
+//      if (cellPatchOri.second == false) continue;
+//      assert(mapFind(node.nbrs,patchID));
+//      if (node.getNbrIDAtPatch(patchID) >= 0) continue;
+      assert(patchUsed[patchID] == false);
+      patchUsed[patchID] = true;
+      // this node embeds this patch
+      for(size_t i = 0; i < patchTris[patchID].size(); i++)
+      {
+        for(int j = 0; j < 3; j++)
+        {
+          int inputTriVtxID = patchTris[patchID][i][j];
+          if (inputTriVtxID >= numInputTriVtx) continue; // we only need to compute embedding for inputTriVtxIDs
+          int cellMeshVtxID = manifoldCellMeshPatchOri2NewVtxIDMap[cellID][patchID][inputTriVtxID];
+
+          PointInTet pit = getPointInTet(cellTetMeshInterps[cellID], cellMeshVtxID);
+          int allTetID = pit.tetID;
+          assert(allTetID >= 0);
+          int finalTetID = allTetID2Final[allTetID + tetStart];
+
+          Vec4i finalTetVtxIndex = finalTetVtxIndices[finalTetID];
+          finalEmbeddingTetVtxID[inputTriVtxID] = finalTetVtxIndex;
+          finalEmbeddingWeights[inputTriVtxID] = pit.weights;
+
+          if (verbose)
+          {
+            Vec4d w(0.0);
+            TetMesh::computeBarycentricWeights(
+                finalTetVertices[finalTetVtxIndex[0]],
+                finalTetVertices[finalTetVtxIndex[1]],
+                finalTetVertices[finalTetVtxIndex[2]],
+                finalTetVertices[finalTetVtxIndex[3]],
+                inputTriMeshGeo.pos(inputTriVtxID), &w[0]);
+            if (len(w - pit.weights) > 1e-6)
+            {
+              cout << "Error: weights don't match: " << w << pit.weights << endl;
+            }
+          }
+          embeddedVtxVisited[inputTriVtxID] = true;
+        }
+      }
+    }
+  }
+
+  assert(allOf(patchUsed.begin(), patchUsed.end(), true));
+  assert(allOf(embeddedVtxVisited.begin(), embeddedVtxVisited.end(), true));
+
+
+  string graphIDStr = to_string(graphID);
+  string interpFilename = "finalTet.interp";
+  if (graphID >= 0) interpFilename = "finalTet" + graphIDStr + ".interp";
+
+  // produce output data: outputInterpWeight & outputTetMesh
+  outputInterpWeight = BarycentricCoordinates(finalEmbeddingTetVtxID, finalEmbeddingWeights);
+  outputTetMesh = TetMeshGeo(finalTetVertices, finalTetVtxIndices);
+
+  // ===========================================================
+  // build additional allCellMesh for algorithm visualization
+  // ===========================================================
+
+  if (allCellMesh)
+  {
+    allCellMesh->clear();
+    if (allCellInerpWeight) allCellInerpWeight->clear();
+
+    for(size_t nodeID = 0; nodeID < cellNodes.size(); nodeID++) // for each node
+    {
+      vector<Vec4i> allCellMeshEmbTetVtxIDs;
+      vector<Vec4d> allCellMeshEmbWeights;
+
+      ImmersionGraphNode & node = cellNodes[nodeID];
+      int cellID = node.getCellID();
+      // create an objMesh::Group for each node
+      allCellMesh->push_back(manifoldCellMesh[cellID]);
+
+      if (allCellInerpWeight)
+      {
+        int tetStart = nodeTetStart[nodeID];
+        for(int cellMeshVtxID = 0; cellMeshVtxID < manifoldCellMesh[cellID].numVertices(); cellMeshVtxID++)
+        {
+          PointInTet pit = getPointInTet(cellTetMeshInterps[cellID], cellMeshVtxID);
+          int cellTetID = pit.tetID;
+          assert(cellTetID >= 0);
+          int finalTetID = allTetID2Final[cellTetID + tetStart];
+          Vec4i finalTetVtxIndex = finalTetVtxIndices[finalTetID];
+          allCellMeshEmbTetVtxIDs.push_back(finalTetVtxIndex);
+          allCellMeshEmbWeights.push_back(pit.weights);
+        }
+      }
+
+      if (allCellInerpWeight)
+      {
+        allCellInerpWeight->emplace_back(move(allCellMeshEmbTetVtxIDs), move(allCellMeshEmbWeights));
+      }
+    }
+  }
+}
+
+void ImmersionMesher::generateImmersedTetMesh(vector<TetMeshGeo> & tetMeshes, vector<BarycentricCoordinates> & embeddingWeights,
+    vector<std::vector<TriMeshGeo>> * allCellMeshes, vector<std::vector<BarycentricCoordinates>> * allCellMeshWeights)
+{
+  buildCellTetMeshes();
+
+  cout << "Given " << computedCellNodes.size() << " graph" << (computedCellNodes.size() <= 1 ? "" : "s") << " to produce..." << endl;
+  int count = 0;
+  // now we go through each immersion and build a tet mesh for it
+  for(int i = 0; i < sizei(computedCellNodes); i++)
+  {
+    bool same = false;
+    // we will check whether two immersions are the same and avoid creating duplicate immersions
+    for(int j = 0; j < i; j++)
+    {
+      if (computedCellNodes[i] == computedCellNodes[j])
+      {
+        cout << "Graph " << i << " and " << j << " are the same" << endl;
+        same = true;
+        break;
+      }
+    }
+
+    if (same == false)
+    {
+      TetMeshGeo tetMesh;
+      BarycentricCoordinates weight;
+      vector<TriMeshGeo> allCell;
+      vector<BarycentricCoordinates> allCellWeight;
+      // produce the final tet mesh for the immersion
+      produceFinalTetMesh(computedCellNodes[i], count++, tetMesh, weight,
+          (allCellMeshes ? &allCell : nullptr), (allCellMeshWeights ? &allCellWeight : nullptr));
+      tetMeshes.emplace_back(move(tetMesh));
+      embeddingWeights.emplace_back(move(weight));
+      if (allCellMeshes)
+        allCellMeshes->emplace_back(move(allCell));
+      if (allCellMeshWeights)
+        allCellMeshWeights->emplace_back(move(allCellWeight));
+    }
+  }
+}
+
diff --git a/libraries/include/ARPACKSolver.h b/libraries/include/ARPACKSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..41f3cfdb41a9b2f82fe63614a5fdd5d2638d6190
--- /dev/null
+++ b/libraries/include/ARPACKSolver.h
@@ -0,0 +1,89 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Computes the first largest or smallest eigenvalues and eigenvectors of
+  a large sparse matrix, using the ARPACK library:
+  http://www.caam.rice.edu/software/ARPACK/
+
+  There is also a routine to compute constrained modes, as published in:
+
+  Hongyi Xu and Jernej Barbic: Pose-Space Subspace Dynamics, 
+  ACM Transactions on Graphics 35(4) (SIGGRAPH 2016), Anaheim, CA, USA
+*/
+
+#ifndef _ARPACKSOLVER_H_
+#define _ARPACKSOLVER_H_
+
+#include "sparseMatrix.h"
+
+class ARPACKSolver
+{
+public:
+ 
+  // Perform eigensolve:
+  // K * x = lambda * M * x
+  // Returns the number of converged eigenvalues.
+  // Assumes that both K and M are symmetric, and that M > 0.
+  // Both matrices are given using the entire matrix (not just the lower/upper triangle).
+  // Mode is either "LM" or "SM" (with SM, must also have K > 0).
+  // Uses mode 2 of ARPACK (regular generalized eigenvalue problem).
+  int SolveGenEigReg(SparseMatrix * K, SparseMatrix * M, int numEigenvalues, double * eigenvalues, double * eigenvectors, const char * mode = "LM", int numLinearSolverThreads=0, int verbose=1);
+
+  // Perform eigensolve:
+  // K * x = lambda * M * x.
+  // Solves for the smallest (in absolute sense) eigenvalues.
+  // Returns the number of converged eigenvalues.
+  // Assumes that both K and M are symmetric, and that M >= 0.
+  // K can be singular.
+  // Uses mode 3 of ARPACK (shift-inverted generalized eigenvalue problem).
+  // "sigma" is the amount of shift. This is needed when K is singular.
+  int SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEigenvalues, double * eigenvalues, double * eigenvectors, double sigma=0.0, int numLinearSolverThreads=0, int verbose=1);
+
+  // Perform constrained eigensolve:
+  // K * x = lambda * M * x, subject to C x = 0.
+  // Solves for the smallest (in absolute sense) eigenvalues,
+  // subject to the constraint C x = 0.
+  // Returns the number of converged eigenvalues.
+  // Assumes that both K and M are symmetric, and that M >= 0.
+  // K can be singular.
+  // Uses mode 3 of ARPACK (shift-inverted generalized eigenvalue problem).
+  // "sigma" is the amount of shift. This is needed when K is singular.
+  // Matrix C can be singular; "eps" is a threshold for regularizing the matrix C.
+  int SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, SparseMatrix * C, int numEigenvalues, double * eigenvalues, double * eigenvectors, double sigma=0.0, double eps=1e-6, int numLinearSolverThreads=0, int verbose=1);
+
+protected:
+};
+
+#endif
+
diff --git a/src/libsparseSolver/CGSolver.h b/libraries/include/CGSolver.h
old mode 100755
new mode 100644
similarity index 64%
rename from src/libsparseSolver/CGSolver.h
rename to libraries/include/CGSolver.h
index dacc117d5c48b2659ac2a0ed7558cbc8aef8e28d..94a84e29cbb1919dfe568839ba4b8d488937da0e
--- a/src/libsparseSolver/CGSolver.h
+++ b/libraries/include/CGSolver.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,7 +32,7 @@
 
 /*
   A conjugate gradient solver built on top of the sparse matrix class.
-  There are two solver versions: without preconditioning, and with
+  There are two solver versions: without preconditioning, and with 
   Jacobi preconditioning.
 
   You can either provide a sparse matrix, or a callback function to
@@ -36,7 +40,7 @@
 
   The sparse matrix must be symmetric and positive-definite.
 
-  The CG solvers were implemented by following Jonathan Shewchuk's
+  The CG solvers were implemented by following Jonathan Shewchuk's 
   An Introduction to the Conjugate Gradient Method Without the Agonizing Pain:
   http://www.cs.cmu.edu/~jrs/jrspapers.html#cg
 
@@ -49,22 +53,24 @@
 #include "linearSolver.h"
 #include "sparseMatrix.h"
 
-namespace vega
-{
 class CGSolver : public LinearSolver
 {
 public:
 
-  // standard constructor
+  // Standard constructor. Provide the matrix A to be used for the solve, A x = b.
+  // Matrix A will not be modified. 
+  // Minor note: the code will generate an internal acceleration structure, by calling A->BuildDiagonalIndices() when using the Jacobi preconditioner. Technically speaking, this modifies the SparseMatrix object since it builds the acceleration structure. It does not modify
+  // the matrix A or any of its entries.
   CGSolver(SparseMatrix * A);
 
-  // This constructor makes it possible to only provide a
-  // "black-box" matrix-vector multiplication routine
-  // (no need to explicitly give the matrix):
-  // given x, the routine must compute A * x, and store it into Ax.
-  // "data" should not be used/touched by the user-written "black-box" routine.
-  // In order to be able to use the Jacobi preconditioner, one can optionally specify the diagonal of the matrix.
-  // If diagonal is not specified, SolveLinearSystemWithJacobiPreconditioner will use the identity preconditioner, i.e., it will be identical to SolveLinearSystemWithoutPreconditioner.
+  // This constructor makes it possible to only provide a "black-box" matrix-vector multiplication routine 
+  // (no need to explicitly give the matrix).
+  // Given x, the routine must compute A * x, and store it into Ax.
+  // "data" should not be used/touched by the user-provided "black-box" routine.
+  // One can then use "SolveLinearSystemWithoutPreconditioner" to solve the linear system.
+  // In order to use "SolveLinearSystemWithJacobiPreconditioner", one needs to specify the diagonal of the matrix.
+  // If the diagonal is not specified, SolveLinearSystemWithJacobiPreconditioner will use the identity preconditioner, 
+  // i.e., the solve will be identical to SolveLinearSystemWithoutPreconditioner.
   typedef void (*blackBoxProductType)(const void * data, const double * x, double * Ax);
   CGSolver(int n, blackBoxProductType callBackFunction, void * data, double * diagonal=NULL);
 
@@ -76,41 +82,35 @@ public:
   // output: solution (in x)
   // "eps" is the convergence criterium: solver converges when the L2 residual errors is less than eps times the initial L2 residual error, must have 0 < eps < 1
   // maximum number of conjugate-gradient iterations is set by "maxIterations"
-  // return value is the number of iterations performed
+  // return value is the number of iterations performed 
   // if solver did not converge, the return value will have a negative sign
-  int SolveLinearSystemWithoutPreconditioner(double * x, const double * b, int verbose=0);
+  int SolveLinearSystemWithoutPreconditioner(double * x, const double * b, double eps=1e-6, int maxIterations=1000, int verbose=0);
 
   // same as above, except it uses Jacobi preconditioning
   // the employed error metric is M^{-1}-weighted L2 residual error (see Shewchuk)
-  int SolveLinearSystemWithJacobiPreconditioner(double * x, const double * b,  int verbose=0);
+  int SolveLinearSystemWithJacobiPreconditioner(double * x, const double * b, double eps=1e-6, int maxIterations=1000, int verbose=0);
+
+  // Solve the linear system with a user-provided preconditioner.
+  // Solving the linear system "preconditioner * x = b" should approximate solving the linear system "(this matrix) * x = b".
+  int SolveLinearSystemWithPreconditioner(LinearSolver * preconditioner, double * x, const double * b, double eps=1e-6, int maxIterations=1000, int verbose=0);
 
   virtual int SolveLinearSystem(double * x, const double * b); // implements the virtual method from LinearSolver by calling "SolveLinearSystemWithJacobiPreconditioner" with default parameters
 
   // computes the dot product of two vectors
   double ComputeDotProduct(double * v1, double * v2); // length of vectors v1, v2 equals numRows (dimension of A)
 
-  void setNumberOfIterations(const size_t iterations);
-
-  void setEpsilon(const double eps);
-
-  size_t getNumberOfIterations() const;
-
-  double getEpsilon() const;
-
 protected:
   int numRows;
   blackBoxProductType multiplicator;
   void * multiplicatorData;
-  SparseMatrix * A;
+  SparseMatrix * A; 
   double * r, * d, * q; // terminology from Shewchuk's work
   double * invDiagonal;
-  size_t numIterations;
-  double epsilon;
 
   double ComputeTriDotProduct(double * x, double * y, double * z); // sum_i x[i] * y[i] * z[i]
   static void DefaultMultiplicator(const void * data, const double * x, double * Ax);
   void InitBuffers();
 };
-}
+
 #endif
 
diff --git a/libraries/include/LagrangeMultiplierSolver.h b/libraries/include/LagrangeMultiplierSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..f819547387d5d1a118047ae89baa57f70013548c
--- /dev/null
+++ b/libraries/include/LagrangeMultiplierSolver.h
@@ -0,0 +1,113 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li, Hongyi Xu                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _LAGRANGEMULTIPLIERSOLVER_H_
+#define _LAGRANGEMULTIPLIERSOLVER_H_
+
+/*
+
+  Solves
+
+        [ A  J^T ] [y]      = [rhs0]
+        [ J  B   ] [lambda]   [rhs1]
+
+  where A is sparse, symmetric and square (m x m), J is sparse and rectangular (c x m) (often a constraint gradient), and B is square and symmetric (c x c) (often zero)
+
+  This system arises when forward-simulating a dynamical system with constraints (enforced via Lagrange multipliers).
+
+  The Pardiso solver is used to solve the system.
+
+*/
+
+#include "PardisoSolver.h"
+
+class LagrangeMultiplierSolver
+{
+public:
+
+  // the constructor will also factor the matrix
+  // if J is passed as NULL, empty matrix is assumed for it (no constraints)
+  // if B is passed as NULL, zero matrix is assumed for B
+  // if updatable=1, one can use function UpdateAJB to update matrices A, J, B (note: unlike in the derived Updatable class, the entire matrix will be re-factored, even if only updating J or B)
+  // if addDirichletBoundaryCondition=1, fixedDOFs can be updated by assigning correct fixed values in x when calling SolveLinearSystem
+  LagrangeMultiplierSolver(const SparseMatrix * A, const SparseMatrix * J, const SparseMatrix * B = NULL,
+      int numFixedDOFs = 0, const int * fixedDOFs = NULL, int numThreads = 0, int updatable = 0, int addDirichletBoundaryCondition = 0);
+  LagrangeMultiplierSolver(const LagrangeMultiplierSolver &);
+  virtual ~LagrangeMultiplierSolver();
+
+  // updates the A, J, B matrix, assuming equal topology as previous A, J, B. 
+  // Pass NULL if you do not wish to update the corresponding matrix. 
+  // If you pass non-NULL for J (or B), then J (or B) MUST have been set in the constructor.
+  // updatable=1 must have been set in the constructor to use this function.
+  void UpdateAJB(const SparseMatrix * A, const SparseMatrix * J = NULL, const SparseMatrix * B = NULL); // updates the A, J, B matrix, assuming equal topology as previous A, J, B. Pass NULL if you do not wish to update the corresponding matrix. If you pass non-NULL for B, then B MUST have been set in the constructor.
+
+  // solve the linear system, using Pardiso
+  // the routine does not modify rhs
+  // if addDirichletBoundaryCondition is 0 when initializing,
+  // x is not an initial guess (it is ignored as input, and used only as output parameter)
+  // otherwise, the fixedDOFs in x are used for Dirichlet boundary conditions
+  // returns Pardiso exit code 
+  MKL_INT SolveLinearSystem(double * x, const double * rhs);
+
+  double CheckLinearSystemSolution(const double * x, const double * rhs, int verbose=1);
+
+  const SparseMatrix * GetSystemMatrix() const { return systemMatrix; }
+
+  int GetNumFixedDOFs() const { return numFixedDOFs; }
+  const int * GetFixedDOFs() const { return fixedDOFs.data(); }
+
+  int GetNumARows() const { return m; }
+  int GetNumJRows() const { return c; }
+
+  void SetNumThreads(int numThreads); // call at any time to change the number of threads
+
+protected:
+  void AddDirichletOnRhs(const double * x); // modify rhsConstrained to satisfy Dirichlet boundary condition
+  bool decompositionDone;
+  int m, c;
+  int numFixedDOFs; 
+  std::vector<int> fixedDOFs;
+  int numThreads;
+  SparseMatrix * systemMatrix;
+  PardisoSolver * pardisoSolver;
+  std::vector<double> xConstrained, rhsConstrained;
+  SparseMatrix * Acons;
+  SparseMatrix * Jcons;
+  SparseMatrix * JTcons;
+  SparseMatrix * ADirichlet, * JDirichlet; // used for updating Dirichlet conditions
+  std::vector<double> DirichletBuffer, DirichletFreeBuffer;
+  int mFree;
+};
+
+#endif
+
+
diff --git a/src/libisotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.h b/libraries/include/MooneyRivlinIsotropicMaterial.h
old mode 100755
new mode 100644
similarity index 84%
rename from src/libisotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.h
rename to libraries/include/MooneyRivlinIsotropicMaterial.h
index eb8edef7f2a12d6299e67a2e952b7c0e6dfb2388..6d5e855b4ad8784fbf170d8ff755da6f041d3317
--- a/src/libisotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.h
+++ b/libraries/include/MooneyRivlinIsotropicMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 #include "isotropicMaterialWithCompressionResistance.h"
 #include "tetMesh.h"
 
-namespace vega
-{
 /*
   The implemented compressible Mooney-Rivlin material has the following energy function:
 
@@ -72,6 +74,6 @@ protected:
   double * EdivNuFactor;
   virtual double GetCompressionResistanceFactor(int elementIndex);
 };
-}
+
 #endif
 
diff --git a/libraries/include/PardisoSolver.h b/libraries/include/PardisoSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..6055db7d40fe959192def94fc3c1b52512d35874
--- /dev/null
+++ b/libraries/include/PardisoSolver.h
@@ -0,0 +1,131 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li, Hongyi Xu                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _PARDISOSOLVER_H_
+#define _PARDISOSOLVER_H_
+
+/*
+  Solves A * x = rhs, where A is a sparse matrix. The following matrix types are supported:
+    structurally symmetric matrix
+    symmetric positive-definite matrix
+    symmetric indefinite matrix
+    unsymmetric matrix
+  The solution is obtained using the Pardiso library from Intel MKL, using multi-threading.
+  Three steps are performed: 
+    1. matrix re-ordering and symbolic factorization (in the constructor)
+    2. numerical matrix factorization (FactorMatrix)
+    3. the actual linear system solve (SolveLinearSystem)
+  The solution method is direct (not iterative), unless one uses the direct-iterative mode. 
+  As such, convergence is robust, and there is no need to tune convergence parameters, unlike, say, in the conjugate gradient method.
+  Memory requirements are minimized by re-ordering the matrix before applying the matrix decomposition.
+  However, for very large systems (e.g. matrices of size 200,000 x 200,000, on a machine with 2 GB RAM), 
+  the matrix decomposition might run out of memory.
+*/
+
+#include "linearSolver.h"
+#include "sparseMatrix.h"
+
+#define MKL_INT int
+
+class PardisoSolver : public LinearSolver
+{
+public:
+  // The constructor computes the permutation to re-order A, and performs symbolic factorization.
+  // Only the topology of A matters for the constructor. A is not modified.
+  // Note: after calling the constructor, you must call "FactorMatrix" to perform numerical factorization.
+  //  "mtype" gives the matrix type:
+  //  = 1   structurally symmetric matrix
+  //  = 2   symmetric positive-definite matrix
+  //  = -2  symmetric indefinite matrix
+  //  = 11  unsymmetric matrix
+  typedef enum { REAL_STRUCTURAL_SYM = 1, REAL_SPD = 2, REAL_SYM_INDEFINITE = -2, REAL_UNSYM = 11 } matrixType;
+  // Matrix re-ordering is specified as follows:
+  // = 0   minimum degree ordering
+  // = 2   nested dissection algorithm from the METIS package
+  // = 3   parallel (OpenMP) version of nested dissection; it can decrease the computation time on multi-core computers, especially when the constructor takes a long time
+  typedef enum { MINIMUM_DEGREE_ORDERING = 0, NESTED_DISSECTION = 2, PARALLEL_NESTED_DISSECTION = 3 } reorderingType;
+  // must have: numThreads >= 1
+  // "directIterative" specifies whether a direct-iterative procedure is used (see Intel MKL's documentation)
+  PardisoSolver(const SparseMatrix * A, int numThreads = 1, matrixType mtype = REAL_SYM_INDEFINITE, reorderingType rtype = NESTED_DISSECTION, int directIterative = 0, int verbose = 0);
+
+  virtual ~PardisoSolver();
+
+  // set the number of threads to use
+  // must have: numThreads >= 1
+  void SetNumThreads(int numThreads) { this->numThreads = numThreads; }
+
+  // Perform the numerical matrix factorization (Cholesky for symmetric matrices, LU for other matrices).
+  // This step is **mandatory** (constructor does not perform it).
+  // You must factor the matrix at least once; and every time the entries of the matrix A change.
+  // If the topology of A changes, you must call the constructor again.
+  // A is not modified.
+  MKL_INT FactorMatrix(const SparseMatrix * A); 
+
+  // solve: A * x = rhs, using the previously computed matrix factorization
+  // rhs is not modified
+  virtual int SolveLinearSystem(double * x, const double * rhs);
+
+  // solve multiple right-hand sides
+  MKL_INT SolveLinearSystemMultipleRHS(double * x, const double * rhs, int numRHS);
+
+  // solve: A * x = rhs, using the direct-iterative solver
+  MKL_INT SolveLinearSystemDirectIterative(const SparseMatrix * A, double * x, const double * rhs);
+
+  /*
+    Advanced routines exposing the substeps of the solve.
+    For REAL_SYM_INDEFINITE, SolveLinearSystem is equivalent to: ForwardSubstitution + DiagonalSubstitution + BackwardSubstitution
+    For REAL_SPD, SolveLinearSystem is equivalent to: ForwardSubstitution + BackwardSubstitution (DiagonalSubstitution is not used)
+  */
+  MKL_INT ForwardSubstitution(double * y, const double * rhs); // L y = rhs
+  MKL_INT DiagonalSubstitution(double * v, const double * y);  // D v = y
+  MKL_INT BackwardSubstitution(double * x, const double * v);  // L^T x = v
+
+protected:
+  int n;
+  double * a;
+  int * ia, * ja;
+  void *pt[64];
+  MKL_INT iparm[64];
+
+  int numThreads;
+  matrixType mtype;
+  reorderingType rtype;
+  int directIterative;
+  int verbose;
+  MKL_INT nrhs; 
+  MKL_INT maxfct, mnum, phase, error, msglvl;
+
+  static void DisabledSolverError();
+};
+
+#endif
+
diff --git a/src/libsparseSolver/SPOOLESSolver.h b/libraries/include/SPOOLESSolver.h
similarity index 84%
rename from src/libsparseSolver/SPOOLESSolver.h
rename to libraries/include/SPOOLESSolver.h
index 81ab30e7a516c4932be980d44d4c8d68bc174207..52cab017fa913a35b9974cb1e3f616dc01c00cf9 100644
--- a/src/libsparseSolver/SPOOLESSolver.h
+++ b/libraries/include/SPOOLESSolver.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,20 +34,16 @@
 #define _SPOOLESSOLVER_H_
 
 /*
-
   Solves A * x = rhs, where A is sparse, usually large, and symmetric.
-
+  
   The solution is obtained using the SPOOLES library (which is free software).
   The solution method is direct (not iterative). As such, convergence
   is often very robust, and there is no need to tune convergence parameters,
   unlike, say, in the Conjugate gradient method.
   Memory requirements are minimized by re-ordering the matrix before applying
   Cholesky decomposition.
-  However, for very large systems (e.g. 200,000 x 200,000 matrices on a
+  However, for very large systems (e.g. 200,000 x 200,000 matrices on a 
   2Gb machine), the Cholesky decomposition might run out of memory.
-
-  Jernej Barbic, MIT, 2007-2009
-
 */
 
 #include <stdio.h>
@@ -51,8 +51,6 @@
 #include "linearSolver.h"
 #include "sparseMatrix.h"
 
-namespace vega
-{
 class SPOOLESSolver : public LinearSolver
 {
 public:
@@ -78,6 +76,6 @@ protected:
 
   static void DisabledSolverError();
 };
-}
+
 #endif
 
diff --git a/src/libsparseSolver/SPOOLESSolverMT.h b/libraries/include/SPOOLESSolverMT.h
similarity index 84%
rename from src/libsparseSolver/SPOOLESSolverMT.h
rename to libraries/include/SPOOLESSolverMT.h
index 3bb61cdb1907302125343c006a09c7826bde9f6f..1abd219b826772370e135ee457bd555e1bef8d09 100644
--- a/src/libsparseSolver/SPOOLESSolverMT.h
+++ b/libraries/include/SPOOLESSolverMT.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,7 +44,7 @@ is often very robust, and there is no need to tune convergence parameters,
 unlike, say, in the Conjugate gradient method.
 Memory requirements are minimized by re-ordering the matrix before applying
 Cholesky decomposition.
-However, for very large systems (e.g. 200,000 x 200,000 matrices on a
+However, for very large systems (e.g. 200,000 x 200,000 matrices on a 
 2Gb machine), the Cholesky decomposition might run out of memory.
 
 */
@@ -50,8 +54,6 @@ However, for very large systems (e.g. 200,000 x 200,000 matrices on a
 #include "sparseMatrix.h"
 #include "linearSolver.h"
 
-namespace vega
-{
 class SPOOLESSolverMT : public LinearSolver
 {
 public:
@@ -78,6 +80,6 @@ protected:
 
   static void DisabledSolverError();
 };
-}
+
 #endif
 
diff --git a/src/libstvk/StVKCubeABCD.h b/libraries/include/StVKCubeABCD.h
similarity index 80%
rename from src/libstvk/StVKCubeABCD.h
rename to libraries/include/StVKCubeABCD.h
index bb93d5b69d89c66e18db340b9e6683044e440e10..46d8deab621904f92d330780ff01b86c12f8a1d4 100644
--- a/src/libstvk/StVKCubeABCD.h
+++ b/libraries/include/StVKCubeABCD.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,8 +35,6 @@
 
 #include "StVKElementABCD.h"
 
-namespace vega
-{
 /*
   This classes stores the St.Venant-Kirchhoff A,B,C,D coefficients for a cube element.
   See also StVKInternalForces.h .
@@ -58,6 +60,6 @@ protected:
   Vec3d C_[8][8][8];
   double D_[8][8][8][8];
 };
-}
+
 #endif
 
diff --git a/src/libstvk/StVKElementABCD.h b/libraries/include/StVKElementABCD.h
similarity index 80%
rename from src/libstvk/StVKElementABCD.h
rename to libraries/include/StVKElementABCD.h
index 628520d643bd4b1cc04884408b2eefdd1d5a70e9..616954d62ca7079349003bead5545bce650bcc35 100644
--- a/src/libstvk/StVKElementABCD.h
+++ b/libraries/include/StVKElementABCD.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,8 +35,6 @@
 
 #include "minivector.h"
 
-namespace vega
-{
 /*
   This abstract class serves as storage space for the St.Venant-Kirchhoff A,B,C,D coefficients for a mesh element. 
   See also StVKInternalForces.h .
@@ -56,6 +58,6 @@ public:
 
 protected:
 };
-}
+
 #endif
 
diff --git a/src/libstvk/StVKElementABCDLoader.h b/libraries/include/StVKElementABCDLoader.h
similarity index 80%
rename from src/libstvk/StVKElementABCDLoader.h
rename to libraries/include/StVKElementABCDLoader.h
index f0cb90984b3bcf48980149c6ee194e1ffc929da0..cefdc1457dd772e9c4693162436ba44f3d99f5aa 100644
--- a/src/libstvk/StVKElementABCDLoader.h
+++ b/libraries/include/StVKElementABCDLoader.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,8 +41,6 @@
 #include "volumetricMesh.h"
 #include "StVKElementABCD.h"
 
-namespace vega
-{
 class StVKElementABCDLoader
 {
 public:
@@ -48,6 +50,6 @@ public:
   //   1 : use the high-memory version (only applies with tet meshes); with this setting, computation speeds will be higher, at the expense of more memory (however, difference is typically not large and speeds might even decrease with large meshes when running out of memory)
   static StVKElementABCD * load(VolumetricMesh * volumetricMesh, unsigned int loadingFlag=0); 
 };
-}
+
 #endif
 
diff --git a/libraries/include/StVKFEM.h b/libraries/include/StVKFEM.h
new file mode 100644
index 0000000000000000000000000000000000000000..caa00f13b108cc334ecc355bb4c7018d6b72ce2b
--- /dev/null
+++ b/libraries/include/StVKFEM.h
@@ -0,0 +1,124 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Bohan Wang, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVK_FEM_H_
+#define _STVK_FEM_H_
+
+#include "volumetricMesh.h"
+#include "StVKElementABCD.h"
+
+#include <vector>
+
+
+// This class implements the StVK force model in a element style.
+// It specifically computes the simulation quantities in a scale of a FEM element,
+// using 'ComputeElementLocalEnergyAndInternalForcesAndStiffnessMatrix'.
+// If you want to compute the quantities in object scale,
+// please look into stencilForceModel lib,
+// or use stVKInternalForces.* and stVKStiffnessMatrix.*.
+// The later files are the same as the previous version only for backward compatibility.
+// The same functionality can be achieved by using stencilForceModel/forceModelAssembler.*.
+
+class StVKFEM
+{
+public:
+  StVKFEM(VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity=false, double g=9.81);
+  virtual ~StVKFEM();  
+
+  // enables or disables the gravity (note: you can also set this in the constructor; 
+  // use this routine to turn the gravity on/off during the simulation)
+  // if addGravity is enabled, ComputeForces will subtract the gravity force from the internal forces 
+  // (note: subtraction, not addition, is used because the internal forces are returned with the sign as described in the f_int(x) comment above)
+  void SetGravity(bool addGravity) { this->addGravity = addGravity; InitGravity(); } 
+
+  // get simulation mesh
+  const VolumetricMesh * GetVolumetricMesh() const { return volumetricMesh; }
+
+  // get integral class object
+  StVKElementABCD * GetPrecomputedIntegrals() const { return precomputedIntegrals; }
+
+  // compute the energy E, internal forces f and stiffness matrix K of a single FEM element.
+  // u is the input displacement with dimention (# all vtx x 3)
+  // E is a pointer to a double
+  // f is a pointer to a dense vector
+  // K is a pointer to a dense matrix in column major
+  // E, f, K can be nullptr. If it is, the function will not compute it.
+  void ComputeElementLocalEnergyAndInternalForcesAndStiffnessMatrix(const double *u, int el, double * energy, double * fint, double * K);
+
+protected:
+  VolumetricMesh * volumetricMesh;
+  StVKElementABCD * precomputedIntegrals;
+
+  std::vector<double> gravityForce;
+  double g;
+  bool addGravity;  
+  void InitGravity(); // aux function
+
+  int numElementVertices;
+  void ResetVector(double * vec); // aux function
+
+  std::vector<double> lambdaLame;
+  std::vector<double> muLame;
+  std::vector<void*> internalElementData;
+
+  int dof, dof2;
+
+  inline void AddMatrix3x3Block(int c, int a, const Mat3d & matrix, double * K)
+  {
+    for (int k = 0; k < 3; k++) 
+    {
+      for (int l = 0; l < 3; l++)
+      {
+        int row = c * 3 + k;
+        int col = a * 3 + l;
+
+        K[col * dof + row] += matrix[k][l];
+      }
+    }
+  }
+
+  inline void AddMatrix3x3Block(int c, int a, const double matrix[9], double * K)
+  {
+    for (int k = 0; k < 3; k++) 
+    {
+      for (int l = 0; l < 3; l++)
+      {
+        int row = c * 3 + k;
+        int col = a * 3 + l;
+
+        K[col * dof + row] += matrix[k * 3 + l];
+      }
+    }
+  }
+};
+
+#endif
diff --git a/libraries/include/StVKForceModel.h b/libraries/include/StVKForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..09619a79b66254e7e1b543daa173dc97b4a9774f
--- /dev/null
+++ b/libraries/include/StVKForceModel.h
@@ -0,0 +1,62 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Force model corresponding to the StVK material.
+*/
+
+#ifndef _STVKFORCEMODEL_H_
+#define _STVKFORCEMODEL_H_
+
+#include "StVKInternalForces.h"
+#include "StVKStiffnessMatrix.h"
+#include "forceModel.h"
+
+class StVKForceModel : public ForceModel
+{
+public:
+  StVKForceModel(StVKInternalForces * stVKInternalForces, StVKStiffnessMatrix * stVKStiffnessMatrix=NULL);
+  virtual ~StVKForceModel(); 
+
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
+
+protected:
+  StVKInternalForces * stVKInternalForces;
+  StVKStiffnessMatrix * stVKStiffnessMatrix;
+  bool ownStiffnessMatrix;
+};
+
+#endif
+
diff --git a/src/libstvk/StVKHessianTensor.h b/libraries/include/StVKHessianTensor.h
similarity index 83%
rename from src/libstvk/StVKHessianTensor.h
rename to libraries/include/StVKHessianTensor.h
index 8b2a6f51adb1e51883711fbf1b3c17f33e2e0ed4..be18f70f0ccaf26ddbbcb00197aa3836a25b942c 100644
--- a/src/libstvk/StVKHessianTensor.h
+++ b/libraries/include/StVKHessianTensor.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,8 +44,6 @@
 #include "StVKElementABCD.h"
 #include "StVKStiffnessMatrix.h"
 
-namespace vega
-{
 class StVKHessianTensor
 {
 public:
@@ -59,8 +61,8 @@ public:
   // compute the vector result=(H:u)v, where H is the Hessian in the zero deformation configuration
   // high-memory version; must call ComputeHessianAtZero before calling EvaluateHessianQuadraticForm
   int ComputeHessianAtZero(int verbose=1); 
-  int SaveHessianAtZeroToFile(char * filename);
-  void EvaluateHessianQuadraticForm(double * u, double * v, double * result); 
+  int SaveHessianAtZeroToFile(const char * filename);
+  void EvaluateHessianQuadraticForm(const double * u, const double * v, double * result); 
 
   // compute the vector result=(H:u)v, where H is the Hessian in the zero deformation configuration
   // low-memory version; no need to call ComputeHessianAtZero, but longer computation times for smaller meshes
@@ -70,7 +72,7 @@ public:
   // the first numRigidModes columns will not be used for this computation
   // result is the output matrix; must be pre-allocated with (k-numRigidModes) * (k-numRigidModes+1) / 2 columns
   // low-memory version; no need to call ComputeHessianAtZero
-  void EvaluateHessianQuadraticFormDirectAll(double * Ulin, int k, double * result, int numRigidModes=0);
+  void EvaluateHessianQuadraticFormDirectAll(double * Ulin, int k, double * result, int numRigidModes=0, int verbose=1);
   
   // low-level routines (advanced use)
   void AddQuadraticTermsContribution(double * u, double * du, SparseMatrix * dK, int elementLow=-1, int elementHigh=-1);
@@ -94,6 +96,6 @@ protected:
   double * lambdaLame;
   double * muLame;
 };
-}
+
 #endif
 
diff --git a/src/libstvk/StVKInternalForces.h b/libraries/include/StVKInternalForces.h
similarity index 76%
rename from src/libstvk/StVKInternalForces.h
rename to libraries/include/StVKInternalForces.h
index 2807b01b79b09056e8d6a84b8073836f21e20eb3..b030911765408564d091562e75091489d3fd6bc9 100644
--- a/src/libstvk/StVKInternalForces.h
+++ b/libraries/include/StVKInternalForces.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -47,13 +51,6 @@ Supported volumetric mesh types:
    (the cubes are a subset of a regular 3D grid; all cubes are axis-aligned and 
     of the same size)
 
-Multi-threaded versions of the classes are also available ("MT" suffix). 
-They use POSIX threads ("pthreads") - which are available under Unix, Mac OS X, 
-and also Windows. The code is thread-safe. The computation of the following 
-quantities is multi-threaded: internal forces, tangent stiffness matrix, 
-strain energy. With large meshes, these routines achieve almost linear scaling 
-(tested on a 8-core CPU).
-
 We were able to run the code under Unix, Max OS X, and Windows.
 
 This code was designed for interactive simulation, but can also be used offline. 
@@ -77,8 +74,6 @@ Note: all matrices are stored in the column-major order (same format as in LAPAC
 #include "volumetricMesh.h"
 #include "StVKElementABCD.h"
 
-namespace vega
-{
 class StVKInternalForces
 {
 public:
@@ -92,21 +87,21 @@ public:
   // they must be (pre-allocated) vectors of length 3 * numVertices
   // the internal forces are returned with the sign corresponding to f_int(x) on the left side of the equation M * x'' + f_int(x) = f_ext
   // i.e., the computed internal forces are negatives of the actual physical internal forces acting on the material
-  virtual void ComputeForces(double * vertexDisplacements, double * internalForces);
-
+  virtual void ComputeForces(const double * vertexDisplacements, double * internalForces);
+  
   // enables or disables the gravity (note: you can also set this in the constructor; use this routine to turn the gravity on/off during the simulation)
   void SetGravity(bool addGravity) { this->addGravity = addGravity; InitGravity(); } // if addGravity is enabled, ComputeForces will subtract the gravity force from the internal forces (note: subtraction, not addition, is used because the internal forces are returned with the sign as described in the f_int(x) comment above)
 
-  virtual double ComputeEnergy(double * vertexDisplacements); // get the nonlinear elastic strain energy
+  virtual double ComputeEnergy(const double * vertexDisplacements); // get the nonlinear elastic strain energy
 
   inline VolumetricMesh * GetVolumetricMesh() { return volumetricMesh; }
   inline StVKElementABCD * GetPrecomputedIntegrals() { return precomputedIntegrals; }
 
   // === advanced routines below === 
-  double ComputeEnergyContribution(double * vertexDisplacements, int elementLow, int elementHigh, double * buffer = NULL); // compute the contribution to strain energy due to the specified elements; needs a buffer for internal calculations; you can pass NULL (and then an internal buffer will be used), or pass your own buffer (useful with multi-threading)
-  void AddLinearTermsContribution(double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
-  void AddQuadraticTermsContribution(double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
-  void AddCubicTermsContribution(double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
+  double ComputeEnergyContribution(const double * vertexDisplacements, int elementLow, int elementHigh, double * buffer = NULL); // compute the contribution to strain energy due to the specified elements; needs a buffer for internal calculations; you can pass NULL (and then an internal buffer will be used), or pass your own buffer (useful with multi-threading)
+  void AddLinearTermsContribution(const double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
+  void AddQuadraticTermsContribution(const double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
+  void AddCubicTermsContribution(const double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
   
 protected:
   VolumetricMesh * volumetricMesh;
@@ -125,6 +120,6 @@ protected:
   double * lambdaLame;
   double * muLame;
 };
-}
+
 #endif
 
diff --git a/src/libisotropicHyperelasticFEM/StVKIsotropicMaterial.h b/libraries/include/StVKIsotropicMaterial.h
old mode 100755
new mode 100644
similarity index 83%
rename from src/libisotropicHyperelasticFEM/StVKIsotropicMaterial.h
rename to libraries/include/StVKIsotropicMaterial.h
index e5d9d71d597eac94a4603939ff12aafb0a5c0569..298d7bf502b2c71412355c7e3f6141166f48a8be
--- a/src/libisotropicHyperelasticFEM/StVKIsotropicMaterial.h
+++ b/libraries/include/StVKIsotropicMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 #include "isotropicMaterialWithCompressionResistance.h"
 #include "tetMesh.h"
 
-namespace vega
-{
 /*
    StVK material. Material properties are read from the tet mesh, and can be heterogeneous.
 
@@ -62,6 +64,6 @@ protected:
   double * EdivNuFactor;
   virtual double GetCompressionResistanceFactor(int elementIndex);
 };
-}
+
 #endif
 
diff --git a/libraries/include/StVKStencilForceModel.h b/libraries/include/StVKStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..0fde039afe46335643d66a0b8ac8f065caea7ce1
--- /dev/null
+++ b/libraries/include/StVKStencilForceModel.h
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVK_STENCIL_FORCEMODEL_H_
+#define _STVK_STENCIL_FORCEMODEL_H_
+
+#include "StVKFEM.h"
+#include "stencilForceModel.h"
+
+// Stencils for StVK FEM.
+// A stencil is one FEM element.
+// See comments in the parent class.
+class StVKStencilForceModel: public StencilForceModel
+{
+public:
+  StVKStencilForceModel(StVKFEM * stvkFEM);
+  virtual ~StVKStencilForceModel();
+
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+  StVKFEM * GetForceModelHandle() { return stvkFEM; }
+
+protected:
+  StVKFEM * stvkFEM;
+};
+
+#endif
+
diff --git a/src/libstvk/StVKStiffnessMatrix.h b/libraries/include/StVKStiffnessMatrix.h
similarity index 78%
rename from src/libstvk/StVKStiffnessMatrix.h
rename to libraries/include/StVKStiffnessMatrix.h
index f7f279b8821dba9991c389503a3a1ed150a94ddc..2b0843310b4483d14e8b781b4f3d88358458ac10 100644
--- a/src/libstvk/StVKStiffnessMatrix.h
+++ b/libraries/include/StVKStiffnessMatrix.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -39,8 +43,6 @@
 #include "sparseMatrix.h"
 #include "StVKInternalForces.h"
 
-namespace vega
-{
 class StVKStiffnessMatrix
 {
 public:
@@ -55,7 +57,7 @@ public:
 
   // evaluates the tangent stiffness matrix in the given deformation configuration
   // "vertexDisplacements" is an array of vertex deformations, of length 3*n, where n is the total number of mesh vertices
-  virtual void ComputeStiffnessMatrix(double * vertexDisplacements, SparseMatrix * sparseMatrix);
+  virtual void ComputeStiffnessMatrix(const double * vertexDisplacements, SparseMatrix * sparseMatrix);
 
   inline void ResetStiffnessMatrix(SparseMatrix * sparseMatrix) {sparseMatrix->ResetToZero();}
 
@@ -65,9 +67,9 @@ public:
   // === the routines below are meant for advanced usage ===
 
   // auxiliary functions, these will add the contributions into 'forces'
-  void AddLinearTermsContribution(double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
-  void AddQuadraticTermsContribution(double * vertexDisplacements,SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
-  void AddCubicTermsContribution(double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
+  void AddLinearTermsContribution(const double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
+  void AddQuadraticTermsContribution(const double * vertexDisplacements,SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
+  void AddCubicTermsContribution(const double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
 
   void GetMatrixAccelerationIndices(int *** row__, int *** column__) { *row__ = row_; *column__ = column_;}
 
@@ -80,7 +82,6 @@ protected:
   int ** column_;
 
   VolumetricMesh * volumetricMesh;
-  StVKInternalForces * stVKInternalForces;
   StVKElementABCD * precomputedIntegrals;
 
   double * lambdaLame;
@@ -101,6 +102,6 @@ inline void StVKStiffnessMatrix::AddMatrix3x3Block(int c, int a, int element, Ma
     for(int l=0; l<3; l++)
       sparseMatrix->AddEntry(3*row[c]+k, 3*column[numElementVertices*c+a]+l, matrix[k][l]);
 }
-}
+
 #endif
 
diff --git a/src/libstvk/StVKTetABCD.h b/libraries/include/StVKTetABCD.h
similarity index 82%
rename from src/libstvk/StVKTetABCD.h
rename to libraries/include/StVKTetABCD.h
index 5c416c292596cb5d18f894018be2ccc40bf34fd0..87ab897a7f7820849d067d3f4144b0ce83723e41 100644
--- a/src/libstvk/StVKTetABCD.h
+++ b/libraries/include/StVKTetABCD.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 #include "StVKElementABCD.h"
 #include "tetMesh.h"
 
-namespace vega
-{
 /*
   This class stores the St.Venant-Kirchhoff A,B,C,D coefficients for a tetrahedral element.
   This is the low-memory version (the version that we use most often).
@@ -77,6 +79,6 @@ protected:
   // creates the elementData structure for a tet
   void StVKSingleTetABCD(Vec3d vertices[4], elementData * target);
 };
-}
+
 #endif
 
diff --git a/src/libstvk/StVKTetHighMemoryABCD.h b/libraries/include/StVKTetHighMemoryABCD.h
similarity index 83%
rename from src/libstvk/StVKTetHighMemoryABCD.h
rename to libraries/include/StVKTetHighMemoryABCD.h
index 746d7ffd82f803ea2a1cf9720fb00c09a2d8ca2b..058a37967989d328168ec4838c6c9da3c40c7916 100644
--- a/src/libstvk/StVKTetHighMemoryABCD.h
+++ b/libraries/include/StVKTetHighMemoryABCD.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 #include "StVKElementABCD.h"
 #include "tetMesh.h"
 
-namespace vega
-{
 /*
   Class "StVKTetHighMemoryABCD" stores (explicitly) the St.Venant-Kirchhoff 
   A,B,C,D coefficients for a tetrahedron (high-memory version).
@@ -65,6 +67,6 @@ protected:
 
   void StVKSingleTetABCD(Vec3d vertices[4], Mat3d A[4][4], double B[4][4], Vec3d C[4][4][4], double D[4][4][4][4]);
 };
-}
+
 #endif
 
diff --git a/libraries/include/ZTAZMultiplicator.h b/libraries/include/ZTAZMultiplicator.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ffdfb8815ef238105a8d107f237f581ace54a48
--- /dev/null
+++ b/libraries/include/ZTAZMultiplicator.h
@@ -0,0 +1,69 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Helper class for the ARPACK solver.
+*/
+
+#ifndef _ZTAZMULTIPLICATOR_H_
+#define _ZTAZMULTIPLICATOR_H_
+
+#include "sparseMatrix.h"
+#include "matrix.h"
+
+/*
+  computes x |--> Z^T A Z x
+
+  Z = P [ -C_p^{-1} C_n  ]
+        [   I            ]
+*/
+
+class ZTAZMultiplicator
+{
+public:
+  ZTAZMultiplicator(SparseMatrix * A, SparseMatrix * P, Matrix<double> * invCp, Matrix<double> * Cn);
+  ~ZTAZMultiplicator();
+  void Multiply(const double * x, double * output);
+
+protected:
+  SparseMatrix * A;
+  SparseMatrix * P;
+  Matrix<double> * invCp;
+  Matrix<double> * Cn;
+  Matrix<double> * invCpCn;
+  Matrix<double> * invCpCnT;
+  double * buffer1;
+  double * buffer2;
+};
+
+#endif
+
diff --git a/libraries/include/arapDeformer.h b/libraries/include/arapDeformer.h
new file mode 100644
index 0000000000000000000000000000000000000000..6a963556c8ca10281b7fe3758f6a16defdea7d9f
--- /dev/null
+++ b/libraries/include/arapDeformer.h
@@ -0,0 +1,150 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "shapeEdit" library , Copyright (C) 2018 USC                          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Koki Nagano, Yijing Li, Jernej Barbic        *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef ARAPDEFORMER_H
+#define ARAPDEFORMER_H
+
+#include "sparseMatrix.h"
+#include "objMesh.h"
+#include "LagrangeMultiplierSolver.h"
+#include <set>
+#include <map>
+#include <vector>
+#include "tetMesh.h"
+#include "cubicMesh.h"
+
+/*
+  As-rigid-as-possible model for obj meshes, tet meshes and cubic meshes.
+
+  1. For obj meshes, this class implements the model for fast geometric shape deformation described in the following paper:
+
+    Olga Sorkine and Marc Alexa: As-Rigid-As-Possible Surface Modeling
+    In Proc. of Eurographics Symposium on Geometry Processing (2007), Vol. 4, p. 30
+
+  2. For tet and cubic meshes, the class implements the extension for volumetric meshes described in the same paper.
+*/
+
+class ARAPModel
+{
+public:
+  ARAPModel(const ObjMesh * objMesh);
+  ARAPModel(const TetMesh * tetMesh, const double * vtxWeights = nullptr);
+  ARAPModel(const CubicMesh * tetMesh, const double * vtxWeights = nullptr);
+  virtual ~ARAPModel();
+
+  void updateRotations(const double * disp);
+
+  void buildDispRHS(std::vector<double> rhs[3]);
+  void buildPosRHS(std::vector<double> rhs[3]);
+
+  const SparseMatrix * getL() const { return L; }
+  const SparseMatrix * getW() const { return W; }
+  const Vec3d & getRestVertex(int i) const { return restVertices[i]; }
+  const std::vector<Vec3d> & getRestVertices() const { return restVertices; }
+
+  const Mat3d & getRotation(int i) const { return R[i]; }
+  const std::vector<Mat3d> & getRotations() const { return R; }
+
+  void setRotations(const Mat3d * rotations);
+
+  // we omit the derivative of rotation Ri, so energy hessian is always the same: a weighted Laplacian matrix
+  // the hessian is of size 3*#vtx
+  void buildEnergyHessian(SparseMatrix ** sparseMatrix) const;
+
+  // compute energy and its gradient; the derivative of rotation is omitted in calculation
+  // either energy or gradient can be nullptr, dim is of size 3*#vtx
+  void getEnergyAndGradient(const double * disp, double * energy, double * gradient = nullptr);
+
+  void buildPosRHSOnOneRotation(int vtxIdx, std::vector<double> rhs[3]);
+
+  static void assembleDimension(const std::vector<double> input[3], double * output);
+
+protected:
+  void initialize();
+
+  int n;
+  SparseMatrix * L;
+  SparseMatrix * W; // W is the wij matrix
+  std::vector<Vec3d> restVertices;
+  std::vector<double> Lp[3];
+  std::vector<Mat3d> R;
+};
+
+
+class ARAPDeformer
+{
+public:
+  ARAPDeformer(const ObjMesh * objMesh, int numFixedVertices, const int * fixedVertices, int numThreads);
+  ARAPDeformer(const TetMesh * tetMesh, int numFixedVertices, const int * fixedVertices, int numThreads);
+  ARAPDeformer(const CubicMesh * tetMesh, int numFixedVertices, const int * fixedVertices, int numThreads);
+  virtual ~ARAPDeformer();
+
+  void setNumThreads(int threads);
+
+  virtual void updateHandles(const std::map<int, Vec3d> & newHandles);
+
+  // do one iteration of rotation and position optimization, return a rest shape (disp = 0) if no constraints
+  void deformOneIter(double * disp); //disp serves as input and output
+  virtual void deformOneIter(const double * dispLast, double * disp);
+
+  // do rotation and position optimization until relative error < epsilon or maxIteration is reached
+  void deform(const double * dispLast, double * disp, double epsilon, unsigned int maxIteration);
+
+  const SparseMatrix * getL() const { return arapModel.getL(); }
+
+protected:
+  // update handle displacements. return true if handle vertex indices don't match
+  bool checkHandles(const std::map<int, Vec3d> & newHandles);
+
+  void rebuildSolver(const std::map<int, Vec3d> & newHandles);
+
+  //Solve 3 linear systems (n * n)
+  //call Lagrange multiplier solver SolveLinearSystem()
+  //store the position to sceneObjectDeformable (it uses displacement. instead of position)
+  void optimizePositions(double * disp);
+
+  ARAPModel arapModel;
+
+  std::map<int, Vec3d> handles;
+
+  std::vector<int> fixedVertices;
+  std::set<int> fixedVerticesSet;
+
+  int numThreads;
+
+  //per-edge Laplacian matrix: n * n
+
+  LagrangeMultiplierSolver * solver[3] = {nullptr, nullptr, nullptr};
+  int n, n3;
+};
+
+#endif
diff --git a/libraries/include/averagingBuffer.h b/libraries/include/averagingBuffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..5b3d9dbc58eb8790644f2cee4517633163ff633f
--- /dev/null
+++ b/libraries/include/averagingBuffer.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _AVERAGING_BUFFER_H_
+#define _AVERAGING_BUFFER_H_
+
+#include <vector>
+// FIFO averaging buffer
+
+class AveragingBuffer
+{
+public:
+  AveragingBuffer(int size);
+  virtual ~AveragingBuffer();
+
+  void addValue(double value);
+  double getAverage() const { return average; }
+
+  int getNumValuesAdded() const { return numValuesAdded; }
+
+  int getBufferSize() const { return size; }
+
+  void setBufferSize(int newSize);
+
+protected:
+  int size;
+  int index;
+  std::vector<double> buffer;
+  double average;
+  int numValuesAdded; // #values stored in the buffer
+};
+
+#endif
+
diff --git a/libraries/include/barycentricCoordinates.h b/libraries/include/barycentricCoordinates.h
new file mode 100644
index 0000000000000000000000000000000000000000..802c08c067a82570411dcd47baebdd41c19870f2
--- /dev/null
+++ b/libraries/include/barycentricCoordinates.h
@@ -0,0 +1,96 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "interpolationCoordinates" library , Copyright (C) 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef BARYCENTRICCOORDINATES_H
+#define BARYCENTRICCOORDINATES_H
+
+#include "volumetricMesh.h"
+#include "interpolationCoordinates.h"
+#include "sparseMatrix.h"
+#include "vec4d.h"
+#include "vec4i.h"
+#include "tetMesh.h"
+#include "objMesh.h"
+#include <vector>
+#include <string>
+
+// Compute and store barycentric coordinates.
+// Optionally, compute the indices of the elements that the embedded vertices belong to.
+
+class BarycentricCoordinates : public InterpolationCoordinates
+{
+public:
+
+  BarycentricCoordinates() {}
+
+  // Load from file. No element indices are available.
+  BarycentricCoordinates(const std::string & filename);
+
+  // Initialize with only element vertex indices and weights. No element indices are available.
+  BarycentricCoordinates(int numLocations, int numElementVertices, const int * elementVertexIndices, const double * weights,
+    const int * elementIndices = nullptr);
+
+  BarycentricCoordinates(const std::vector<Vec4i> & tetVtxIndices, const std::vector<Vec4d> & tetWeights, const int * elementIndices = nullptr);
+
+  virtual ~BarycentricCoordinates() {}
+
+  virtual void deform(const double * verticesDisp, double * locationDisp) const override;
+
+  int getNumLocations() const { return numLocations; }
+  int getNumElementVertices() const { return numElementVertices; }
+
+  const int * getEmbeddingVertexIndices(int embeddedVtx) const { return &indices[numElementVertices * embeddedVtx]; }
+  const double * getEmbeddingWeights(int embeddedVtx) const { return &weights[numElementVertices * embeddedVtx]; }
+
+  // Get internal data.
+  std::vector<int> & getEmbeddingVertexIndices() { return indices; }
+  std::vector<double> & getEmbeddingWeights() { return weights; }
+  std::vector<int> & getElements() { return elements; }
+
+  // Return embedding element index if it's available; -1 otherwise.
+  int getEmbeddingElement(int embeddedVtx) const { return elements[embeddedVtx]; }
+
+  // Save weights to file.
+  int saveInterpolationWeights(const std::string & filename) const;
+
+  SparseMatrix * generateInterpolationMatrix() const;
+
+protected:
+  int numLocations = 0;
+  int numElementVertices = 0;
+  std::vector<int> indices;
+  std::vector<double> weights;
+  std::vector<int> elements;
+};
+
+#endif /* BARYCENTRICCOORDINATES_H */
+
diff --git a/libraries/include/basicAlgorithms.h b/libraries/include/basicAlgorithms.h
new file mode 100644
index 0000000000000000000000000000000000000000..37f7e0711725fd978796e6ac3b7f1b7b602d3078
--- /dev/null
+++ b/libraries/include/basicAlgorithms.h
@@ -0,0 +1,228 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef ALGORITHM_H
+#define ALGORITHM_H
+
+#include "range.h"
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <vector>
+#include <cmath>
+
+template<class Container>
+int sizei(const Container & c) { return (int)c.size(); }
+
+// return true if all elements in [first, last] == value
+template<class InputIt, class T>
+bool allOf(InputIt first, InputIt last, const T & value)
+{
+  auto l = [&](const typename std::iterator_traits<InputIt>::value_type & it)
+  {
+    return it == value;
+  };
+  return std::find_if_not(first, last, l) == last;
+}
+
+template<class Container, class Range>
+void insertRange(Container & a, const Range & b) { a.insert(b.begin(), b.end()); }
+
+template<class Container, class Range>
+void insertRangeToEnd(Container & a, const Range & b) { a.insert(a.end(), b.begin(), b.end()); }
+
+// remove duplicate elements in vec and sort them as a side effect
+// elements of type T need a default constructor
+template<class T>
+void sortAndDeduplicate(std::vector<T> & vec)
+{
+  std::sort(vec.begin(), vec.end());
+  auto newEnd = std::unique(vec.begin(), vec.end());
+  vec.resize(std::distance(vec.begin(), newEnd));
+}
+
+// remove duplicate elements in vec and sort them as a side effect
+// use vector::erase to remove duplicate elements so that they don't need a default constructor
+template<class T>
+void sortAndDeduplicateWithErase(std::vector<T> & vec)
+{
+  std::sort(vec.begin(), vec.end());
+  auto newEnd = std::unique(vec.begin(), vec.end());
+  vec.erase(newEnd, vec.end());
+}
+
+// run binary search on a range to find whehter a val is in the range
+template<class RamdomAccessRange, class T>
+bool binarySearchFound(const RamdomAccessRange & range, const T & val)
+{
+  return binary_search(range.begin(), range.end(), val);
+}
+
+template<class T>
+T clamp(const T & a, const T & low, const T & high)
+{
+  if (a < low) { return low; }
+  if (a > high) { return high; }
+  return a;
+}
+
+template<class T>
+void clampSelf(T & a, const T & low, const T & high)
+{
+  if (a < low) { a = low; }
+  else if (a > high) { a = high; }
+}
+
+// return 1 if v > 0, -1 if v < 0, 0 if v == 0
+template<class T>
+int sign(const T & v)
+{
+  if (v > 0) return 1;
+  if (v < 0) return -1;
+  return 0;
+}
+
+// return 0.0 if v < 0.0
+// useful when you compute a value which should be >= 0.0 but might gives negative values due to numerical errors
+inline double sqrtSafe(double v)
+{
+  if (v < 0.0) { return 0.0; }
+  return std::sqrt(v);
+}
+
+// for a range of elements [first, last) and a unary func on the element,
+// find the element that gives max func(element)
+template<class InputIt, class UnaryFunction>
+InputIt maxFunctionValue(InputIt first, InputIt last, UnaryFunction func)
+{
+  if (first == last) { return last; }
+  InputIt largest = first;
+  auto value = func(*first);
+  first++;
+  for (; first != last; first++)
+  {
+    auto newValue = func(*first);
+    if (value < newValue)
+    {
+      largest = first;
+      value = newValue;
+    }
+  }
+  return largest;
+}
+
+// compute median for [first, last)
+// if first == last, return default value: value_type()
+// computing median involves averaging two values, this is done here by dividing by value_type(2)
+template<class RandomIt>
+typename std::iterator_traits<RandomIt>::value_type median(RandomIt first, RandomIt last)
+{
+  typedef typename std::iterator_traits<RandomIt>::value_type value;
+  if (first == last) { return value(); }
+  auto size = distance(first, last);
+  auto nth = first + size/2;
+  nth_element(first, nth, last);
+  if (size % 2 == 1) { return *nth; }
+
+  // size % 2 == 0
+  auto v0 = *nth;
+  nth_element(first, nth-1, last);
+  return (v0 + *(nth-1)) / value(2);
+}
+
+// remove elements from vector "inputVector" by given indices stored in "indices"
+// indices are sorted
+template<class InputVector, class IndexRange>
+void removeByIndices(InputVector & inputVector, const IndexRange & indices)
+{
+  int vecSize = inputVector.size();
+//  int indSize = distance(indices.begin(), indices.end());
+  auto ID = indices.begin();
+  int newEnd = 0;
+  for(int i = 0; i < vecSize; i++)
+  {
+    if (ID != indices.end() && i == *ID)
+    {
+      ID++;
+      continue;
+    }
+    if (i != newEnd)
+    {
+      inputVector[newEnd] = std::move(inputVector[i]);
+    }
+    newEnd++;
+  }
+  inputVector.resize(newEnd);
+}
+
+
+// The template function must be run on a sorted range from first to last.
+// It functions like std::unique, which operates on a sorted range and moves duplicated elements to the back of the range,
+// and finally returns the iterator pointing to the end of the deduplicated elements.
+// However, reduceDuplicates not only moves duplicate elements to the back, but also calls a reduce operator on
+// those duplicated elements.
+// reduce is a binary function: void reduce(T & entryA, T & entryB), reduces data in entryA and entryB and store the result into entryA
+// e.g. input buffer is [0, 1, 2.1, 2.2, 3, 4.1, 4.2, 4.3], and we only compare with the digits before decimal point
+// then the result is a buffer: [0, 1, 2.2, 3, 4.3, x, x, x], the function returns the iterator on the second 4,
+// and reduce is called on (2.1,2.2) and (4.1,4.2), (4.1,4.3)
+template<class ForwardIt, class BinaryReduce, class BinaryPredicate>
+ForwardIt reduceDuplicates(ForwardIt first, ForwardIt last, BinaryReduce reduce, BinaryPredicate p)
+{
+  if (first == last)
+    return last;
+
+  ForwardIt result = first;
+  while (++first != last)
+  {
+    if (p(*result, *first) == false) // if *result and *first are not equal
+    {
+      ++result; // move result forward
+      if (result != first)           // if result and first does not point to the same location, then
+        *result = std::move(*first); // move *first to *result
+    }
+    else // *result and *first are equal
+    {
+      reduce(*result, *first);
+    }
+  }
+  return ++result;
+}
+
+template<class ForwardIt, class BinaryReduce>
+ForwardIt reduceDuplicates(ForwardIt first, ForwardIt last, BinaryReduce reduce)
+{
+  using T = decltype(*first);
+  return reduceDuplicates(first, last ,reduce, std::equal_to<const T &>());
+}
+
+#endif
diff --git a/src/libintegratorSparse/centralDifferencesSparse.h b/libraries/include/centralDifferencesSparse.h
similarity index 83%
rename from src/libintegratorSparse/centralDifferencesSparse.h
rename to libraries/include/centralDifferencesSparse.h
index d4da4c46a0976093d2542d965e3955f012955d3b..35cd1ef35dc381a64978251b25d25cb6731b9fb3 100644
--- a/src/libintegratorSparse/centralDifferencesSparse.h
+++ b/libraries/include/centralDifferencesSparse.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,7 +32,7 @@
 
 /*
 
-A class to timestep dynamics (e.g. nonlinear deformable FEM)
+A class to timestep dynamics (e.g. nonlinear deformable FEM) 
 using the EXPLICIT central differences integrator. Normally, the implicit newmark and
 implicit backward Euler classes are recommended for nonlinear deformable FEM
 due to stability under large deformations.  See also integratorBase.h.
@@ -37,7 +41,7 @@ This implementation follows
 WRIGGERS P.: Computational Contact Mechanics. John
 Wiley & Sons, Ltd., 2002., page 275
 
-This class supports two damping modes:
+This class supports two damping modes: 
 1. Standard Rayleigh model
   D = dampingMassCoef * M + dampingStiffnessCoef * K
      where K is the stiffness matrix AT THE ORIGIN
@@ -45,16 +49,16 @@ This class supports two damping modes:
   D = dampingMassCoef * M + dampingStiffnessCoef * K(q)
      where K is the tangential stiffness matrix in the CURRENT deformed configuration q
 
-Mode 1. is computationally faster as it does not require system updates (i.e.,
-matrix inversion). Mode 1. is useful, for example, if you want to timestep
+Mode 1. is computationally faster as it does not require system updates (i.e., 
+matrix inversion). Mode 1. is useful, for example, if you want to timestep 
 linear modal analysis simulations (stiffness matrix is constant in that case).
 
 Mode 2. gives a better damping model for large deformations, but because the
 system matrix changes, requires factoring a linear system anew at each timestep.
 
-In order to use this class, you need to set the timestep very small, or
-else the explicit integrator will go unstable. Roughly speaking, the timestep
-must resolve the highest frequency present in your simulation.
+In order to use this class, you need to set the timestep very small, or 
+else the explicit integrator will go unstable. Roughly speaking, the timestep 
+must resolve the highest frequency present in your simulation. 
 
 See also integratorBase.h .
 
@@ -76,8 +80,6 @@ See also integratorBase.h .
   #include "CGSolver.h"
 #endif
 
-namespace vega
-{
 class CentralDifferencesSparse : public IntegratorBaseSparse
 {
 public:
@@ -88,13 +90,13 @@ public:
   inline virtual void SetTimestep(double timestep) { this->timestep = timestep; DecomposeSystemMatrix(); }
 
   // performs one timestep of simulation
-  virtual int DoTimestep();
+  virtual int DoTimestep(); 
 
-  // sets q, and (optionally) qvel
-  // returns 0
+  // sets q, and (optionally) qvel 
+  // returns 0 
   virtual int SetState(double * q, double * qvel=NULL);
 
-  // tangentialDampingMode:
+  // tangentialDampingMode: 
   // 0 = no updates of the damping matrix under deformations (not recommended for large deformations)
   // 1 = update at every timestep (default)
   // k>1 = update every kth timestep
@@ -104,8 +106,6 @@ public:
 
   virtual void ResetToRest();
 
-  SparseMatrix *GetSystemMatrix() const;
-
 protected:
   double * rhs;
   double * rhsConstrained;
@@ -133,4 +133,3 @@ protected:
 
 #endif
 
-}
diff --git a/libraries/include/closestPointField.h b/libraries/include/closestPointField.h
new file mode 100644
index 0000000000000000000000000000000000000000..f208a850359098e70cea7b8d882995921fde5de6
--- /dev/null
+++ b/libraries/include/closestPointField.h
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  ClosestPointField is similar to a distance field, 
+  except it also stores the nearest point, not just the distance to it.
+*/
+
+#ifndef _CLOSEST_POINT_FIELD_H_
+#define _CLOSEST_POINT_FIELD_H_
+
+#include "distanceField.h"
+
+class ClosestPointField : public DistanceField
+{
+public:
+  ClosestPointField();
+  ~ClosestPointField();
+
+  int computeUnsignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+  int computeSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+
+  virtual int load(const std::string& filename);
+  virtual int save(const std::string& filename, bool doublePrecision=true);
+  virtual void set(int resolutionX, int resolutionY, int resolutionZ, Vec3d bmin_, Vec3d bmax_, float * distanceData, float * closestPointData);
+
+  void closestPoint(float pos[3], float result[3]);
+
+  inline void closestPoint(int i, int j, int k, float result[3]) { int pos = 3*((k * (resolutionY+1) + j ) * (resolutionX+1) + i); result[0] = closestPointData[pos]; result[1] = closestPointData[pos+1]; result[2] = closestPointData[pos+2]; }
+
+  virtual bool sanityCheck();
+
+  void getClosestPointData(float ** floatBuffer);
+
+  inline void setClosestPoint(int i, int j, int k, float value[3]) { int pos = 3*((k * (resolutionY+1) + j ) * (resolutionX+1) + i); closestPointData[pos] = value[0]; closestPointData[pos+1] = value[1]; closestPointData[pos+2] = value[2]; }
+
+protected:
+  float * closestPointData;
+
+  virtual int ZigZagSigned(void * objMeshOctree, void * meshGraph);
+  virtual int ZigZagUnsigned(void * objMeshOctree, void * meshGraph);
+  virtual int ZigZagSigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+  virtual int ZigZagUnsigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+};
+
+#endif
+
diff --git a/libraries/include/clothBW.h b/libraries/include/clothBW.h
new file mode 100644
index 0000000000000000000000000000000000000000..a874711ded3c92259b0cead6edd17fc2187e49f3
--- /dev/null
+++ b/libraries/include/clothBW.h
@@ -0,0 +1,233 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "clothBW" library , Copyright (C) 2018 USC                            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+ This class implements the cloth model presented in:
+
+ Baraff and Witkin: Large Steps in Cloth Simulation, SIGGRAPH 1998. 
+ 
+ It can compute the elastic energy, the internal elastic forces, and the tangent stiffness matrix,
+ for stretch/shear and bend terms.
+ Damping is not implemented, but you can use the damping in the Vega integrator class.
+
+ Our implementation follows the original Baraff and Witkin paper, with the insights
+ presented in the following report:
+
+ David Pritchard: Implementing Baraff & Witkin's Cloth Simulation, May 2003, with minor updates April 2012
+*/
+
+#ifndef _CLOTHBW_H_
+#define _CLOTHBW_H_
+
+#include "sparseMatrix.h"
+#include "vec3d.h"
+
+class ClothBW
+{
+public:
+
+  // cloth material parameters
+  struct MaterialGroup
+  {
+    double tensileStiffness;
+    double shearStiffness;
+    double bendStiffnessU;
+    double bendStiffnessV;
+    double damping;
+  };
+
+  // creates the cloth elastic model, from a given triangle mesh
+  // "masses" is an array of length "numVertices"
+  // "restPositions" is an array of length 3x"numVertices"
+  // "triangles" is an integer array of length 3x"numTriangles" (giving integer indices of the three vertices forming a triangle)
+  // "triangleGroups" is an integer array of length "numTriangles" (giving the integer index of the material group to which each triangle belongs)
+  // "triangleUVs" is an double array of length 3x2x"numTriangles", indicating the uv for every vertex; this array can be user-provided, or the constructor can compute it automatically
+  // all indices in this class are 0-indexed
+  // constructor that does not require triangleUVs input (it computes UVs automatically; note: the UVs are continuous only within each triangle; the UV map is not global (which is fine, provided one does not want to simulate anisotropic effects) )
+  ClothBW(int numVertices, const double * restPositions, const double * masses,
+      int numTriangles, const int * triangles, const int * triangleGroups,
+      int numMaterialGroups, const MaterialGroup * materialGroups, int addGravity=0);
+  
+  // constructor with triangleUVs input
+  ClothBW(int numVertices, const double * restPositions, const double * masses,
+      int numTriangles, const int * triangles, const double * triangleUVs, const int * triangleGroups,
+      int numMaterialGroups, const MaterialGroup * materialGroups, int addGravity=0);
+    
+  virtual ~ClothBW();
+
+  int GetNumVertices() const { return numVertices; }
+  int GetNumTriangles() const { return numTriangles; }
+  int GetNumQuads() const { return numQuads; }
+  int GetNumMaterialGroups() const { return (int)materialGroups.size(); }
+
+  const int * GetTriangles() const { return triangles.data(); }
+  const Vec3d * GetRestPositions() const { return restPositions.data(); }
+
+  const int *GetTriangleVertexIndices(int tri) const { return triangles.data() + tri * 3; }
+  const int *GetQuadVertexIndices(int quad) const { return quadComponentIndices[quad].v; }
+  const int *GetQuadTriangleIndices(int quad) const { return quadComponentIndices[quad].tri; }
+
+  void SetRestUVStretchValues(double bu, double bv) { this->bu = bu; this->bv = bv; } // these are the b_u and b_v stretch values from Equation (10) in the BW paper (default values are 1.0)
+
+  // computes the gravitational force (result goes into f)
+  void SetGravity(bool addGravity, double g=9.81) { this->addGravity = addGravity; this->g = g; } // if addGravity is enabled, ComputeForces will add the gravity force to the elastic forces
+
+  // this allows users to toggle on/off computation of the stretch/shear forces, bend forces,
+  // stretch/shear stiffness matrix, and bend stiffness matrix
+  // the function ComputeEnergy uses mode[0] and mode[1] flags
+  // mode[0] = computeStretchAndShearForce
+  // mode[1] = computeBendForce
+  // mode[2] = computeStretchAndShearStiffnessMatrices
+  // mode[3] = computeBendStiffnessMatrices
+  void SetComputationMode(const bool mode[4]);
+
+  // allows user to toggle on/off the use of rest angles in bend force/stiffness matrix
+  // calculations; if set to 1, bend force/matrix will be computed in relation to the quad's rest
+  // angle. if set to 0, bend force/matrix will be computed in relation to a flat angle of 0.0
+  // default is 1.
+  void UseRestAnglesForBendingForces(bool useRestAnglesForBend) { useRestAnglesForBendingForces = useRestAnglesForBend; }
+
+  // creates the mass matrix (which is diagonal); each diagonal entry is expanded into a diagonal submatrix of size 'expanded' (typically, for 3D simulations, expanded should be 3)
+  void GenerateMassMatrix(SparseMatrix ** M, int expanded=3) const;
+
+  // === compute elastic energy, force and stiffness matrix ===
+
+  // compute the elastic energy, under deformation u
+  virtual double ComputeEnergy(const double * u);
+
+  // compute the internal elastic force, under deformation u
+  // note: the force has the sign of the left side of the dynamic equation, Mu'' + Du' + f_int(u) = f_ext(t), i.e., f_int(u), that is, **opposite** to an external force f_ext(t) acting on the body 
+  virtual void ComputeForce(const double * u, double * f, bool addForce=false); // if addForce is "true", f will be not be reset to zero prior to adding the forces
+
+  // compute the tangent stiffness matrix of the elastic force
+  // call once to establish the location of sparse entries of the stiffness matrix
+  void GenerateStiffnessMatrixTopology(SparseMatrix ** K) const;
+
+  virtual void ComputeStiffnessMatrix(const double * u, SparseMatrix * K, bool addMatrix=false);
+
+  virtual void ComputeForceAndMatrix(const double * u, double * f, SparseMatrix * K, bool addQuantity = false);
+
+  // compute the damping force
+  // unimplemented
+  // (use damping provided in the integrator class)
+  // virtual void ComputeDampingForce(const double * u, double * uvel, double * f, bool addForce=false);
+
+  // compute the energy E, internal forces f and stiffness matrix K of a single triangle.
+  // u is the input displacement with dimention (# vtx x 3)
+  // E is a pointer to a double
+  // f is a pointer to a array of 9 doubles (3x3)
+  // K is a pointer to a array of 81 doubles ((3x3) x (3x3)). The matrix is store in column major.
+  // E, f, K can be nullptr. If it is, the function will not compute it.
+  void ComputeTriangleElement(int tri, const double *u, double *E, double f[9], double K[81]) { ComputeStretchAndShear(u, tri, E, f, K); }
+  // compute the bending energy E, internal forces f and stiffness matrix K of a edge that involves two triangles.
+  // function arguments are the same as the previous one.
+  void ComputeQuadElement(int quad, const double *u, double *E, double f[12], double K[144]) { ComputeBending(u, quad, E, f, K); }
+
+  // compute the gravity of a single vertex
+  void ComputeVertexGravity(int vid, double g[3]);
+
+protected:
+
+  struct WuvInfo // derivatives of wu, wv with regard to vtx displacements
+  {
+    double pwupx[3];
+    double pwvpx[3];
+  };
+
+  struct BendInfo // store vtx and triangle indices for computing bend force
+  {
+    int v[4];
+    int tri[2];
+  };
+
+  static double GetTriangleSurfaceArea(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2);
+
+  static WuvInfo ComputeWuvInfo(const double triangleUV[6]);
+
+  double ComputeBendingStiffness(const BendInfo & bendInfo, const double * triangleUVs);
+
+  void GenerateBW(int numVertices, const double * restPositions, const double * masses,
+    int numTriangles, const int * triangles, const double * triangleUVs, const int * triangleGroups,
+    int numMaterialGroups, const MaterialGroup * materialGroups, int addGravity=0);
+
+  void ComputeStretchAndShear(const double * u, int triangleID, double * energy, double f[9], double K[81]);
+
+  void ComputeBending(const double * u, int quadId, double * energy, double f[12], double K[144]);
+
+  void AddStretchAndShear(const double * u, int startTriangle, int endTriangle, double * energy, double * f, SparseMatrix * K);
+
+  void AddBend(const double * u, int startQuad, int endQuad, double * energy, double * f, SparseMatrix * K);
+
+  // unimplemented
+  // (use damping provided in the integrator class)
+  //void AddDampingForce(double * uvel, double * f, int startTriangle, int endTriangle);
+
+  void ComputeGravity(double * f);
+
+  int numVertices;
+  std::vector<double> masses;
+  std::vector<Vec3d> restPositions;
+  int numTriangles;
+  std::vector<int> triangles;
+
+  std::vector<WuvInfo> wuvInfos;
+  std::vector<int> inverseIndicesStretchAndShear;
+  std::vector<int> triangleGroups;
+  std::vector<double> alphas; // a scaling factor used in stretch and shear energy
+
+  // Internal variables for bending computation:
+  // "numQuads" is number of 'quads' made up of two adjacent triangles. Important because each quad contains a bendable edge (the edge shared by the two triangles).
+  // "restAngles" is array of size numQuads containing the rest angles of edges that can bend (indexed by numQuads)
+  // "inverseIndicesQuad" is numQuads x 16 integer array
+  // "quadComponentIndices" is numQuads x 6 integer array
+  int numQuads;
+  std::vector<double> restAngles;       // rest angle for each quad
+  std::vector<double> bendStiffnesses;  // bend stiffness for each quad
+  std::vector<int> inverseIndicesQuad;
+  std::vector<BendInfo> quadComponentIndices;
+
+  int numMaterialGroups;
+
+  std::vector<MaterialGroup> materialGroups;
+
+  double bu; // stretch constraint in u (default = 1.0)
+  double bv; // stretch constraint in v (default = 1.0)
+
+  int addGravity;
+  double g;
+
+  bool cond[4];
+  bool useRestAnglesForBendingForces;
+};
+
+#endif
+
diff --git a/libraries/include/clothBWForceModel.h b/libraries/include/clothBWForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..67cd6bc069699ace00cf9e4ab631fcae63d4410f
--- /dev/null
+++ b/libraries/include/clothBWForceModel.h
@@ -0,0 +1,59 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Cloth model from [Baraff Witkin 1998] 
+*/
+
+#ifndef _CLOTHBWFORCEMODEL_H_
+#define _CLOTHBWFORCEMODEL_H_
+
+#include "clothBW.h"
+#include "forceModel.h"
+
+class ClothBWForceModel : public ForceModel
+{
+public:
+  ClothBWForceModel(ClothBW * clothBW);
+  virtual ~ClothBWForceModel();
+  
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix) override;
+protected:
+  ClothBW * clothBW;
+};
+
+#endif
+
diff --git a/libraries/include/clothBWFromObjMesh.h b/libraries/include/clothBWFromObjMesh.h
new file mode 100644
index 0000000000000000000000000000000000000000..25857d0934ea974e9abb2a64f8ea1c94a5979fa8
--- /dev/null
+++ b/libraries/include/clothBWFromObjMesh.h
@@ -0,0 +1,65 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "clothBW" library , Copyright (C) 2018 USC                            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Constructs a cloth model from an obj mesh.
+*/
+
+#ifndef _CLOTHBWFROMOBJMESH_H_
+#define _CLOTHBWFROMOBJMESH_H_
+
+#include "clothBW.h"
+#include "objMesh.h"
+
+class ClothBWFromObjMesh
+{
+public:
+  
+  // generate a cloth model from the given mesh (builds tensile, shear, and bending springs)
+  // surface density and stiffnesses are the same for every triangle
+  static ClothBW * GenerateClothBW(const ObjMesh * mesh, double surfaceDensity, const ClothBW::MaterialGroup & material,int addGravity=0);
+
+  // NOTE: materialGroup 0 is hard-coded as "default" in ObjMesh.cpp. So if a .mtl file 
+  // specifies 2 materials (with 2 'usemtl' calls), there will actually be 3 material groups.
+  // As a result, the density/stiffness arrays that specify a value for each material group
+  // all must contain a value at the beginning for the default material (even if no vertices
+  // belong to this default group).
+  
+  // generate a cloth model from the given mesh (builds tensile, shear, and bending springs)
+  // user passes array of doubles to specify surface densities and stiffness values for each material group
+  static ClothBW * GenerateClothBW(const ObjMesh * mesh, int numMaterialGroups, const double * groupSurfaceDensities, const ClothBW::MaterialGroup * materials, int addGravity=0);
+  
+protected:
+};
+
+#endif
+
diff --git a/libraries/include/clothBWStencilForceModel.h b/libraries/include/clothBWStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..37cd5494036a485a7e938a7f967beac840ad79e0
--- /dev/null
+++ b/libraries/include/clothBWStencilForceModel.h
@@ -0,0 +1,61 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef _CLOTHBW_STENCIL_FORCEMODEL_H_
+#define _CLOTHBW_STENCIL_FORCEMODEL_H_
+
+#include "clothBW.h"
+#include "stencilForceModel.h"
+
+// Stencils for cloth simulation. There are two different stencils here.
+// One is a triangle (# vertices = 3) (for in-place stretch and shear);
+// and the other is an edge joining two triangles (# vertices = 4) (for bending).
+// See comments in the parent class.
+class ClothBWStencilForceModel : public StencilForceModel
+{
+public:
+  ClothBWStencilForceModel(ClothBW * clothBW);
+  virtual ~ClothBWStencilForceModel();
+
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+
+  virtual void GetVertexGravityForce(int vertexId, double gravity[3]) override;
+
+  ClothBW * GetForceModelHandle() { return clothBW; }
+protected:
+  ClothBW * clothBW;
+};
+
+#endif
+
diff --git a/libraries/include/commandLineParser.h b/libraries/include/commandLineParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..964d7f6064c7bc5a58a4f51804570a01428f27bb
--- /dev/null
+++ b/libraries/include/commandLineParser.h
@@ -0,0 +1,108 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "getopts" library , Copyright (C) 2018 USC                            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef COMMANDLINEPARSER_H
+#define COMMANDLINEPARSER_H
+
+#include "vegalong.h"
+#include <string>
+#include <vector>
+
+// parser for command line arguments
+// same argument syntax as in getopts.h:
+//
+// switches come with either "-" or "/", optionally followed by "=". e.g. -a  /h  -x=
+// the parser accepts:
+//
+// string:
+//   -s abc
+//   -sabc
+// whitespace must follow a string argument, no -sstring-a is allowed
+// number
+//   -a123.4
+//   -a 123.4
+//   -v-57
+// there must be no whitespace between the switch and a negative number, no -v -57 is allowed
+// boolean
+//   -b+
+//   -b
+//   -b-
+// the parser accepts a "false" boolean value only when a '-' after the switch without whitespace, like -b-
+// all the other situations are considered as "true"
+
+class CommandLineParser
+{
+public:
+  CommandLineParser();
+  virtual ~CommandLineParser() {}
+
+  void addOption(const std::string & name, int & value);
+  void addOption(const std::string & name, char * value);
+  void addOption(const std::string & name, bool & value);
+  void addOption(const std::string & name, vegalong & value);
+  void addOption(const std::string & name, float & value);
+  void addOption(const std::string & name, double & value);
+  void addOption(const std::string & name, std::string & value);
+
+  // parse args from argv. numSkipArg tells how many entries in argv are skipped before args are reached
+  // on success, return argc
+  // on failure, return the 0-index of the arg that caused failure
+  int parse(int argc, char ** argv, int numSkipArg = 1);
+  int parse(int argc, const char ** argv, int numSkipArg = 1);
+
+protected:
+
+  enum Type
+  {
+    INT = 1,
+    CSTR = 2,
+    BOOL = 3,
+    LONG = 4,
+    FLOAT = 5,
+    DOUBLE = 6,
+    STRING = 7
+  };
+
+  struct Entry
+  {
+    std::string name;
+    Type type;
+    void * value;
+    Entry(const std::string & name, Type type, void * value);
+  };
+  std::vector<Entry> entries;
+};
+
+
+
+
+#endif
diff --git a/src/libvolumetricMesh/computeStiffnessMatrixNullspace.h b/libraries/include/computeStiffnessMatrixNullspace.h
similarity index 83%
rename from src/libvolumetricMesh/computeStiffnessMatrixNullspace.h
rename to libraries/include/computeStiffnessMatrixNullspace.h
index 5dbabc34081437f47f4ffbfb7cc3dabd6e603df0..2b8ab860520428d31fa46913840b711c931fcf9b 100644
--- a/src/libvolumetricMesh/computeStiffnessMatrixNullspace.h
+++ b/libraries/include/computeStiffnessMatrixNullspace.h
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -44,8 +48,6 @@
   Use "includeRotationalNullspace" to set the desired nullspace size (3 when deformed, or 6 when undeformed).
 */
 
-namespace vega
-{
 class ComputeStiffnessMatrixNullspace
 {
 public:
@@ -57,6 +59,6 @@ public:
   static void RemoveNullspaceComponent(int n, int nullspaceDimension, const double * nullspaceOrthonormalBasis, double *x);
 
 };
-}
+
 #endif
 
diff --git a/libraries/include/configFile.h b/libraries/include/configFile.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f8427d0f6e5d8cca4fee90f7dafd340ef7e1c46
--- /dev/null
+++ b/libraries/include/configFile.h
@@ -0,0 +1,195 @@
+#ifndef _CONFIGFILE_H_
+#define _CONFIGFILE_H_
+
+/*
+
+* Copyright (c) 2007, Carnegie Mellon University
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Code author: Jernej Barbic
+  CMU, 2005-2007
+  Version 1.0
+
+  A class to parse text configuration files. 
+  See configFile-example.cpp and configFile-example.config for examples. 
+  It is by far easier to look at the example first, than to proceed
+  straight to reading the class interface.
+
+  Supported types for option entries:
+  int (integer)
+  bool (boolean, true/false)
+  float (single precision floating point)
+  double (double precision floating point)
+  char* (a C-style string)
+  Vec3d (vector of three double values)
+  string (std::string)
+  vector<string> (0 to multiple lines of string)
+
+  Configuration files are text files. The format for each option entry is:
+  *<option name>
+  <option value>
+
+  Example:
+  *temperature
+  36.6
+
+  *rainy
+  # boolean option can accept both integer (0/1) or text (true/false) input
+  false
+
+  *color
+  # Vec3d option can accept numbers separated by either comma "," or space " ", or surrounded by "()" or "[]"
+  # 0.4, 0.8, 0.5
+  # [0.4 0.8 0.5]
+  # ( 0.4 0.8 0.5 )
+  0.4 0.8 0.5
+
+  *street
+  # string/char* option accepts one line of characters. Memory preallocation
+  # is required for char* option
+  Hoover
+
+  *toDoList
+  # vector<string> option can accept 0 to multiple lines of text
+  wash car
+  buy food
+  repair chair
+
+  Blank lines are ignored. 
+  A line is a comment if it begins with a "#".
+  The options can appear in arbitrary order. They need not follow the order
+  in which they were created via addOption/addOptionOptional.
+
+*/
+
+#include <vector>
+#include <string>
+#include "vec3d.h"
+
+class ConfigFile
+{
+public:
+  ConfigFile();
+  virtual ~ConfigFile();
+
+  // === routines to define the valid option entries for your configuration file ===
+  // each option entry is either mandatory, or optional (in which case you need to provide a default value)
+  // option names are case IN-sensitive (i.e., upper/lower case does not matter)
+  // "destLocation" will be overwritten with the value read from the configuration file when "parseOptions" is called (or default value will be used in case of optional option entries if a particular configuration file did not provide the entry)
+
+  // routines to specify mandatory option entries
+  // if "parseOptions" does not find a mandatory option in a particular configuration file, it will exit with a non-zero code
+  int addOption(const char * optionName, int * destLocation);
+  int addOption(const char * optionName, bool * destLocation);
+  int addOption(const char * optionName, float * destLocation);
+  int addOption(const char * optionName, double * destLocation);
+  int addOption(const char * optionName, Vec3d * destLocation);
+  int addOption(const char * optionName, char * destLocation); // for strings, you must pass a pointer to a pre-allocated string buffer (and not a pointer to a (char*) pointer)
+  int addOption(const char * optionName, std::string * destLocation);
+  int addOption(const char * optionName, std::vector<std::string> * destLocation); // allow arbitrary lines of strings
+
+  // routines to specify option entries that are optional
+  // if not specified in a particular config file, the value will default to the given default value
+  template<class T>
+  int addOptionOptional(const char * optionName, T * destLocation, const T & defaultValue);
+  int addOptionOptional(const char * optionName, char * destLocation, const char * defaultValue);
+
+  // === routines to parse config files ===
+  // after you have specified your option entries with addOption and/or addOptionOptional, 
+  // call "parseOptions" to open a particular configuration file and load the option values to their destination locations.
+  int parseOptions(const char * filename, int verbose = 1); // returns 0 on success, and a non-zero value on failure
+  int parseOptions(std::istream & in, int verbose = 1);     // makes it possible to parse from std::istream
+  int parseOptions(FILE * fin, int verbose=1);              // makes it possible to parse from a file stream
+
+  // after calling "parseOptions", you can print out the values of all options, to see the values that were read from the configuration file
+  void printOptions() const;
+
+  // whether an option is loaded
+  // this data is reset everytime before calling parseOptions()
+  // return false if no such option name
+  // note: take O(n) to find the option
+  bool isOptionLoaded(const char * optionName) const;
+
+  // save option to disk
+  // Option values are stored as pointers. So it will save the values stored at the variables passed in addOption(Optional)
+  // when saveOptions() is called.
+  // return 0 if succeeds
+  int saveOptions(const char * filename) const;
+
+  // === recursive parsing ===
+
+  // following routines allow recursive parsing. first convert config file to a vector of entries (<option name>, <option value>)
+  // then in each level, a ConfigFile object parses the entry vector, assigns the correct options that the object knows, 
+  // and passes the unrecognized entries to the next level
+  struct Entry 
+  {
+    std::string option;
+    std::string value;
+    int lineCount;
+    Entry() : lineCount(-1) {}
+  };
+  typedef std::vector<Entry> Entries;
+
+  // parse a file and return its config entries
+  static int parse(const char * filename, Entries & entries, const char * stoppingString = "**EOF", int verbose = 1);
+  static int parse(std::istream & in, Entries & entries, const char * stoppingString = "**EOF", int verbose = 1); // makes it possible to parse from a file stream
+ 
+  // parse option from entries. If remainingEntries is provided, those entries that are not recognized are pushed into remainingEntries
+  int parseOptions(const Entries & entries, Entries * remainingEntries = NULL, int verbose = 1);
+
+  // === additional settings ===
+
+  // (optional) set a stopping string (when it is encountered in a file, parsing will stop); default: **EOF
+  // if the stopping string does not appear in a file, it will be parsed to the end
+  void setStoppingString(const char * stoppingString);
+
+  // you can disable printing out warnings (default: enabled)
+  void suppressWarnings(int suppressWarnings_) { this->suppressWarnings_ = suppressWarnings_; }
+
+protected:
+  std::vector<std::string> optionNames;
+  std::vector<int> optionTypes;
+  std::vector<void*> destLocations;
+
+  std::vector<bool> optionSet;     // whether an option is set or not as default in addOptionOptional() or by the loaded configFile
+  std::vector<bool> optionLoaded;  // whether an option is loaded from file
+  
+  int seekOption(const char * optionName) const; // returns -1 if not found
+
+  template<class T>
+  int addOptionHelper(const char * optionName, T * destLocation);
+
+  int parseNumber(int type, const char * line, void * dest, int optionIndex, int verbose);
+
+  static void strip(std::string & s);
+  static void upperCase(char * s); // converts a string to upper case
+
+  int suppressWarnings_;
+
+  char stoppingString[32];
+};
+
+#endif
+
diff --git a/libraries/include/constrainedDOFs.h b/libraries/include/constrainedDOFs.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef54835b6c48526284e0cd30d733212f7cbd4aad
--- /dev/null
+++ b/libraries/include/constrainedDOFs.h
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "constrainedDOFs" library , Copyright (C) 2007 CMU, 2009 MIT          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic, Yijing Li                                 *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _CONSTRAINED_DOFS_H_
+#define _CONSTRAINED_DOFS_H_
+
+/*
+  Insert or remove given components from a linear array.
+  The dynamic solver uses these routines to fix the specified vertices and remove
+  rows from mass and stiffness matrices, as necessary.
+*/
+class ConstrainedDOFs
+{
+public:
+  // The input fixedRows array must be pre-sorted
+
+  // inserts zero entries into an array, at the specified locations
+  // the locations must be given with respect to the full array 
+  // input: xConstrained
+  // output: x
+  static void InsertDOFs(int mFull, const double * xConstrained, double * x, int numFixedRows, const int * fixedRows, int oneIndexed=0);   
+
+  // removes entries at the specified locations from an array
+  // the locations must be given with respect to the full array 
+  // input: x
+  // output: xConstrained
+  static void RemoveDOFs(int mFull, double * xConstrained, const double * x, int numFixedRows, const int * fixedRows, int oneIndexed=0);   
+
+  // translates the array indices from original indices to indices after removal of the specified entries
+  // input: DOFs (must be sorted) (0-indexed)
+  // output: DOFsConstrained (0-indexed)
+  // oneIndexed applies only to fixedRows array, NOT to DOFsConstrained or DOFs
+  static void FullDOFsToConstrainedDOFs(int mFull, int numDOFs, int * DOFsConstrained, const int * DOFs, int numFixedRows, const int * fixedRows, int oneIndexed=0);   
+
+  // find the free DOFs that do not belong to fixedDOFs
+  // input: fixedDOFs, pre-sorted
+  // output freeDOFs, sorted
+  // oneIndexed applies only to fixedDOFs array, not freeDOFs
+  // example: mFull=7, fixedDOFs=(2, 3, 5), oneIndexed=0 => freeDOFs=(0,1,4,6)
+  static void FindFreeDOFs(int mFull, int * freeDOFs, int numFixedDOFs, const int * fixedDOFs, int oneIndexed=0);  
+
+  // return true if DOFs is sorted (from small to large) and in range of [0, mFull) if (oneIndexed == 0) or [1, mFull] if (oneIndex == 1)
+  static bool CheckValidSortedDOFs(int mFull, int numDOFs, const int * DOFs, int oneIndexed=0);
+
+};
+#endif
+
diff --git a/libraries/include/containerHelper.h b/libraries/include/containerHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ba331636d4925ff4279c38cc13913b1b70c9358
--- /dev/null
+++ b/libraries/include/containerHelper.h
@@ -0,0 +1,434 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef CONTAINERHELPER_H
+#define CONTAINERHELPER_H
+
+#include <vector>
+#include <set>
+#include <map>
+#include <iostream>
+#include <cstring>
+#include <cmath>
+#include <algorithm>
+#include <numeric>
+#include <unordered_set>
+
+/////////////////////////////////////////////////////
+//            Check element in set/map             //
+/////////////////////////////////////////////////////
+
+template <class T, class C, class A>
+bool setFind(const std::set<T,C,A> & s, const T & x) { return s.find(x) != s.end(); }
+template <class T, class C, class A>
+bool setNotFind(const std::set<T,C,A> & s, const T & x) { return s.find(x) == s.end(); }
+
+template <class T, class U, class C, class A>
+bool mapFind(const std::map<T, U, C, A> & s, const T & x) { return s.find(x) != s.end(); }
+template <class T, class U, class C, class A>
+bool mapNotFind(const std::map<T, U, C, A> & s, const T & x) { return s.find(x) == s.end(); }
+
+template <class T, class H, class P, class A>
+bool usetFind(const std::unordered_set<T,H,P,A> & s, const T & x) { return s.find(x) != s.end(); }
+
+template <class T, class H, class P, class A>
+bool usetNotFind(const std::unordered_set<T,H,P,A> & s, const T & x) { return s.find(x) == s.end(); }
+
+/////////////////////////////////////////////////////
+//            insert Data in Container             //
+/////////////////////////////////////////////////////
+
+template<class T, class A, class Container>
+void vectorInsertRangeBack(std::vector<T,A> & v, const Container & c) { v.insert(v.end(), c.begin(), c.end()); }
+
+/////////////////////////////////////////////////////
+//                Memory operations                //
+/////////////////////////////////////////////////////
+
+template<class T, class A>
+inline void memset(std::vector<T, A> & v, unsigned char value = 0)
+{
+  memset(v.data(), value, sizeof(T) * v.size());
+}
+
+template<class T, class A>
+inline void memcpy(std::vector<T, A> & v, const T * src)
+{
+  memcpy(v.data(), src, sizeof(T) * v.size());
+}
+
+// free any memory allocated in the vector
+template<class T, class A>
+inline void freeMemory(std::vector<T, A> & v)
+{
+  std::vector<T> tmp;
+  v.swap(tmp);
+}
+
+template<class T, class A>
+inline bool inVectorRange(int index, const std::vector<T,A> & vec)
+{
+  return index >= 0 && index < (int)vec.size();
+}
+
+/////////////////////////////////////////////////////
+//               Mapping operations                //
+/////////////////////////////////////////////////////
+
+// create a reverse mapping from each value of vec to its index
+// assuming the values in vec are unique
+template<class T, class A>
+std::map<int, int> getReverseMapping(const std::vector<T,A> & vec)
+{
+  std::map<int, int> ret;
+  for(size_t i = 0; i < vec.size(); i++)
+    ret[vec[i]] = i;
+  return ret;
+}
+
+// return a vector containing unique elements
+// inpID2unqID: optionally return the mapping: input ID -> output unique ID
+// unqID2inpID: optionally return the mapping: output unique ID -> input ID
+template<class T, class A>
+std::vector<T,A> findUniqueElements(const std::vector<T,A> vec, std::vector<int> * inpID2unqID = nullptr, std::vector<int> * unqID2inpID = nullptr);
+
+/////////////////////////////////////////////////////
+//           Euclidean norm / distance             //
+/////////////////////////////////////////////////////
+
+// the squared Euclidean norm of the vector
+template<class T>
+T squaredEuclideanNorm(const std::vector<T> & v);
+template<class T>
+T squaredEuclideanNorm(size_t r, const T * v);
+
+// the squared Euclidean distance between the two vectors of the same size
+template<class T>
+T squaredEuclideanDistance(const std::vector<T> & v1, const std::vector<T> & v2);
+template<class T>
+T squaredEuclideanDistance(size_t r, const T * v1, const T * v2);
+
+
+// compute Euclidean norm / distance of double arrays
+inline double EuclideanNorm(const std::vector<double> & v)
+{
+  return sqrt(squaredEuclideanNorm(v));
+}
+
+inline double EuclideanNorm(size_t r, const double * v)
+{
+  return sqrt(squaredEuclideanNorm(r, v));
+}
+
+inline double EuclideanDistance(const std::vector<double> & v1, const std::vector<double> & v2)
+{
+  return sqrt(squaredEuclideanDistance(v1,v2));
+}
+
+inline double EuclideanDistance(size_t r, const double * v1, const double * v2)
+{
+  return sqrt(squaredEuclideanDistance(r, v1, v2));
+}
+
+/////////////////////////////////////////////////////
+//                  Set operation                  //
+/////////////////////////////////////////////////////
+
+// return whether two sets intersect
+template<class T, class C, class A>
+bool intersect(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2);
+
+// compute the intersection of two vectors and store the result in out (previous data in out is removed)
+template<class T>
+void intersect(const std::vector<T> & v1, const std::vector<T> & v2, std::vector<T> & out);
+
+// return whether two sorted vectors intersect
+template<class T>
+bool intersectSorted(const std::vector<T> & v1, const std::vector<T> & v2);
+
+template<class T,class C, class A>
+int getIntersectionSize(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2);
+
+// return true if A is a subset of B
+template<class T,class C, class A>
+bool isSubset(const std::set<T,C,A> & setA, const std::set<T,C,A> & setB);
+
+// compute s1 / s2
+template<class T, class C, class A>
+std::set<T,C,A> setMinus(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2);
+
+
+/////////////////////////////////////////////////////
+//                 Print elements                  //
+/////////////////////////////////////////////////////
+
+// print (a->x, b->y, c->z, ...) to ostream
+template <class T, class U>
+void printMap(std::ostream & os, const std::map<T, U> & s);
+
+/////////////////////////////////////////////////////
+//         SAVE & LOAD IMPLEMENTATIONS             //
+/////////////////////////////////////////////////////
+// for use in saving/loading a class which includes vector as member var.
+// Same save/load file format as used in vega mayaPlugin
+template<class T>
+bool saveToAscii(const std::vector<T> & v, std::ostream & out);
+
+template<class T>
+bool loadFromAscii(std::vector<T> & v, std::istream & in);
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Below are template implementation
+
+template<class T,class C, class A>
+bool intersect(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2)
+{
+  if (s1.size() == 0 || s2.size() == 0)
+    return false;
+  const std::set<T> * ps1 = &s1, * ps2 = &s2;
+  if(s1.size() > s2.size())
+  {
+    ps1 = &s2;
+    ps2 = &s1;
+  }
+  for(typename std::set<T,C,A>::const_iterator it = ps1->begin(); it != ps1->end(); it++)
+    if (ps2->find(*it) != ps2->end())
+      return true;
+  return false;
+}
+
+template<class T>
+void intersect(const std::vector<T> & v1, const std::vector<T> & v2, std::vector<T> & out)
+{
+  out.clear();
+  if (v1.size() == 0 || v2.size() == 0)
+    return;
+  const std::vector<T> * pv1 = &v1, * pv2 = &v2;
+  if (v1.size() > v2.size())
+  {
+    pv1 = &v2; // pv1 points to the smaller set
+    pv2 = &v1;
+  }
+  std::set<T> s2;
+  s2.insert(pv2->begin(), pv2->end());
+  for(size_t i = 0; i < pv1->size(); i++)
+  {
+    const T & x = (*pv1)[i];
+    if (s2.find(x) != s2.end())
+      out.push_back(x);
+  }
+}
+
+template<class T>
+bool intersectSorted(const std::vector<T> & v1, const std::vector<T> & v2)
+{
+  if (v1.size() == 0 || v2.size() == 0)
+    return false;
+  const std::vector<T> * pv1 = &v1, * pv2 = &v2;
+  if (v1.size() > v2.size())
+  {
+    pv1 = &v2; // pv1 points to the smaller set
+    pv2 = &v1;
+  }
+
+  for(size_t i = 0; i < pv1->size(); i++)
+    if (binary_search(pv2->begin(), pv2->end(),(*pv1)[i]))
+      return true;
+
+  return false;
+}
+
+template<class T,class C, class A>
+int getIntersectionSize(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2)
+{
+  if (s1.size() == 0 || s2.size() == 0)
+    return 0;
+  const std::set<T,C,A> * ps1 = &s1, * ps2 = &s2;
+  if(s1.size() > s2.size()) // make ps1->size() <= ps2->size()
+  {
+    ps1 = &s2;
+    ps2 = &s1;
+  }
+  int count = 0;
+  for(typename std::set<T,C,A>::const_iterator it = ps1->begin(); it != ps1->end(); it++)
+    if (ps2->find(*it) != ps2->end())
+      count++;
+  return count;
+}
+
+template<class T,class C, class A>
+bool isSubset(const std::set<T,C,A> & setA, const std::set<T,C,A> & setB)
+{
+  if (setA.size() > setB.size())
+    return false;
+  for(typename std::set<T,C,A>::const_iterator it = setA.begin(); it != setA.end(); it++)
+    if (setB.find(*it) == setB.end())
+      return false;
+  return true;
+}
+
+template<class T, class C, class A>
+std::set<T,C,A> setMinus(const std::set<T,C,A> & s1, const std::set<T,C,A> & s2)
+{
+  std::set<T,C,A> ret;
+  for(const auto & e : s1)
+  {
+    if (setNotFind(s2, e))
+      ret.insert(e);
+  }
+  return ret;
+}
+
+template<class T>
+T squaredEuclideanNorm(const std::vector<T> & v)
+{
+  T sum = 0;
+  for(size_t i = 0; i < v.size(); i++)
+    sum += v[i] * v[i];
+  return sum;
+}
+
+template<class T>
+T squaredEuclideanNorm(size_t r, const T * v)
+{
+  T sum = 0;
+  for(size_t i = 0; i < r; i++)
+    sum += v[i] * v[i];
+  return sum;
+}
+
+template<class T>
+T squaredEuclideanDistance(const std::vector<T> & v0, const std::vector<T> & v1)
+{
+  T sum = 0;
+  for(size_t i = 0; i < v0.size(); i++)
+  {
+    T value = (v0[i] - v1[i]);
+    sum += value * value;
+  }
+  return sum;
+}
+
+template<class T>
+T squaredEuclideanDistance(size_t r, const T * v1, const T * v2)
+{
+  T sum = 0;
+  for(size_t i = 0; i < r; i++)
+  {
+    T value = (v1[i] - v2[i]);
+    sum += value * value;
+  }
+  return sum;
+}
+
+// helper template class to stream a range with begin(), end() implemented
+template<class Range> class RangeStreamer
+{
+public:
+  RangeStreamer(const Range & input) : range(input) {}
+
+  friend std::ostream & operator << (std::ostream & os, const RangeStreamer & r)
+  {
+    os << "{";
+    bool first = true;
+    for(const auto & v : r.range)
+    {
+      if (first) { first = false; }
+      else { os << ", "; }
+      os << v;
+    }
+    os << "}";
+    return os;
+  }
+protected:
+  const Range & range;
+};
+
+// use this function to create RangeStreamer so that you can use << to output a range (e.g. a container)
+// usage:
+// vector<int> a = {1,2,3};
+// cout << streamRange(a) << endl; // print {1, 2, 3}
+template<class Range>
+RangeStreamer<Range> streamRange(const Range & input) { return RangeStreamer<Range>(input); }
+
+template <class T, class U>
+void printMap(std::ostream & os, const std::map<T, U> & s)
+{
+  os << "(";
+  for(typename std::map<T, U>::const_iterator it = s.begin(); it != s.end(); it++)
+  {
+    if (it != s.begin()) os << ", ";
+    os << "<" << it->first << " -> " << it->second << ">";
+  }
+  os << ")";
+}
+
+template<class T, class A>
+std::vector<T,A> findUniqueElements(const std::vector<T,A> vec, std::vector<int> * inpID2unqID, std::vector<int> * unqID2inpID)
+{
+  std::vector<T,A> ret;
+  if (inpID2unqID) inpID2unqID->resize(vec.size());
+  if (unqID2inpID) unqID2inpID->clear();
+
+  if (vec.size() == 0) return ret;
+
+  std::vector<int> buffer(vec.size());
+  std::iota(buffer.begin(), buffer.end(), 0);
+
+  auto cmp = [&](const int & p0, const int & p1)
+  {
+    return vec[p0] < vec[p1];
+  };
+  std::sort(buffer.begin(), buffer.end(), cmp);
+
+  ret.push_back(vec[buffer[0]]);
+  if (unqID2inpID) unqID2inpID->push_back(buffer[0]);
+  if (inpID2unqID) inpID2unqID->at(buffer[0]) = 0;
+  for(std::size_t i = 1; i < vec.size(); i++)
+  {
+    int vecID = buffer[i];
+    if ((ret.back() != vec[vecID]))
+    {
+      ret.push_back(vec[vecID]);
+      if (unqID2inpID) unqID2inpID->push_back(vecID);
+    }
+    if (inpID2unqID) inpID2unqID->at(vecID) = ret.size()-1;
+  }
+
+  return ret;
+}
+#endif
diff --git a/libraries/include/corotationalLinearFEM.h b/libraries/include/corotationalLinearFEM.h
new file mode 100644
index 0000000000000000000000000000000000000000..9cb79e3c529099343b9a4104c0d31f93e74219e1
--- /dev/null
+++ b/libraries/include/corotationalLinearFEM.h
@@ -0,0 +1,140 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "corotational linear FEM" library , Copyright (C) 2018 USC            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _COROTATIONALLINEARFEM_H_
+#define _COROTATIONALLINEARFEM_H_
+
+/*
+  Corotational linear FEM deformable model for tet meshes and cubic meshes.
+
+  1. For tet meshes, this class implements the deformable model described in the following paper:
+
+    M. Mueller, M. Gross: Interactive Virtual Materials.
+    In Proc. of Graphics Interface 2004 (2004), pp. 239–246.
+
+  In [Mueller 2004], the tangent stiffness matrix is approximate (warp=1). 
+  This class can also compute the exact tangent stiffness matrix (warp=2).
+  The implementation is described in:
+  J. Barbic: Exact Corotational Linear FEM Stiffness Matrix, Technical Report, USC, 2012
+
+  It is also possible to turn warping off (warp=0). This gives fast linear FEM dynamics,
+  but large deformations are not well-represented.
+
+  2. For cubic meshes, the class implements the following paper:
+
+    Jesus Perez, Alvaro G. Perez and Miguel A. Otaduy:
+    Simulation of Hyperelastic Materials Using Energy Constraints
+    Proc. of Congreso Espanol de Informatica Grafica, 2013
+
+  Note that for cubic meshes, only the warp=1 and warp=0 modes are supported.
+  For cubic meshes with warp=2, the code behaves the same as for warp=1.
+
+  3. This class supports both isotropic and orthotropic materials. By default, isotropic material is used.
+  The choice of material is controlled by the material specification in the .veg file.
+  For orthotropic materials, we implement the following:
+
+     Yijing Li and Jernej Barbic: 
+     Stable Orthotropic Materials, 
+     Symposium on Computer Animation 2014.
+     
+  See also:
+     http://en.wikipedia.org/wiki/Orthotropic_material
+     http://www.solidmechanics.org/text/Chapter3_2/Chapter3_2.htm
+*/
+
+#include "tetMesh.h"
+#include "sparseMatrix.h"
+
+class CorotationalLinearFEM
+{
+public:
+
+  // initializes corotational linear FEM
+  // input: tetMesh and cubicMesh
+  CorotationalLinearFEM(VolumetricMesh * volumetricMesh);
+  virtual ~CorotationalLinearFEM();
+
+  void GetStiffnessMatrixTopology(SparseMatrix ** stiffnessMatrixTopology); // returns a zero matrix containing the locations of non-zero elements in the stiffness matrix
+
+  // computes elastic energy, the internal forces and (warped) stiffness matrix for the entire mesh
+  // vertex displacements (input) and internal forces (output) must be (pre-allocated) vectors of length 3 * numVertices
+  // the internal forces are returned with the sign corresponding to f_int(x) on the left side of the equation M * x'' + f_int(x) = f_ext
+  // i.e., the computed internal forces are *negatives* of the actual physical internal forces acting on the material
+  // warp:
+  //   0: no warping (linear FEM)
+  //   1: stiffness warping (corotational linear FEM with approximate stiffness matrix) [Mueller 2004]
+  //   2: corotational linear FEM with exact tangent stiffness matrix (see the technical report [Barbic 2012]), only works on tetMesh
+  // if you do not want to compute the energy, internal forces or stiffness matrix (any combination), pass a NULL pointer for that argument
+  virtual void ComputeEnergyAndForceAndStiffnessMatrix(const double * vertexDisplacements, double * energy, double * internalForces, SparseMatrix * stiffnessMatrix, int warp=1);
+
+  // this routine is same as above, except that (1) it adds to the existing value, (2) only traverses elements from elementLo <= element <= elementHi - 1
+  // if you do not want to compute the energy, internal forces or stiffness matrix (any combination), pass a NULL pointer for that argument
+  void AddEnergyAndForceAndStiffnessMatrixOfSubmesh(const double * vertexDisplacements, double * energy, double * internalForces, SparseMatrix * stiffnessMatrix, int warp, int elementLo, int elementHi);
+
+  // this routine is same as above, except that (1) it only computes a single element. (2) the output forces and the matrix 
+  // are stored in dense format.
+  void ComputeElementEnergyAndForceAndStiffnessMatrix(int elementID, const double * vertexDisplacements, double * elementEnergy, 
+    double * elementInternalForces, double * elementStiffnessMatrix, int warp);
+
+  inline VolumetricMesh * GetVolumetricMesh() { return volumetricMesh; }
+
+  static void inverse3x3(double * A, double * AInv); // inverse of a row-major 3x3 matrix
+  static void inverse4x4(double * A, double * AInv); // inverse of a row-major 4x4 matrix
+
+protected:
+  int numVertices;
+  VolumetricMesh * volumetricMesh;
+  double * undeformedPositions;
+  double ** MInverse;
+  double ** KElementUndeformed;
+
+  void WarpMatrix(double * K, double * R, double * RK, double * RKRT);
+
+  // acceleration indices
+  int ** rowIndices;
+  int ** columnIndices;
+  void ClearRowColumnIndices();
+  void BuildRowColumnIndices(SparseMatrix * sparseMatrix);
+
+  bool computeElasticityStiffnessTensor(double E[36], int el);
+  // build inverse of M = [ v0   v1   v2   v3 ]
+  //                      [  1    1    1    1 ]
+  // volumetricMesh must be TET of CUBIC. If it's CUBIC, M consists of vertices forming a tet in the center of the cube.
+  static void buildMInverse(double * MInverse, VolumetricMesh * volumetricMesh);
+
+  void clear();
+
+  static void GetStiffnessMatrixTopology(VolumetricMesh * mesh, SparseMatrix ** stiffnessMatrixTopology);
+};
+
+#endif
+
diff --git a/libraries/include/corotationalLinearFEMForceModel.h b/libraries/include/corotationalLinearFEMForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7f9d2a6c81d6d05af07f395f69b5ae83dd71626
--- /dev/null
+++ b/libraries/include/corotationalLinearFEMForceModel.h
@@ -0,0 +1,66 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Force model for the Corotational linear FEM material.
+*/
+
+#ifndef _COROTATIONALLINEARFEMFORCEMODEL_H_
+#define _COROTATIONALLINEARFEMFORCEMODEL_H_
+
+#include "corotationalLinearFEM.h"
+#include "forceModel.h"
+
+class CorotationalLinearFEMForceModel : public ForceModel
+{
+public:
+  CorotationalLinearFEMForceModel(CorotationalLinearFEM * corotationalLinearFEM, int warp=1);
+  virtual ~CorotationalLinearFEMForceModel(); 
+
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
+
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix) override;
+
+  inline void SetWarp(int warp) { this->warp = warp; }
+  inline int GetWarp() { return warp; }
+  static inline int GetMaxWarp() { return 2; }
+
+protected:
+  CorotationalLinearFEM * corotationalLinearFEM;
+  int warp;
+};
+
+#endif
+
diff --git a/libraries/include/corotationalLinearFEMStencilForceModel.h b/libraries/include/corotationalLinearFEMStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..f8774ab19a1e888e21e7cfb58ca8f039a9745ba3
--- /dev/null
+++ b/libraries/include/corotationalLinearFEMStencilForceModel.h
@@ -0,0 +1,60 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _COROTATIONALLINEARFEM_STENCIL_FORCEMODEL_H_
+#define _COROTATIONALLINEARFEM_STENCIL_FORCEMODEL_H_
+
+#include "corotationalLinearFEM.h"
+#include "stencilForceModel.h"
+
+// Stencils for corotational linear FEM.
+// A stencil is one FEM element.
+// See comments in the parent class.
+class CorotationalLinearFEMStencilForceModel : public StencilForceModel
+{
+public:
+  CorotationalLinearFEMStencilForceModel(CorotationalLinearFEM * corotationalLinearFEM);
+  virtual ~CorotationalLinearFEMStencilForceModel();
+
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+  CorotationalLinearFEM * GetForceModelHandle() { return corotationalLinearFEM; }
+  void SetWarp(int warp) { this->warp = warp; }
+
+protected:
+  CorotationalLinearFEM * corotationalLinearFEM;
+  int warp;
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/cubicMesh.h b/libraries/include/cubicMesh.h
similarity index 69%
rename from src/libvolumetricMesh/cubicMesh.h
rename to libraries/include/cubicMesh.h
index 924666d424f423aeefcfa14706fa96f8027bae8a..06f97ca262df0207051a6b63fa415fc60df6605b 100644
--- a/src/libvolumetricMesh/cubicMesh.h
+++ b/libraries/include/cubicMesh.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,24 +41,37 @@
 
   To generate a CubicMesh from an input triangle mesh (optionally flood-filling 
   interior chambers), you can use the "Large Modal Deformation Factory" 
-  application ( http://www.jernejbarbic.com/code ) .
+  application ( http://www.jernejbarbic.com/vega ) .
 
 */
 
+// vtx order in CUBIC:
+//
+//     3 - - - 2
+//    /|      /|
+//   7 - - - 6 |       y
+//   | |     | |       |
+//   | 0 - - | 1       |_ _ _x
+//   |/      |/       /
+//   4 - - - 5       z
+
 #ifndef _CUBICMESH_H_
 #define _CUBICMESH_H_
 
 #include "volumetricMesh.h"
 // see also volumetricMesh.h for a description of the routines
 
-namespace vega
-{
 class CubicMesh : public VolumetricMesh
 {
 public:
-  
-  // loads the mesh from a text file (.veg format; see documentation and examples)
-  CubicMesh(char * filename, int verbose=1); 
+  // loads the mesh from a file 
+  // ASCII: .veg text input formut, see documentation and the provided examples
+  // BINARY: .vegb binary input format
+  CubicMesh(const char * filename, fileFormatType fileFormat = ASCII, int verbose=1); 
+
+  // load from a stream
+  // if memoryLoad is 0, binaryStream is FILE* (load from a file), otherwise, it is char* (load from a memory buffer)
+  CubicMesh(void * binaryStream, int memoryLoad = 0);
 
   // constructs a mesh from the given vertices and elements, with a single region and material
   // "vertices" is double-precision array of length 3 x numVertices 
@@ -88,11 +105,17 @@ public:
   virtual ~CubicMesh();
 
   // saves the mesh to a text file (.veg format, see examples and documentation)
-  virtual int save(char * filename) const;
+  virtual int saveToAscii(const char * filename) const;
+
+  // saves the mesh to binary format
+  // returns: 0 = success, non-zero = error
+  // output: if bytesWritten is non-NULL, it will contain the number of bytes written 
+  virtual int saveToBinary(const char * filename, unsigned int * bytesWritten = NULL) const;
+  virtual int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten = NULL, bool countBytesOnly = false) const;
 
   // === misc queries ===
 
-  static const VolumetricMesh::elementType elementType() { return elementType_; }
+  static VolumetricMesh::elementType elementType() { return elementType_; }
   virtual VolumetricMesh::elementType getElementType() const { return elementType(); }
 
   inline double getCubeSize() const { return cubeSize; }
@@ -112,7 +135,7 @@ public:
 
   // === interpolation ===
 
-  virtual void computeBarycentricWeights(int el, Vec3d pos, double * weights) const;
+  virtual void computeBarycentricWeights(int el, const Vec3d & pos, double * weights) const;
 
   int interpolateData(double * volumetricMeshVertexData, int numLocations, int r, double * interpolationLocations, double * destMatrix, double zeroThreshold = -1.0) const;
 
@@ -126,13 +149,25 @@ public:
 
   virtual void interpolateGradient(int element, const double * U, int numFields, Vec3d pos, double * grad) const;
 
+  // advanced, to ensure computeBarycentricWeights, containsVertex, generateInterpolationWeights, generateContainingElements work even when elements are cubes, transformed via a general linear transformation
+  // parallelepiped=1 : the elements are cubes transformed via a linear transformation (i.e., they are parallelepipeds)
+  // parallelepiped=0 : (default) the elements are axis-aligned cubes 
+  void setParallelepipedMode(int parallelepipedMode);
+
 protected:
   double cubeSize;
+  double invCubeSize;
   static const VolumetricMesh::elementType elementType_;
   CubicMesh(int numElementVertices): VolumetricMesh(numElementVertices) {}
+  void SetInverseCubeSize();
+  int parallelepipedMode; // normally this is 0; in advanced usage, it can be 1 (see above)
+
+  // computes the normalized location of "pos" inside el
+  // when inside the element, one has 0 <= alpha <= 1, 0 <= beta <= 1, 0 <= gamma <= 1
+  void computeAlphaBetaGamma(int el, Vec3d pos, double * alpha, double * beta, double * gamma) const;
 
   friend class VolumetricMeshExtensions;
 };
-}
+
 #endif
 
diff --git a/libraries/include/delaunayMesher.h b/libraries/include/delaunayMesher.h
new file mode 100644
index 0000000000000000000000000000000000000000..511629eceaf96126ea41f33bf7cdd45811e3ec29
--- /dev/null
+++ b/libraries/include/delaunayMesher.h
@@ -0,0 +1,399 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Compute 3D Delaunay tetrahedralization of a set of input points in 3D.
+  The tetrahedralization can be updated incrementally by adding new points.
+*/
+
+#ifndef _DELAUNAYMESHER_H_
+#define _DELAUNAYMESHER_H_
+
+#include <stddef.h>
+#include <cassert>
+#include <iostream>
+#include <list>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+#include "objMeshOctree.h"
+#include "vec3d.h"
+#include "vegalong.h"
+#include "triangle.h"
+#include "triple.h"
+#include "tetMesh.h"
+#include "tetKey.h"
+#include "triKey.h"
+
+class TetMesh;
+
+class DelaunayMesher
+{
+public:
+  DelaunayMesher();
+  virtual ~DelaunayMesher();
+
+  /***************************************************************************
+   *              Basic Functions for Delaunay Tetrahedralization            *
+   ***************************************************************************/
+  // compute Delaunay tetrahedralization of a set of input points
+  // must have points.size() >= 4
+  // epsilon: threshold used to check whether input vertices are degenerate
+  //          the larger the epsilon, the more input will be treated as degenerate by DelaunayMesher
+  // returns true upon success, and false upon failure (degeneracy)
+  // use getMesh() to get the result
+  bool computeDelaunayTetrahedralization(const std::vector<Vec3d> & points,
+    double epsilon = 1e-9);
+
+  // return the current Delaunay mesh
+  TetMesh * getMesh() const;
+
+  // after computeDelaunayTetrahedralization, insert one additional point into the tetrahedralization
+  bool addOnePoint(const Vec3d & p);
+  size_t getNumVertices() const { return inputVertices.size(); } // get the total number of input vertices 
+  const Vec3d & getVertex(int index) const { return inputVertices[index]; } // get the position of an input vertex
+
+  // clear the mesh to empty (one can then call computeDelaunayTetrahedralization on a new input)
+  void clear();
+
+  // **********************************************************************************
+  // All functionality below here mostly exists to support our isomesher and tetMesher
+  // We do not expect functions below here to be of general-purpose use
+  // **********************************************************************************
+
+  /***************************************************************************
+   *  Functions for CDT (Constrained Delaunay Tetrahedralization)            *
+   *                                                                         *
+   ***************************************************************************/
+
+  // initialize the Constrained Delaunay Tetrahedralization algorithm; to conform to the surface faces of "mesh"
+  // the tet mesh "mesh" must be manifold and free of self-intersections
+  // further calls to this->addOnePoint() will incrementally rebuild the tetrahedralization while preserving the constrained Delaunay property
+  bool initializeCDT(TetMesh * mesh, double epsilon = 1e-6);
+
+  // Must do remove outside and all infite balls first
+  int buildCDT();
+  // get the surface mesh of the tet mesh "mesh" passed to initializeCDT (used in the Constrained Delaunay Tetrahedralization)
+  inline const ObjMesh * getCDTBoundaryMesh() const { return boundaryMesh; }
+
+  /***************************************************************************
+   *                      Voronoi Edge Query Functions                       *
+   ***************************************************************************/
+
+  // if input is true, DelaunayMesher will record Voronoi Edge update information for each subsequent operation (computeDelaunayTetrahedralization or addOnePoint)
+  // get last Voronoi Edge update by calling getDeletedVoronoiEdges() and getAddedVoronoiEdges
+  void computeVoronoiEdgeModification(bool compute = true) { computeVEdgeModification = compute; }
+
+  // A struct for Voronoi Edge
+  // A Voronoi Edge is the line connecting two adjacent tet centers in Delaunay, or an infinite line starting at one border tet center,
+  // and pointing outside perpendicular to a border face on this tet
+  struct VoronoiEdge
+  {
+    bool finite; //whether this edge has finite length
+    Vec3d start; // start point of the edge
+    Vec3d end;   // end point if it's not infinite, otherwise undefined
+    Vec3d direction; // direction of the edge if it's infinite, otherwise undefined
+    VoronoiEdge() : finite(true), start(0.0), end(0.0), direction(0.0) {}
+    VoronoiEdge(bool isFinite, const Vec3d & start, const Vec3d & other);
+    bool isFinite() const { return finite; }
+  };
+
+  typedef std::map<UTriKey, VoronoiEdge> VoronoiEdgeMap;
+  typedef VoronoiEdgeMap::iterator VEdgeIter;
+  typedef VoronoiEdgeMap::const_iterator VEdgeCIter;
+
+  // vEdgeDeleted and vEdgeAdded will hold Voronoi edges only for last operation on Delaunay
+  // e.g. when addOnePoint() is called, vEdgeDeleted is reset and only stores the edges deleted by the last addOnePoint() call
+  const VoronoiEdgeMap & getDeletedVoronoiEdges() const { return vEdgeDeleted; }
+  const VoronoiEdgeMap & getAddedVoronoiEdges() const { return vEdgeAdded; }
+
+  /***************************************************************************
+   *                     Delaunay Ball Query Functions                       *
+   ***************************************************************************/
+
+  // Delaunay Balls are used for representing Delaunay
+  typedef vegaunsignedlong label_t;
+  struct DelaunayBall;
+  struct DelaunayBallCompare
+  {
+    bool operator() (const DelaunayBall * const & p1, const DelaunayBall * const & p2) const { return p1->label < p2->label; }
+  };
+
+  typedef std::set<DelaunayBall *, DelaunayBallCompare> BallSet;
+  typedef BallSet::iterator BallIter;
+  typedef BallSet::const_iterator BallCIter;
+
+  // used to traverse all the balls
+  inline BallCIter getBallSetBegin() const { return balls.begin(); }
+  inline BallCIter getBallSetEnd() const { return balls.end(); }
+
+  // used to traverse all the ballsToDelete
+  inline BallCIter getBallToDeleteSetBegin() const { return ballsToDelete.begin(); }
+  inline BallCIter getBallToDeletelSetEnd() const { return ballsToDelete.end(); }
+
+  // used to traverse all the ballsAdded
+  inline BallCIter getBallAddedSetBegin() const { return ballsAdded.begin(); }
+  inline BallCIter getBallAddedSetEnd() const { return ballsAdded.end(); }
+
+  // There're two kinds of Delaunay Balls: regular and infinite
+  // Regular ball (constructor input parameter v0 >= 0) is built by four vertices and represent a tetrahedron.
+  // Infinite ball (constructor input parameter v0 < 0) is a ball with infinite radius representing the half space separated by a border face of the Delaunay
+  // the other three vtx indices of one infinite ball give the border face triangle on the convex hull, their orientation points inside the convex hull
+  // Infinite ball's center is at infinity, on a line perpendicular to the border face and intersecting the face at its circumcenter
+  // In short, regular ball represents the space inside one tetrahedron, infinite ball represents the empty space outside one border face on the convex hull
+  struct DelaunayBall : public OTetKey
+  {
+    // get vertex of this ball
+    // if it's infinite ball, getVtx(0) returns -1
+    inline int getVtx(int ind) const { return v[ind]; }
+    // get the array stores the 4 vtx indices
+    inline const int * getVertices() const {return &v[0];}
+    // get the position of vertices
+    inline Vec3d getPosition(int ind) const { return parent.getVertex(v[ind]); }
+
+    inline bool isRegular() const { return v[0] >= 0; }
+    inline bool isInfinite() const { return v[0] < 0; }
+
+    // get ball center if it's regular ball
+    inline const Vec3d & getCenter() const { return center; }
+
+    // for regular ball, return >0 for outside, ==0 for on the ball, <0 for inside
+    // for infinite ball, return >0 for on the inner side of the border face (or the plane the face lies on), ==0 for on the plane, <0 for on the outside of the border face
+    int contain(const Vec3d & p) const;
+    // Used only in CDT. Always return false for infinite ball
+    // For regular ball, if atleast one of the four ball vtx cannot be seen from p because the CDT boundary mesh obscures the sight, return false. Otherwise return true
+    // This can be tested by creating a line segment from p to one ball vtx and test intersection of this line segment with boundary mesh
+    // Notice that if the ball vtx is a mesh vtx of the boundary mesh, this does not counted as one valid intersection in this case
+    bool visibleTo(const Vec3d & p) const;
+    inline DelaunayBall * getNeighbor(const int ind) const { return nbr[ind]; }
+
+  protected:
+
+    // ==================== Member Vars =================================
+
+    const DelaunayMesher & parent; // pointer to DelaunayMesher
+
+    Vec3d center; //for regular ball, the center
+
+    // neighboring balls to this ball; there are always four neighbors
+    // regular ball: for every face of this tet, there is a neighboring tet across that face (may be an infinite ball)
+    // infinite ball: the border face (triangle) has three neighbors, one for each face edge; neighbors of the ball are the regular ball across the triangle, and the three infintie balls corresponding to the convex hull neighboring triangles
+    DelaunayBall * nbr[4];
+
+    label_t label; //label unique to every Delaunay Ball
+
+    // ==================== End Member Vars =============================
+
+    DelaunayBall(int v0, int v1, int v2, int v3, DelaunayMesher * parent, label_t label);
+
+    //return value:
+    //1 means point is outside the ball
+    //0 means point is on the ball
+    //-1 means point is inside the ball
+    int contains(int vtx) const;
+
+    // A regular face is one face in Delaunay. Two Delaunay Balls (regular vs. regular, or regular vs. infinite) share one regular face
+    // An irregular face is one line segment (f.v[1], f.v[2]) on a face.
+    // Two infinite balls share an irregular face. This is actually two border faces share one triangle edge.
+    inline static bool isFaceRegular(const UTriKey & f) { return f[0] >= 0; }
+    inline static bool isFaceRegular(const OTriKey & f) { return f[0] >= 0; }
+
+    // get the indices forming the infinite ball border face triangle
+    const int * getInfiniteBallTriangle() const { assert(!isRegular()); return &v[1]; }
+
+    // if uface[i] == key, return i, otherwise return -1
+    int getNeighborIndex(const UTriKey & key) const;
+    // if oface[i] == key, return i, otherwise return -1
+    int getNeighborIndex(const OTriKey & key) const;
+
+    inline int getVtxOpposeFace(const OTriKey & key) const { return v[getNeighborIndex(key)]; }
+    DelaunayBall * getNeighborByFace(const OTriKey & key) const { return nbr[getNeighborIndex(key)]; }
+    // if uface[i] == key, set DelaunayBall::nbr[i] to be nbr
+    void setNeighbor(const UTriKey & key, DelaunayBall * nbr);
+
+    // get the Voronoi Edge connecting this Delaunay ball and one neighbor ball at nbr[nbrIndex]
+    VoronoiEdge getVoronoiEdge(int nbrIndex) const;
+
+    friend class DelaunayMesher;
+
+    friend std::ostream & operator << (std::ostream & o, const DelaunayBall & ball);
+
+  };
+
+  // recover an edge by flipping, level means the maximal levels of recursion the recover process can have
+  int segmentRecoveryUsingFlip(const OEdgeKey & edge, int level);
+  // recover an edge by adding steiner points
+  int segmentRecoveryUsingSteinerPoint(const OEdgeKey & edge);
+
+  // simply add a ball for the delaunayMesher, we won't check whether it is still a delaunay mesh
+  // v is an array of the four indices of the vertices in the delaunay mesh
+  DelaunayMesher::DelaunayBall * addBall(const int * v);
+  // simply remove a ball for the delaunayMesher, a hole may exist
+  void removeBall(DelaunayBall * ball);
+
+  /***************************************************************************
+   *                          Debugging Functions                            *
+   ***************************************************************************/
+
+  // check whether each tet satisfyies the Delaunay criterion by looping over every tet-vtx pair
+  // It's slow. For debugging purpose.
+  bool checkDelaunay() const;
+
+protected:
+  // First, the apex of the tet, second, the shared face of the tet, third, the pointer to the tet
+  typedef std::list<triple <int, OTriKey, DelaunayBall*> > TetAroundEdge;
+
+  // Fliping a face, the face is shared by ball0 and ball1
+  // ball0 and ball1 will be removed and three new balls will be added to the delaunay mesh
+  // It can return one of the three added balls by setting requestNewBallIdx and newBallAroundEdge
+  // By setting allowFlat, sometimes the three added balls may be flat and then removed by the next flip32.
+  int flip23(const OTriKey & face, DelaunayBall * ball0, DelaunayBall * ball1, int requestNewBallIdx = -1, DelaunayBall ** newBallAroundEdge = NULL, int allowFlat = -1);
+  // Fliping a edge, there must be 3 tets around the edge
+  int flip32(const OEdgeKey & edge, const TetAroundEdge & tetsAroundEdge);
+
+  // remove an edge by flipping, level means the maximal levels of recursion
+  int segmentRemovalUsingFlip(const OEdgeKey & edge, int level = 1);
+
+
+  // Find two delaunay balls that have the face
+  int getTwoBallsByFace(const OTriKey & face, std::pair<DelaunayBall*, DelaunayBall *> & twoBalls);
+
+
+  // Find one delaunay ball that has the face
+  DelaunayBall * getOneBallByFace(const OTriKey & face);
+
+  // Find tets that have the edge
+  int getTetsAroundEdge(const OEdgeKey & edge, TetAroundEdge & tetsAroundEdge);
+
+  // Clear the counter of some tets
+  int clearCounter(const DelaunayMesher::TetAroundEdge & tetsAroundEdge);
+
+  // The origin of the return ball should be start
+  int getOneBallBySegment(const int start, const int end);
+
+  //void debug() const;
+
+//  double getCheckContainingTime() const { return checkContainingWatch.getElapsedTime(); }
+//  double getUpdateTime() const { return updateWatch.getElapsedTime(); }
+
+  DelaunayMesher::DelaunayBall * addBall(const int v0, const int v1, const int v2, const int v3);
+
+  // compute circumcenter of a tet
+  static Vec3d circumcenter(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+
+  // get all the Delaunay balls containing the point getDelaunayVtx(i)
+  void getBallsContainingPoint(int i, BallSet & balls);
+  
+  // used to construct neighboring information for new Delaunay balls
+  // search face in neighboringStructure. If face is found, ball is neighbor to neighboringStructure[face], build related neighboring data
+  int buildNeighbor(const UTriKey & face, DelaunayBall * ball, std::map<UTriKey, DelaunayBall *> & neighboringStructure);
+  inline int buildNeighbor(int v0, int v1, int v2, DelaunayBall * ball, std::map<UTriKey, DelaunayBall *> & neighboringStructure)  {return buildNeighbor(UTriKey(v0, v1, v2), ball, neighboringStructure);}
+
+  // get only one ball containing the point getDelaunayVtx(i)
+  DelaunayBall * getOneBallContainingPoint(int i);
+
+  DelaunayBall * createBall(int v0, int v1, int v2, int v3);
+
+  //DelaunayMesher(const DelaunayMesher &);
+
+  // initialize the first four vertices for Delaunay 
+  // return false if failed 
+  bool initialize(int a, int b, int c, int d);
+
+  bool update(int i); // update Delaunay for the new point at index i
+
+  Vec3d getFaceNormal(const OTriKey & face) const;
+
+  static inline double det(const Vec3d & a, const Vec3d & b, const Vec3d & c) { return dot(a, cross(b, c)); }
+
+  static bool isTetMeshFaceManifold(const TetMesh * tetMesh); // check whether any tet mesh face is at most shared by two tets
+
+  int toPlane(int i, int a, int b, int c) const;
+
+  int toCircumsphere(int i, int a, int b, int c) const;
+
+  int toCircumsphere(int i, int a, int b, int c, int d) const;
+
+  int toPlane(const Vec3d & p, int a, int b, int c) const;
+
+  int toCircumsphere(const Vec3d & p, int a, int b, int c) const;
+
+  int toCircumsphere(const Vec3d & p, int a, int b, int c, int d) const;
+
+  int flippable(const OTriKey &face, const int v0, const int v1);
+
+  double epsilon;
+  std::vector<Vec3d> inputVertices;
+  std::set<Vec3d> verticesSet;
+
+  BallSet balls;
+  BallSet ballsToDelete;
+  BallSet ballsAdded;
+
+  std::map<UTriKey, DelaunayBall *> neighboringStructure; // Used for modifying balls
+
+  // Look up by the index of the vertex, only return one of the balls that contains the vertex
+  std::vector <DelaunayBall*> vertex2ball;
+
+  std::set <UEdgeKey> recoveredEdge;
+
+  // ============ DEBUG CODE ====================
+//  typedef std::map<label_t, DelaunayBall *> BallMap;
+//  typedef BallMap::iterator BallMapIter;
+//  typedef BallMap::const_iterator BallMapCIter;
+//  BallMap ballMap;
+////  std::map<label_t, label_t> ballConnections;
+//
+//  struct Operation {
+//    std::vector<label_t> deletedBalls;
+//    std::vector<label_t> addedBalls;
+//  };
+//
+//  std::vector<Operation> opStack;
+//  TetMeshManifold tetManifold;
+  // ============ END DEBUG CODE ==================
+
+  bool computeVEdgeModification;
+  VoronoiEdgeMap vEdgeDeleted;
+  VoronoiEdgeMap vEdgeAdded;
+
+  // data for CDT
+  ObjMesh * boundaryMesh;
+  //std::vector<UTriKey> boundaryTriangles;
+  ObjMeshOctree<TriangleBasic> * boundaryOctree;
+
+  label_t nextBallLabel;
+};
+
+#endif
+
diff --git a/libraries/include/disjointSet.h b/libraries/include/disjointSet.h
new file mode 100644
index 0000000000000000000000000000000000000000..b6290be78fe84562beddfbc6a4fbd14cfb327a62
--- /dev/null
+++ b/libraries/include/disjointSet.h
@@ -0,0 +1,131 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC*
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yili Zhao, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  "Disjoint set", a union-find data structure
+  that keeps track of a set of elements partitioned into a number of disjoint (non-overlapping) subsets.
+
+  Operations:
+  1) MakeSet: Make each set containing only a given element.
+  2) Find: Determine which set a particular element is in. Also useful for determining if two elements are in the same set.
+  3) Union: Combine or merge two sets into a single set.
+
+  Heuristics such as path compression etc. have been implemented.
+*/
+
+#ifndef _DISJOINT_SET_H_
+#define _DISJOINT_SET_H_
+
+#include <vector>
+#include <map>
+
+// implementation of a disjoint set on a fixed array of elements with continuous IDs
+class DisjointSet
+{
+public:
+  enum{ NO_SUCCESSOR = -1 };
+
+  // constructor
+  DisjointSet() {}
+  // initialize num elements and each elemeent is in its own set
+  // 'num' is the total number of elements
+  DisjointSet(int num) { extendSizeTo(num); }
+
+  void clear() { parent.clear(); depth.clear(); }
+
+  int size() const { return parent.size(); }
+
+  // extend the data structure to have size 'num'
+  // previous set relationship is maintained
+  void extendSizeTo(int num);
+
+  // makes each element be its own set (already done in the constructor)
+  void makeSet();
+
+  // returns the representative of the set that x belongs to (path compression is implemented in this function as well)
+  int findSet(int x) const;
+
+  // merge two sets (the smaller one will be absorbed by the larger one)
+  // x and y can be arbitrary elements, they need not be representatives
+  void unionSet(int x, int y);
+  template<class IntIterator>
+  void unionRange(IntIterator itBegin, IntIterator itEnd);
+  template<class IntRange>
+  void unionRange(IntRange range) { unionRange(range.begin(), range.end()); }
+
+  // create a mapping from elementID to new continuous IDs for each set
+  // returned NewIDMapping is: [0, size()) -> [0, #set)
+  std::vector<int> createOldToNewIDMapping();
+
+protected:
+  mutable std::vector<int> parent;
+  std::vector<int> depth;
+};
+
+// implementation of a disjoint set on a dynamic set of elements with possibly incontinuous IDs
+class DisjointSetDynamic
+{
+public:
+  DisjointSetDynamic() {}
+
+  void addElement(int elementID);
+
+  void clear() { set.clear(); setID.clear(); }
+
+  // return the setID of the set this element belongs to
+  // if this element has not been added before, it will be added and form an individual set
+  int findSet(int elementID);
+
+  // union the sets the two input elements (x, y) belong to
+  // if any one of the input element is not added before, it will be added and form an individual set
+  void unionSet(int elementIDx, int elementIDy);
+
+  // return all remaining sets with their elementIDs inside
+  std::vector<std::vector<int>> getAllSets() const;
+
+protected:
+  int getSetID(int elementID);
+
+  DisjointSet set;
+  std::map<int, int> setID; // elementID -> setID
+};
+
+template<class IntIterator>
+void DisjointSet::unionRange(IntIterator itBegin, IntIterator itEnd)
+{
+  auto jt = itBegin;
+  jt++;
+  for(; jt != itEnd; jt++)
+    unionSet(*itBegin, *jt);
+}
+
+#endif
diff --git a/libraries/include/distanceField.h b/libraries/include/distanceField.h
new file mode 100644
index 0000000000000000000000000000000000000000..7296079ebce867f170baaf78843011fbd4d6fd29
--- /dev/null
+++ b/libraries/include/distanceField.h
@@ -0,0 +1,219 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This code generates a distance filed, either signed or unsigned, 
+  to the given triangle mesh.
+
+  The distance field can be loaded/saved to a file.
+  You can also lookup the field (once computed or loaded) at arbitrary 
+  locations inside the field, using trilinear interpolation.
+
+  Input mesh need not have triangle faces (e.g., can have quads; 
+  they will be triangulated).
+
+  For signed field generation, the input mesh must be a closed manifold mesh.
+
+  Input mesh must be given in the .obj format:
+  http://www.royriggs.com/obj.html
+
+  By default, the bounding box will be a cube, obtained by fitting the  
+  smallest cube to the geometry, and then expanded (scaled) from its 
+  center by a factor of 1.5. You can provide your own bounding boxes. 
+  However, note: (1) the provided bounding box must cover all the geometry, 
+  and (2) bounding boxes that are not cubes were not (yet) tested.
+
+  The bounding box will be divided into a "resolution" number of 
+  cubes ("voxels") along each axis. The distance field will be computed at the 
+  vertices of these voxels. So, if resolution is 256, the bounding box 
+  will get divided into 256 x 256 x 256 voxels, and the distance field 
+  will be computed on the resulting 257 x 257 x 257 grid of voxel vertices. 
+  Note that when indexing voxels, the indices (i,j,k) will run from 0 to 255 
+  inclusive, whereas when indexing voxel vertices (also called "grid vertices"), 
+  they will run from 0 to 256 inclusive.
+
+  Distance field data is stored at voxel vertices. 
+  In memory, distance field value at voxel vertex (i,j,k) is stored 
+  at location k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i .
+
+  Internally, the code builds an octree on top of the triangle mesh. 
+  There are two parameters that control this process (you can keep them 
+  at default values, which worked well in practice for us) :
+  the max depth of the octree is "maxDepth", and
+  the max number of triangles intersecting an octree cell is "maxTriCount".
+  Note: once max depth level is reached, the maxTriCount bound is not imposed any more.
+*/
+
+#ifndef _DISTANCEFIELD_H_
+#define _DISTANCEFIELD_H_
+
+#include "objMesh.h"
+#include "objMeshOrientable.h"
+#include "objMeshOctree.h"
+#include "distanceFieldBase.h"
+#include "vegalong.h"
+
+class DistanceField : public DistanceFieldBase
+{
+public:
+
+  DistanceField();
+  virtual ~DistanceField();
+
+  DistanceField(int resolutionX, int resolutionY, int resolutionZ);
+
+  // computes unsigned distance field
+  virtual int computeUnsignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ,int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+
+  // computes signed distance field
+  virtual int computeSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+
+  // computes signed distance field using flood fill
+  // must call computeUnsignedField for the same objMesh and same resolution first
+  virtual int computeFloodFillSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, int maxTriCount=15, int maxDepth=10, int zMin = -1, int zMax = -1);
+
+  void enableVoronoiDiagramComputation(bool computeVoronoiDiagram);
+
+  // loads a previously computed distance field from a disk file
+  virtual int load(const std::string& filename); // returns 0 on success
+
+  // opens the distance field for stream processing
+  // this is advanced routine, don't use in normal circumstances; use "load" instead
+  int openStreamDistanceField(const std::string& filename, Vec3d * bmin, Vec3d * bmax, int * resolutionX, int * resolutionY, int * resolutionZ, bool * floatData, std::ifstream & fin);
+  void retrieveZSlice(std::ifstream & fin, bool floatData, int resolutionX, int resolutionY, int resolutionZ, float * slice);
+
+  // saves the current distance field to a disk file (e.g. after computing it once, for later fast reloading) 
+  virtual int save(const std::string& filename, bool doublePrecision);
+  int saveVoronoiDiagram(const std::string& filename);
+
+  // exports the distance field to a file, in text format
+  virtual int saveToText(const std::string& filename);
+
+  // sets the distance field to the given external data
+  virtual void set(int resolutionX, int resolutionY, int resolutionZ, Vec3d bmin_, Vec3d bmax_, float * distanceData);
+
+  // Is data from the given file in single or double precision?
+  // note: this class uses single precision everywhere, but some older code
+  // used double precision, so routines were necessary to load binary distance field files computed using old versions
+  int isDoublePrecision(const std::string & filename, bool & doublePrecision);
+
+  // return distance field value at grid vertex (i,j,k)
+  // each of i,j,k must be an integer from {0, ..., resolution{X,Y,Z}}
+  virtual inline float distance(int i, int j, int k) const { return distanceData[(k * (resolutionY + 1) + j ) * (resolutionX + 1) + i]; }
+  // computes distance and gradient at arbitrary position
+  virtual float distance(Vec3d pos, int constrainToBox=0) const;
+
+  // alters the distance at a particular grid vertex (i,j,k)
+  virtual inline void setDistance(int i, int j, int k, float value) { distanceData[(k * (resolutionY + 1) + j ) * (resolutionX + 1) + i] = value; }
+
+  // gradient is computed with respect to trilinear interpolation
+  // note: gradient is discontinuous at the cell boundaries
+  virtual Vec3d gradient(const Vec3d& pos);
+
+  virtual bool sanityCheck(); // checks if distance for any two adjacent voxels is less than voxel grid spacing apart (which it must be by triangle inequality, for both signed and unsigned fields)
+
+  virtual float maxValue();
+  virtual float minValue();
+  virtual void  maxMinValue(float* maxValue, float* minValue);
+
+  virtual float maxAbsValue();
+  virtual float maxAbsValue(float threshold); // only abs values up to threshold
+  virtual float maxNonInftyAbsValue();
+
+  // compute and return minimum distance value on the surface of the bounding box
+  // the value is also saved so that it can be retrieved by calling getMinBoundaryValue()
+  float computeMinBoundaryValue(); 
+  float getMinBoundaryValue() { return minBoundaryDistance;}
+
+  // returns the entire distance data, by allocating the buffer and copying the distance data into it 
+  virtual void getDistanceData(float ** floatBuffer);
+
+  // returns the pointer to the internally allocated data
+  const float * getDistanceDatap() const { return distanceData; }
+  float * getDistanceDatap() { return distanceData; }
+
+  // Is voxel with indices (i,j,k) intersecting the zero-isocontour? (only applies to signed distance fields)
+  bool isSurfaceVoxel(int i, int j, int k); 
+  // total number of such voxels
+  int numSurfaceVoxels(float levelSetValue = 0.0); 
+  // If distance field were resampled to customResolution, is voxel with indices (i,j,k) intersecting the isocontour with value levelSetValue ?
+  bool isSurfaceVoxel(int customResolutionX, int customResolutionY, int customResolutionZ, int i, int j, int k, float levelSetValue);
+  // total number of such voxels
+  int numSurfaceVoxels(int customResolutionX, int customResolutionY, int customResolutionZ, float levelSetValue); 
+
+  // sets the computation range for the z-coordinate (by default, the entire z-range will be computed)
+  inline void setComputationZRange(int zMin, int zMax);
+
+  virtual void print();
+
+  virtual int ZigZagSigned(void * objMeshOctree, void * meshGraph);
+  virtual int ZigZagUnsigned(void * objMeshOctree, void * meshGraph);
+  virtual int ZigZagFloodFillSigned(void * objMeshOctree, void * meshGraph);
+
+  virtual int ZigZagSigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+  virtual int ZigZagUnsigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+  virtual int ZigZagFloodFillSigned(void * objMeshOctree, void * meshGraph, int zLo, int zHi, int asterisk=0);
+
+  void computeFloodFillTag(ObjMesh* objMesh);
+  void freeMemory(){free(distanceData); distanceData = NULL;};
+
+ // returns earliest contact time assuming point travels with constant velocity
+  // returns -1.0 if there is no contact
+  double pointCCD(Vec3d startPos, Vec3d endPos);
+
+  virtual void offsetDistanceField(double offset);
+protected:
+  int maxTriCount;
+  int maxDepth;
+ 
+  float * distanceData; // the raw distance data
+  float * pseudoData; // for debug
+
+  char * floodFillTag;
+  int * voronoiDiagram; 
+  bool computeVoronoiDiagram;
+
+  int zMin, zMax;
+  //ObjMeshOctree<TriangleWithCollisionInfo> * objMeshOctree;
+  //ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals> * objMeshOrientedOctree;
+  float minBoundaryDistance;
+  vegalong GetFilesize(const char *filename);
+};
+
+inline void DistanceField::setComputationZRange(int zMin, int zMax)
+{
+  this->zMin = zMin;
+  this->zMax = zMax;
+}
+
+#endif
+
diff --git a/libraries/include/distanceFieldBase.h b/libraries/include/distanceFieldBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..6b2f1b2eb54d01c2454bb0ad8136e06643bee58b
--- /dev/null
+++ b/libraries/include/distanceFieldBase.h
@@ -0,0 +1,317 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This code generates a distance filed, either signed or unsigned, 
+  to the given triangle mesh.
+
+  The distance field can be loaded/saved to a file.
+  You can also lookup the field (once computed or loaded) at arbitrary 
+  locations inside the field, using trilinear interpolation.
+
+  Input mesh need not have triangle faces (e.g., can have quads; 
+  they will be triangulated).
+
+  For signed field generation, the input mesh must be a closed manifold mesh.
+
+  Input mesh must be given in the .obj format:
+  http://www.royriggs.com/obj.html
+
+  By default, the bounding box will be a cube, obtained by fitting the  
+  smallest cube to the geometry, and then expanded (scaled) from its 
+  center by a factor of 1.5. You can provide your own bounding boxes. 
+  However, note: (1) the provided bounding box must cover all the geometry, 
+  and (2) bounding boxes that are not cubes were not (yet) tested.
+
+  The bounding box will be divided into a "resolution" number of 
+  cubes ("voxels") along each axis. The distance field will be computed at the 
+  vertices of these voxels. So, if resolution is 256, the bounding box 
+  will get divided into 256 x 256 x 256 voxels, and the distance field 
+  will be computed on the resulting 257 x 257 x 257 grid of voxel vertices. 
+  Note that when indexing voxels, the indices (i,j,k) will run from 0 to 255 
+  inclusive, whereas when indexing voxel vertices (also called "grid vertices"), 
+  they will run from 0 to 256 inclusive.
+
+  Distance field data is stored at voxel vertices. 
+  In memory, distance field value at voxel vertex (i,j,k) is stored 
+  at location k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i .
+
+  Internally, the code builds an octree on top of the triangle mesh. 
+  There are two parameters that control this process (you can keep them 
+  at default values, which worked well in practice for us) :
+  the max depth of the octree is "maxDepth", and
+  the max number of triangles intersecting an octree cell is "maxTriCount".
+  Note: once max depth level is reached, the maxTriCount bound is not imposed any more.
+*/
+
+#ifndef _DISTANCEFIELDBASE_H_
+#define _DISTANCEFIELDBASE_H_
+
+#include "objMesh.h"
+#include <algorithm>
+
+class DistanceFieldBase
+{
+public:
+
+  DistanceFieldBase();
+  virtual ~DistanceFieldBase() {};
+
+  DistanceFieldBase(int resolutionX, int resolutionY, int resolutionZ);
+
+  // sets the bounding box within which the distance field will be computed
+  // set it before calling computeSigned/UnsignedField
+  void setBoundingBox(const Vec3d & bmin, const Vec3d & bmax);
+  void getBoundingBox(Vec3d & bmin, Vec3d & bmax) const;
+  // set a tight-fitting bounding box around the model, and expand it by the expansion ratio given
+  // note: if you don't set any bounding boxes, you get an automaticBoundingBox with 1.5 expansion ratio by default
+  void setAutomaticBoundingBox(bool allBoxSidesEqual=true, double expansionRatio=1.5);
+  
+  // compute bounding box with uniform cube voxels, of the specified resolution
+  // this routine uses the parameters set by the "setAutomaticBoundingBox" 
+  // if allBoxSidesEqual=true, the voxels will not necessarily be cubes
+  void computeBoundingBox(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ);
+
+  // loads a previously computed distance field from a disk file
+  virtual int load(const std::string& filename) = 0; // returns 0 on success
+
+  // saves the current distance field to a disk file (e.g. after computing it once, for later fast reloading) 
+  virtual int save(const std::string& filename, bool doublePrecision) = 0;
+
+  // exports the distance field to a file, in text format
+  virtual int saveToText(const std::string& filename) = 0;
+
+  // return distance field value at grid vertex (i,j,k)
+  // each of i,j,k must be an integer from {0, ..., resolution{X,Y,Z}}
+  virtual float distance(int i, int j, int k) const = 0;
+  // computes distance and gradient at arbitrary position
+  virtual float distance(Vec3d pos, int constrainToBox=0) const = 0;
+  // alters the distance at a particular grid vertex (i,j,k)
+  virtual void setDistance(int i, int j, int k, float value) = 0;
+  
+  // gradient is computed with respect to trilinear interpolation
+  // note: gradient is discontinuous at the cell boundaries
+  virtual Vec3d gradient(const Vec3d& pos) = 0;
+
+  // pack/unpack index
+  inline int packedVoxelIndex(int i, int j, int k) const; // returns a 32-bit unique voxel index
+  inline int packedVoxelIndex(const Vec3d & pos) const; // returns a 32-bit unique voxel index, for voxel constaining pos
+  inline void unpackVoxelIndex(int packedIndex, int & i, int & j, int & k) const;
+  
+  inline void voxelIndices(const Vec3d & pos, int * i, int * j, int * k) const; // returns indices of voxel containing pos
+
+  inline bool validGridIndex(int i, int j, int k); // tells whether the grid index is valid: 0<=i<=resolutionX, 0<=j<=resolutionY, 0<=z<=resolutionZ
+  inline bool validVoxelIndex(int i, int j, int k);// tells whether the voxel index is valid: 0<=i<resolutionX, 0<=j<resolutionY, 0<=z<resolutionZ
+
+  inline bool insideBox(const Vec3d & pos) const; // tells whether the pos is inside box or not
+  
+  // returns the world-coordinate position of the grid vertex with indices (i,j,k)
+  // must have: 0 <= i,j,k <= resolution{X,Y,Z}   (i,j,k of course not necessarily sorted)
+  inline Vec3d getGridPosition(int i, int j, int k) const;
+
+  // /set/get distance field resolution
+  inline void setResolution(int resolutionX, int resolutionY, int resolutionZ) { this->resolutionX = resolutionX; this->resolutionY = resolutionY; this->resolutionZ = resolutionZ; setGridParameters(); }
+  inline int getResolutionX() const { return resolutionX; }
+  inline int getResolutionY() const { return resolutionY; }
+  inline int getResolutionZ() const { return resolutionZ; }
+
+  // get the diagonal of the bounding box
+  inline double diagonal() const { return len(bmax_-bmin_);}
+
+  // get the lower-left-front corner of bounding box
+  inline const Vec3d & bmin() const { return bmin_; }
+  // get the upper-right-back corner of bounding box
+  inline const Vec3d & bmax() const { return bmax_; }
+  // alternative interface to bmin, bmax functions above
+  inline void bmin(float * bmin) const { bmin[0] = (float) bmin_[0]; bmin[1] = (float) bmin_[1]; bmin[2] = (float) bmin_[2]; }
+  inline void bmax(float * bmax) const { bmax[0] = (float) bmax_[0]; bmax[1] = (float) bmax_[1]; bmax[2] = (float) bmax_[2]; }
+  inline void bmin(double * bmin) const { bmin[0] = bmin_[0]; bmin[1] = bmin_[1]; bmin[2] = bmin_[2]; }
+  inline void bmax(double * bmax) const { bmax[0] = bmax_[0]; bmax[1] = bmax_[1]; bmax[2] = bmax_[2]; }
+
+  // returns the spatial dimensions of the voxels
+  // i.e., this is the spatial distance between consecutive grid vertices along the three dimensions
+  void getGridSpacing(double * gridX, double * gridY, double * gridZ) const;
+  void getInvGridSpacing(double * invGridX, double * invGridY, double * invGridZ) const;
+
+  // conversion between absolute units to grid units
+  double absoluteUnitsToGridUnits(double absoluteUnits) const;
+  double gridUnitsToAbsoluteUnits(double gridUnits) const;
+
+  virtual bool sanityCheck() = 0; // checks if distance for any two adjacent voxels is less than voxel grid spacing apart (which it must be by triangle inequality, for both signed and unsigned fields)
+
+  virtual float maxValue() = 0;
+  virtual float minValue() = 0;
+
+  virtual float maxAbsValue() = 0;
+  virtual float maxAbsValue(float threshold) = 0; // only abs values up to threshold
+  virtual float maxNonInftyAbsValue() = 0;
+
+  //virtual void print() = 0;
+
+  virtual void offsetDistanceField(double offset) = 0;
+
+protected:
+  // set side, gridX, girdY, gridZ, invGridX, invGridY, invGridZ
+  void setGridParameters();
+
+  int resolutionX, resolutionY, resolutionZ;
+  Vec3d bmin_, bmax_;
+  Vec3d side;
+  double gridX, gridY, gridZ;           // spatial distance between consecutive grid vertices along the three dimensions
+  double invGridX, invGridY, invGridZ;  // inverse of gridX/Y/Z
+
+  bool useAutomaticBox;
+  double expansionRatio;
+  bool allBoxSidesEqual;
+
+  bool bboxComputed;
+  //float minBoundaryDistance;
+};
+
+
+inline void DistanceFieldBase::getBoundingBox(Vec3d & bmin, Vec3d & bmax) const
+{
+  bmin = bmin_;
+  bmax = bmax_;
+}
+
+inline void DistanceFieldBase::setBoundingBox(const Vec3d& bmin, const Vec3d& bmax)
+{
+  useAutomaticBox = false;
+  bmin_ = bmin;
+  bmax_ = bmax;
+  setGridParameters();
+}
+
+inline void DistanceFieldBase::setGridParameters() 
+{
+  side = bmax_ - bmin_;
+  gridX = side[0] / resolutionX;
+  gridY = side[1] / resolutionY;
+  gridZ = side[2] / resolutionZ;
+  invGridX = 1.0 / gridX;
+  invGridY = 1.0 / gridY;
+  invGridZ = 1.0 / gridZ;
+}
+
+inline void DistanceFieldBase::setAutomaticBoundingBox(bool allBoxSidesEqual, double expansionRatio)
+{
+  useAutomaticBox = true;
+  this->expansionRatio = expansionRatio;
+  this->allBoxSidesEqual = allBoxSidesEqual;
+}
+
+inline void DistanceFieldBase::getGridSpacing(double * gridX, double * gridY, double * gridZ) const
+{
+  *gridX = this->gridX;
+  *gridY = this->gridY;
+  *gridZ = this->gridZ;
+}
+
+inline void DistanceFieldBase::getInvGridSpacing(double * invGridX, double * invGridY, double * invGridZ) const
+{
+  *invGridX = this->invGridX;
+  *invGridY = this->invGridY;
+  *invGridZ = this->invGridZ;
+}
+
+inline Vec3d DistanceFieldBase::getGridPosition(int i, int j, int k) const
+{
+ return Vec3d (bmin_[0] + i * gridX, bmin_[1] + j * gridY, bmin_[2] + k * gridZ);
+}
+
+
+inline void DistanceFieldBase::voxelIndices(const Vec3d& pos, int * i, int * j, int * k) const // returns indices of voxel containing pos
+{
+  *i = (int)((pos[0] - bmin_[0]) * invGridX);
+  *j = (int)((pos[1] - bmin_[1]) * invGridY);
+  *k = (int)((pos[2] - bmin_[2]) * invGridZ);
+}
+
+
+inline bool DistanceFieldBase::insideBox(const Vec3d& pos) const
+{
+  int i = (int)((pos[0] - bmin_[0]) * invGridX);
+  int j = (int)((pos[1] - bmin_[1]) * invGridY);
+  int k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  return ((i >= 0) && (i < resolutionX) && (j >= 0) && (j < resolutionY) && (k >= 0) && (k < resolutionZ));
+}
+
+inline bool DistanceFieldBase::validGridIndex(int i, int j, int k)
+{
+  return ((i >= 0) && (i <= resolutionX) && (j >= 0) && (j <= resolutionY) && (k >= 0) && (k <= resolutionZ));
+}
+
+inline bool DistanceFieldBase::validVoxelIndex(int i, int j, int k)
+{
+  return ((i >= 0) && (i < resolutionX) && (j >= 0) && (j < resolutionY) && (k >= 0) && (k < resolutionZ));
+}
+
+inline int DistanceFieldBase::packedVoxelIndex(const Vec3d & pos) const
+{
+  // get the indices
+  int i = (int)((pos[0] - bmin_[0]) * invGridX);
+  int j = (int)((pos[1] - bmin_[1]) * invGridY);
+  int k = (int)((pos[2] - bmin_[2]) * invGridZ);
+
+  return (k * resolutionY + j ) * resolutionX + i;
+}
+
+inline int DistanceFieldBase::packedVoxelIndex(int i, int j, int k) const
+{
+  return (k * resolutionY + j ) * resolutionX + i;
+}
+
+inline void DistanceFieldBase::unpackVoxelIndex(int packedIndex, int & i, int & j, int & k) const
+{
+  i = packedIndex % resolutionX;
+  packedIndex = packedIndex / resolutionX;
+  j = packedIndex % resolutionY;
+  k = packedIndex / resolutionY;
+}
+
+inline double DistanceFieldBase::absoluteUnitsToGridUnits(double absoluteUnits) const
+{
+  double invGridSize = (std::min)((std::min)(invGridX, invGridY), invGridZ);
+  return absoluteUnits * invGridSize;
+}
+
+inline double DistanceFieldBase::gridUnitsToAbsoluteUnits(double gridUnits) const
+{
+  double gridSize = (std::max)((std::max)(gridX, gridY), gridZ);
+  return gridUnits * gridSize;
+}
+
+
+#endif
+
diff --git a/libraries/include/distanceFieldCreator.h b/libraries/include/distanceFieldCreator.h
new file mode 100644
index 0000000000000000000000000000000000000000..da1f02fad5be29b44b4598270fd9fa74305d4281
--- /dev/null
+++ b/libraries/include/distanceFieldCreator.h
@@ -0,0 +1,92 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Hongyi Xu, Jernej Barbic                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This is a "master" class to compute unsigned or signed distance fields.
+  The specific algorithm is controlled using "SignedFieldCreationMode" (below).
+*/
+
+#ifndef _DISTANCEFIELD_CREATOR_H_
+#define _DISTANCEFIELD_CREATOR_H_
+
+#include <vector>
+#include "distanceField.h"
+#include "distanceFieldNarrowBand.h"
+#include "closestPointField.h"
+#include "objMesh.h"
+
+class DistanceFieldCreator
+{
+public:
+  // if useCubicBox=0, the bounding box will not be a cube, but the aspect ratios of the bounding box will be set so that voxels are cubes, by following the resolutionX, resolutionY, resolutionZ parameters in the Compute* routines below
+  // if useCubicBox=1, the bounding box will be a cube, but the voxels will not necessarily be cubes
+  DistanceFieldCreator(ObjMesh * objMesh, double expansionRatio = 1.5, bool useCubicBox = true, const Vec3d * bbmin = NULL, const Vec3d * bbmax = NULL);
+  virtual ~DistanceFieldCreator() {}
+
+  enum SignedFieldCreationMode
+  {
+    BASIC,       // assumes the input obj mesh is manifold and self-intersection-free
+    POLYGONSOUP, // handles non-manifold and/or self-intersecting meshes, using the SignedDistanceFieldFromPolygonSoup pipeline,
+                 // as published in:
+                 // Hongyi Xu, Jernej Barbic: 
+                 // Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014, Montreal, Canada
+    AUTO         // uses BASIC if mesh is manifold, otherwise POLYGONSOUP
+  };
+
+  // Compute a distance field from objmesh
+  // If calculateSignedField=0, an unsigned field will be computed.
+  // If calculateSignedField=1, a signed field will be computed.
+  // computeVoronoiDiagram: whether to compute a Voronoi diagram (stored in DistanceField)
+  // closestPointFlag: whether to return a ClosestPointField or not
+  DistanceField * ComputeDistanceField(int resolutionX, int resolutionY, int resolutionZ, int calculateSignedField, SignedFieldCreationMode mode,
+    double sigma, int subtractSigma = 1, bool computeVoronoiDiagram = false, int maxTriCount = 15, int maxDepth = 10, int closestPointFlag = 0,
+    const char * precomputedUnsignedFieldFilename = NULL);
+
+  // Compute a distance field in a narrow band
+  DistanceFieldNarrowBand * ComputeDistanceFieldNarrowBand(int resolutionX, int resolutionY, int resolutionZ, double bandWidth, int calculateSignedField,
+    SignedFieldCreationMode mode, double sigma, int subtractSigma = 1, int maxTriCount = 15, int maxDepth = 10,
+    const char * precomputedUnsignedFieldFilename = NULL);
+
+protected:
+
+  ObjMesh * objMesh;
+
+  double expansionRatio;
+  bool useCubicBox;
+  bool autoBoundingBox;
+  Vec3d bbmin, bbmax;
+
+  void setBoundingBox(DistanceFieldBase* field, int resolutionX, int resolutionY, int resolutionZ);
+};
+
+#endif
+
diff --git a/libraries/include/distanceFieldNarrowBand.h b/libraries/include/distanceFieldNarrowBand.h
new file mode 100644
index 0000000000000000000000000000000000000000..f7c5714e990472bb90e1e96ed8d1cd358b14a9cc
--- /dev/null
+++ b/libraries/include/distanceFieldNarrowBand.h
@@ -0,0 +1,154 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Narrowband distance field computation.
+*/
+
+#ifndef _DISTANCEFIELDNARROWBAND_H_
+#define _DISTANCEFIELDNARROWBAND_H_
+
+#include "objMesh.h"
+#include "triple.h"
+#include "distanceFieldBase.h"
+#include "vegalong.h"
+#include <map>
+#include <float.h>
+
+class DistanceFieldNarrowBand : public DistanceFieldBase
+{
+public:
+
+  DistanceFieldNarrowBand();
+  virtual ~DistanceFieldNarrowBand();
+
+  // computes unsigned distance field in a narrow band
+  // filename is the obj filename
+  // bandWidth is given in absolute units
+  virtual int computeUnsignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, double bandWidth, int maxTriCount=15, int maxDepth=10);
+
+  // computes signed distance field in a narrow band
+  // filename is the obj filename
+  // offset is given in absolute units
+  virtual int computeSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, double bandWidth, int maxTriCount=15, int maxDepth=10);
+
+  virtual void offsetDistanceField(double sigma); // add sigma to all the distance field values
+  // computes the signed distance field, in the interior region (bounded by the zero isosurface), in a narrow band
+  // must call computeUnsignedField using the same objMesh and same resolution first
+  // also, you should first offset the unsigned distance field by a proper 'sigma' (typically, by passing -sigma, where sigma > 0)
+  virtual int computeInteriorSignedField(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ, double bandWidth, int maxTriCount=15, int maxDepth=10);
+
+  // loads a previously computed distance field from a disk file
+  virtual int load(const std::string& filename); // returns 0 on success
+
+  // saves the current distance field to a disk file (e.g. after computing it once, for later fast reloading) 
+  virtual int save(const std::string& filename, bool doublePrecision); // saves in a compressed format (saves the computed grid points only)
+  virtual int saveToText(const std::string& filename);
+  int saveToDistanceField(const std::string& filename, bool doublePrecision); // saves in full format (FLT_MAX or -FLT_MAX are stored for uncomputed grid points)
+
+  // return distance field value at grid vertex (i,j,k)
+  // each of i,j,k must be an integer from {0, ..., resolution{X,Y,Z}}
+  virtual inline float distance(int i, int j, int k) const;
+  // computes distance and gradient at arbitrary position
+  virtual float distance(Vec3d pos, int constrainToBox=0) const;
+  // alters the distance at a particular grid vertex (i,j,k)
+  virtual inline void setDistance(int i, int j, int k, float value);
+
+  virtual Vec3d gradient(const Vec3d & pos);
+
+  virtual bool sanityCheck(); // checks if distance for any two adjacent voxels is less than voxel grid spacing apart (which it must be by triangle inequality, for both signed and unsigned fields)
+
+  virtual float maxValue();
+  virtual float minValue();
+  virtual void  maxMinValue(float* maxValue, float* minValue);
+
+  virtual float maxAbsValue();
+  virtual float maxAbsValue(float threshold); // only abs values up to threshold
+  virtual float maxNonInftyAbsValue();
+
+  virtual int breadthFirstTraversalSigned(void * objMeshOctree, float offset, int zLo, int zHi, int asterisk=0);
+  virtual int breadthFirstTraversalUnsigned(void * objMeshOctree, float offset, int zLo, int zHi, int asterisk=0);
+  virtual int breadthFirstTraversalInteriorSigned(void * objMeshOctree, float offset, int zLo, int zHi, int asterisk=0);
+
+  void freeMemory();
+  
+  void findSurfaceGridPoints(ObjMesh* objMeshIn);
+
+  typedef triple<int,int,int> gridPoint;
+  std::map<gridPoint, float> * getDistanceData() {return &distanceData;};
+  
+protected:
+  int maxTriCount;
+  int maxDepth;
+
+  void finalizeGridPointStatus();
+  enum { COMPUTED, EXTERIOR_UNCOMPUTED, INTERIOR_UNCOMPUTED };
+  char * gridPointStatus; // needed for the sign on uncomputed grid points; unsigned field only has two values: COMPUTED, EXTERIOR_UNCOMPUTED
+
+  int signFieldFlooded;
+  //float * fieldData;
+  std::map<gridPoint, float> distanceData;
+
+  vegalong GetFilesize(const char *filename);
+  
+  std::vector<vegalong> surfaceGridPoints;
+};
+
+inline float DistanceFieldNarrowBand::distance(int i, int j, int k) const
+{
+  vegalong index = (k * (resolutionY+1) + j) * (resolutionX + 1) + i;
+
+  std::map<gridPoint, float>::const_iterator it = distanceData.find(gridPoint(i,j,k));
+
+  if (it == distanceData.end())
+  {
+    if (gridPointStatus[index] == DistanceFieldNarrowBand::INTERIOR_UNCOMPUTED)
+      return -FLT_MAX;
+    else 
+      return FLT_MAX;
+  }
+  else
+    return it->second;
+}
+
+inline void DistanceFieldNarrowBand::setDistance(int i, int j, int k, float value)
+{
+  std::map<gridPoint, float>::iterator it = distanceData.find(gridPoint(i,j,k));
+  
+  if (it == distanceData.end())
+    distanceData.insert(std::pair<gridPoint, float>(gridPoint(i,j,k), value));
+  else
+    it->second = value;
+}
+
+#endif
+
diff --git a/src/libminivector/eig3.h b/libraries/include/eig3.h
similarity index 94%
rename from src/libminivector/eig3.h
rename to libraries/include/eig3.h
index aedad6aa65481aa8b7e3c2617de6299eecbe3f23..0b36472adf59a252347728c31f5c7d61a8cde8de 100644
--- a/src/libminivector/eig3.h
+++ b/libraries/include/eig3.h
@@ -3,10 +3,8 @@
 
 #ifndef _eig_h
 
-namespace vega
-{
 /* Symmetric matrix A => eigenvectors in columns of V, corresponding
    eigenvalues in d. */
 void eigen_decomposition(double A[3][3], double V[3][3], double d[3]);
-}
+
 #endif
diff --git a/src/libintegratorSparse/eulerSparse.h b/libraries/include/eulerSparse.h
similarity index 73%
rename from src/libintegratorSparse/eulerSparse.h
rename to libraries/include/eulerSparse.h
index d1ca7516ff6bb79abe2fe76f6e253efdd17924fd..3313184fa2c0c1c9241bf31e24fbbbdbe2886677 100644
--- a/src/libintegratorSparse/eulerSparse.h
+++ b/libraries/include/eulerSparse.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -55,8 +59,6 @@
 #endif
 
 
-namespace vega
-{
 class EulerSparse : public IntegratorBaseSparse
 {
 public:
@@ -64,20 +66,33 @@ public:
   // constrainedDOFs is an integer array of degrees of freedom that are to be fixed to zero (e.g., to permanently fix a vertex in a deformable simulation)
   // constrainedDOFs are 0-indexed (separate DOFs for x,y,z), and must be pre-sorted (ascending)
   // dampingMatrix is optional and provides damping (in addition to mass damping)
-  EulerSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int symplectic=0, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0);
+  EulerSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int symplectic=0, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, int numSolverThreads=1);
 
   virtual ~EulerSparse();
 
-  // sets q, and (optionally) qvel
-  // returns 0
+  // sets q, and (optionally) qvel 
+  // returns 0 
   virtual int SetState(double * q, double * qvel=NULL);
 
-  virtual int DoTimestep();
+  virtual int DoTimestep(); 
 
 protected:
   int symplectic;
+  SparseMatrix * systemMatrix;
+  double * bufferConstrained;
+  
+  #ifdef PARDISO
+    PardisoSolver * pardisoSolver;
+  #endif
+
+  #ifdef SPOOLES
+    SPOOLESSolver * spoolesSolver;
+  #endif
 
+  #ifdef PCG
+    CGSolver * jacobiPreconditionedCGSolver;
+  #endif
 };
-}
+
 #endif
 
diff --git a/libraries/include/fileIO.h b/libraries/include/fileIO.h
new file mode 100644
index 0000000000000000000000000000000000000000..7f92bdb24d40eb54ec95eb58eb447dd89825f6a4
--- /dev/null
+++ b/libraries/include/fileIO.h
@@ -0,0 +1,49 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef FILEIO_H
+#define FILEIO_H
+
+#include <fstream>
+#include <functional>
+#include <string>
+
+// read each line from fin
+// empty line will be skipped, so input parameter line to processLine is always not empty
+// the input function processLine should return 0 when success
+// return 0 when success, return the error code of processLine otherwise
+// Note: when using filename as input parameter, the function returns 1 when it fails to open the file
+int readEachLine(const std::string & filename, std::function<int(std::string & line)> processLine, const char * comment = "#");
+int readEachLine(std::istream & fin, std::function<int(std::string & line)> processLine, const char * comment = "#");
+
+
+#endif
diff --git a/libraries/include/filterIterator.h b/libraries/include/filterIterator.h
new file mode 100644
index 0000000000000000000000000000000000000000..a39a6a6e4a9ce703fe7689cf0b83e6b0d7f24adc
--- /dev/null
+++ b/libraries/include/filterIterator.h
@@ -0,0 +1,95 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef FILTERITERATOR_H
+#define FILTERITERATOR_H
+
+#include <iterator>
+#include <functional>
+
+template <class BaseIterator>
+struct FilterIterator
+{
+  typedef typename std::iterator_traits<BaseIterator>::value_type value_type;
+  typedef std::function<bool (const value_type &)> Filter;
+
+  FilterIterator() = default;
+  FilterIterator(Filter filter, BaseIterator base, BaseIterator end = {})
+      : iter_(base), end_(end), filter_(filter)
+  {
+    while (iter_ != end_ && !filter_(*iter_))
+    {
+      iter_++;
+    }
+  }
+
+  FilterIterator & operator++()
+  {
+    do
+    {
+      iter_++;
+    } while (iter_ != end_ && !filter_(*iter_));
+    return *this;
+  }
+
+  FilterIterator operator++(int)
+  {
+    FilterIterator copy = *this;
+    ++(*this);
+    return copy;
+  }
+
+  value_type & operator *() { return *iter_; }
+  const value_type & operator *() const { return *iter_; }
+
+  bool operator == (const FilterIterator & it2) const { return iter_ == it2.iter_; }
+  bool operator != (const FilterIterator & it2) const { return !(*this == it2); }
+
+  bool operator == (const BaseIterator & it2) const { return iter_ == it2; }
+  bool operator != (const BaseIterator & it2) const { return !(*this == it2); }
+
+  inline friend bool operator == (const BaseIterator & it, const FilterIterator & it2) { return it == it2.iter_; }
+  inline friend bool operator != (const BaseIterator & it, const FilterIterator & it2) { return it != it2.iter_; }
+
+private:
+  BaseIterator iter_, end_;
+  Filter filter_;
+};
+
+template <class BaseIterator>
+FilterIterator<BaseIterator> makeFilterIterator(typename FilterIterator<BaseIterator>::Filter f,
+    BaseIterator base, BaseIterator end = {})
+{
+  return { f, base, end };
+}
+
+#endif
diff --git a/libraries/include/finiteDifferenceTester.h b/libraries/include/finiteDifferenceTester.h
new file mode 100644
index 0000000000000000000000000000000000000000..88e139d21ace21d809c3eecf84b2015577cdfcbb
--- /dev/null
+++ b/libraries/include/finiteDifferenceTester.h
@@ -0,0 +1,85 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+   Tests a Vega FEM force model, using two-point, or five-point finite differences.
+*/
+
+#ifndef FINITEDIFFERENCETESTER_H
+#define FINITEDIFFERENCETESTER_H
+
+#include "forceModel.h"
+#include "sparseMatrix.h"
+#include <vector>
+
+class FiniteDifferenceTester
+{
+public:
+  enum Mode
+  {
+    TWO_POINT, // f'(x) <- (f(x+h) - f(x)) / h
+    FIVE_POINT // f'(x) <- (-f(x+2h) + 8 f(x+h) - 8 f(x-h) + f(x-2h)) / (12h)
+  };
+
+  // Warning: parallelism enabled by numThreads > 1 only works if forceModel->GetElasticEnergy() and
+  //   forceModel->GetInternalForce() are parallel-safe
+  FiniteDifferenceTester(ForceModel * forceModel, double timestep, Mode mode, int numThreads);
+  virtual ~FiniteDifferenceTester();
+
+  void setTimestep(double timestep) { h = timestep; }
+
+  // use ForceModel::GetInternalForce() to compute analtical force f_a
+  // use ForceModel::GetElasticEnergy() to compute finite difference force f_d
+  // return relative error: ||f_a - f_d|| / ||f_a||, if ||f_a|| == 0, return ||f_a - f_d||
+  double testInternalForce(const double * u);
+
+  // use ForceModel::GetInternalForceAndMatrix() to compute analtical tangent stiffness matrix K_a
+  // use ForceModel::GetInternalForce() to compute finite difference matrix K_d
+  // return relative error: ||K_a - K_d||_F / ||K_a||_F,  || ||_F is frobNorm, if ||K_a|| == 0, return ||K_a - K_d||_F
+  // to save time, we assume the topology (non-zero entry locations) are correct in the stiffness matrix. So when
+  //   computing ||K_a - K_d||_F, we only compare those non-zero entries, ignoring other entries in K_d which is computed densely
+  // relativeErrorOnUnsymmetry returns ||K_a - K_a^T||_F / ||K_a||_F, if ||K_a||_F == 0, return ||K_a - K_a^T||_F
+  double testStiffnessMatrix(const double * u, double * relativeErrorOnUnsymmetry = nullptr);
+
+protected:
+  ForceModel * forceModel = nullptr;
+  double h = 0.0; // timestep
+  Mode mode = TWO_POINT;
+  int numThreads = 0;
+  int r = 0;
+
+  std::vector<double> analyticForce, finiteForce;
+  SparseMatrix * stiffnessMatrix0 = nullptr, * transposedStiffness0 = nullptr;
+  SparseMatrix * unsymmetricStiffness = nullptr;
+};
+
+#endif /* FINITEDIFFERENCETESTER_H_ */
diff --git a/libraries/include/forceModel.h b/libraries/include/forceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..29a49a6450dc593c73515c08209fc784b422be78
--- /dev/null
+++ b/libraries/include/forceModel.h
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Abstract class for f in Mu'' + Du' + f = f_ext .
+  Serves as a connecting class between integrators and classes to calculate internal forces and tangent stiffness matrices.
+*/
+
+#ifndef _FORCEMODEL_H_
+#define _FORCEMODEL_H_
+
+#include <stdlib.h>
+#include "sparseMatrix.h"
+
+class ForceModel
+{
+public:
+  virtual ~ForceModel();
+
+  inline int Getr() { return r; }
+
+  virtual double GetElasticEnergy(const double * u) { return 0.0; }
+  virtual void GetInternalForce(const double * u, double * internalForces) = 0;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) = 0;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) = 0; 
+
+  // sometimes computation time can be saved if we know that we will need both internal forces and tangent stiffness matrices:
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix); 
+
+  // reset routines
+  virtual void ResetToZero() {}
+  virtual void Reset(double * q) {}
+
+protected:
+  int r;
+};
+
+#endif
+
diff --git a/libraries/include/forceModelAssembler.h b/libraries/include/forceModelAssembler.h
new file mode 100644
index 0000000000000000000000000000000000000000..6e0b40d49c9306b9d7ea4b229d8a448491064038
--- /dev/null
+++ b/libraries/include/forceModelAssembler.h
@@ -0,0 +1,98 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _FORCEMODEL_ASSEMBLER_H_
+#define _FORCEMODEL_ASSEMBLER_H_
+
+#include "forceModel.h"
+#include "stencilForceModel.h"
+
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+
+/*
+  For each stencil type, this class assembles values at individual stencils into global object quantities.
+  E.g., form the global internal force vector from stencil force vectors, or form the global tangent stiffness matrix
+  from stencil tangent stiffness matrices.
+  Please see also stencilForceModel.h for more information.
+  This class inherits from ForceModel and implements the necessary interfaces.
+  If Intel TBB is provided, assembly will be performed in parallel.
+  The number of threads can be controlled outside the class using the Intel TBB APIs.
+  If Intel TBB is not provided, the computation will be single-threaded.
+*/
+
+class ForceModelAssembler : public ForceModel
+{
+public:
+  ForceModelAssembler(StencilForceModel *stencilForceModel);
+  virtual ~ForceModelAssembler();
+
+  // See comments in the parent class for the following functions.
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override;
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix) override;
+
+  // This function computes the energy, internal forces and tangent stiffness matrix of the 'object'.
+  // u is the displacement vector of all vertices.
+  // energy points to a double variable.
+  // internalForces points to a double array which dimension is the same as u.
+  // tangentStiffnessMatrix point to a sparse matrix object.
+  // energy, internalForces, tangentStiffnessMatrix can be nullptr. If nullptr, the corresponding quantity will not be computed.
+  virtual void GetEnergyAndForceAndMatrix(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+
+protected:
+  StencilForceModel * stencilForceModel = nullptr;
+  SparseMatrix * Ktemplate = nullptr;
+  std::vector<std::vector<int>> inverseIndices;
+
+  // data structures for parallelism
+#ifdef USE_TBB
+  typedef tbb::cache_aligned_allocator<double> BufferAllocator;
+  typedef std::vector<double, BufferAllocator> Buffer;
+  std::vector<tbb::enumerable_thread_specific<Buffer>*> localBuffers;
+  tbb::affinity_partitioner * partitioners = nullptr;
+  tbb::enumerable_thread_specific<double> energyLocalBuffer;
+  tbb::spin_mutex * internalForceVertexLocks, *stiffnessMatrixVertexRowLocks = nullptr;
+#else
+  // data structures for single-threaded computation
+  typedef std::allocator<double> BufferAllocator;
+  typedef std::vector<double, BufferAllocator> Buffer;
+#endif
+
+  std::vector<Buffer> bufferExamplars;
+};
+
+#endif
+
diff --git a/libraries/include/frameRateLock.h b/libraries/include/frameRateLock.h
new file mode 100644
index 0000000000000000000000000000000000000000..f407262b1b3b97d7098904527ef99dddfc231a00
--- /dev/null
+++ b/libraries/include/frameRateLock.h
@@ -0,0 +1,66 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "performanceCounter" library , Copyright (C) 2018 USC                 *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "performanceCounter.h"
+#include <cassert>
+
+// used to lock frame-rate
+class FrameRateLock
+{
+public:
+  FrameRateLock(double targetFrameRate = 30.0) : frameRate(targetFrameRate), invFrameRate(1.0 / frameRate) { assert(frameRate > 0.0); }
+  virtual ~FrameRateLock() {}
+
+  // start internal counter
+  void start() { counter.StartCounter(); }
+
+  // called in openGL idle function, pause the code until target frame rate is reached
+  inline void lock();
+
+protected:
+  double frameRate, invFrameRate;
+  PerformanceCounter counter;
+};
+
+
+inline void FrameRateLock::lock()
+{
+  double elapsedTime = 0.0;
+  do
+  {
+    counter.StopCounter();
+    elapsedTime = counter.GetElapsedTime();
+  }
+  while (elapsedTime <= invFrameRate);
+
+  counter.StartCounter();
+}
diff --git a/libraries/include/generateGradientMatrix.h b/libraries/include/generateGradientMatrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd9203e57d2510d52fd4626cacece2f2edcf49c6
--- /dev/null
+++ b/libraries/include/generateGradientMatrix.h
@@ -0,0 +1,61 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _GENERATEGRADIENTMATRIX_H_
+#define _GENERATEGRADIENTMATRIX_H_
+
+#include "sparseMatrix.h"
+#include "tetMesh.h"
+
+// creates the sparse matrix G that computes the gradient
+// of a 3D vector field on a tet mesh
+// optionally, computes G^T G as well
+
+class GenerateGradientMatrix
+{
+public:
+
+  // for a 3D vector field on a tet mesh
+  // weights serve as GTw^2G
+  static void Generate(const TetMesh * tetMesh, SparseMatrix ** G, SparseMatrix ** GTG = NULL, double * GTGElementWeights = NULL); // of the entire mesh 
+
+  static void GenerateElementMatrix(const TetMesh * tetMesh, int el, double * G); // of a single element; G is 9 x 12
+
+  // the version that operates on scalar fields
+  static void GenerateForScalarField(const TetMesh * tetMesh, SparseMatrix ** GTG, double * GTGElementWeights); // of the entire mesh
+
+protected:
+  static void GenerateElementMatrixEntries(const TetMesh * tetMesh, int el, double * m); // of a single element, condensed array "m" of 12 doubles
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/generateInterpolationMatrix.h b/libraries/include/generateInterpolationMatrix.h
similarity index 79%
rename from src/libvolumetricMesh/generateInterpolationMatrix.h
rename to libraries/include/generateInterpolationMatrix.h
index 4e60862fd103e328f9ccdfe1c977ce1e55a813ad..39a9a3c7a9c65fa3523ca59e470ba59816ea4e86 100644
--- a/src/libvolumetricMesh/generateInterpolationMatrix.h
+++ b/libraries/include/generateInterpolationMatrix.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,8 +41,6 @@
 // y ... triangle mesh quantity
 // x ... volumetric mesh quantity
 
-namespace vega
-{
 class GenerateInterpolationMatrix
 {
 public:
@@ -47,8 +49,8 @@ public:
   // number of volumetric mesh vertices.
   // numTargetLocations, numElementVertices, vertices, weights, can be 
   // generated using the interpolation capabilities of the VolumetricMesh class
-  static void generate(int numTargetLocations, int numElementVertices, int * vertices, double * weights, SparseMatrix ** A, int numSourceLocations=-1); 
+  static void generate(int numTargetLocations, int numElementVertices, const int * vertices, const double * weights, SparseMatrix ** A, int numSourceLocations=-1); 
 };
-}
+
 #endif
 
diff --git a/libraries/include/generateLaplacian.h b/libraries/include/generateLaplacian.h
new file mode 100644
index 0000000000000000000000000000000000000000..379df753e60be296b9ce5f6104b968e0f0697f67
--- /dev/null
+++ b/libraries/include/generateLaplacian.h
@@ -0,0 +1,54 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "shapeEdit" library , Copyright (C) 2018 USC                          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Koki Nagano, Yijing Li, Jernej Barbic        *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef GENERATELAPLACIAN_H
+#define GENERATELAPLACIAN_H
+
+#include "sparseMatrix.h"
+#include "tetMesh.h"
+#include "objMesh.h"
+
+// generate Laplacian matrix for tet mesh [Wang 2015 Linear Subspace Design for Real-Time Shape Deformation]
+SparseMatrix * generateLaplacian(const TetMesh * tetMesh, bool linearlyPrecise);
+
+// generate Laplacian matrix for obj mesh [Sorkine 2007 ARAP]
+// if clampCotangent == true, clamp the cotangent weights in the Lapalcian matrix to be >= 0
+SparseMatrix * generateLaplacian(const ObjMesh * objMesh, bool clampCotangent);
+
+// return ||A 1_n||_F
+double testConstantPrecision(const SparseMatrix * A);
+
+// return ||A \bar{V}||_F, \bar{V} is a nx3 matrix storing rest positions of the mesh
+double testLinearPrecision(const SparseMatrix * A, const VolumetricMesh * mesh);
+//double testLinearPrecision(const SparseMatrix * A, const ObjMesh * mesh);
+
+#endif
diff --git a/src/libvolumetricMesh/generateMassMatrix.h b/libraries/include/generateMassMatrix.h
similarity index 72%
rename from src/libvolumetricMesh/generateMassMatrix.h
rename to libraries/include/generateMassMatrix.h
index 1854274e9f55301d3c2439eef6803baf761a161f..86ff259007c89de019dbfa0c324947dadfef6c57 100644
--- a/src/libvolumetricMesh/generateMassMatrix.h
+++ b/libraries/include/generateMassMatrix.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,8 +41,6 @@
 #include "volumetricMesh.h"
 #include "sparseMatrix.h"
 
-namespace vega
-{
 class GenerateMassMatrix
 {
 public:
@@ -49,13 +51,13 @@ public:
   // matrix will be 3*numVertices x 3*numVertices).
   // In order to save some space, set it to false (output matrix will be 
   // numVertices x numVertices).
-  static void computeMassMatrix(VolumetricMesh * volumetricMesh, SparseMatrix ** massMatrix, bool inflate3Dim = false);
+  static void computeMassMatrix(const VolumetricMesh * volumetricMesh, SparseMatrix ** massMatrix, bool inflate3Dim = false);
   // computes the mass belonging to each vertex, by lumping the mass matrix
-  static void computeVertexMasses(VolumetricMesh * volumetricMesh, double * masses);
+  // masses has size of volumetricMesh->getNumVertices() if (inflat3Dim == false) or 3 * n if (inflate3Dim == true)
+  static void computeVertexMasses(const VolumetricMesh * volumetricMesh, double * masses, bool inflate3Dim = false);
 
 protected:
 };
 
-}
 #endif
 
diff --git a/src/libvolumetricMesh/generateMeshGraph.h b/libraries/include/generateMeshGraph.h
old mode 100755
new mode 100644
similarity index 76%
rename from src/libvolumetricMesh/generateMeshGraph.h
rename to libraries/include/generateMeshGraph.h
index 4ee034479e3923b9c15c671f69ca15010fa4b378..525d8741a5de9169ba2982aac857ac50fa58b3fb
--- a/src/libvolumetricMesh/generateMeshGraph.h
+++ b/libraries/include/generateMeshGraph.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -38,13 +42,11 @@
 // generates a graph of the vertices of a volumetric mesh
 // two vertices are connected if they share an edge
 
-namespace vega
-{
 class GenerateMeshGraph
 {
 public:
-  static Graph * Generate(VolumetricMesh * volumetricMesh);
+  static Graph * Generate(const VolumetricMesh * volumetricMesh);
 };
-}
+
 #endif
 
diff --git a/src/libvolumetricMesh/generateSurfaceMesh.h b/libraries/include/generateSurfaceMesh.h
similarity index 59%
rename from src/libvolumetricMesh/generateSurfaceMesh.h
rename to libraries/include/generateSurfaceMesh.h
index 24fc323a0c26d4f1b2887cf35f43c714beed0dc1..cb7f60bb6a5bfa9476c8781b41961405447c7f6e 100644
--- a/src/libvolumetricMesh/generateSurfaceMesh.h
+++ b/libraries/include/generateSurfaceMesh.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -43,18 +47,20 @@
 #include "objMesh.h"
 
 
-namespace vega
-{
 class GenerateSurfaceMesh
 {
 public:
-  // The output surface mesh is usually a triangle mesh, but can also be a quad mesh for cubic meshes.
-  // The last argument specifies whether output mesh should be quads or triangles (in the case of cubic volumetric meshes).
-  // All vertices of the volumetric mesh are included in the output surface mesh, even if they don't touch any surface triangle (e.g., vertices in the interior of the volumetric mesh).
-  static ObjMesh * ComputeMesh(VolumetricMesh * volumetricMesh, bool triangulateOutputMesh=false); 
+  // The output surface mesh is a triangle mesh for tet meshes and can also be a quad mesh for cubic meshes.
+  // All vertices of the volumetric mesh are included in the output surface mesh, even if they don't touch any surface triangle
+  //   (e.g., vertices in the interior of the volumetric mesh).
+  // triangulate: specifies whether output mesh should be quads or triangles (in the case of cubic volumetric meshes).
+  // allElementFaces = true: build ALL faces for ALL elements in the mesh
+  //                 = false: build only surface faces of the mesh (default)
+  static ObjMesh * ComputeMesh(const VolumetricMesh * volumetricMesh, bool triangulate=false, bool allElementFaces = false); 
 
-  static ObjMesh * ComputeMesh(VolumetricMesh * volumetricMesh, ObjMesh * superMesh, bool triangulateOutputMesh=false); // computes the surface of the mesh, but only the part which is also the outer surface of the given super mesh (advanced routine)
+  // computes the surface of the mesh, but only the part which is also the outer surface of the given super mesh (advanced routine)
+  static ObjMesh * ComputeMesh(const VolumetricMesh * volumetricMesh, const ObjMesh * superMesh, bool triangulate=false);
 };
-}
+
 #endif
 
diff --git a/src/libvolumetricMesh/volumetricMeshLoader.cpp b/libraries/include/generateTetMeshFromCubicMesh.h
similarity index 67%
rename from src/libvolumetricMesh/volumetricMeshLoader.cpp
rename to libraries/include/generateTetMeshFromCubicMesh.h
index fed64bd2ef344a0058cff912644d7370caede098..bb6f13150050e8f9c58bb25439faff595c3daf8c 100644
--- a/src/libvolumetricMesh/volumetricMeshLoader.cpp
+++ b/libraries/include/generateTetMeshFromCubicMesh.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,28 +30,20 @@
  *                                                                       *
  *************************************************************************/
 
-#include "volumetricMeshLoader.h"
+#ifndef _GENERATETETMESHFROMCUBICMESH_H_
+#define _GENERATETETMESHFROMCUBICMESH_H_
+
 #include "cubicMesh.h"
 #include "tetMesh.h"
 
-namespace vega
-{
-VolumetricMesh * VolumetricMeshLoader::load(char * filename, int verbose)
-{
-  VolumetricMesh::elementType elementType_ = VolumetricMesh::getElementType(filename);
-  if (elementType_ == VolumetricMesh::INVALID)
-  {
-    return NULL;
-  }
+// converts a cubic mesh into a tet mesh
+// only supports uniform material properties
 
-  VolumetricMesh * volumetricMesh = NULL;
-
-  if (elementType_ == TetMesh::elementType())
-    volumetricMesh = new TetMesh(filename, verbose); 
+class GenerateTetMeshFromCubicMesh
+{
+public:
+  static TetMesh * Generate(CubicMesh * cubicMesh, double E=1E9, double nu=0.45, double density=1000);
+};
 
-  if (elementType_ == CubicMesh::elementType())
-    volumetricMesh = new CubicMesh(filename, verbose); 
+#endif
 
-  return volumetricMesh;
-}
-}
diff --git a/src/libintegrator/getIntegratorSolver.h b/libraries/include/getIntegratorSolver.h
similarity index 93%
rename from src/libintegrator/getIntegratorSolver.h
rename to libraries/include/getIntegratorSolver.h
index 24f7ffc6afba9980f1d2f556b824e15caad7d85d..8ea45aafefe9fa40114163172ea767157265d9e1 100644
--- a/src/libintegrator/getIntegratorSolver.h
+++ b/libraries/include/getIntegratorSolver.h
@@ -1,12 +1,10 @@
 #ifndef _GETINTEGRATORSOLVER_H_
 #define _GETINTEGRATORSOLVER_H_
 
-namespace vega
-{
 // returns the string corresponding to the selected integrator solver
 // "solver" must be pre-allocated
 // result: PARDISO, SPOOLES or PCG
 void GetIntegratorSolver(char * solver);
-}
+ 
 #endif
 
diff --git a/libraries/include/getopts.h b/libraries/include/getopts.h
new file mode 100644
index 0000000000000000000000000000000000000000..4fe008e2566751c525dd0ecea02f8ad9090a002e
--- /dev/null
+++ b/libraries/include/getopts.h
@@ -0,0 +1,17 @@
+/* getopts.h */
+
+#ifndef _GETOPTS_H_
+#define _GETOPTS_H_
+
+#define OPTINT 1
+#define OPTSTR 2
+#define OPTBOOL 3
+#define OPTLONG 4
+#define OPTSUBOPT 5
+typedef struct {const char *sw;
+               int opttyp;
+               void *var;} opt_t;
+int getopts(int argc, char **argv, opt_t opttable[]);
+
+#endif
+
diff --git a/libraries/include/graph.h b/libraries/include/graph.h
new file mode 100644
index 0000000000000000000000000000000000000000..f1ece40c4ddf78b02cb9b9bec9c4ef75f7c52e5c
--- /dev/null
+++ b/libraries/include/graph.h
@@ -0,0 +1,153 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "graph" library , Copyright (C) 2018 USC                              *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _GRAPH_H_
+#define _GRAPH_H_
+
+#include <vector>
+#include <set>
+#include <map>
+#include "sparseMatrix.h"
+
+/*
+  A class to store an undirected graph (nodes connected with edges).
+*/
+
+class Graph
+{
+public:
+
+  Graph(); 
+  // if sortEdgeVertices=1, each edge (v0, v1) in the file will be sorted to ensure v0 < v1 
+  // before added into internal data
+  Graph(const char * filename, int sortEdgeVertices=1); // load graph from file
+  // each edge is given by two integers; length of "edges" should be 2xnumEdges
+  // if sortEdgeVertices=1, each edge (v0, v1) in edges will be sorted to ensure v0 < v1 
+  // before added into internal data
+  Graph(int numVertices, int numEdges, const int * edges, int sortEdgeVertices=1);
+
+  // convert a matrix into a graph; numVertices = matrix->GetNumRows()
+  // two vtx (v0, v1) share an edge if they have sparse entries at matrix(v0,v1) or matrix(v1,v0)
+  Graph(const SparseMatrix * matrix);
+
+  Graph(const Graph & graph);
+  Graph & operator=(const Graph & graph);
+  virtual ~Graph();
+
+  void Save(const char * filename) const; // save graph to file
+
+  int GetNumVertices() const;
+  int GetNumEdges() const;
+
+  int GetNumNeighbors(int vertex) const;
+  int GetNeighbor(int vertex, int i) const;
+  // return 0 if vtx1 and vtx2 are not neighbors
+  // return neighbor index + 1 (range: [1, #neighbor]) otherwise
+  int IsNeighbor(int vtx1, int vtx2) const;
+  // neighborhoodSize >= 0
+  // find all vertices whose distance to vertex <= neighborhoodSize
+  // return their vertex ID and distance
+  std::map<int,int> GetNeighborhoodWithDistance(int vertex, int neighborhoodSize);
+  std::map<int,int> GetNeighborhoodWithDistance(const std::set<int> & seedVertices, int neighborhoodSize);
+
+  int GetMinDegree() const;
+  int GetMaxDegree() const;
+  double GetAvgDegree() const;
+  double GetStdevDegree() const;
+
+  void ExpandNeighbors(); // connects every node to all the neighbors of every neighbor
+  void PrintInfo() const;
+
+  // if scaleRows == 1, each row will be scaled to sum to one
+  void GetLaplacian(SparseMatrix ** L, int scaleRows=0) const;
+
+  // returns the Cartesian graph product of "this" and graph2
+  Graph * CartesianProduct(Graph & graph2) const;
+  // return the index of vertex (vertex1, vertex2) in the cartesian product of "this" with another graph (which is not needed explicitly)
+  int GetCartesianProductVertexIndex(int vertex1, int vertex2) const;
+  // converts in the opposite direction
+  void GetCartesianProductVertexIndexComponents(int productVertex, int * vertex1, int * vertex2) const;
+
+  // clusters given vertices into connected components
+  // previous data stored in clusters will be cleared
+  void Cluster(const std::set<int> & vertices, std::vector<std::set<int> > & clusters) const;
+
+  // computes the shortest distance from the given seed vertices (distance of zero) to all the graph vertices
+  // input: seed vertices
+  // output: distance to the set of seed vertices, for each mesh vertex
+  void ShortestDistance(const std::set<int> & seedVertices, std::vector<int> & distances) const;
+
+  // computes one of the shortest path from seedVertices to one of the destinationVertices
+  // return whether the path exists.
+  // if path != NULL, previous data stored in path will be cleared. On return, it stores the path from
+  // one of the seed vertices to one of the destination vertices, both ends included
+  bool FindShortestPath(const std::set<int> & seedVertices, const std::set<int> & destinationVertices, std::vector<int> * path = NULL) const;
+
+  // find shortest path only in the local vertices set
+  bool FindLocalShortestPath(const std::set<int> & localVertices, const std::set<int> & seedVertices, const std::set<int> & destinationVertices, std::vector<int> * path = NULL) const;
+
+  // find the connected component of vtx
+  void GetConnectedComponent(int vtx, std::set<int> & connectedVertices) const;
+
+protected:
+  int numVertices, numEdges; // num vertices, num edges
+  std::set< std::pair<int, int> > edges;
+  // for each vtx, mapping: vtx index of neighbor [0, numVertices) -> neighbor index [0, #neighbor)
+  std::vector< std::map<int, int> > vertexNeighbors;
+  std::vector< std::vector<int> > vertexNeighborsVector;
+
+  void BuildVertexNeighbors();
+  void BuildVertexNeighborsVector();
+};
+
+inline int Graph::GetNumVertices() const
+{
+  return numVertices;
+}
+
+inline int Graph::GetNumEdges() const
+{
+  return numEdges;
+}
+
+inline int Graph::GetNumNeighbors(int vertex) const
+{
+  return (int) vertexNeighborsVector[vertex].size();
+}
+
+inline int Graph::GetNeighbor(int vertex, int i) const
+{
+  return vertexNeighborsVector[vertex][i];
+}
+
+#endif
+
diff --git a/libraries/include/hashHelper.h b/libraries/include/hashHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..0e427f66c7d73d42c13dd4cdd258c5d22f8ce4b0
--- /dev/null
+++ b/libraries/include/hashHelper.h
@@ -0,0 +1,17 @@
+// The following code is from Boost library.
+// Boost has the following license text:
+// Distributed under the Boost Software License, Version 1.0.
+//    (See accompanying file LICENSE_1_0.txt or copy at
+//          https://www.boost.org/LICENSE_1_0.txt)
+#ifndef HASHHELPER_H
+#define HASHHELPER_H
+#include <cstddef>
+
+inline std::size_t hashCombine(std::size_t seed, std::size_t newValue)
+{
+    seed ^= newValue + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+      return seed;
+}
+
+
+#endif
diff --git a/libraries/include/hashTable.h b/libraries/include/hashTable.h
new file mode 100644
index 0000000000000000000000000000000000000000..67d2dbc5481dbca527117ef82950846ddd0cc39e
--- /dev/null
+++ b/libraries/include/hashTable.h
@@ -0,0 +1,87 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "hashTable" library , Copyright (C) 2007 CMU                          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A simple hash table. 
+  Keys are of type 'unsigned int', and datatype can be arbitrary (templated).
+
+  The hash function is:
+  key * (key + 3) % p,
+  where appropriate p is chosen automatically by the constructor.
+*/
+
+#ifndef _HASHTABLE_H_
+#define _HASHTABLE_H_
+
+#include <vector>
+#include <list>
+
+template<class Data>
+class HashTable
+{
+public:
+  HashTable(unsigned int suggestedSize);
+
+  // clears the hash table
+  void clear();
+  
+  // insert (key, data)
+  void insert(unsigned int key, Data & data); // makes an internal copy of data; does not check for key duplication; if you want to use data that you allocated externally, pass pointers to your data as 'data'
+
+  // erase entry with the given key
+  void erase(unsigned int key); // does not check for duplicate entries (only erases the first matching entry)
+
+  Data * find(unsigned int key); // returns pointer to internal copy of data if found, or NULL otherwise
+
+  bool contains(unsigned int key, Data & data); // does (key,Data) pair appear in the hash table
+
+  void printInfo(); // prints info on hash table efficiency
+
+protected:
+  inline unsigned int hashFunction(unsigned int key);
+  bool isPrime(unsigned int n);
+  
+  typedef std::pair<unsigned int, Data > listEntry;
+  std::vector<std::list<listEntry> > buckets;
+  typedef typename std::list<listEntry>::iterator listIterator;
+
+  unsigned int p;
+};
+
+template<class Data>
+inline unsigned int HashTable<Data>::hashFunction(unsigned int key)
+{
+  return (key*(key+3) % p);
+}
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.h b/libraries/include/homogeneousMooneyRivlinIsotropicMaterial.h
old mode 100755
new mode 100644
similarity index 84%
rename from src/libisotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.h
rename to libraries/include/homogeneousMooneyRivlinIsotropicMaterial.h
index 4a591f0c13335c891dfb514ec5ce663d793ff463..318cfe01dd38e3dd620276e84963747752a50828
--- a/src/libisotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.h
+++ b/libraries/include/homogeneousMooneyRivlinIsotropicMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,8 +35,6 @@
 
 #include "isotropicMaterialWithCompressionResistance.h"
 
-namespace vega
-{
 /*
   The implemented compressible Mooney-Rivlin material has the following energy function:
 
@@ -70,6 +72,6 @@ protected:
   double EdivNuFactor;
   virtual double GetCompressionResistanceFactor(int elementIndex);
 };
-}
+
 #endif
 
diff --git a/src/libisotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.h b/libraries/include/homogeneousNeoHookeanIsotropicMaterial.h
old mode 100755
new mode 100644
similarity index 83%
rename from src/libisotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.h
rename to libraries/include/homogeneousNeoHookeanIsotropicMaterial.h
index 6d2eb9654647d2b3e19b22e92c3916942e9953c9..01bd1a73c7a4716ad35caedf3faac927ab9eb6f6
--- a/src/libisotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.h
+++ b/libraries/include/homogeneousNeoHookeanIsotropicMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,8 +35,6 @@
 
 #include "isotropicMaterialWithCompressionResistance.h"
 
-namespace vega
-{
 /*
    Homogeneous neo-Hookean material. Material properties are constant throughout the mesh.
 
@@ -63,6 +65,6 @@ protected:
   double EdivNuFactor;
   virtual double GetCompressionResistanceFactor(int elementIndex);
 };
-}
+
 #endif
 
diff --git a/src/libisotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.h b/libraries/include/homogeneousStVKIsotropicMaterial.h
old mode 100755
new mode 100644
similarity index 83%
rename from src/libisotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.h
rename to libraries/include/homogeneousStVKIsotropicMaterial.h
index ef7dc63bd9da3c10cfd457c0e131fe543b41d843..1e6fc5234c6aec25a4f3bf38a3a7dfdd9f74698c
--- a/src/libisotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.h
+++ b/libraries/include/homogeneousStVKIsotropicMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 #include "isotropicMaterialWithCompressionResistance.h"
 #include "tetMesh.h"
 
-namespace vega
-{
 /*
    Homogeneous StVK material. Material properties are constant throughout the mesh.
  
@@ -64,6 +66,6 @@ protected:
   double EdivNuFactor;
   virtual double GetCompressionResistanceFactor(int elementIndex);
 };
-}
+
 #endif
 
diff --git a/libraries/include/imageFormats.h b/libraries/include/imageFormats.h
new file mode 100644
index 0000000000000000000000000000000000000000..9b2ddb0a19146d9b85c74be2cbe9742aa90842c1
--- /dev/null
+++ b/libraries/include/imageFormats.h
@@ -0,0 +1,39 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/code                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+// Enable the libraries available on your system.
+// You must also set the path in the Makefile accordingly.
+
+//#define ENABLE_JPEG
+//#define ENABLE_TIFF
+//#define ENABLE_PNG
+
diff --git a/libraries/include/imageIO.h b/libraries/include/imageIO.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f1db5f1f988e2e7e9bfece09f991ad4b59fd8c5
--- /dev/null
+++ b/libraries/include/imageIO.h
@@ -0,0 +1,111 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "imageIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yili Zhao, Jernej Barbic                                 *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Load/save PPM, PNG, JPEG, TIFF, TGA image formats.
+
+  PPM and TGA are built-in (no external dependencies).
+  PNG depends on libpng, JPEG depends on jpeglib and TIFF depends on libtiff.
+
+  In order to enable PNG, JPEG or TIFF, uncomment the corresponding line in imageFormats.h, and link against the external library.
+*/
+
+#ifndef _IMAGEIO_H_
+#define _IMAGEIO_H_
+
+class ImageIO
+{
+public:
+
+  ImageIO();
+
+  // makeInternalCopy:
+  // 0: do not copy pixels internally (shallow copy)
+  // 1: copy pixels internally (deep copy)
+  ImageIO(unsigned int width, unsigned int height, unsigned int bytesPerPixel, unsigned char * pixels, int makeInternalCopy=1);
+  ~ImageIO();
+
+  inline unsigned int getWidth() { return width; }
+  inline unsigned int getHeight() { return height; }
+  inline unsigned int getBytesPerPixel() { return bytesPerPixel; }
+  inline unsigned char * getPixels() { return pixels; }
+  inline unsigned char getPixel(int x, int y, int channel) { return pixels[(y * width + x) * bytesPerPixel + channel]; }
+
+  // error codes
+  typedef enum { OK, INVALID_FILE_FORMAT, IO_ERROR, MEMORY_ERROR, OTHER_ERROR } errorType;
+
+  // generic loader, infers the file format automatically from the filename extension
+  // input: filename
+  // output: fileFormat
+  typedef enum { FORMAT_PPM, FORMAT_TGA, FORMAT_JPEG, FORMAT_TIFF, FORMAT_PNG, FORMAT_NONE } fileFormatType;
+  errorType load(const char * filename, fileFormatType * fileFormat);
+  errorType save(const char * filename, fileFormatType fileFormat);
+
+  // loaders specific to each file format
+
+  // no external dependency
+  errorType loadPPM(const char * filename);
+  errorType savePPM(const char * filename);
+
+  // uncompressed TGA only
+  // no external dependency
+  errorType loadTGA(const char * filename);
+  errorType saveTGA(const char * filename); 
+
+  // using jpeglib
+  errorType loadJPEG(const char * filename);
+  errorType saveJPEG(const char * filename);
+  errorType saveJPEGWithGivenQuality(const char * filename, int quality = 95);
+
+  // using libtiff
+  errorType loadTIFF(const char * filename);
+  errorType saveTIFF(const char * filename);
+
+  // using libpng
+  errorType loadPNG(const char * filename);
+  errorType savePNG(const char * filename);
+
+  // flips the image vertically
+  void flipVertically(); 
+
+protected:
+  unsigned int width, height;
+  unsigned int bytesPerPixel;
+  unsigned char * pixels;
+  int ownPixels;
+
+  errorType loadNONE(const char * filename);
+  errorType saveNONE(const char * filename);
+};
+
+#endif
+
diff --git a/src/libintegratorSparse/implicitBackwardEulerSparse.h b/libraries/include/implicitBackwardEulerSparse.h
similarity index 73%
rename from src/libintegratorSparse/implicitBackwardEulerSparse.h
rename to libraries/include/implicitBackwardEulerSparse.h
index 37bdae01994a0c751fd946b43fac830727274e41..a3abe30d45a77e69df6576137376e76c36f4119c 100644
--- a/src/libintegratorSparse/implicitBackwardEulerSparse.h
+++ b/libraries/include/implicitBackwardEulerSparse.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -35,8 +39,6 @@
 
 #include "implicitNewmarkSparse.h"
 
-namespace vega
-{
 class ImplicitBackwardEulerSparse : public ImplicitNewmarkSparse
 {
 public:
@@ -44,17 +46,17 @@ public:
   // constrainedDOFs is an integer array of degrees of freedom that are to be fixed to zero (e.g., to permanently fix a vertex in a deformable simulation)
   // constrainedDOFs are 0-indexed (separate DOFs for x,y,z), and must be pre-sorted (ascending)
   // numThreads applies only to the PARDISO and SPOOLES solvers; if numThreads > 0, the sparse linear solves are multi-threaded; default: 0 (use single-threading)
-  ImplicitBackwardEulerSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int positiveDefiniteSolver=0, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, int maxIterations = 1, double epsilon = 1E-6, int numSolverThreads=0);
+  ImplicitBackwardEulerSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, int maxIterations = 1, double epsilon = 1E-6, int numSolverThreads=0); 
 
   virtual ~ImplicitBackwardEulerSparse();
 
-  // sets q, and (optionally) qvel
-  // returns 0
+  // sets q, and (optionally) qvel 
+  // returns 0 
   virtual int SetState(double * q, double * qvel=NULL);
-  virtual int DoTimestep();
+  virtual int DoTimestep(); 
 
 protected:
 };
-}
+
 #endif
 
diff --git a/src/libintegratorSparse/implicitNewmarkSparse.h b/libraries/include/implicitNewmarkSparse.h
similarity index 77%
rename from src/libintegratorSparse/implicitNewmarkSparse.h
rename to libraries/include/implicitNewmarkSparse.h
index a746b213c0778884ba459e183e63798f2dfb97c9..ead5345755f5cb6542d780a10b1723c58e856ab4 100644
--- a/src/libintegratorSparse/implicitNewmarkSparse.h
+++ b/libraries/include/implicitNewmarkSparse.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,7 +36,7 @@
 
   See also integratorBase.h .
 
-  This class either uses SPOOLES, PARDISO, or our own Jacobi-preconitioned
+  This class either uses SPOOLES, PARDISO, or our own Jacobi-preconitioned 
   CG to solve the large sparse linear systems.
 
   You can switch between these solvers at compile time,
@@ -59,18 +63,16 @@
 #include "sparseMatrix.h"
 #include "integratorBaseSparse.h"
 
-// #ifdef PARDISO
-//   #include "sparseSolvers.h"
-// #endif
-// #ifdef SPOOLES
-//   #include "sparseSolvers.h"
-// #endif
-// #ifdef PCG
-//   #include "CGSolver.h"
-// #endif
-
-namespace vega
-{
+#ifdef PARDISO
+  #include "sparseSolvers.h"
+#endif
+#ifdef SPOOLES
+  #include "sparseSolvers.h"
+#endif
+#ifdef PCG
+  #include "CGSolver.h"
+#endif
+
 class ImplicitNewmarkSparse : public IntegratorBaseSparse
 {
 public:
@@ -78,7 +80,7 @@ public:
   // constrainedDOFs is an integer array of degrees of freedom that are to be fixed to zero (e.g., to permanently fix a vertex in a deformable simulation)
   // constrainedDOFs are 0-indexed (separate DOFs for x,y,z), and must be pre-sorted (ascending)
   // numThreads applies only to the PARDISO solver; if numThreads > 0, the sparse linear solves are multi-threaded; default: 0 (use single-threading)
-  ImplicitNewmarkSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int positiveDefiniteSolver=0, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, int maxIterations = 1, double epsilon = 1E-6, double NewmarkBeta=0.25, double NewmarkGamma=0.5, int numSolverThreads=0);
+  ImplicitNewmarkSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, int maxIterations = 1, double epsilon = 1E-6, double NewmarkBeta=0.25, double NewmarkGamma=0.5, int numSolverThreads=0); 
 
   virtual ~ImplicitNewmarkSparse();
 
@@ -86,15 +88,17 @@ public:
   virtual void SetDampingMatrix(SparseMatrix * dampingMatrix);
   inline virtual void SetTimestep(double timestep) { this->timestep = timestep; UpdateAlphas(); }
 
-  // sets q, qvel
+  // sets q, qvel 
   // automatically computes acceleration assuming zero external force
   // returns 0 on succes, 1 if solver fails to converge
   // note: there are also other state setting routines in the base class
   virtual int SetState(double * q, double * qvel=NULL);
 
+  // see parent class for usage
+  virtual void SetTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset, int reuseTopology=1);
+
   // performs one step of simulation (returns 0 on sucess, and 1 on failure)
-  // failure can occur, for example, if you are using the positive definite solver and the system matrix has negative eigenvalues
-  virtual int DoTimestep();
+  virtual int DoTimestep(); 
 
   inline void SetNewmarkBeta(double NewmarkBeta) { this->NewmarkBeta = NewmarkBeta; UpdateAlphas(); }
   inline void SetNewmarkGamma(double NewmarkGamma) { this->NewmarkGamma = NewmarkGamma; UpdateAlphas(); }
@@ -102,8 +106,6 @@ public:
   // dynamic solver is default (i.e. useStaticSolver=false)
   virtual void UseStaticSolver(bool useStaticSolver);
 
-  SparseMatrix *GetSystemMatrix() const;
-
 protected:
   SparseMatrix * rayleighDampingMatrix;
   SparseMatrix * tangentStiffnessMatrix;
@@ -114,16 +116,21 @@ protected:
   // parameters for implicit Newmark
   double NewmarkBeta,NewmarkGamma;
   double alpha1, alpha2, alpha3, alpha4, alpha5, alpha6;
-  double epsilon;
+  double epsilon; 
   int maxIterations;
 
   void UpdateAlphas();
   bool useStaticSolver;
 
-  int positiveDefiniteSolver;
   int numSolverThreads;
+  #ifdef PARDISO
+    PardisoSolver * pardisoSolver;
+  #endif
 
+  #ifdef PCG
+    CGSolver * jacobiPreconditionedCGSolver;
+  #endif
 };
-}
+
 #endif
 
diff --git a/src/libintegrator/integratorBase.h b/libraries/include/integratorBase.h
old mode 100755
new mode 100644
similarity index 78%
rename from src/libintegrator/integratorBase.h
rename to libraries/include/integratorBase.h
index a5f5f6347d0260fc6478351fc350c966ac6f271c..cfb5a8be289ae8d2c6d55925487245ff869d2237
--- a/src/libintegrator/integratorBase.h
+++ b/libraries/include/integratorBase.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -35,49 +39,49 @@ This code can numerically timestep a system of ODEs of the form:
 
 M * q'' + (alpha * M + beta * K(q)) q' + R(q) = fext(t) , where
 
-q is an unknown vector function, M is an arbitrary symmetric positive-definite
-matrix, fext(t) are arbitrary user-provided external forces, alpha and beta
-are arbitrary non-negative scalar constants (Rayleigh damping parameters),
-R(q) is an arbitrary user-provided vector function, and K(q) = d R / d q is
-its user-provided gradient.
+q is an unknown vector function, M is an arbitrary symmetric positive-definite 
+matrix, fext(t) are arbitrary user-provided external forces, alpha and beta 
+are arbitrary non-negative scalar constants (Rayleigh damping parameters), 
+R(q) is an arbitrary user-provided vector function, and K(q) = d R / d q is 
+its user-provided gradient. 
 
 Such a system arises, for example, when simulating a nonlinear
 deformable object using the Finite Element Method (FEM), or also using
-mass-spring systems.  This code has been used for simulations of large
+mass-spring systems.  This code has been used for simulations of large 
 deformations of 3D solid deformable objects, with dynamics (see [1]).
 
 The code supports several numerical integrators (see derived classes).
 
 The code can handle both large sparse systems and dense reduced systems.
-For example, the large sparse version can be used to simulate a general
-deforming tetrahedral mesh. The dense version can be used for simulations
+For example, the large sparse version can be used to simulate a general 
+deforming tetrahedral mesh. The dense version can be used for simulations 
 that employ model reduction (see [1]).
 
-The class in this file (IntegratorBase) is the abstract base class. In practice,
-you need to use one of the provided derived classes, depending on whether
+The class in this file (IntegratorBase) is the abstract base class. In practice, 
+you need to use one of the provided derived classes, depending on whether 
 you want to simulate large sparse systems or dense reduced systems.
 
-All these classes are generic in that you can provide your own arbitrary
-internal forces R(q) and their gradients K(q). You do so by deriving from the
-InternalForceModel class, and passing that class to the appropriate integrator
-class constructor. Several force model classes are provided, including
-the cubic polynomial reduced StVK model from [1], and a linearized version of
+All these classes are generic in that you can provide your own arbitrary 
+internal forces R(q) and their gradients K(q). You do so by deriving from the 
+InternalForceModel class, and passing that class to the appropriate integrator 
+class constructor. Several force model classes are provided, including 
+the cubic polynomial reduced StVK model from [1], and a linearized version of 
 that model.
 
-For dense simulations, you need a BLAS and LAPACK library. This is necessary
+For dense simulations, you need a BLAS and LAPACK library. This is necessary 
 to solve the r x r linear systems inside the implicit Newmark solver.
 We have successfully used the following BLAS and LAPACK libraries:
 1. Windows: Intel Math Kernel Library for Windows
 2. Red Hat Linux: Intel Math Kernel Library for Linux
-3. Mac OS X: both BLAS and LAPACK are already included with Mac OS X
+3. Mac OS X: both BLAS and LAPACK are already included with Mac OS X 
    (the vecLib framework)
-You can probably also download the generic BLAS and LAPACK implementations
+You can probably also download the generic BLAS and LAPACK implementations 
 from www.netlib.org. All dense matrices are stored in column-major format.
 
-For large sparse simulations, you need a large sparse linear system solver.
+For large sparse simulations, you need a large sparse linear system solver. 
 The code supports the following solvers:
-1. SPOOLES (a free solver),
-2. PARDISO (we used the commercial version that comes with Intel MKL; this
+1. SPOOLES (a free solver), 
+2. PARDISO (we used the commercial version that comes with Intel MKL; this 
 solver is multi-threaded and can be executed across multiple cores of a CPU),
 3. our own Jacobi-preconditioned Conjugate Solver (from our "sparseMatrix" library).
 We were able to run sparse simulations on Windows, Linux and Mac OS X.
@@ -87,11 +91,11 @@ terms are neglected, and the system only computes the static equilibrium
 under the currently applied external forces.
 
 References:
-[1] Jernej Barbic, Doug L. James: Real-Time Subspace Integration for
-St.Venant-Kirchhoff Deformable Models, ACM Transactions on Graphics 24(3)
+[1] Jernej Barbic, Doug L. James: Real-Time Subspace Integration for 
+St.Venant-Kirchhoff Deformable Models, ACM Transactions on Graphics 24(3) 
 (SIGGRAPH 2005), p. 982-990, Los Angeles, CA, August 2005
-[2] Jernej Barbic: Real-time Reduced Large-Deformation Models and Distributed
-Contact for Computer Graphics and Haptics, PhD Thesis, Carnegie Mellon University,
+[2] Jernej Barbic: Real-time Reduced Large-Deformation Models and Distributed 
+Contact for Computer Graphics and Haptics, PhD Thesis, Carnegie Mellon University, 
 August 2007
 
 Both publications are available online at www.jernejbarbic.com .
@@ -100,14 +104,12 @@ Both publications are available online at www.jernejbarbic.com .
 
 #include <stdlib.h>
 
-namespace vega
-{
 // This abstract class is derived into: IntegratorBaseDense (dense systems)
 // and ImplicitNewmarkSparse ((large) sparse systems).
 class IntegratorBase
 {
 public:
-  // r is the dimension of the simulation; it equals 3*n for unreduced systems, where n is the number of vertices in the simulation mesh; with reduction, r equals the size of the simulation basis
+  // r is the dimension of the simulation; it equals 3*n for unreduced systems, where n is the number of vertices in the simulation mesh; with reduction, r equals the size of the simulation basis 
   // the damping coefficients are tangential Rayleigh damping coefficients, see [2]
   IntegratorBase(int r, double timestep, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0);
 
@@ -116,38 +118,38 @@ public:
   // === set/get the state (position,velocity and acceleration) ===
 
   // set integrator to zero (q, qvel, and qaccel are set to zero)
-  virtual void ResetToRest();
+  virtual void ResetToRest(); 
 
   // sets the position and the velocity
-  // this routine will internally automatically compute proper acceleration
+  // this routine will internally automatically compute proper acceleration 
   // returns 0 on success, 1 if solver fails to converge
   virtual int SetState(double * q, double * qvel=NULL) = 0;
 
   // sets the position, velocity, and acceleration
   // note: if you don't set all three at once, old values will persist, which may not be what you want
-  void SetqState(const double * q, const double * qvel=NULL, const double * qaccel=NULL);
+  virtual void SetqState(const double * q, const double * qvel=NULL, const double * qaccel=NULL);
 
   // copies the state into spaces provided by q,qvel,qaccel (each a vector of length r; if NULL is provided for either of q,qvel,qccel, that part of the state is not copied)
-  void GetqState(double * q, double * qvel=NULL, double * qaccel=NULL);
+  virtual void GetqState(double * q, double * qvel=NULL, double * qaccel=NULL);
 
-  // set/get invidivual position components:
-  inline void SetQ(int index, double qIndex) { q[index] = qIndex; }
-  inline double GetQ(int index) { return q[index]; }
+  // set/get individual position components:
+  inline virtual void SetQ(int index, double qIndex) { q[index] = qIndex; } 
+  inline virtual double GetQ(int index) { return q[index]; } 
 
   // obtain pointers to the internally stored position, velocity, and acceleration
   // (advanced usage)
-  inline double * Getq() { return q; }
-  inline double * Getqvel() { return qvel; }
-  inline double * Getqaccel() { return qaccel; }
+  inline virtual double * Getq() { return q; }
+  inline virtual double * Getqvel() { return qvel; }
+  inline virtual double * Getqaccel() { return qaccel; }
 
   // == set external forces (a vector of r numbers) ===
 
   // external forces remain in force until explicity changed
-  void SetExternalForces(double * externalForces);
-  void AddExternalForces(double * externalForces);
+  void SetExternalForces(double * externalForces); 
+  void AddExternalForces(double * externalForces); 
   void GetExternalForces(double * externalForces);
   double * GetExternalForces() { return externalForces; };
-  void SetExternalForcesToZero();
+  void SetExternalForcesToZero(); 
 
   // === set integration and simulation parameters ===
 
@@ -159,8 +161,8 @@ public:
   virtual void SetInternalForceScalingFactor(double internalForceScalingFactor) { this->internalForceScalingFactor = internalForceScalingFactor; }
 
   // tangential Rayleigh damping parameters
-  inline void SetDampingMassCoef(double dampingMassCoef) { this->dampingMassCoef = dampingMassCoef; }
-  inline void SetDampingStiffnessCoef(double dampingStiffnessCoef) { this->dampingStiffnessCoef = dampingStiffnessCoef;}
+  inline virtual void SetDampingMassCoef(double dampingMassCoef) { this->dampingMassCoef = dampingMassCoef; }
+  inline virtual void SetDampingStiffnessCoef(double dampingStiffnessCoef) { this->dampingStiffnessCoef = dampingStiffnessCoef;}
   inline double GetDampingMassCoef() { return dampingMassCoef; }
   inline double GetDampingStiffnessCoef() { return dampingStiffnessCoef; }
 
@@ -169,7 +171,7 @@ public:
 
   virtual int DoTimestep() = 0;
 
-  // === misc queries ===
+  // === misc ===
 
   inline int GetNumDOFs() { return r; }
   inline int Getr() { return r; }
@@ -180,12 +182,16 @@ public:
   virtual double GetForceAssemblyTime() = 0;
   virtual double GetSystemSolveTime() = 0;
 
+  // constrain the system to ||q||^2 < R2
+  // useful to prevent large values from occuring
+  virtual void ConstrainToSphere(double R2);
+
 protected:
 
   double * q; // current deformation amplitudes
   double * qvel; // current velocities of deformation amplitudes
   double * qaccel; // current acceleration (used inside implicit newmark integration)
-  double * qresidual, * qdelta; // aux integration variables
+  double * qresidual, * qdelta; // aux integration variables 
   double * q_1; // deformation amplitudes at previous time-step
   double * qvel_1;
   double * qaccel_1;
@@ -193,18 +199,18 @@ protected:
   double * internalForces; // current internal force amplitudes
   double * externalForces; // current external force amplitudes
 
-  double internalForceScalingFactor;
+  double internalForceScalingFactor; 
 
   double * buffer;
 
-  // these two store the damping parameters
+  // these two store the damping parameters 
   double dampingMassCoef;
   double dampingStiffnessCoef;
 
-  int r; // number of reduced DOFs
+  int r; // number of reduced DOFs 
 
-  double timestep;
+  double timestep; 
 };
-}
+
 #endif
 
diff --git a/src/libintegratorSparse/integratorBaseSparse.h b/libraries/include/integratorBaseSparse.h
similarity index 73%
rename from src/libintegratorSparse/integratorBaseSparse.h
rename to libraries/include/integratorBaseSparse.h
index df4af5ae4b7df5975673b1b932536224c4f2d403..50ab255cc072c7ce9ba7666731fb0406145473d6 100644
--- a/src/libintegratorSparse/integratorBaseSparse.h
+++ b/libraries/include/integratorBaseSparse.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,10 +44,6 @@
 #include "forceModel.h"
 #include "integratorBase.h"
 
-namespace vega
-{
-class LinearSolver;
-
 class IntegratorBaseSparse : public IntegratorBase
 {
 public:
@@ -60,6 +60,11 @@ public:
   // damping matrix provides damping in addition to mass and stiffness damping (it does not replace it)
   virtual void SetDampingMatrix(SparseMatrix * dampingMatrix);
 
+  // add an offset to the tangent stiffness matrix (default: no offset)
+  virtual void SetTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset, int reuseTopology=1); // you must use reuseTopology=0 if the offset has been previously already set, and the topology of the new tangentStiffnessMatrixOffset matrix is different from the old one
+  virtual void AddTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset); // the operand must have same topology as the previously set tangent stiffness matrix
+  virtual void ClearTangentStiffnessMatrixOffset();
+
   // performs one step of simulation (returns 0 on sucess, and 1 on failure)
   // failure can occur, for example, if you are using the positive definite solver and the system matrix has negative eigenvalues
   virtual int DoTimestep() = 0;
@@ -70,26 +75,21 @@ public:
 
   virtual double GetKineticEnergy();
   virtual double GetTotalMass();
-  virtual SparseMatrix *GetSystemMatrix() const;
-
-  const LinearSolver *getLinearSolver() const;
-
-  void setLinearSolver(LinearSolver *solver);
 
 protected:
-  SparseMatrix * massMatrix;
+  SparseMatrix * massMatrix; 
   ForceModel * forceModel;
   int ownDampingMatrix;
   SparseMatrix * dampingMatrix;
+  
+  SparseMatrix * tangentStiffnessMatrixOffset;
 
   int numConstrainedDOFs;
   int * constrainedDOFs;
 
   double systemSolveTime;
   double forceAssemblyTime;
-
-  LinearSolver *linearSolver;
 };
-}
+
 #endif
 
diff --git a/src/libintegrator/integratorSolverSelection.h b/libraries/include/integratorSolverSelection.h
similarity index 77%
rename from src/libintegrator/integratorSolverSelection.h
rename to libraries/include/integratorSolverSelection.h
index 850d1c5d186eaed18a9c59dd31acc88819385eaf..40dd1e0ee50bc0f46c6b42cc37a971899fe8dbd2 100644
--- a/src/libintegrator/integratorSolverSelection.h
+++ b/libraries/include/integratorSolverSelection.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/libraries/include/interpolationCoordinates.h b/libraries/include/interpolationCoordinates.h
new file mode 100644
index 0000000000000000000000000000000000000000..5edb8455ea4b74924fd1ad9cd7fbeddef324ef8d
--- /dev/null
+++ b/libraries/include/interpolationCoordinates.h
@@ -0,0 +1,51 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "interpolationCoordinates" library , Copyright (C) 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef INTERPOLATIONCOORDINATES_H
+#define INTERPOLATIONCOORDINATES_H
+
+class InterpolationCoordinates
+{
+public:
+  virtual ~InterpolationCoordinates() {}
+
+  // deform embedded geometry
+  // embedderDisp: vertex displacements of the embedder mesh
+  // embeddedDisp: vertex displacements of the embedded mesh
+  virtual void deform(const double * embedderDisp, double * embeddedDisp) const = 0;
+
+protected:
+  InterpolationCoordinates() {}
+};
+
+#endif
+
diff --git a/src/libstvk/StVKStiffnessMatrixMT.h b/libraries/include/interpolationWeightsMultiLoad.h
similarity index 55%
rename from src/libstvk/StVKStiffnessMatrixMT.h
rename to libraries/include/interpolationWeightsMultiLoad.h
index 26613b7e0a4d38181769df76938fc20d152ba3a8..cfedfb2f026eb5a06ef8c9097071a90f768eeb20 100644
--- a/src/libstvk/StVKStiffnessMatrixMT.h
+++ b/libraries/include/interpolationWeightsMultiLoad.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -27,39 +31,18 @@
  *************************************************************************/
 
 /*
-  This class is a multi-threaded version of the class "StVKStiffnessMatrix".
-  It uses POSIX threads ("pthreads") as the threading API.
-  Each thread assembles the stiffness matrix with respect to a subset of all the mesh elements. 
-  At the end, the individual results are added into a global stiffness matrix.
-
-  See also StVKStiffnessMatrix.h .
+  Load/save many sets of interpolation weights from one file.
 */
 
-#ifndef _STVKSTIFFNESSMATRIXMT_H_
-#define _STVKSTIFFNESSMATRIXMT_H_
-
-#include "StVKStiffnessMatrix.h"
-
-namespace vega
-{
-class StVKStiffnessMatrixMT : public StVKStiffnessMatrix
-{
-public:
-  // multicore version of StVKStiffnessMatrix
-  StVKStiffnessMatrixMT(StVKInternalForces *  stVKInternalForces, int numThreads);
-  virtual ~StVKStiffnessMatrixMT();
+#ifndef _INTERPOLATIONWEIGHTSMULTILOAD_H_
+#define _INTERPOLATIONWEIGHTSMULTILOAD_H_
 
-  // evaluates the stiffness matrix in the given deformation configuration
-  virtual void ComputeStiffnessMatrix(double * vertexDisplacements, SparseMatrix * sparseMatrix);
+// input: filename, numModels
+// output: numTargetLocations, numElementVertices, vertices, weights
+int multiLoadInterpolationWeightsBinary(const char * filename, int * numModels, int ** numTargetLocations, int ** numElementVertices, int *** vertices, double *** weights); // binary version; returns 0 on success
 
-  int GetStartElement(int rank);
-  int GetEndElement(int rank);
+// input: filename, numModels, numTargetLocations, numElementVertices, vertices, weights
+int multiSaveInterpolationWeightsBinary(const char * filename, int numModels, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights); // binary version; returns 0 on success
 
-protected:
-  int numThreads;
-  int * startElement, * endElement;
-  SparseMatrix ** sparseMatrixBuffer;
-};
-}
 #endif
 
diff --git a/src/libsparseSolver/invMKSolver.h b/libraries/include/invMKSolver.h
similarity index 77%
rename from src/libsparseSolver/invMKSolver.h
rename to libraries/include/invMKSolver.h
index 108ff2ca257feea172d941b6ae063f8c15826d2a..d44358a75a2c5e3ca3732104110d2d7e1f649a96 100644
--- a/src/libsparseSolver/invMKSolver.h
+++ b/libraries/include/invMKSolver.h
@@ -1,24 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "Large Modal Deformation Factory",                                    *
- * a pre-processing utility for model reduction of                       *
- * deformable objects undergoing large deformations.                     *
- *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,14 +31,16 @@
  *                                                                       *
  *************************************************************************/
 
+/*
+  Helper class for the ARPACK solver.
+*/
+
 #ifndef _INVMKSOLVER_H_
 #define _INVMKSOLVER_H_
 
 #include "linearSolver.h"
 #include "sparseMatrix.h"
 
-namespace vega
-{
 // computes x |--> M^{-1} K x
 class InvMKSolver
 {
@@ -52,5 +54,5 @@ protected:
   LinearSolver * invMSolver;
   SparseMatrix * K;
 };
-}
+
 #endif
diff --git a/libraries/include/invZTAZMSolver.h b/libraries/include/invZTAZMSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..ba7238974d4f44ae29bb32ceaddc5043a4923753
--- /dev/null
+++ b/libraries/include/invZTAZMSolver.h
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Helper class for the ARPACK solver.
+*/
+
+#ifndef _INVZTAZMSOLVER_H_
+#define _INVZTAZMSOLVER_H_
+
+#include "invZTAZSolver.h"
+#include "ZTAZMultiplicator.h"
+
+/*
+  Given a vector x, compute (Z^T A Z)^{-1} (Z^T M Z) x
+  Z = P [ -C_p^{-1} C_n  ]
+        [   I            ]
+*/
+
+class InvZTAZMSolver
+{
+public:
+  InvZTAZMSolver(SparseMatrix * A, SparseMatrix * M, PardisoSolver * ZTAZPardisoSolver, 
+                 SparseMatrix * P, Matrix<double> * invCp, Matrix<double> * Cn);
+  virtual ~InvZTAZMSolver();
+
+  // output <-- (Z^T A Z)^{-1} (Z^T M Z) x
+  // x <-- (Z^T M Z) x
+  void ComputeInvZTAZM(double * x, double * output);
+
+protected:
+  InvZTAZSolver * invZTAZSolver;
+  ZTAZMultiplicator * zTMZMultiplicator;
+  double * buffer;
+  int dim;
+};
+
+#endif
+
diff --git a/libraries/include/invZTAZSolver.h b/libraries/include/invZTAZSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..66920271a692a0a5b8d7eb096690d91237502c10
--- /dev/null
+++ b/libraries/include/invZTAZSolver.h
@@ -0,0 +1,69 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Helper class for the ARPACK solver.
+*/
+
+#ifndef _INVZTAZSOLVER_H_
+#define _INVZTAZSOLVER_H_
+
+#include "ZTAZMultiplicator.h"
+#include "CGSolver.h"
+#include "PardisoSolver.h"
+/*
+  Given a vector rhs, compute (Z^T A Z)^{-1} rhs
+  Z = P [ -C_p^{-1} C_n  ]
+        [   I            ]
+*/
+class InvZTAZSolver
+{
+public:
+  InvZTAZSolver(SparseMatrix * A, PardisoSolver * ZTAZPardisoSolver, SparseMatrix * P, Matrix<double> * invCp, Matrix<double> * Cn);
+  virtual ~InvZTAZSolver();
+
+  // Solve for: (Z^T A Z) x = rhs
+  //   x = (Z^T A Z)^{-1} rhs
+  // rhs is not modified
+  void ComputeInvZTAZ(double * x, double * rhs);
+
+protected:
+  PardisoSolver * ZTAZPardisoSolver;
+  CGSolver * cgSolver;
+  ZTAZMultiplicator * zTAZMultiplicator;
+
+  static void Multiplicator(const void * data, const double * x, double * Ax);
+
+};
+
+#endif
+
diff --git a/libraries/include/isosurfaceMesher.h b/libraries/include/isosurfaceMesher.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1aea55a866abd112c433e7e91e55d9156321629
--- /dev/null
+++ b/libraries/include/isosurfaceMesher.h
@@ -0,0 +1,209 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Compute a quality triangular mesh of a distance field isosurface (level set),
+  using the following paper:
+
+  Steve Oudot, Laurent Rineau, Mariette Yvinec:
+  Meshing Volumes Bounded by Smooth Surfaces
+  Proceedings of the 14th International Meshing Roundtable 2005, pp 203-219
+*/
+
+#ifndef _ISOSURFACEMESHER_H_
+#define _ISOSURFACEMESHER_H_
+
+#include <time.h>
+#include <vector>
+#include <map>
+#include <set>
+
+#include "distanceFieldBase.h"
+#include "objMesh.h"
+#include "tetMesh.h"
+#include "triple.h"
+#include "triangle.h"
+#include "delaunayMesher.h"
+
+#define USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+#ifdef USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+#include "objMeshOctree.h"
+#else
+#include "exactOctree.h"
+#endif
+
+class IsosurfaceMesher
+{
+public:
+
+  // initialize the mesher
+  // the mesher will mesh an isosurface of the distance field "field"
+  IsosurfaceMesher(DistanceFieldBase * distanceField);
+  virtual ~IsosurfaceMesher();
+
+  // calculate the isosurface
+  // isovalue: the isovalue of the distance field to be meshed
+  // angularBound: the angles of the triangle on the isosurface mesh should not be larger than angularBound
+  //               the angular bound is passed in radians
+  // radialBound: the largest circumcircle of the triangle on the isosurface mesh should not be larger than "radialBound"
+  // numInitialSamples: number of points generated in the first step of the algorithm (internal initial mesh)
+  // epsilon: threshold used to check whether input vertices are degenerate
+  //          the larger the epsilon, the more input will be treated as degenerate by DelaunayMesher
+  // maxNumberOfIterations: the routine will terminate if this number of iterations is exceeded. < 0 means no limitation
+  // maxTimeSeconds: the routine will terminate if this computation time (in seconds) is exceeded. < 0 means no limitation
+  // return value: true if timeout occurred, false otherwise
+  bool compute(double isovalue, double angularBound, double radialBound,
+               int numInitialSamples = 20, double epsilon = 1e-6,
+               int maxNumberOfIterations = -1, double maxTimeSeconds = -1.0);
+
+  // get the result of compute()
+  ObjMesh * getMesh(bool enforceManifold = true);
+
+  // =========== advanced routines ===================
+
+  // pass a mesh representing the zero isosurface, to be remeshed via compute()
+  // only isovalue=0 is supported in this mode
+  IsosurfaceMesher(ObjMesh * detailedSurfaceMesh);
+
+  // =========== debugging =================
+
+  void saveMarchingCubesMesh(bool save = true) { saveMarchingCubesObj = save; }
+  void checkDelaunayAfterComputation(bool check = true) { checkDelaunay = check; }
+  const ObjMesh * getMarchingCubesMesh() const { return marchingCubesMesh; }
+
+  // stepping functions
+  void setStepping(double isovalue, double angularBound, double radialBound, size_t numInitialSamples = 20, double epsilon = 1e-6);
+  void doOneStep();
+  bool endStepping() const;
+
+  const DelaunayMesher & getCurrentDelaunay() const { return delaunay; }
+
+  // if keepAllDelaunayVtx is false, only vertices that belong to at least one triangle are kept
+  // otherwise, all vertices in the Delaunay mesh are added to the resulting obj mesh
+  ObjMesh * buildCurrentSurfaceMesh(bool keepAllDelaunayVtx = false) const;
+
+  // remove non-manifold faces and edges, orient faces
+  static bool enforceManifoldnessAndOrientNormals(ObjMesh * &objMesh);
+protected:
+  IsosurfaceMesher(const IsosurfaceMesher &);
+
+  // represent a face on the isosurface mesh
+  struct IsoFace
+  {
+    Vec3d isopoint{0.0};   // a point on the isosurface near this face. This point is also the center of a circumsphere of the triangle face
+    double radius = 0.0;   // radius of the circumsphere centered at isopoint
+    double maxCosAngle = 0.0; // max cosine of the angles of this triangle
+    UTriKey tri;     // the triangle vtx index of this face
+    IsoFace() {}
+    IsoFace(const UTriKey & tri, const Vec3d & v0, const Vec3d & v1, const Vec3d & v2, const Vec3d & isopoint);
+  };
+  // compare the radius of two IsoFace, used in a set to find the face with largest circumcircle radius
+  struct IsoFaceRadiusCompare
+  {
+    bool operator() (const IsoFace & a, const IsoFace & b) const;
+  };
+  // compare the radius of two IsoFace, used in a set to find the face with largest maxCosAngle
+  struct IsoFaceAngleCompare
+  {
+    bool operator() (const IsoFace & a, const IsoFace & b) const;
+  };
+
+  // the set that compares IsoFace circumcircle radius, used to find the face with largest circumcircle radius
+  typedef std::set<IsoFace, IsoFaceRadiusCompare> IsoFaceRadiusSet;
+  // the set that compares IsoFace maxCosAngle, used to find the face with largest maxCosAngle
+  typedef std::set<IsoFace, IsoFaceAngleCompare> IsoFaceAngleSet;
+
+  typedef IsoFaceRadiusSet::iterator RadiusIter;
+  typedef IsoFaceAngleSet::iterator AngleIter;
+  typedef std::pair<RadiusIter, AngleIter> IterPair;
+  typedef std::map<UTriKey, IterPair> IterPairMap;
+
+  // DelaunayMesher related typedefs for easy interaction with DelaunayMesher
+  typedef DelaunayMesher::VoronoiEdge VoronoiEdge;
+  typedef DelaunayMesher::VoronoiEdgeMap VoronoiEdgeMap;
+  typedef DelaunayMesher::VEdgeIter VEdgeIter;
+  typedef DelaunayMesher::VEdgeCIter VEdgeCIter;
+
+  // void intersection(const std::vector<TriangleBasic*> & triangles, const Vec3d & v, const Vec3d & direction, bool isRay, std::vector<double> & t);
+
+  static void generateInitialPointSetByDiscrepancy(const ObjMesh * objMesh, std::vector<Vec3d> & point, const unsigned int numInitialSamples);
+
+  ObjMesh * computeOneMesh(ObjMesh * objMesh, int & maxNumberOfIterations, double & maxTimeSeconds, bool & timeout);
+  bool initializeOneMesh(ObjMesh * objMesh);
+  bool addOneTriangle(double * targetRadius = NULL); // return true if it won't stop
+
+  DistanceFieldBase * field = nullptr;
+  ObjMesh * detailedSurfaceMesh = nullptr;
+  double isovalue = 0.0;
+  double angularBound = 30.0, cosAngularBound = 0.5, radialBound = 1.0;
+  size_t numInitialSamples = 200;
+  DelaunayMesher delaunay;
+  double epsilon = 0.0;
+
+  ObjMesh * splitIsosurfaceMesh = nullptr;
+  std::vector<ObjMesh *> splitComponent;
+  double fieldDiagonal = 0.0;
+
+#ifdef USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+  ObjMeshOctree<TriangleBasic> * objMeshOctree = nullptr;
+#else
+  // data for exact octree query
+  ExactTriMeshOctree octree;
+  std::vector<Vec3i> inputMeshTriangles;
+  TriMeshRef inputMeshRef;
+#endif
+
+  // index for stepping
+  size_t splitComponentIndex = 0;
+  int oneMeshLoopIndex = 0;
+
+  IsoFaceRadiusSet radiusSet;  // a set stores the faces for the isosurface to be generated and order according to its circumcircle radius
+  IsoFaceAngleSet angleSet;    // a set stores the faces and order according to its max cosine angle
+  IterPairMap faceIters;       // map of UTriKey -> two iterators pointing to the face locations in radiusSet and angleSet
+
+  ObjMesh * isoMesh = nullptr;
+
+  // StopWatch delaunayUpdateCounter;
+  // StopWatch octreeCounter;
+  // StopWatch intersectionCounter;
+  bool checkDelaunay = false;
+  bool saveMarchingCubesObj = false;
+
+  ObjMesh * marchingCubesMesh = nullptr;
+
+  double ignoredSplitComponentVtxRatio = 0.01;
+
+  int globalLoopIndex = 0;
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEM.h b/libraries/include/isotropicHyperelasticFEM.h
old mode 100755
new mode 100644
similarity index 78%
rename from src/libisotropicHyperelasticFEM/isotropicHyperelasticFEM.h
rename to libraries/include/isotropicHyperelasticFEM.h
index b7d5ef465786e9f816e2113d1f47c3a4863ace98..17afa6ab5f6bcc4b82bebef1e73170924deddb49
--- a/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEM.h
+++ b/libraries/include/isotropicHyperelasticFEM.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -34,8 +38,6 @@
 #include "sparseMatrix.h"
 #include "isotropicMaterial.h"
 
-namespace vega
-{
 /*
   Implementation of hyperelastic isotropic nonlinear FEM elasticity, using
   linear tetrahedral elements. 
@@ -47,8 +49,6 @@ es (derivative of internal forces). It can also handle and restore from element
 
   Citations:
 
-namespace vega
-{
   IRVING G., TERAN J., FEDKIW R.: Invertible Finite
   Elements for Robust Simulation of Large Deformation. In Proc.  of the Symp. on Comp. Animation 2004 (2004), pp. 131–140.
 
@@ -73,21 +73,21 @@ public:
   IsotropicHyperelasticFEM(TetMesh * tetMesh, IsotropicMaterial * isotropicMaterial, double inversionThreshold=-DBL_MAX, bool addGravity=false, double g=9.81);
   virtual ~IsotropicHyperelasticFEM();
 
-  double ComputeEnergy(double * u); // get the nonlinear elastic strain energy
+  double ComputeEnergy(const double * u); // get the nonlinear elastic strain energy
 
   // get the nonlinear internal forces
   // both vertex displacements "u" and internal forces refer to the vertices of the simulation mesh
   // they must be (pre-allocated) vectors of length 3 * numVertices
   // the internal forces are returned with the sign corresponding to f_int(x) on the left side of the equation M * x'' + f_int(x) = f_ext
   // i.e., the computed internal forces are negatives of the actual physical internal forces acting on the material
-  void ComputeForces(double * u, double * internalForces);
+  void ComputeForces(const double * u, double * internalForces);
 
   // allocate memory for the non-zero entries of the stiffness matrix
   void GetStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix);
   // get the nonlinear stiffness matrix given the vertex displacement vector u
-  void GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix);
+  void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix);
   // get both nonlinear internal forces and nonlinear stiffness matrix
-  void GetForceAndTangentStiffnessMatrix(double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+  void GetForceAndTangentStiffnessMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
 
   // compute damping forces based on the velocity of the vertices,
   // see p6 section 6.2 of [Irving 04]
@@ -100,20 +100,28 @@ public:
 
   void SetMaterial(IsotropicMaterial * isotropicMaterial_) { isotropicMaterial = isotropicMaterial_; }
 
+  // enforces the tangent stiffness matrix to be symmetric positive-definite (which is good for stability)
+  void EnforceSPD(bool enforceSPD) { this->enforceSPD = enforceSPD; }
+
   // === Advanced functions below; you normally do not need to use them: ===
-  // Computes strain energy, internal forces, and/or tangent stiffness matrix, as requested by computationMode. It returns 0 on success, and non-zero on failure.
-  // computationMode:
-  // bit 0: compute energy
-  // bit 1: compute internal force
-  // bit 2: compute stiffness matrix
-  typedef enum { COMPUTE_ENERGY=1, COMPUTE_INTERNALFORCES=2, COMPUTE_TANGENTSTIFFNESSMATRIX=4 } computationModeType;
-  virtual int GetEnergyAndForceAndTangentStiffnessMatrixHelper(double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix, int computationMode);
+  // Computes strain energy, internal forces, and/or tangent stiffness matrix, 
+  // It returns 0 on success, and non-zero on failure.
+  virtual int GetEnergyAndForceAndTangentStiffnessMatrixHelper(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
   // Initialization for "GetEnergyAndForceAndTangentStiffnessMatrixPrologue" (must always be called before calling "GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse")
-  void GetEnergyAndForceAndTangentStiffnessMatrixHelperPrologue(double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix, int computationMode);
-  // The workhorse (main computational routine); processes mesh elements startEl <= el < endEl (assembles partial strain energy, internal forces, and/or tangent stiffness matrix, as requested by computationMode. It returns 0 on success, and non-zero on failure.
-  int GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse(int startEl, int endEl, double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix, int computationMode);
+  void GetEnergyAndForceAndTangentStiffnessMatrixHelperPrologue(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+  // The workhorse (main computational routine); processes mesh elements startEl <= el < endEl 
+  // (assembles partial strain energy, internal forces, and/or tangent stiffness matrix. They can be nullptr.
+  // It returns 0 on success, and non-zero on failure.)
+  int GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse(int startEl, int endEl, const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+  // the same as above, except that it processes a single mesh element el ans store the date in dense format.
+  void GetElementLocalEnergyAndForceAndMatrix(int el, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix);
 
 protected:
+  void ComputeTetVolume(int el);
+  void ComputeAreaWeightedVertexNormals(int el);
+  void PrepareDeformGrad(int el);
+  // compute local energy, internalForces, tangentStiffnessMatrix assuming currentVerticesPosition is updated
+  int ComputeElementLocalData(int el, const double * u, double * energy, double internalForces[12], double tangentStiffnessMatrix[144]);
   TetMesh * tetMesh; // the tet mesh
   IsotropicMaterial * isotropicMaterial; // the material 
 
@@ -132,6 +140,8 @@ protected:
   bool addGravity;
   double g;
 
+  bool enforceSPD;
+
   // this is the b=(A1N1 + A2N2 + A3N3) in the paper,
   // see p.3 section 4
   Vec3d * areaWeightedVertexNormals;
@@ -157,9 +167,6 @@ protected:
   // compute inv(Dm)
   void PrepareDeformGrad(); // called once in the constructor
 
-  // given a vector, find a unit vector that is orthogonal to it
-  void FindOrthonormalVector(Vec3d & v, Vec3d & result);
-
   // Compute strain energy based on the user-specified material
   virtual double ComputeEnergyFromStretches(int elementIndex, double * lambda);
   // Compute the diagonalized first Piola-Kirchhoff stress P^hat
@@ -222,13 +229,16 @@ protected:
   */
   int teranToRowMajorMatrix[9];
 
-  // Modified SVD of a 3x3 matrix
-  // See comment at the implementation of this function for intended usage.
-  // This routine uses a threshold to determine when the singular values are negligible 
-  // (see the macro modifiedSVD_singularValue_eps in the implementation).
-  // Note: you can overload this function (in your own derived class) if you want to provide your own, custom/faster SVD.
-  virtual int ModifiedSVD(Mat3d & F, Mat3d & U, Vec3d & Fhat, Mat3d & V);
+  // enforce SPD:
+  // on the "A" matrix in Teran's paper, Section 8
+  // Aij is the entry in row i and column j of 3x3 matrix A (which is symmetric)
+  void FixPositiveIndefiniteness(double & A11, double & A12, double & A13,
+                                 double & A22, double & A23, double & A33);
+
+  // on the "B" matrix in Teran's paper, Section 8
+  // Bij is the entry in row i and column j of 2x2 matrix B (which is symmetric)
+  void FixPositiveIndefiniteness(double & B11, double & B12);
 };
-}
+
 #endif
 
diff --git a/libraries/include/isotropicHyperelasticFEMForceModel.h b/libraries/include/isotropicHyperelasticFEMForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..fdcaa74651e01328106f533cf803d0268df03639
--- /dev/null
+++ b/libraries/include/isotropicHyperelasticFEMForceModel.h
@@ -0,0 +1,61 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Force model for the hyperelastic isotropic nonlinear FEM material.
+*/
+
+#ifndef _ISOTROPICHYPERELASTICFEMFORCEMODEL_H_
+#define _ISOTROPICHYPERELASTICFEMFORCEMODEL_H_
+
+#include "isotropicHyperelasticFEM.h"
+#include "forceModel.h"
+
+class IsotropicHyperelasticFEMForceModel : public ForceModel
+{
+public:
+  IsotropicHyperelasticFEMForceModel(IsotropicHyperelasticFEM * isotropicHyperelasticFEM);
+  virtual ~IsotropicHyperelasticFEMForceModel(); 
+
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
+
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix) override;
+
+protected:
+  IsotropicHyperelasticFEM * isotropicHyperelasticFEM;
+};
+
+#endif
+
diff --git a/src/libreducedStvk/StVKReducedInternalForcesMT.h b/libraries/include/isotropicHyperelasticFEMStencilForceModel.h
similarity index 50%
rename from src/libreducedStvk/StVKReducedInternalForcesMT.h
rename to libraries/include/isotropicHyperelasticFEMStencilForceModel.h
index b98c36ac8acaf65d56ffd6c7289bbf3a23496729..7bbd0176e6482cacbad34e64ba5d963491239a25 100644
--- a/src/libreducedStvk/StVKReducedInternalForcesMT.h
+++ b/libraries/include/isotropicHyperelasticFEMStencilForceModel.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,38 +30,28 @@
  *                                                                       *
  *************************************************************************/
 
-/*
-  This is a multi-threaded version of the StVKReducedInternalForces class.
-  It uses POSIX threads ("pthreads") as the threading API.
+#ifndef _ISOTROPICHYPERELASTICFEM_STENCIL_FORCEMODEL_H_
+#define _ISOTROPICHYPERELASTICFEM_STENCIL_FORCEMODEL_H_
 
-  The computation of the cubic polynomials is multi-threaded. Speedup is in practice very good, almost linear. We have seen computation time reductions from 30 minutes to 4 minutes when using 8 CPU cores.
+#include "isotropicHyperelasticFEM.h"
+#include "stencilForceModel.h"
 
-  The evaluation of the reduced forces is not multi-threaded in this implementation
-  (although your BLAS implementation might invoke multi-threading automatically with BLAS 2 and BLAS 3 routines).
-
-  See also StVKReducedInternalForces.h and StVKInternalForces.h .
-*/
-
-#ifndef _STVKREDUCEDINTERNALFORCESMT_H_
-#define _STVKREDUCEDINTERNALFORCESMT_H_
-
-#include "StVKReducedInternalForces.h"
-
-class StVKReducedInternalForcesMT : public StVKReducedInternalForces
+// Stencils for isotropic hyperelastic FEM.
+// A stencil here refers to one FEM element.
+// See comments in the parent class.
+class IsotropicHyperelasticFEMStencilForceModel : public StencilForceModel
 {
 public:
-  // creates the reduced coefficients
-  StVKReducedInternalForcesMT(int r, double * U, VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity, double g, int numThreads, int verbose);
+  IsotropicHyperelasticFEMStencilForceModel(IsotropicHyperelasticFEM * isotropicHyperelasticFEM);
+  virtual ~IsotropicHyperelasticFEMStencilForceModel();
 
-  ~StVKReducedInternalForcesMT();
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
 
-  int GetStartElement(int rank);  
-  int GetEndElement(int rank);
+  IsotropicHyperelasticFEM * GetForceModelHandle() { return isotropicHyperelasticFEM; }
 
 protected:
-  int numThreads;
-  int * startElement, * endElement;
-  double * internalForceBuffer;
+  IsotropicHyperelasticFEM * isotropicHyperelasticFEM;
 };
 
 #endif
diff --git a/src/libisotropicHyperelasticFEM/isotropicMaterial.h b/libraries/include/isotropicMaterial.h
old mode 100755
new mode 100644
similarity index 78%
rename from src/libisotropicHyperelasticFEM/isotropicMaterial.h
rename to libraries/include/isotropicMaterial.h
index da92cc4922da2799a4cbe9166e160e333a4fac9b..b4f04fc2790e288ac7b69b66c5dd5f7f05d4db38
--- a/src/libisotropicHyperelasticFEM/isotropicMaterial.h
+++ b/libraries/include/isotropicMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #ifndef _ISOTROPICMATERIAL_H_
 #define _ISOTROPICMATERIAL_H_
 
-namespace vega
-{
 class IsotropicMaterial
 {
 public:
@@ -43,6 +45,6 @@ public:
 
 protected:
 };
-}
+
 #endif
 
diff --git a/src/libisotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.h b/libraries/include/isotropicMaterialWithCompressionResistance.h
old mode 100755
new mode 100644
similarity index 82%
rename from src/libisotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.h
rename to libraries/include/isotropicMaterialWithCompressionResistance.h
index c4cc4cb3ce935834955de033b951e523883922d5..e8f0235e55e9f3b82f3b026ea31449d30ba545d2
--- a/src/libisotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.h
+++ b/libraries/include/isotropicMaterialWithCompressionResistance.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,8 +35,6 @@
 
 #include "isotropicMaterial.h"
 
-namespace vega
-{
 class IsotropicMaterialWithCompressionResistance : public IsotropicMaterial
 {
 public:
@@ -51,6 +53,6 @@ protected:
   void AddCompressionResistanceGradient(int elementIndex, double * invariants, double * gradient);
   void AddCompressionResistanceHessian(int elementIndex, double * invariants, double * hessian); 
 };
-}
+
 #endif
 
diff --git a/libraries/include/lapack-headers.h b/libraries/include/lapack-headers.h
new file mode 100644
index 0000000000000000000000000000000000000000..967662b7bbe86abf69ec803af275bdbb24b014b7
--- /dev/null
+++ b/libraries/include/lapack-headers.h
@@ -0,0 +1,19 @@
+#if defined(WIN32) || defined(_WIN32) || defined(linux) || defined(__linux__)
+  #include "mkl_cblas.h"
+  #include "mkl_types.h"
+  #include "mkl_lapack.h"
+  #include "mkl_blas.h"
+#elif defined(__APPLE__)
+  #include <Accelerate/Accelerate.h>
+  #ifdef __GNUC__
+    #ifndef __clang__
+    // The following below applies to Mac OS X, when using the gcc compiler as opposed to clang.
+    // On our MacBooks with recent OS X versions, the following headers are required to find CBLAS routines when using gcc (as opposed to clang).
+      #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+        #include </System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers/cblas.h>
+        #include </System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers/clapack.h>
+      #endif
+    #endif
+  #endif
+#endif
+
diff --git a/libraries/include/linearFEMForceModel.h b/libraries/include/linearFEMForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..31112e526ea2c328336c5e7ba7c2e4f12e37de54
--- /dev/null
+++ b/libraries/include/linearFEMForceModel.h
@@ -0,0 +1,61 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Force model for the linear FEM material.
+  It builds the linear model using the StVK library
+  (but this choice of StVK vs some other model does not matter; due to linearity).
+*/
+
+#ifndef _LINEARFEMFORCEMODEL_H_
+#define _LINEARFEMFORCEMODEL_H_
+
+#include "StVKInternalForces.h"
+#include "forceModel.h"
+
+class LinearFEMForceModel : public ForceModel
+{
+public:
+  LinearFEMForceModel(StVKInternalForces * stVKInternalForces);
+  virtual ~LinearFEMForceModel(); 
+
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
+
+protected:
+  SparseMatrix * K;
+};
+
+#endif
+
diff --git a/libraries/include/linearFEMStencilForceModel.h b/libraries/include/linearFEMStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..b82436fc0803e09866eca4a90ebfa2b321e81aac
--- /dev/null
+++ b/libraries/include/linearFEMStencilForceModel.h
@@ -0,0 +1,64 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef _LINEARFEM_STENCIL_FORCEMODEL_H_
+#define _LINEARFEN_STENCIL_FORCEMODEL_H_
+
+#include "stencilForceModel.h"
+
+#include <vector>
+
+// Stencils for linear FEM.
+// A stencil is one FEM element.
+// See comments in the parent class.
+class LinearFEMStencilForceModel : public StencilForceModel
+{
+public:
+  // this class is able to linearize any non-linear force model
+  // therefore, any StencilForceModel (except itself) can be used to initialize this class.
+  LinearFEMStencilForceModel(StencilForceModel * stencilForceModel);
+  virtual ~LinearFEMStencilForceModel();
+
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+  StencilForceModel * GetForceModelHandle() { return stencilForceModel; }
+
+protected:
+  StencilForceModel * stencilForceModel;
+
+  std::vector<std::vector<double>> elementK;
+};
+
+#endif
+
diff --git a/src/libsparseSolver/linearSolver.h b/libraries/include/linearSolver.h
similarity index 77%
rename from src/libsparseSolver/linearSolver.h
rename to libraries/include/linearSolver.h
index a3bed330a6350bdf1d3488b70c0b97c667bb84fc..4ef61fc226dfa35a67ec928db7fb5380ba632ce9 100644
--- a/src/libsparseSolver/linearSolver.h
+++ b/libraries/include/linearSolver.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,31 +36,24 @@
 /*
   Abstract class to solve
   A * x = rhs, where A is a square matrix.
-
+  
   Jernej Barbic, USC, 2010
 */
 
-#include <string>
 #include <stdio.h>
 #include <stdlib.h>
 
-namespace vega
-{
 class LinearSolver
 {
 public:
   virtual ~LinearSolver();
 
   // solve: A * x = rhs
+  // return: 0 on success, error code otherwise
   virtual int SolveLinearSystem(double * x, const double * rhs) = 0;
 
-  const std::string &getSolverType() const;
-
-  void setSolverType(std::string &type);
-
 protected:
-  std::string solverType;
 };
-}
+
 #endif
 
diff --git a/libraries/include/listIO.h b/libraries/include/listIO.h
new file mode 100644
index 0000000000000000000000000000000000000000..974d9c8381b04e7cb6f5ddb9acb14ce0fdbc367a
--- /dev/null
+++ b/libraries/include/listIO.h
@@ -0,0 +1,78 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "listIO" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _LISTIO_H_
+#define _LISTIO_H_
+
+/*
+  A class to load an integer list from a text file into memory. 
+*/
+
+#include <stdio.h>
+#include <vector>
+#include <set>
+
+class ListIO
+{
+public:
+
+  // returns 0 on success and non-zero otherwise
+  // load: sort the loaded entries if sortAfterLoad is true
+  // numbers are separated by ' ' or ','. Support syntax "1:5" to load {1,2,3,4,5}
+  // comments start with '#'
+  static int load(const char * filename, int * numListEntries, int ** listEntries, int offset=0, bool sortAfterLoad = true);
+  static int save(const char * filename, int numListEntries, const int * listEntries, int offset=0);
+
+  static int load(const char * filename, std::vector<int> & listEntries, int offset=0, bool sortAfterLoad = true);
+  static int save(const char * filename, const std::vector<int> & listEntries, int offset=0);
+
+  static int load(const char * filename, std::set<int> & listEntries, int offset=0);
+  static int save(const char * filename, const std::set<int> & listEntries, int offset=0);
+
+  static int loadBinary(const char * filename, int * numListEntries, int ** listEntries, int offset=0);
+  static int loadBinary(FILE * fin, int * numListEntries, int ** listEntries, int offset=0);
+  static int saveBinary(const char * filename, int numListEntries, int * listEntries, int offset=0);
+  static int saveBinary(FILE * fout, int numListEntries, int * listEntries, int offset=0);
+
+  // loads/saves multiple lists to one binary file
+  static int loadBinaryMulti(const char * filename, int * numLists, int ** numListEntries, int *** listEntries, int offset=0);
+  static int saveBinaryMulti(const char * filename, int numLists, int * numListEntries, int ** listEntries, int offset=0);
+
+  static void sort(int numListEntries, int * listEntries);
+  static void print(int numListEntries, int * listEntries);
+  static void stripBlanks(char * s);
+
+protected:
+};
+
+#endif
+
diff --git a/include/macros.h b/libraries/include/macros.h
old mode 100755
new mode 100644
similarity index 78%
rename from include/macros.h
rename to libraries/include/macros.h
index 7ce1d543f5745781e4e069142adf96a28fac0b3b..8a1910c903c158b0401624d5f76b45620a597978
--- a/include/macros.h
+++ b/libraries/include/macros.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "macros" include file, Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "macros" include files, Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/libraries/include/marchingCubes.h b/libraries/include/marchingCubes.h
new file mode 100644
index 0000000000000000000000000000000000000000..38d92bbcd216101f996e4f5ebc12c56f0cb68144
--- /dev/null
+++ b/libraries/include/marchingCubes.h
@@ -0,0 +1,95 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Jernej Barbic                             *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This code implements marching cubes with topological guarantees, 
+  similar to the following publications. Our code has been implemented
+  from scratch by Danyong Zhao and Jernej Barbic.
+
+  Thomas Lewiner, Hélio Lopes, Antônio Wilson Vieira, Geovan Tavares:
+  Efficient implementation of marching cubes cases with topological guarantees
+  Journal of Graphics Tools 8(2): pp. 1-15, 2003
+
+  E. V. Chernyaev. Marching Cubes 33: construction of topologically correct isosurfaces. 
+  Technical Report CERN CN 95–17, CERN, 1995.
+*/
+
+#ifndef _MARCHINGCUBES_H_
+#define _MARCHINGCUBES_H_
+
+#include <vector>
+#include <map>
+
+#include "triple.h"
+#include "objMesh.h"
+#include "distanceFieldBase.h"
+
+class MarchingCubes
+{
+public:
+
+  // computes the isosurface mesh, using marching cubes with topological guarantees
+  // the input distance field can be complete, or narrow band
+  // isoValue is the isosurface value to be meshed (given in absolute units, not grid units)
+  // output: a triangle mesh corresponding to the isosurface
+  static ObjMesh * compute(const DistanceFieldBase * distanceField, float isoValue = 0.0);
+
+protected:
+  MarchingCubes(const DistanceFieldBase * distanceField, float isoValue = 0.0);
+  virtual ~MarchingCubes() {}
+
+  const DistanceFieldBase * distanceFieldBase;
+  float isoValue;
+
+  int resolutionX, resolutionY, resolutionZ;
+
+  // executes the marching cubes algorithm
+  ObjMesh * compute();
+
+  // void computeTriangleVertices(int i, int j, int k, bool center, int vtx[13]);
+  bool faceTest(int face, float cube[8]);
+  int interiorTest(int edge, float cube[8]);
+  inline float getDistance(int i, int j, int k) const
+  {
+    float offset = distanceFieldBase->distance(i, j, k) - isoValue;
+//    return offset + (offset == 0.0f) * FLT_EPSILON;
+    if (offset == 0.0f) return FLT_EPSILON;
+    return offset;
+  }
+
+  // computes all the tables needed for marching cubes
+  static void createTable();
+  static void printTable();
+};
+
+#endif
+
diff --git a/libraries/include/marchingCubesTable.h b/libraries/include/marchingCubesTable.h
new file mode 100644
index 0000000000000000000000000000000000000000..2cfb55a0d2ad8a443ddff419ad509f3b4abf40aa
--- /dev/null
+++ b/libraries/include/marchingCubesTable.h
@@ -0,0 +1,1370 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Jernej Barbic                             *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Marching cubes tables. These tables were generated programmatically,
+  using our code.
+*/
+
+static char ambiguityTable[15][64] = 
+{
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 1, 2, 4, 3, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 2, 3, 4, 1, 0, 3, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 2, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 
+  { 0, 1, 2, 7, 3, -1, 11, -1, 4, 8, -1, -1, 14, -1, -1, -1, 5, 9, 12, 42, 15, -1, 21, 34, 17, 20, -1, 32, 45, 29, 26, 40, 6, 10, 13, 19, 16, -1, 44, 33, 18, 43, -1, 31, 22, 28, 25, 39, -1, -1, -1, 30, -1, -1, 24, 38, -1, 27, -1, 37, 23, 36, 35, 41 }, 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static int marchingCubeSymmetries[256][3] = {
+  {  0,  0,  0 },   {  1,  0,  0 },   {  1,  1,  2 },   {  2,  0,  0 },   {  1,  2,  9 },   {  3,  0,  0 },   {  2,  3,  3 },   {  5,  0,  0 }, 
+  {  1,  3,  1 },   {  2,  1,  6 },   {  3,  3,  3 },   {  5,  1,  6 },   {  2,  5,  1 },   {  5,  4,  9 },   {  5,  9,  3 },   {  8,  0,  0 }, 
+  {  1,  4,  4 },   {  2,  2,  5 },   {  3,  4,  4 },   {  5,  2, 10 },   {  4,  2,  4 },   {  6,  2,  5 },   {  6,  9, 21 },   { 11,  0,  0 }, 
+  {  3,  8,  5 },   {  5,  5,  5 },   {  7,  3,  5 },   {  9,  1,  5 },   {  6, 16,  9 },   { 14,  3,  5 },   { 12, 12,  3 },   {  5, 24, -7 }, 
+  {  1,  5,  8 },   {  3,  1, 16 },   {  2,  4,  2 },   {  5,  3, 17 },   {  3,  6, 18 },   {  7,  0,  0 },   {  5, 10, 21 },   {  9,  0,  0 }, 
+  {  4,  3,  1 },   {  6,  4,  6 },   {  6, 11, 17 },   { 14,  1,  6 },   {  6, 17,  1 },   { 12,  4,  9 },   { 11,  6,  3 },   {  5, 25,-14 }, 
+  {  2,  8,  4 },   {  5,  7, 16 },   {  5, 12,  4 },   {  8,  1,  4 },   {  6, 18,  4 },   { 12,  5, 16 },   { 14,  7,  4 },   {  5, 28,-12 }, 
+  {  6, 21,  8 },   { 11,  4,  5 },   { 12, 15,  4 },   {  5, 30,-23 },   { 10,  5,  1 },   {  6, 32, -7 },   {  6, 39,-12 },   {  2, 12, -7 }, 
+  {  1,  6, 11 },   {  4,  0,  0 },   {  3,  5,  2 },   {  6,  0,  0 },   {  2,  6, 11 },   {  6,  3, 11 },   {  5, 11, 11 },   { 14,  0,  0 }, 
+  {  3,  9,  1 },   {  6,  5, 20 },   {  7,  4,  1 },   { 12,  1,  6 },   {  5, 14,  1 },   { 11,  3,  1 },   {  9,  4,  1 },   {  5, 26, -8 }, 
+  {  3, 10, 14 },   {  6,  6, 16 },   {  7,  5,  2 },   { 12,  2, 10 },   {  6, 19, 23 },   { 10,  1,  5 },   { 12, 13, 11 },   {  6, 24,-13 }, 
+  {  7,  7, 12 },   { 12,  9,  5 },   { 13,  1,  3 },   {  7,  9, -7 },   { 12, 20,  1 },   {  6, 33, -2 },   {  7, 13, -8 },   {  3, 12, -7 }, 
+  {  2, 10, 15 },   {  6,  7, 15 },   {  5, 13,  2 },   { 11,  2,  2 },   {  5, 16, 18 },   { 12,  7, 18 },   {  8,  3,  2 },   {  5, 29,-13 }, 
+  {  6, 22, 18 },   { 10,  2,  6 },   { 12, 17,  2 },   {  6, 27,-14 },   { 14,  9,  1 },   {  6, 34,-19 },   {  5, 39,-19 },   {  2, 14,-14 }, 
+  {  5, 20, 15 },   { 14,  5, 15 },   {  9,  5,  2 },   {  5, 32,-22 },   { 11, 10, 15 },   {  6, 35,-22 },   {  5, 41,-20 },   {  2, 16,-13 }, 
+  { 12, 23, 15 },   {  6, 37, -3 },   {  7, 14, -9 },   {  3, 16,-22 },   {  6, 46,-10 },   {  4,  6, -2 },   {  3, 21,-19 },   {  1,  8, -7 }, 
+  {  1,  7,  7 },   {  3,  2, 19 },   {  4,  1,  2 },   {  6,  1, 10 },   {  3,  7, 22 },   {  7,  1,  9 },   {  6, 10,  3 },   { 12,  0,  0 }, 
+  {  2,  7, 13 },   {  5,  6, 20 },   {  6, 12, 22 },   { 11,  1,  6 },   {  5, 15, 22 },   {  9,  2,  9 },   { 14,  6,  3 },   {  5, 27,-15 }, 
+  {  2,  9, 14 },   {  5,  8, 19 },   {  6, 13, 19 },   { 14,  2, 10 },   {  6, 20, 14 },   { 12,  6, 19 },   { 10,  3,  3 },   {  6, 25,-18 }, 
+  {  5, 18, 13 },   {  8,  2,  5 },   { 12, 16, 13 },   {  5, 31,-18 },   { 11,  9, 13 },   {  5, 34, -2 },   {  6, 40,-15 },   {  2, 13,-15 }, 
+  {  3, 11,  7 },   {  7,  2,  8 },   {  6, 14,  2 },   { 12,  3, 17 },   {  7,  6,  7 },   { 13,  0,  0 },   { 12, 14, 21 },   {  7,  8,-12 }, 
+  {  6, 23, 13 },   { 12, 10, 20 },   { 10,  4,  2 },   {  6, 28,-23 },   { 12, 21, 22 },   {  7, 10, -2 },   {  6, 41,-16 },   {  3, 13,-14 }, 
+  {  5, 21,  8 },   {  9,  3,  8 },   { 11,  8,  4 },   {  5, 33, -1 },   { 12, 22,  8 },   {  7, 11, -1 },   {  6, 42,-20 },   {  3, 14, -1 }, 
+  { 14, 11,  8 },   {  5, 36,-11 },   {  6, 44,-11 },   {  2, 17,-11 },   {  6, 47,  0 },   {  3, 18, -2 },   {  4,  7,  0 },   {  1,  9,-11 }, 
+  {  2, 11,  7 },   {  6,  8, 12 },   {  6, 15,  7 },   { 10,  0,  0 },   {  5, 17, 23 },   { 12,  8, 23 },   { 11,  7, 11 },   {  6, 26, -8 }, 
+  {  5, 19, 12 },   { 14,  4, 12 },   { 12, 18, 12 },   {  6, 29, -4 },   {  8,  4,  1 },   {  5, 35, -4 },   {  5, 40,-16 },   {  2, 15, -4 }, 
+  {  5, 22, 14 },   { 11,  5, 14 },   { 12, 19, 14 },   {  6, 30, -1 },   { 14, 10, 14 },   {  6, 36,-17 },   {  6, 43, -6 },   {  4,  4, -1 }, 
+  {  9,  7, 12 },   {  5, 37,-21 },   {  7, 15,  0 },   {  3, 17,-18 },   {  5, 44,-17 },   {  2, 19, -2 },   {  3, 22,-16 },   {  1, 10, -8 }, 
+  {  5, 23,  7 },   { 12, 11,  7 },   { 14,  8,  2 },   {  6, 31, -9 },   {  9,  6,  7 },   {  7, 12, -5 },   {  5, 42, -5 },   {  3, 15, -5 }, 
+  { 11, 11,  7 },   {  6, 38,-21 },   {  6, 45, -5 },   {  4,  5, -4 },   {  5, 45,-10 },   {  3, 19, -4 },   {  2, 21, -5 },   {  1, 11, -4 }, 
+  {  8,  5,  7 },   {  5, 38, -3 },   {  5, 43, -9 },   {  2, 18, -1 },   {  5, 46, -6 },   {  3, 20, -3 },   {  2, 22, -6 },   {  1, 12, -1 }, 
+  {  5, 47,  0 },   {  2, 20, -3 },   {  3, 23,  0 },   {  1, 13, -9 },   {  2, 23,  0 },   {  1, 14, -2 },   {  1, 15,  0 },   {  0,  1,  0 }
+};
+
+static char faceTest_num[256][7] = 
+{
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 3, 1, 5, 4, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 1, 4, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 3, 1, 2, 5, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 2, 2, 1, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 2, 5, 2, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 4, 5, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 2, 4, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 3, 0, 0, 0, 0, 0 },  { 1, 3, 0, 0, 0, 0, 0 },  { 3, 5, 2, 3, 0, 0, 0 },  { 2, 3, 2, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 6, 0, 0, 0, 0, 0 },  { 1, 6, 0, 0, 0, 0, 0 },  { 3, 1, 6, 2, 0, 0, 0 },  { 2, 2, 6, 0, 0, 0, 0 },  { 1, 6, 0, 0, 0, 0, 0 },  { 2, 5, 6, 0, 0, 0, 0 },  { 2, 6, 1, 0, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },
+  { 3, 6, 4, 3, 0, 0, 0 },  { 2, 6, 3, 0, 0, 0, 0 },  { 6, 2, 3, 4, 1, 5, 6 },  { 3,-3,-2,-6, 0, 0, 0 },  { 2, 4, 6, 0, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },  { 3,-1,-4,-6, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 1, 5, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 3, 0, 0, 0, 0, 0 },  { 2, 1, 3, 0, 0, 0, 0 },  { 2, 5, 3, 0, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 2, 3, 4, 0, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },  { 3,-3,-4,-5, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 4, 0, 0, 0, 0, 0 },  { 1, 3, 0, 0, 0, 0, 0 },  { 3, 3, 4, 5, 0, 0, 0 },  { 1, 3, 0, 0, 0, 0, 0 },  { 2, 4, 3, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 3, 0, 0, 0, 0, 0 },  { 2, 3, 5, 0, 0, 0, 0 },  { 2, 3, 1, 0, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 5, 1, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 1, 6, 0, 0, 0, 0, 0 },  { 3, 1, 4, 6, 0, 0, 0 },  { 1, 6, 0, 0, 0, 0, 0 },  { 2, 6, 4, 0, 0, 0, 0 },  { 3, 3, 2, 6, 0, 0, 0 },  { 6, 1, 2, 3, 4, 5, 6 },  { 2, 3, 6, 0, 0, 0, 0 },  { 3,-6,-4,-3, 0, 0, 0 },
+  { 1, 6, 0, 0, 0, 0, 0 },  { 2, 1, 6, 0, 0, 0, 0 },  { 2, 6, 5, 0, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },  { 2, 6, 2, 0, 0, 0, 0 },  { 3,-1,-6,-2, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },  { 1,-6, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 2, 3, 0, 0, 0, 0 },  { 3,-5,-2,-3, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },  { 1,-3, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1, 4, 0, 0, 0, 0, 0 },  { 1, 2, 0, 0, 0, 0, 0 },  { 2, 2, 4, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 5, 4, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 2, 5, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 1, 2, 0, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 3,-1,-2,-5, 0, 0, 0 },  { 1,-2, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 2, 4, 1, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 3,-1,-5,-4, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-4, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-1, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },
+  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 1,-5, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 },  { 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static char interiorTest_num[256] = 
+  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static unsigned char triangleTable_1_0[5] =  { 3, 0, 8, 3, 0 };
+static unsigned char * triangleTable_1[1] = { triangleTable_1_0 };
+
+static unsigned char triangleTable_2_0[5] =  { 3, 0, 0, 1, 9 };
+static unsigned char * triangleTable_2[1] = { triangleTable_2_0 };
+
+static unsigned char triangleTable_3_0[8] =  { 6, 0, 3, 1, 8, 9, 8, 1 };
+static unsigned char * triangleTable_3[1] = { triangleTable_3_0 };
+
+static unsigned char triangleTable_4_0[5] =  { 3, 0, 10, 1, 2 };
+static unsigned char * triangleTable_4[1] = { triangleTable_4_0 };
+
+static unsigned char triangleTable_5_0[8] =  { 6, 0, 1, 2, 10, 8, 3, 0 };
+static unsigned char triangleTable_5_1[14] =  { 12, 0, 8, 3, 10, 10, 1, 0, 0, 8, 10, 2, 10, 3 };
+static unsigned char * triangleTable_5[2] = { triangleTable_5_0, triangleTable_5_1 };
+
+static unsigned char triangleTable_6_0[8] =  { 6, 0, 0, 2, 9, 10, 9, 2 };
+static unsigned char * triangleTable_6[1] = { triangleTable_6_0 };
+
+static unsigned char triangleTable_7_0[11] =  { 9, 0, 8, 3, 2, 10, 8, 2, 10, 9, 8 };
+static unsigned char * triangleTable_7[1] = { triangleTable_7_0 };
+
+static unsigned char triangleTable_8_0[5] =  { 3, 0, 3, 11, 2 };
+static unsigned char * triangleTable_8[1] = { triangleTable_8_0 };
+
+static unsigned char triangleTable_9_0[8] =  { 6, 0, 2, 0, 11, 8, 11, 0 };
+static unsigned char * triangleTable_9[1] = { triangleTable_9_0 };
+
+static unsigned char triangleTable_10_0[8] =  { 6, 0, 2, 3, 11, 9, 0, 1 };
+static unsigned char triangleTable_10_1[14] =  { 12, 0, 9, 0, 11, 11, 2, 1, 1, 9, 11, 3, 11, 0 };
+static unsigned char * triangleTable_10[2] = { triangleTable_10_0, triangleTable_10_1 };
+
+static unsigned char triangleTable_11_0[11] =  { 9, 0, 11, 2, 1, 9, 11, 1, 9, 8, 11 };
+static unsigned char * triangleTable_11[1] = { triangleTable_11_0 };
+
+static unsigned char triangleTable_12_0[8] =  { 6, 0, 11, 10, 3, 1, 3, 10 };
+static unsigned char * triangleTable_12[1] = { triangleTable_12_0 };
+
+static unsigned char triangleTable_13_0[11] =  { 9, 0, 10, 1, 0, 8, 10, 0, 8, 11, 10 };
+static unsigned char * triangleTable_13[1] = { triangleTable_13_0 };
+
+static unsigned char triangleTable_14_0[11] =  { 9, 0, 9, 0, 3, 11, 9, 3, 11, 10, 9 };
+static unsigned char * triangleTable_14[1] = { triangleTable_14_0 };
+
+static unsigned char triangleTable_15_0[8] =  { 6, 0, 10, 9, 8, 8, 11, 10 };
+static unsigned char * triangleTable_15[1] = { triangleTable_15_0 };
+
+static unsigned char triangleTable_16_0[5] =  { 3, 0, 7, 8, 4 };
+static unsigned char * triangleTable_16[1] = { triangleTable_16_0 };
+
+static unsigned char triangleTable_17_0[8] =  { 6, 0, 7, 3, 4, 0, 4, 3 };
+static unsigned char * triangleTable_17[1] = { triangleTable_17_0 };
+
+static unsigned char triangleTable_18_0[8] =  { 6, 0, 9, 0, 1, 7, 8, 4 };
+static unsigned char triangleTable_18_1[14] =  { 12, 0, 7, 8, 1, 1, 9, 4, 4, 7, 1, 0, 1, 8 };
+static unsigned char * triangleTable_18[2] = { triangleTable_18_0, triangleTable_18_1 };
+
+static unsigned char triangleTable_19_0[11] =  { 9, 0, 1, 9, 4, 7, 1, 4, 7, 3, 1 };
+static unsigned char * triangleTable_19[1] = { triangleTable_19_0 };
+
+static unsigned char triangleTable_20_0[8] =  { 6, 0, 2, 10, 1, 8, 4, 7 };
+static unsigned char triangleTable_20_1[20] =  { 18, 0, 7, 2, 10, 7, 10, 4, 2, 8, 1, 4, 10, 1, 4, 1, 8, 8, 2, 7 };
+static unsigned char * triangleTable_20[2] = { triangleTable_20_0, triangleTable_20_1 };
+
+static unsigned char triangleTable_21_0[11] =  { 9, 0, 10, 1, 2, 4, 3, 0, 4, 7, 3 };
+static unsigned char triangleTable_21_1[17] =  { 15, 0, 4, 10, 1, 2, 7, 3, 4, 7, 10, 0, 4, 1, 10, 7, 2 };
+static unsigned char * triangleTable_21[2] = { triangleTable_21_0, triangleTable_21_1 };
+
+static unsigned char triangleTable_22_0[11] =  { 9, 0, 7, 8, 4, 2, 9, 0, 2, 10, 9 };
+static unsigned char triangleTable_22_1[17] =  { 15, 0, 2, 7, 8, 4, 10, 9, 2, 10, 7, 0, 2, 8, 7, 10, 4 };
+static unsigned char * triangleTable_22[2] = { triangleTable_22_0, triangleTable_22_1 };
+
+static unsigned char triangleTable_23_0[14] =  { 12, 0, 4, 7, 9, 2, 10, 9, 7, 3, 2, 7, 2, 9 };
+static unsigned char * triangleTable_23[1] = { triangleTable_23_0 };
+
+static unsigned char triangleTable_24_0[8] =  { 6, 0, 3, 11, 2, 4, 7, 8 };
+static unsigned char triangleTable_24_1[14] =  { 12, 0, 4, 7, 2, 2, 3, 8, 8, 4, 2, 11, 2, 7 };
+static unsigned char * triangleTable_24[2] = { triangleTable_24_0, triangleTable_24_1 };
+
+static unsigned char triangleTable_25_0[11] =  { 9, 0, 4, 7, 11, 2, 4, 11, 2, 0, 4 };
+static unsigned char * triangleTable_25[1] = { triangleTable_25_0 };
+
+static unsigned char triangleTable_26_0[11] =  { 9, 0, 3, 11, 2, 0, 1, 9, 4, 7, 8 };
+static unsigned char triangleTable_26_1[17] =  { 15, 0, 3, 11, 2, 9, 4, 7, 1, 9, 7, 8, 1, 7, 1, 8, 0 };
+static unsigned char triangleTable_26_2[17] =  { 15, 0, 8, 4, 7, 2, 1, 9, 11, 2, 9, 0, 11, 9, 11, 0, 3 };
+static unsigned char triangleTable_26_3[17] =  { 15, 0, 0, 1, 9, 7, 11, 2, 4, 7, 2, 3, 4, 2, 4, 3, 8 };
+static unsigned char triangleTable_26_4[29] =  { 27, 1, 12, 3, 11, 12, 0, 3, 1, 12, 2, 0, 12, 8, 12, 4, 7, 12, 1, 9, 8, 12, 7, 2, 12, 11, 9, 4, 12 };
+static unsigned char triangleTable_26_5[29] =  { 27, 1, 12, 0, 1, 12, 8, 0, 4, 12, 9, 8, 12, 3, 12, 11, 2, 12, 4, 7, 3, 12, 2, 9, 12, 1, 7, 11, 12 };
+static unsigned char triangleTable_26_6[29] =  { 27, 1, 12, 8, 4, 12, 3, 8, 11, 12, 7, 3, 12, 0, 12, 1, 9, 12, 11, 2, 0, 12, 9, 7, 12, 4, 2, 1, 12 };
+static unsigned char triangleTable_26_7[17] =  { 15, 0, 1, 9, 4, 7, 11, 4, 0, 3, 8, 11, 1, 4, 1, 11, 2 };
+static unsigned char * triangleTable_26[8] = 
+{ 
+  triangleTable_26_0, triangleTable_26_1, triangleTable_26_2, triangleTable_26_3, triangleTable_26_4, triangleTable_26_5, triangleTable_26_6, triangleTable_26_7
+};
+
+static unsigned char triangleTable_27_0[14] =  { 12, 0, 7, 1, 4, 7, 2, 1, 11, 2, 7, 4, 1, 9 };
+static unsigned char * triangleTable_27[1] = { triangleTable_27_0 };
+
+static unsigned char triangleTable_28_0[11] =  { 9, 0, 4, 7, 8, 10, 3, 11, 10, 1, 3 };
+static unsigned char triangleTable_28_1[17] =  { 15, 0, 10, 4, 7, 8, 1, 3, 10, 1, 4, 11, 10, 7, 4, 1, 8 };
+static unsigned char * triangleTable_28[2] = { triangleTable_28_0, triangleTable_28_1 };
+
+static unsigned char triangleTable_29_0[14] =  { 12, 0, 11, 10, 7, 10, 1, 0, 10, 0, 4, 7, 10, 4 };
+static unsigned char * triangleTable_29[1] = { triangleTable_29_0 };
+
+static unsigned char triangleTable_30_0[14] =  { 12, 0, 0, 3, 11, 9, 0, 11, 7, 8, 4, 9, 11, 10 };
+static unsigned char triangleTable_30_1[14] =  { 12, 0, 4, 7, 11, 9, 4, 11, 3, 8, 0, 9, 11, 10 };
+static unsigned char triangleTable_30_2[26] =  { 24, 1, 12, 7, 8, 10, 12, 11, 9, 4, 12, 3, 12, 0, 0, 12, 8, 12, 10, 9, 12, 3, 11, 7, 12, 4 };
+static unsigned char triangleTable_30_3[26] =  { 24, 1, 12, 3, 8, 10, 12, 11, 9, 0, 12, 7, 12, 4, 4, 12, 8, 12, 10, 9, 12, 7, 11, 3, 12, 0 };
+static unsigned char * triangleTable_30[4] = { triangleTable_30_0, triangleTable_30_1, triangleTable_30_2, triangleTable_30_3 };
+
+static unsigned char triangleTable_31_0[11] =  { 9, 0, 4, 7, 11, 4, 11, 9, 11, 10, 9 };
+static unsigned char * triangleTable_31[1] = { triangleTable_31_0 };
+
+static unsigned char triangleTable_32_0[5] =  { 3, 0, 9, 5, 4 };
+static unsigned char * triangleTable_32[1] = { triangleTable_32_0 };
+
+static unsigned char triangleTable_33_0[8] =  { 6, 0, 4, 9, 5, 3, 0, 8 };
+static unsigned char triangleTable_33_1[14] =  { 12, 0, 3, 0, 5, 5, 4, 8, 8, 3, 5, 9, 5, 0 };
+static unsigned char * triangleTable_33[2] = { triangleTable_33_0, triangleTable_33_1 };
+
+static unsigned char triangleTable_34_0[8] =  { 6, 0, 1, 5, 0, 4, 0, 5 };
+static unsigned char * triangleTable_34[1] = { triangleTable_34_0 };
+
+static unsigned char triangleTable_35_0[11] =  { 9, 0, 5, 4, 8, 3, 5, 8, 3, 1, 5 };
+static unsigned char * triangleTable_35[1] = { triangleTable_35_0 };
+
+static unsigned char triangleTable_36_0[8] =  { 6, 0, 10, 1, 2, 4, 9, 5 };
+static unsigned char triangleTable_36_1[14] =  { 12, 0, 4, 9, 2, 2, 10, 5, 5, 4, 2, 1, 2, 9 };
+static unsigned char * triangleTable_36[2] = { triangleTable_36_0, triangleTable_36_1 };
+
+static unsigned char triangleTable_37_0[11] =  { 9, 0, 1, 2, 10, 9, 5, 4, 8, 3, 0 };
+static unsigned char triangleTable_37_1[17] =  { 15, 0, 1, 2, 10, 4, 8, 3, 5, 4, 3, 0, 5, 3, 5, 0, 9 };
+static unsigned char triangleTable_37_2[17] =  { 15, 0, 0, 8, 3, 10, 5, 4, 2, 10, 4, 9, 2, 4, 2, 9, 1 };
+static unsigned char triangleTable_37_3[17] =  { 15, 0, 9, 5, 4, 3, 2, 10, 8, 3, 10, 1, 8, 10, 8, 1, 0 };
+static unsigned char triangleTable_37_4[29] =  { 27, 1, 12, 1, 2, 12, 9, 1, 5, 12, 10, 9, 12, 0, 12, 8, 3, 12, 5, 4, 0, 12, 3, 10, 12, 2, 4, 8, 12 };
+static unsigned char triangleTable_37_5[29] =  { 27, 1, 12, 9, 5, 12, 0, 9, 8, 12, 4, 0, 12, 1, 12, 2, 10, 12, 8, 3, 1, 12, 10, 4, 12, 5, 3, 2, 12 };
+static unsigned char triangleTable_37_6[29] =  { 27, 1, 12, 0, 8, 12, 1, 0, 2, 12, 3, 1, 12, 9, 12, 5, 4, 12, 2, 10, 9, 12, 4, 3, 12, 8, 10, 5, 12 };
+static unsigned char triangleTable_37_7[17] =  { 15, 0, 5, 4, 8, 3, 2, 8, 9, 1, 0, 2, 5, 8, 5, 2, 10 };
+static unsigned char * triangleTable_37[8] = 
+{ 
+  triangleTable_37_0, triangleTable_37_1, triangleTable_37_2, triangleTable_37_3, triangleTable_37_4, triangleTable_37_5, triangleTable_37_6, triangleTable_37_7
+};
+
+static unsigned char triangleTable_38_0[11] =  { 9, 0, 2, 10, 5, 4, 2, 5, 4, 0, 2 };
+static unsigned char * triangleTable_38[1] = { triangleTable_38_0 };
+
+static unsigned char triangleTable_39_0[14] =  { 12, 0, 3, 5, 8, 3, 10, 5, 2, 10, 3, 8, 5, 4 };
+static unsigned char * triangleTable_39[1] = { triangleTable_39_0 };
+
+static unsigned char triangleTable_40_0[8] =  { 6, 0, 4, 9, 5, 11, 2, 3 };
+static unsigned char triangleTable_40_1[20] =  { 18, 0, 3, 4, 9, 3, 9, 2, 4, 11, 5, 2, 9, 5, 2, 5, 11, 11, 4, 3 };
+static unsigned char * triangleTable_40[2] = { triangleTable_40_0, triangleTable_40_1 };
+
+static unsigned char triangleTable_41_0[11] =  { 9, 0, 5, 4, 9, 11, 0, 8, 11, 2, 0 };
+static unsigned char triangleTable_41_1[17] =  { 15, 0, 11, 5, 4, 9, 2, 0, 11, 2, 5, 8, 11, 4, 5, 2, 9 };
+static unsigned char * triangleTable_41[2] = { triangleTable_41_0, triangleTable_41_1 };
+
+static unsigned char triangleTable_42_0[11] =  { 9, 0, 11, 2, 3, 5, 0, 1, 5, 4, 0 };
+static unsigned char triangleTable_42_1[17] =  { 15, 0, 5, 11, 2, 3, 4, 0, 5, 4, 11, 1, 5, 2, 11, 4, 3 };
+static unsigned char * triangleTable_42[2] = { triangleTable_42_0, triangleTable_42_1 };
+
+static unsigned char triangleTable_43_0[14] =  { 12, 0, 1, 5, 2, 5, 4, 8, 5, 8, 11, 2, 5, 11 };
+static unsigned char * triangleTable_43[1] = { triangleTable_43_0 };
+
+static unsigned char triangleTable_44_0[11] =  { 9, 0, 4, 9, 5, 3, 10, 1, 3, 11, 10 };
+static unsigned char triangleTable_44_1[17] =  { 15, 0, 3, 4, 9, 5, 11, 10, 3, 11, 4, 1, 3, 9, 4, 11, 5 };
+static unsigned char * triangleTable_44[2] = { triangleTable_44_0, triangleTable_44_1 };
+
+static unsigned char triangleTable_45_0[14] =  { 12, 0, 1, 0, 8, 10, 1, 8, 4, 9, 5, 10, 8, 11 };
+static unsigned char triangleTable_45_1[14] =  { 12, 0, 5, 4, 8, 10, 5, 8, 0, 9, 1, 10, 8, 11 };
+static unsigned char triangleTable_45_2[26] =  { 24, 1, 12, 4, 9, 11, 12, 8, 10, 5, 12, 0, 12, 1, 1, 12, 9, 12, 11, 10, 12, 0, 8, 4, 12, 5 };
+static unsigned char triangleTable_45_3[26] =  { 24, 1, 12, 0, 9, 11, 12, 8, 10, 1, 12, 4, 12, 5, 5, 12, 9, 12, 11, 10, 12, 4, 8, 0, 12, 1 };
+static unsigned char * triangleTable_45[4] = { triangleTable_45_0, triangleTable_45_1, triangleTable_45_2, triangleTable_45_3 };
+
+static unsigned char triangleTable_46_0[14] =  { 12, 0, 5, 4, 10, 3, 11, 10, 4, 0, 3, 4, 3, 10 };
+static unsigned char * triangleTable_46[1] = { triangleTable_46_0 };
+
+static unsigned char triangleTable_47_0[11] =  { 9, 0, 5, 4, 8, 5, 8, 10, 8, 11, 10 };
+static unsigned char * triangleTable_47[1] = { triangleTable_47_0 };
+
+static unsigned char triangleTable_48_0[8] =  { 6, 0, 8, 9, 7, 5, 7, 9 };
+static unsigned char * triangleTable_48[1] = { triangleTable_48_0 };
+
+static unsigned char triangleTable_49_0[11] =  { 9, 0, 3, 0, 9, 5, 3, 9, 5, 7, 3 };
+static unsigned char * triangleTable_49[1] = { triangleTable_49_0 };
+
+static unsigned char triangleTable_50_0[11] =  { 9, 0, 7, 8, 0, 1, 7, 0, 1, 5, 7 };
+static unsigned char * triangleTable_50[1] = { triangleTable_50_0 };
+
+static unsigned char triangleTable_51_0[8] =  { 6, 0, 1, 5, 7, 7, 3, 1 };
+static unsigned char * triangleTable_51[1] = { triangleTable_51_0 };
+
+static unsigned char triangleTable_52_0[11] =  { 9, 0, 2, 10, 1, 7, 9, 5, 7, 8, 9 };
+static unsigned char triangleTable_52_1[17] =  { 15, 0, 7, 2, 10, 1, 8, 9, 7, 8, 2, 5, 7, 10, 2, 8, 1 };
+static unsigned char * triangleTable_52[2] = { triangleTable_52_0, triangleTable_52_1 };
+
+static unsigned char triangleTable_53_0[14] =  { 12, 0, 0, 9, 5, 3, 0, 5, 10, 1, 2, 3, 5, 7 };
+static unsigned char triangleTable_53_1[14] =  { 12, 0, 2, 10, 5, 3, 2, 5, 9, 1, 0, 3, 5, 7 };
+static unsigned char triangleTable_53_2[26] =  { 24, 1, 12, 10, 1, 7, 12, 5, 3, 2, 12, 9, 12, 0, 0, 12, 1, 12, 7, 3, 12, 9, 5, 10, 12, 2 };
+static unsigned char triangleTable_53_3[26] =  { 24, 1, 12, 9, 1, 7, 12, 5, 3, 0, 12, 10, 12, 2, 2, 12, 1, 12, 7, 3, 12, 10, 5, 9, 12, 0 };
+static unsigned char * triangleTable_53[4] = { triangleTable_53_0, triangleTable_53_1, triangleTable_53_2, triangleTable_53_3 };
+
+static unsigned char triangleTable_54_0[14] =  { 12, 0, 0, 2, 8, 2, 10, 5, 2, 5, 7, 8, 2, 7 };
+static unsigned char * triangleTable_54[1] = { triangleTable_54_0 };
+
+static unsigned char triangleTable_55_0[11] =  { 9, 0, 2, 10, 5, 2, 5, 3, 5, 7, 3 };
+static unsigned char * triangleTable_55[1] = { triangleTable_55_0 };
+
+static unsigned char triangleTable_56_0[11] =  { 9, 0, 2, 3, 11, 9, 7, 8, 9, 5, 7 };
+static unsigned char triangleTable_56_1[17] =  { 15, 0, 9, 2, 3, 11, 5, 7, 9, 5, 2, 8, 9, 3, 2, 5, 11 };
+static unsigned char * triangleTable_56[2] = { triangleTable_56_0, triangleTable_56_1 };
+
+static unsigned char triangleTable_57_0[14] =  { 12, 0, 9, 5, 0, 11, 2, 0, 5, 7, 11, 5, 11, 0 };
+static unsigned char * triangleTable_57[1] = { triangleTable_57_0 };
+
+static unsigned char triangleTable_58_0[14] =  { 12, 0, 8, 0, 1, 7, 8, 1, 2, 3, 11, 7, 1, 5 };
+static unsigned char triangleTable_58_1[14] =  { 12, 0, 11, 2, 1, 7, 11, 1, 0, 3, 8, 7, 1, 5 };
+static unsigned char triangleTable_58_2[26] =  { 24, 1, 12, 2, 3, 5, 12, 1, 7, 11, 12, 0, 12, 8, 8, 12, 3, 12, 5, 7, 12, 0, 1, 2, 12, 11 };
+static unsigned char triangleTable_58_3[26] =  { 24, 1, 12, 0, 3, 5, 12, 1, 7, 8, 12, 2, 12, 11, 11, 12, 3, 12, 5, 7, 12, 2, 1, 0, 12, 8 };
+static unsigned char * triangleTable_58[4] = { triangleTable_58_0, triangleTable_58_1, triangleTable_58_2, triangleTable_58_3 };
+
+static unsigned char triangleTable_59_0[11] =  { 9, 0, 11, 2, 1, 11, 1, 7, 1, 5, 7 };
+static unsigned char * triangleTable_59[1] = { triangleTable_59_0 };
+
+static unsigned char triangleTable_60_0[14] =  { 12, 0, 7, 8, 5, 11, 10, 3, 10, 1, 3, 8, 9, 5 };
+static unsigned char triangleTable_60_1[14] =  { 12, 0, 9, 3, 8, 11, 5, 7, 11, 10, 5, 9, 1, 3 };
+static unsigned char triangleTable_60_2[26] =  { 24, 0, 7, 11, 5, 1, 8, 9, 1, 9, 5, 10, 5, 11, 8, 1, 3, 11, 8, 3, 1, 5, 10, 8, 11, 7 };
+static unsigned char triangleTable_60_3[26] =  { 24, 1, 12, 11, 10, 8, 12, 7, 12, 8, 9, 12, 3, 11, 10, 5, 12, 7, 12, 5, 9, 1, 12, 12, 1, 3 };
+static unsigned char triangleTable_60_4[26] =  { 24, 1, 12, 5, 7, 1, 12, 10, 12, 1, 3, 12, 9, 5, 7, 11, 12, 10, 12, 11, 3, 8, 12, 12, 8, 9 };
+static unsigned char * triangleTable_60[5] = { triangleTable_60_0, triangleTable_60_1, triangleTable_60_2, triangleTable_60_3, triangleTable_60_4 };
+
+static unsigned char triangleTable_61_0[11] =  { 9, 0, 9, 1, 0, 10, 5, 11, 5, 7, 11 };
+static unsigned char triangleTable_61_1[17] =  { 15, 0, 1, 0, 11, 5, 7, 9, 0, 7, 11, 1, 11, 10, 9, 7, 0 };
+static unsigned char * triangleTable_61[2] = { triangleTable_61_0, triangleTable_61_1 };
+
+static unsigned char triangleTable_62_0[11] =  { 9, 0, 3, 8, 0, 7, 11, 5, 11, 10, 5 };
+static unsigned char triangleTable_62_1[17] =  { 15, 0, 8, 0, 5, 11, 10, 3, 0, 10, 5, 8, 5, 7, 3, 10, 0 };
+static unsigned char * triangleTable_62[2] = { triangleTable_62_0, triangleTable_62_1 };
+
+static unsigned char triangleTable_63_0[8] =  { 6, 0, 11, 5, 7, 5, 11, 10 };
+static unsigned char * triangleTable_63[1] = { triangleTable_63_0 };
+
+static unsigned char triangleTable_64_0[5] =  { 3, 0, 6, 5, 10 };
+static unsigned char * triangleTable_64[1] = { triangleTable_64_0 };
+
+static unsigned char triangleTable_65_0[8] =  { 6, 0, 6, 5, 10, 3, 0, 8 };
+static unsigned char triangleTable_65_1[20] =  { 18, 0, 8, 6, 5, 8, 5, 0, 6, 3, 10, 0, 5, 10, 0, 10, 3, 3, 6, 8 };
+static unsigned char * triangleTable_65[2] = { triangleTable_65_0, triangleTable_65_1 };
+
+static unsigned char triangleTable_66_0[8] =  { 6, 0, 5, 10, 6, 0, 1, 9 };
+static unsigned char triangleTable_66_1[14] =  { 12, 0, 0, 1, 6, 6, 5, 9, 9, 0, 6, 10, 6, 1 };
+static unsigned char * triangleTable_66[2] = { triangleTable_66_0, triangleTable_66_1 };
+
+static unsigned char triangleTable_67_0[11] =  { 9, 0, 6, 5, 10, 8, 1, 9, 8, 3, 1 };
+static unsigned char triangleTable_67_1[17] =  { 15, 0, 8, 6, 5, 10, 3, 1, 8, 3, 6, 9, 8, 5, 6, 3, 10 };
+static unsigned char * triangleTable_67[2] = { triangleTable_67_0, triangleTable_67_1 };
+
+static unsigned char triangleTable_68_0[8] =  { 6, 0, 5, 1, 6, 2, 6, 1 };
+static unsigned char * triangleTable_68[1] = { triangleTable_68_0 };
+
+static unsigned char triangleTable_69_0[11] =  { 9, 0, 8, 3, 0, 6, 1, 2, 6, 5, 1 };
+static unsigned char triangleTable_69_1[17] =  { 15, 0, 6, 8, 3, 0, 5, 1, 6, 5, 8, 2, 6, 3, 8, 5, 0 };
+static unsigned char * triangleTable_69[2] = { triangleTable_69_0, triangleTable_69_1 };
+
+static unsigned char triangleTable_70_0[11] =  { 9, 0, 6, 5, 9, 0, 6, 9, 0, 2, 6 };
+static unsigned char * triangleTable_70[1] = { triangleTable_70_0 };
+
+static unsigned char triangleTable_71_0[14] =  { 12, 0, 2, 6, 3, 6, 5, 9, 6, 9, 8, 3, 6, 8 };
+static unsigned char * triangleTable_71[1] = { triangleTable_71_0 };
+
+static unsigned char triangleTable_72_0[8] =  { 6, 0, 10, 6, 5, 3, 11, 2 };
+static unsigned char triangleTable_72_1[14] =  { 12, 0, 3, 11, 5, 5, 10, 2, 2, 3, 5, 6, 5, 11 };
+static unsigned char * triangleTable_72[2] = { triangleTable_72_0, triangleTable_72_1 };
+
+static unsigned char triangleTable_73_0[11] =  { 9, 0, 5, 10, 6, 0, 11, 2, 0, 8, 11 };
+static unsigned char triangleTable_73_1[17] =  { 15, 0, 0, 5, 10, 6, 8, 11, 0, 8, 5, 2, 0, 10, 5, 8, 6 };
+static unsigned char * triangleTable_73[2] = { triangleTable_73_0, triangleTable_73_1 };
+
+static unsigned char triangleTable_74_0[11] =  { 9, 0, 10, 6, 5, 1, 9, 0, 3, 11, 2 };
+static unsigned char triangleTable_74_1[17] =  { 15, 0, 10, 6, 5, 0, 3, 11, 9, 0, 11, 2, 9, 11, 9, 2, 1 };
+static unsigned char triangleTable_74_2[17] =  { 15, 0, 2, 3, 11, 5, 9, 0, 6, 5, 0, 1, 6, 0, 6, 1, 10 };
+static unsigned char triangleTable_74_3[17] =  { 15, 0, 1, 9, 0, 11, 6, 5, 3, 11, 5, 10, 3, 5, 3, 10, 2 };
+static unsigned char triangleTable_74_4[29] =  { 27, 1, 12, 10, 6, 12, 1, 10, 9, 12, 5, 1, 12, 2, 12, 3, 11, 12, 9, 0, 2, 12, 11, 5, 12, 6, 0, 3, 12 };
+static unsigned char triangleTable_74_5[29] =  { 27, 1, 12, 1, 9, 12, 2, 1, 3, 12, 0, 2, 12, 10, 12, 6, 5, 12, 3, 11, 10, 12, 5, 0, 12, 9, 11, 6, 12 };
+static unsigned char triangleTable_74_6[29] =  { 27, 1, 12, 2, 3, 12, 10, 2, 6, 12, 11, 10, 12, 1, 12, 9, 0, 12, 6, 5, 1, 12, 0, 11, 12, 3, 5, 9, 12 };
+static unsigned char triangleTable_74_7[17] =  { 15, 0, 9, 0, 3, 11, 6, 3, 1, 10, 2, 6, 9, 3, 9, 6, 5 };
+static unsigned char * triangleTable_74[8] = 
+{ 
+  triangleTable_74_0, triangleTable_74_1, triangleTable_74_2, triangleTable_74_3, triangleTable_74_4, triangleTable_74_5, triangleTable_74_6, triangleTable_74_7
+};
+
+static unsigned char triangleTable_75_0[14] =  { 12, 0, 2, 1, 9, 11, 2, 9, 5, 10, 6, 11, 9, 8 };
+static unsigned char triangleTable_75_1[14] =  { 12, 0, 6, 5, 9, 11, 6, 9, 1, 10, 2, 11, 9, 8 };
+static unsigned char triangleTable_75_2[26] =  { 24, 1, 12, 5, 10, 8, 12, 9, 11, 6, 12, 1, 12, 2, 2, 12, 10, 12, 8, 11, 12, 1, 9, 5, 12, 6 };
+static unsigned char triangleTable_75_3[26] =  { 24, 1, 12, 1, 10, 8, 12, 9, 11, 2, 12, 5, 12, 6, 6, 12, 10, 12, 8, 11, 12, 5, 9, 1, 12, 2 };
+static unsigned char * triangleTable_75[4] = { triangleTable_75_0, triangleTable_75_1, triangleTable_75_2, triangleTable_75_3 };
+
+static unsigned char triangleTable_76_0[11] =  { 9, 0, 3, 11, 6, 5, 3, 6, 5, 1, 3 };
+static unsigned char * triangleTable_76[1] = { triangleTable_76_0 };
+
+static unsigned char triangleTable_77_0[14] =  { 12, 0, 0, 8, 1, 6, 5, 1, 8, 11, 6, 8, 6, 1 };
+static unsigned char * triangleTable_77[1] = { triangleTable_77_0 };
+
+static unsigned char triangleTable_78_0[14] =  { 12, 0, 11, 9, 3, 11, 5, 9, 6, 5, 11, 3, 9, 0 };
+static unsigned char * triangleTable_78[1] = { triangleTable_78_0 };
+
+static unsigned char triangleTable_79_0[11] =  { 9, 0, 6, 5, 9, 6, 9, 11, 9, 8, 11 };
+static unsigned char * triangleTable_79[1] = { triangleTable_79_0 };
+
+static unsigned char triangleTable_80_0[8] =  { 6, 0, 6, 5, 10, 8, 4, 7 };
+static unsigned char triangleTable_80_1[14] =  { 12, 0, 8, 4, 10, 10, 6, 7, 7, 8, 10, 5, 10, 4 };
+static unsigned char * triangleTable_80[2] = { triangleTable_80_0, triangleTable_80_1 };
+
+static unsigned char triangleTable_81_0[11] =  { 9, 0, 10, 6, 5, 3, 4, 7, 3, 0, 4 };
+static unsigned char triangleTable_81_1[17] =  { 15, 0, 3, 10, 6, 5, 0, 4, 3, 0, 10, 7, 3, 6, 10, 0, 5 };
+static unsigned char * triangleTable_81[2] = { triangleTable_81_0, triangleTable_81_1 };
+
+static unsigned char triangleTable_82_0[11] =  { 9, 0, 5, 10, 6, 4, 7, 8, 0, 1, 9 };
+static unsigned char triangleTable_82_1[17] =  { 15, 0, 5, 10, 6, 8, 0, 1, 7, 8, 1, 9, 7, 1, 7, 9, 4 };
+static unsigned char triangleTable_82_2[17] =  { 15, 0, 9, 0, 1, 6, 7, 8, 10, 6, 8, 4, 10, 8, 10, 4, 5 };
+static unsigned char triangleTable_82_3[17] =  { 15, 0, 4, 7, 8, 1, 10, 6, 0, 1, 6, 5, 0, 6, 0, 5, 9 };
+static unsigned char triangleTable_82_4[29] =  { 27, 1, 12, 5, 10, 12, 4, 5, 7, 12, 6, 4, 12, 9, 12, 0, 1, 12, 7, 8, 9, 12, 1, 6, 12, 10, 8, 0, 12 };
+static unsigned char triangleTable_82_5[29] =  { 27, 1, 12, 4, 7, 12, 9, 4, 0, 12, 8, 9, 12, 5, 12, 10, 6, 12, 0, 1, 5, 12, 6, 8, 12, 7, 1, 10, 12 };
+static unsigned char triangleTable_82_6[29] =  { 27, 1, 12, 9, 0, 12, 5, 9, 10, 12, 1, 5, 12, 4, 12, 7, 8, 12, 10, 6, 4, 12, 8, 1, 12, 0, 6, 7, 12 };
+static unsigned char triangleTable_82_7[17] =  { 15, 0, 7, 8, 0, 1, 10, 0, 4, 5, 9, 10, 7, 0, 7, 10, 6 };
+static unsigned char * triangleTable_82[8] = 
+{ 
+  triangleTable_82_0, triangleTable_82_1, triangleTable_82_2, triangleTable_82_3, triangleTable_82_4, triangleTable_82_5, triangleTable_82_6, triangleTable_82_7
+};
+
+static unsigned char triangleTable_83_0[14] =  { 12, 0, 9, 4, 7, 1, 9, 7, 6, 5, 10, 1, 7, 3 };
+static unsigned char triangleTable_83_1[14] =  { 12, 0, 10, 6, 7, 1, 10, 7, 4, 5, 9, 1, 7, 3 };
+static unsigned char triangleTable_83_2[26] =  { 24, 1, 12, 6, 5, 3, 12, 7, 1, 10, 12, 4, 12, 9, 9, 12, 5, 12, 3, 1, 12, 4, 7, 6, 12, 10 };
+static unsigned char triangleTable_83_3[26] =  { 24, 1, 12, 4, 5, 3, 12, 7, 1, 9, 12, 6, 12, 10, 10, 12, 5, 12, 3, 1, 12, 6, 7, 4, 12, 9 };
+static unsigned char * triangleTable_83[4] = { triangleTable_83_0, triangleTable_83_1, triangleTable_83_2, triangleTable_83_3 };
+
+static unsigned char triangleTable_84_0[11] =  { 9, 0, 8, 4, 7, 1, 6, 5, 1, 2, 6 };
+static unsigned char triangleTable_84_1[17] =  { 15, 0, 1, 8, 4, 7, 2, 6, 1, 2, 8, 5, 1, 4, 8, 2, 7 };
+static unsigned char * triangleTable_84[2] = { triangleTable_84_0, triangleTable_84_1 };
+
+static unsigned char triangleTable_85_0[14] =  { 12, 0, 6, 5, 2, 7, 3, 4, 3, 0, 4, 5, 1, 2 };
+static unsigned char triangleTable_85_1[14] =  { 12, 0, 1, 4, 5, 7, 2, 6, 7, 3, 2, 1, 0, 4 };
+static unsigned char triangleTable_85_2[26] =  { 24, 0, 6, 7, 2, 0, 5, 1, 0, 1, 2, 3, 2, 7, 5, 0, 4, 7, 5, 4, 0, 2, 3, 5, 7, 6 };
+static unsigned char triangleTable_85_3[26] =  { 24, 1, 12, 7, 3, 5, 12, 6, 12, 5, 1, 12, 4, 7, 3, 2, 12, 6, 12, 2, 1, 0, 12, 12, 0, 4 };
+static unsigned char triangleTable_85_4[26] =  { 24, 1, 12, 2, 6, 0, 12, 3, 12, 0, 4, 12, 1, 2, 6, 7, 12, 3, 12, 7, 4, 5, 12, 12, 5, 1 };
+static unsigned char * triangleTable_85[5] = { triangleTable_85_0, triangleTable_85_1, triangleTable_85_2, triangleTable_85_3, triangleTable_85_4 };
+
+static unsigned char triangleTable_86_0[14] =  { 12, 0, 5, 9, 0, 6, 5, 0, 8, 4, 7, 6, 0, 2 };
+static unsigned char triangleTable_86_1[14] =  { 12, 0, 7, 8, 0, 6, 7, 0, 9, 4, 5, 6, 0, 2 };
+static unsigned char triangleTable_86_2[26] =  { 24, 1, 12, 8, 4, 2, 12, 0, 6, 7, 12, 9, 12, 5, 5, 12, 4, 12, 2, 6, 12, 9, 0, 8, 12, 7 };
+static unsigned char triangleTable_86_3[26] =  { 24, 1, 12, 9, 4, 2, 12, 0, 6, 5, 12, 8, 12, 7, 7, 12, 4, 12, 2, 6, 12, 8, 0, 9, 12, 5 };
+static unsigned char * triangleTable_86[4] = { triangleTable_86_0, triangleTable_86_1, triangleTable_86_2, triangleTable_86_3 };
+
+static unsigned char triangleTable_87_0[11] =  { 9, 0, 4, 5, 9, 6, 7, 2, 7, 3, 2 };
+static unsigned char triangleTable_87_1[17] =  { 15, 0, 5, 9, 2, 7, 3, 4, 9, 3, 2, 5, 2, 6, 4, 3, 9 };
+static unsigned char * triangleTable_87[2] = { triangleTable_87_0, triangleTable_87_1 };
+
+static unsigned char triangleTable_88_0[11] =  { 9, 0, 11, 2, 3, 7, 8, 4, 5, 10, 6 };
+static unsigned char triangleTable_88_1[17] =  { 15, 0, 11, 2, 3, 4, 5, 10, 8, 4, 10, 6, 8, 10, 8, 6, 7 };
+static unsigned char triangleTable_88_2[17] =  { 15, 0, 6, 5, 10, 3, 8, 4, 2, 3, 4, 7, 2, 4, 2, 7, 11 };
+static unsigned char triangleTable_88_3[17] =  { 15, 0, 7, 8, 4, 10, 2, 3, 5, 10, 3, 11, 5, 3, 5, 11, 6 };
+static unsigned char triangleTable_88_4[29] =  { 27, 1, 12, 11, 2, 12, 7, 11, 8, 12, 3, 7, 12, 6, 12, 5, 10, 12, 8, 4, 6, 12, 10, 3, 12, 2, 4, 5, 12 };
+static unsigned char triangleTable_88_5[29] =  { 27, 1, 12, 7, 8, 12, 6, 7, 5, 12, 4, 6, 12, 11, 12, 2, 3, 12, 5, 10, 11, 12, 3, 4, 12, 8, 10, 2, 12 };
+static unsigned char triangleTable_88_6[29] =  { 27, 1, 12, 6, 5, 12, 11, 6, 2, 12, 10, 11, 12, 7, 12, 8, 4, 12, 2, 3, 7, 12, 4, 10, 12, 5, 3, 8, 12 };
+static unsigned char triangleTable_88_7[17] =  { 15, 0, 8, 4, 5, 10, 2, 5, 7, 11, 6, 2, 8, 5, 8, 2, 3 };
+static unsigned char * triangleTable_88[8] = 
+{ 
+  triangleTable_88_0, triangleTable_88_1, triangleTable_88_2, triangleTable_88_3, triangleTable_88_4, triangleTable_88_5, triangleTable_88_6, triangleTable_88_7
+};
+
+static unsigned char triangleTable_89_0[14] =  { 12, 0, 7, 11, 2, 4, 7, 2, 10, 6, 5, 4, 2, 0 };
+static unsigned char triangleTable_89_1[14] =  { 12, 0, 5, 10, 2, 4, 5, 2, 11, 6, 7, 4, 2, 0 };
+static unsigned char triangleTable_89_2[26] =  { 24, 1, 12, 10, 6, 0, 12, 2, 4, 5, 12, 11, 12, 7, 7, 12, 6, 12, 0, 4, 12, 11, 2, 10, 12, 5 };
+static unsigned char triangleTable_89_3[26] =  { 24, 1, 12, 11, 6, 0, 12, 2, 4, 7, 12, 10, 12, 5, 5, 12, 6, 12, 0, 4, 12, 10, 2, 11, 12, 7 };
+static unsigned char * triangleTable_89[4] = { triangleTable_89_0, triangleTable_89_1, triangleTable_89_2, triangleTable_89_3 };
+
+static unsigned char triangleTable_90_0[14] =  { 12, 0, 11, 2, 3, 4, 7, 8, 1, 9, 0, 6, 5, 10 };
+static unsigned char triangleTable_90_1[20] =  { 18, 0, 2, 3, 11, 8, 4, 7, 6, 5, 0, 1, 10, 6, 5, 9, 0, 6, 0, 1 };
+static unsigned char triangleTable_90_2[20] =  { 18, 0, 7, 8, 4, 0, 1, 9, 5, 10, 3, 11, 6, 5, 10, 2, 3, 5, 3, 11 };
+static unsigned char triangleTable_90_3[20] =  { 18, 0, 0, 1, 9, 10, 6, 5, 4, 7, 2, 3, 8, 4, 7, 11, 2, 4, 2, 3 };
+static unsigned char triangleTable_90_4[20] =  { 18, 0, 5, 10, 6, 2, 3, 11, 7, 8, 1, 9, 4, 7, 8, 0, 1, 7, 1, 9 };
+static unsigned char triangleTable_90_5[20] =  { 18, 0, 8, 4, 7, 5, 10, 6, 11, 2, 9, 0, 3, 11, 2, 1, 9, 11, 9, 0 };
+static unsigned char triangleTable_90_6[20] =  { 18, 0, 9, 0, 1, 3, 11, 2, 10, 6, 8, 4, 5, 10, 6, 7, 8, 10, 8, 4 };
+static unsigned char triangleTable_90_7[32] =  { 30, 1, 5, 12, 6, 12, 3, 11, 2, 12, 10, 12, 2, 3, 12, 9, 0, 6, 12, 11, 10, 12, 1, 7, 8, 4, 0, 1, 12, 9, 12, 5 };
+static unsigned char triangleTable_90_8[32] =  { 30, 1, 1, 12, 0, 12, 7, 8, 4, 12, 9, 12, 4, 7, 12, 10, 6, 0, 12, 8, 9, 12, 5, 3, 11, 2, 6, 5, 12, 10, 12, 1 };
+static unsigned char triangleTable_90_9[32] =  { 30, 1, 0, 12, 9, 3, 11, 12, 1, 12, 2, 11, 2, 12, 6, 5, 12, 3, 12, 0, 10, 12, 1, 4, 7, 8, 12, 10, 6, 9, 12, 5 };
+static unsigned char triangleTable_90_10[32] =  { 30, 1, 6, 12, 10, 7, 8, 12, 5, 12, 4, 8, 4, 12, 0, 1, 12, 7, 12, 6, 9, 12, 5, 2, 3, 11, 12, 9, 0, 10, 12, 1 };
+static unsigned char triangleTable_90_11[32] =  { 30, 1, 3, 12, 2, 8, 4, 12, 11, 12, 7, 4, 7, 12, 5, 10, 12, 8, 12, 3, 6, 12, 11, 1, 9, 0, 12, 6, 5, 2, 12, 10 };
+static unsigned char triangleTable_90_12[32] =  { 30, 1, 11, 12, 3, 12, 9, 0, 1, 12, 2, 12, 1, 9, 12, 6, 5, 3, 12, 0, 2, 12, 10, 8, 4, 7, 5, 10, 12, 6, 12, 11 };
+static unsigned char triangleTable_90_13[32] =  { 30, 1, 10, 12, 5, 2, 3, 12, 6, 12, 11, 3, 11, 12, 8, 4, 12, 2, 12, 10, 7, 12, 6, 9, 0, 1, 12, 7, 8, 5, 12, 4 };
+static unsigned char triangleTable_90_14[32] =  { 30, 1, 7, 12, 4, 12, 1, 9, 0, 12, 8, 12, 0, 1, 12, 11, 2, 4, 12, 9, 8, 12, 3, 5, 10, 6, 2, 3, 12, 11, 12, 7 };
+static unsigned char triangleTable_90_15[32] =  { 30, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
+static unsigned char triangleTable_90_16[32] =  { 30, 1, 4, 12, 8, 12, 2, 3, 11, 12, 7, 12, 11, 2, 12, 5, 10, 8, 12, 3, 7, 12, 6, 0, 1, 9, 10, 6, 12, 5, 12, 4 };
+static unsigned char triangleTable_90_17[32] =  { 30, 1, 9, 12, 1, 4, 7, 12, 0, 12, 8, 7, 8, 12, 11, 2, 12, 4, 12, 9, 3, 12, 0, 10, 6, 5, 12, 3, 11, 1, 12, 2 };
+static unsigned char triangleTable_90_18[32] =  { 30, 1, 8, 12, 7, 12, 10, 6, 5, 12, 4, 12, 5, 10, 12, 0, 1, 7, 12, 6, 4, 12, 9, 11, 2, 3, 1, 9, 12, 0, 12, 8 };
+static unsigned char triangleTable_90_19[38] =  { 36, 1, 12, 5, 9, 11, 6, 12, 1, 12, 0, 5, 12, 4, 3, 12, 2, 12, 1, 10, 2, 12, 10, 12, 8, 4, 9, 0, 12, 7, 12, 6, 3, 11, 12, 7, 8, 12 };
+static unsigned char triangleTable_90_20[38] =  { 36, 1, 12, 1, 10, 8, 0, 12, 5, 12, 6, 1, 12, 2, 7, 12, 4, 12, 5, 9, 4, 12, 9, 12, 11, 2, 10, 6, 12, 3, 12, 0, 7, 8, 12, 3, 11, 12 };
+static unsigned char triangleTable_90_21[38] =  { 36, 1, 12, 11, 6, 0, 3, 12, 10, 12, 5, 11, 12, 7, 9, 12, 1, 12, 10, 2, 1, 12, 2, 12, 4, 7, 6, 5, 12, 8, 12, 3, 9, 0, 12, 8, 4, 12 };
+static unsigned char triangleTable_90_22[38] =  { 36, 1, 12, 7, 11, 9, 4, 12, 3, 12, 2, 7, 12, 6, 1, 12, 0, 12, 3, 8, 0, 12, 8, 12, 10, 6, 11, 2, 12, 5, 12, 4, 1, 9, 12, 5, 10, 12 };
+static unsigned char triangleTable_90_23[32] =  { 30, 1, 1, 12, 2, 12, 7, 11, 6, 12, 10, 12, 6, 7, 12, 9, 4, 2, 12, 11, 10, 12, 5, 3, 8, 0, 4, 5, 12, 9, 12, 1 };
+static unsigned char triangleTable_90_24[32] =  { 30, 1, 5, 12, 4, 12, 3, 8, 0, 12, 9, 12, 0, 3, 12, 10, 2, 4, 12, 8, 9, 12, 1, 7, 11, 6, 2, 1, 12, 10, 12, 5 };
+static unsigned char triangleTable_90_25[32] =  { 30, 1, 2, 12, 10, 3, 8, 12, 1, 12, 0, 8, 0, 12, 4, 5, 12, 3, 12, 2, 9, 12, 1, 6, 7, 11, 12, 9, 4, 10, 12, 5 };
+static unsigned char triangleTable_90_26[32] =  { 30, 1, 4, 12, 9, 7, 11, 12, 5, 12, 6, 11, 6, 12, 2, 1, 12, 7, 12, 4, 10, 12, 5, 0, 3, 8, 12, 10, 2, 9, 12, 1 };
+static unsigned char triangleTable_90_27[32] =  { 30, 1, 7, 12, 6, 8, 0, 12, 11, 12, 3, 0, 3, 12, 1, 10, 12, 8, 12, 7, 2, 12, 11, 5, 9, 4, 12, 2, 1, 6, 12, 10 };
+static unsigned char triangleTable_90_28[32] =  { 30, 1, 10, 12, 1, 12, 8, 0, 3, 12, 2, 12, 3, 8, 12, 6, 7, 1, 12, 0, 2, 12, 11, 9, 4, 5, 7, 11, 12, 6, 12, 10 };
+static unsigned char triangleTable_90_29[32] =  { 30, 1, 11, 12, 7, 2, 1, 12, 6, 12, 10, 1, 10, 12, 9, 4, 12, 2, 12, 11, 5, 12, 6, 8, 0, 3, 12, 5, 9, 7, 12, 4 };
+static unsigned char triangleTable_90_30[32] =  { 30, 1, 3, 12, 0, 12, 5, 9, 4, 12, 8, 12, 4, 5, 12, 11, 6, 0, 12, 9, 8, 12, 7, 1, 10, 2, 6, 7, 12, 11, 12, 3 };
+static unsigned char triangleTable_90_31[32] =  { 30, 1, 2, 12, 11, 1, 9, 12, 3, 12, 0, 9, 0, 12, 4, 7, 12, 1, 12, 2, 8, 12, 3, 6, 5, 10, 12, 8, 4, 11, 12, 7 };
+static unsigned char triangleTable_90_32[32] =  { 30, 1, 6, 12, 11, 12, 0, 3, 8, 12, 7, 12, 8, 0, 12, 5, 9, 11, 12, 3, 7, 12, 4, 2, 1, 10, 9, 4, 12, 5, 12, 6 };
+static unsigned char triangleTable_90_33[32] =  { 30, 1, 8, 12, 3, 4, 5, 12, 0, 12, 9, 5, 9, 12, 10, 2, 12, 4, 12, 8, 1, 12, 0, 11, 6, 7, 12, 1, 10, 3, 12, 2 };
+static unsigned char triangleTable_90_34[32] =  { 30, 1, 9, 12, 5, 12, 11, 6, 7, 12, 4, 12, 7, 11, 12, 0, 3, 5, 12, 6, 4, 12, 8, 10, 2, 1, 3, 8, 12, 0, 12, 9 };
+static unsigned char triangleTable_90_35[20] =  { 18, 0, 7, 11, 6, 8, 0, 3, 2, 9, 4, 4, 5, 10, 2, 1, 9, 10, 2, 4 };
+static unsigned char triangleTable_90_36[20] =  { 18, 0, 9, 4, 5, 0, 3, 8, 7, 2, 1, 1, 10, 6, 7, 11, 2, 6, 7, 1 };
+static unsigned char triangleTable_90_37[20] =  { 18, 0, 5, 9, 4, 10, 2, 1, 0, 11, 6, 6, 7, 8, 0, 3, 11, 8, 0, 6 };
+static unsigned char triangleTable_90_38[20] =  { 18, 0, 11, 6, 7, 2, 1, 10, 5, 0, 3, 3, 8, 4, 5, 9, 0, 4, 5, 3 };
+static unsigned char triangleTable_90_39[20] =  { 18, 0, 6, 7, 11, 5, 9, 4, 8, 1, 10, 10, 2, 3, 8, 0, 1, 3, 8, 10 };
+static unsigned char triangleTable_90_40[20] =  { 18, 0, 2, 1, 10, 3, 8, 0, 9, 7, 11, 11, 6, 5, 9, 4, 7, 5, 9, 11 };
+static unsigned char triangleTable_90_41[14] =  { 12, 0, 0, 3, 8, 9, 4, 5, 1, 10, 2, 11, 6, 7 };
+static unsigned char triangleTable_90_42[20] =  { 18, 0, 3, 11, 5, 5, 9, 3, 2, 1, 10, 3, 9, 0, 7, 8, 4, 5, 11, 6 };
+static unsigned char triangleTable_90_43[20] =  { 18, 0, 7, 8, 1, 1, 10, 7, 4, 5, 9, 7, 10, 6, 3, 11, 2, 1, 8, 0 };
+static unsigned char triangleTable_90_44[20] =  { 18, 0, 5, 10, 3, 3, 8, 5, 6, 7, 11, 5, 8, 4, 1, 9, 0, 3, 10, 2 };
+static unsigned char triangleTable_90_45[20] =  { 18, 0, 1, 9, 7, 7, 11, 1, 0, 3, 8, 1, 11, 2, 5, 10, 6, 7, 9, 4 };
+static unsigned char * triangleTable_90[46] = 
+{ 
+  triangleTable_90_0, triangleTable_90_1, triangleTable_90_2, triangleTable_90_3, triangleTable_90_4, triangleTable_90_5, triangleTable_90_6, triangleTable_90_7, 
+  triangleTable_90_8, triangleTable_90_9, triangleTable_90_10, triangleTable_90_11, triangleTable_90_12, triangleTable_90_13, triangleTable_90_14, triangleTable_90_15, 
+  triangleTable_90_16, triangleTable_90_17, triangleTable_90_18, triangleTable_90_19, triangleTable_90_20, triangleTable_90_21, triangleTable_90_22, triangleTable_90_23, 
+  triangleTable_90_24, triangleTable_90_25, triangleTable_90_26, triangleTable_90_27, triangleTable_90_28, triangleTable_90_29, triangleTable_90_30, triangleTable_90_31, 
+  triangleTable_90_32, triangleTable_90_33, triangleTable_90_34, triangleTable_90_35, triangleTable_90_36, triangleTable_90_37, triangleTable_90_38, triangleTable_90_39, 
+  triangleTable_90_40, triangleTable_90_41, triangleTable_90_42, triangleTable_90_43, triangleTable_90_44, triangleTable_90_45
+};
+
+static unsigned char triangleTable_91_0[11] =  { 9, 0, 9, 4, 5, 2, 1, 10, 6, 7, 11 };
+static unsigned char triangleTable_91_1[17] =  { 15, 0, 9, 4, 5, 7, 11, 2, 7, 2, 1, 7, 1, 6, 10, 6, 1 };
+static unsigned char triangleTable_91_2[17] =  { 15, 0, 7, 11, 6, 2, 1, 9, 2, 9, 4, 2, 4, 10, 5, 10, 4 };
+static unsigned char triangleTable_91_3[17] =  { 15, 0, 2, 1, 10, 9, 4, 7, 9, 7, 11, 9, 11, 5, 6, 5, 11 };
+static unsigned char triangleTable_91_4[29] =  { 27, 1, 4, 5, 12, 5, 10, 12, 9, 12, 1, 6, 12, 10, 7, 11, 12, 2, 1, 12, 7, 12, 6, 4, 12, 9, 12, 11, 2 };
+static unsigned char triangleTable_91_5[29] =  { 27, 1, 1, 10, 12, 10, 6, 12, 2, 12, 11, 5, 12, 6, 9, 4, 12, 7, 11, 12, 9, 12, 5, 1, 12, 2, 12, 4, 7 };
+static unsigned char triangleTable_91_6[29] =  { 27, 1, 11, 6, 12, 6, 5, 12, 7, 12, 4, 10, 12, 5, 2, 1, 12, 9, 4, 12, 2, 12, 10, 11, 12, 7, 12, 1, 9 };
+static unsigned char triangleTable_91_7[17] =  { 15, 0, 11, 2, 1, 11, 4, 7, 6, 5, 10, 11, 1, 4, 9, 4, 1 };
+static unsigned char * triangleTable_91[8] = 
+{ 
+  triangleTable_91_0, triangleTable_91_1, triangleTable_91_2, triangleTable_91_3, triangleTable_91_4, triangleTable_91_5, triangleTable_91_6, triangleTable_91_7
+};
+
+static unsigned char triangleTable_92_0[14] =  { 12, 0, 11, 6, 5, 3, 11, 5, 4, 7, 8, 3, 5, 1 };
+static unsigned char triangleTable_92_1[14] =  { 12, 0, 8, 4, 5, 3, 8, 5, 6, 7, 11, 3, 5, 1 };
+static unsigned char triangleTable_92_2[26] =  { 24, 1, 12, 4, 7, 1, 12, 5, 3, 8, 12, 6, 12, 11, 11, 12, 7, 12, 1, 3, 12, 6, 5, 4, 12, 8 };
+static unsigned char triangleTable_92_3[26] =  { 24, 1, 12, 6, 7, 1, 12, 5, 3, 11, 12, 4, 12, 8, 8, 12, 7, 12, 1, 3, 12, 4, 5, 6, 12, 11 };
+static unsigned char * triangleTable_92[4] = { triangleTable_92_0, triangleTable_92_1, triangleTable_92_2, triangleTable_92_3 };
+
+static unsigned char triangleTable_93_0[11] =  { 9, 0, 6, 7, 11, 4, 5, 0, 5, 1, 0 };
+static unsigned char triangleTable_93_1[17] =  { 15, 0, 7, 11, 0, 5, 1, 6, 11, 1, 0, 7, 0, 4, 6, 1, 11 };
+static unsigned char * triangleTable_93[2] = { triangleTable_93_0, triangleTable_93_1 };
+
+static unsigned char triangleTable_94_0[11] =  { 9, 0, 11, 6, 7, 0, 3, 8, 4, 5, 9 };
+static unsigned char triangleTable_94_1[17] =  { 15, 0, 11, 6, 7, 5, 9, 0, 5, 0, 3, 5, 3, 4, 8, 4, 3 };
+static unsigned char triangleTable_94_2[17] =  { 15, 0, 5, 9, 4, 0, 3, 11, 0, 11, 6, 0, 6, 8, 7, 8, 6 };
+static unsigned char triangleTable_94_3[17] =  { 15, 0, 0, 3, 8, 11, 6, 5, 11, 5, 9, 11, 9, 7, 4, 7, 9 };
+static unsigned char triangleTable_94_4[29] =  { 27, 1, 6, 7, 12, 7, 8, 12, 11, 12, 3, 4, 12, 8, 5, 9, 12, 0, 3, 12, 5, 12, 4, 6, 12, 11, 12, 9, 0 };
+static unsigned char triangleTable_94_5[29] =  { 27, 1, 3, 8, 12, 8, 4, 12, 0, 12, 9, 7, 12, 4, 11, 6, 12, 5, 9, 12, 11, 12, 7, 3, 12, 0, 12, 6, 5 };
+static unsigned char triangleTable_94_6[29] =  { 27, 1, 9, 4, 12, 4, 7, 12, 5, 12, 6, 8, 12, 7, 0, 3, 12, 11, 6, 12, 0, 12, 8, 9, 12, 5, 12, 3, 11 };
+static unsigned char triangleTable_94_7[17] =  { 15, 0, 9, 0, 3, 9, 6, 5, 4, 7, 8, 9, 3, 6, 11, 6, 3 };
+static unsigned char * triangleTable_94[8] = 
+{ 
+  triangleTable_94_0, triangleTable_94_1, triangleTable_94_2, triangleTable_94_3, triangleTable_94_4, triangleTable_94_5, triangleTable_94_6, triangleTable_94_7
+};
+
+static unsigned char triangleTable_95_0[8] =  { 6, 0, 9, 4, 5, 6, 7, 11 };
+static unsigned char triangleTable_95_1[14] =  { 12, 0, 9, 7, 11, 6, 5, 9, 9, 11, 6, 7, 9, 4 };
+static unsigned char * triangleTable_95[2] = { triangleTable_95_0, triangleTable_95_1 };
+
+static unsigned char triangleTable_96_0[8] =  { 6, 0, 6, 4, 10, 9, 10, 4 };
+static unsigned char * triangleTable_96[1] = { triangleTable_96_0 };
+
+static unsigned char triangleTable_97_0[11] =  { 9, 0, 3, 0, 8, 10, 4, 9, 10, 6, 4 };
+static unsigned char triangleTable_97_1[17] =  { 15, 0, 10, 3, 0, 8, 6, 4, 10, 6, 3, 9, 10, 0, 3, 6, 8 };
+static unsigned char * triangleTable_97[2] = { triangleTable_97_0, triangleTable_97_1 };
+
+static unsigned char triangleTable_98_0[11] =  { 9, 0, 0, 1, 10, 6, 0, 10, 6, 4, 0 };
+static unsigned char * triangleTable_98[1] = { triangleTable_98_0 };
+
+static unsigned char triangleTable_99_0[14] =  { 12, 0, 8, 3, 4, 10, 6, 4, 3, 1, 10, 3, 10, 4 };
+static unsigned char * triangleTable_99[1] = { triangleTable_99_0 };
+
+static unsigned char triangleTable_100_0[11] =  { 9, 0, 4, 9, 1, 2, 4, 1, 2, 6, 4 };
+static unsigned char * triangleTable_100[1] = { triangleTable_100_0 };
+
+static unsigned char triangleTable_101_0[14] =  { 12, 0, 9, 1, 2, 4, 9, 2, 3, 0, 8, 4, 2, 6 };
+static unsigned char triangleTable_101_1[14] =  { 12, 0, 8, 3, 2, 4, 8, 2, 1, 0, 9, 4, 2, 6 };
+static unsigned char triangleTable_101_2[26] =  { 24, 1, 12, 3, 0, 6, 12, 2, 4, 8, 12, 1, 12, 9, 9, 12, 0, 12, 6, 4, 12, 1, 2, 3, 12, 8 };
+static unsigned char triangleTable_101_3[26] =  { 24, 1, 12, 1, 0, 6, 12, 2, 4, 9, 12, 3, 12, 8, 8, 12, 0, 12, 6, 4, 12, 3, 2, 1, 12, 9 };
+static unsigned char * triangleTable_101[4] = { triangleTable_101_0, triangleTable_101_1, triangleTable_101_2, triangleTable_101_3 };
+
+static unsigned char triangleTable_102_0[8] =  { 6, 0, 6, 4, 0, 0, 2, 6 };
+static unsigned char * triangleTable_102[1] = { triangleTable_102_0 };
+
+static unsigned char triangleTable_103_0[11] =  { 9, 0, 8, 3, 2, 8, 2, 4, 2, 6, 4 };
+static unsigned char * triangleTable_103[1] = { triangleTable_103_0 };
+
+static unsigned char triangleTable_104_0[11] =  { 9, 0, 3, 11, 2, 4, 10, 6, 4, 9, 10 };
+static unsigned char triangleTable_104_1[17] =  { 15, 0, 4, 3, 11, 2, 9, 10, 4, 9, 3, 6, 4, 11, 3, 9, 2 };
+static unsigned char * triangleTable_104[2] = { triangleTable_104_0, triangleTable_104_1 };
+
+static unsigned char triangleTable_105_0[14] =  { 12, 0, 10, 6, 9, 2, 0, 11, 0, 8, 11, 6, 4, 9 };
+static unsigned char triangleTable_105_1[14] =  { 12, 0, 4, 11, 6, 2, 9, 10, 2, 0, 9, 4, 8, 11 };
+static unsigned char triangleTable_105_2[26] =  { 24, 0, 10, 2, 9, 8, 6, 4, 8, 4, 9, 0, 9, 2, 6, 8, 11, 2, 6, 11, 8, 9, 0, 6, 2, 10 };
+static unsigned char triangleTable_105_3[26] =  { 24, 1, 12, 2, 0, 6, 12, 10, 12, 6, 4, 12, 11, 2, 0, 9, 12, 10, 12, 9, 4, 8, 12, 12, 8, 11 };
+static unsigned char triangleTable_105_4[26] =  { 24, 1, 12, 9, 10, 8, 12, 0, 12, 8, 11, 12, 4, 9, 10, 2, 12, 0, 12, 2, 11, 6, 12, 12, 6, 4 };
+static unsigned char * triangleTable_105[5] = { triangleTable_105_0, triangleTable_105_1, triangleTable_105_2, triangleTable_105_3, triangleTable_105_4 };
+
+static unsigned char triangleTable_106_0[14] =  { 12, 0, 1, 10, 6, 0, 1, 6, 11, 2, 3, 0, 6, 4 };
+static unsigned char triangleTable_106_1[14] =  { 12, 0, 3, 11, 6, 0, 3, 6, 10, 2, 1, 0, 6, 4 };
+static unsigned char triangleTable_106_2[26] =  { 24, 1, 12, 11, 2, 4, 12, 6, 0, 3, 12, 10, 12, 1, 1, 12, 2, 12, 4, 0, 12, 10, 6, 11, 12, 3 };
+static unsigned char triangleTable_106_3[26] =  { 24, 1, 12, 10, 2, 4, 12, 6, 0, 1, 12, 11, 12, 3, 3, 12, 2, 12, 4, 0, 12, 11, 6, 10, 12, 1 };
+static unsigned char * triangleTable_106[4] = { triangleTable_106_0, triangleTable_106_1, triangleTable_106_2, triangleTable_106_3 };
+
+static unsigned char triangleTable_107_0[11] =  { 9, 0, 10, 2, 1, 11, 6, 8, 6, 4, 8 };
+static unsigned char triangleTable_107_1[17] =  { 15, 0, 2, 1, 8, 6, 4, 10, 1, 4, 8, 2, 8, 11, 10, 4, 1 };
+static unsigned char * triangleTable_107[2] = { triangleTable_107_0, triangleTable_107_1 };
+
+static unsigned char triangleTable_108_0[14] =  { 12, 0, 6, 4, 11, 4, 9, 1, 4, 1, 3, 11, 4, 3 };
+static unsigned char * triangleTable_108[1] = { triangleTable_108_0 };
+
+static unsigned char triangleTable_109_0[11] =  { 9, 0, 0, 9, 1, 4, 8, 6, 8, 11, 6 };
+static unsigned char triangleTable_109_1[17] =  { 15, 0, 9, 1, 6, 8, 11, 0, 1, 11, 6, 9, 6, 4, 0, 11, 1 };
+static unsigned char * triangleTable_109[2] = { triangleTable_109_0, triangleTable_109_1 };
+
+static unsigned char triangleTable_110_0[11] =  { 9, 0, 3, 11, 6, 3, 6, 0, 6, 4, 0 };
+static unsigned char * triangleTable_110[1] = { triangleTable_110_0 };
+
+static unsigned char triangleTable_111_0[8] =  { 6, 0, 8, 6, 4, 6, 8, 11 };
+static unsigned char * triangleTable_111[1] = { triangleTable_111_0 };
+
+static unsigned char triangleTable_112_0[11] =  { 9, 0, 10, 6, 7, 8, 10, 7, 8, 9, 10 };
+static unsigned char * triangleTable_112[1] = { triangleTable_112_0 };
+
+static unsigned char triangleTable_113_0[14] =  { 12, 0, 7, 3, 6, 3, 0, 9, 3, 9, 10, 6, 3, 10 };
+static unsigned char * triangleTable_113[1] = { triangleTable_113_0 };
+
+static unsigned char triangleTable_114_0[14] =  { 12, 0, 1, 7, 0, 1, 6, 7, 10, 6, 1, 0, 7, 8 };
+static unsigned char * triangleTable_114[1] = { triangleTable_114_0 };
+
+static unsigned char triangleTable_115_0[11] =  { 9, 0, 10, 6, 7, 10, 7, 1, 7, 3, 1 };
+static unsigned char * triangleTable_115[1] = { triangleTable_115_0 };
+
+static unsigned char triangleTable_116_0[14] =  { 12, 0, 1, 2, 9, 7, 8, 9, 2, 6, 7, 2, 7, 9 };
+static unsigned char * triangleTable_116[1] = { triangleTable_116_0 };
+
+static unsigned char triangleTable_117_0[11] =  { 9, 0, 1, 0, 9, 3, 2, 7, 2, 6, 7 };
+static unsigned char triangleTable_117_1[17] =  { 15, 0, 0, 9, 7, 2, 6, 1, 9, 6, 7, 0, 7, 3, 1, 6, 9 };
+static unsigned char * triangleTable_117[2] = { triangleTable_117_0, triangleTable_117_1 };
+
+static unsigned char triangleTable_118_0[11] =  { 9, 0, 7, 8, 0, 7, 0, 6, 0, 2, 6 };
+static unsigned char * triangleTable_118[1] = { triangleTable_118_0 };
+
+static unsigned char triangleTable_119_0[8] =  { 6, 0, 2, 7, 3, 7, 2, 6 };
+static unsigned char * triangleTable_119[1] = { triangleTable_119_0 };
+
+static unsigned char triangleTable_120_0[14] =  { 12, 0, 6, 7, 8, 10, 6, 8, 3, 11, 2, 10, 8, 9 };
+static unsigned char triangleTable_120_1[14] =  { 12, 0, 2, 3, 8, 10, 2, 8, 7, 11, 6, 10, 8, 9 };
+static unsigned char triangleTable_120_2[26] =  { 24, 1, 12, 3, 11, 9, 12, 8, 10, 2, 12, 7, 12, 6, 6, 12, 11, 12, 9, 10, 12, 7, 8, 3, 12, 2 };
+static unsigned char triangleTable_120_3[26] =  { 24, 1, 12, 7, 11, 9, 12, 8, 10, 6, 12, 3, 12, 2, 2, 12, 11, 12, 9, 10, 12, 3, 8, 7, 12, 6 };
+static unsigned char * triangleTable_120[4] = { triangleTable_120_0, triangleTable_120_1, triangleTable_120_2, triangleTable_120_3 };
+
+static unsigned char triangleTable_121_0[11] =  { 9, 0, 11, 6, 7, 10, 2, 9, 2, 0, 9 };
+static unsigned char triangleTable_121_1[17] =  { 15, 0, 6, 7, 9, 2, 0, 11, 7, 0, 9, 6, 9, 10, 11, 0, 7 };
+static unsigned char * triangleTable_121[2] = { triangleTable_121_0, triangleTable_121_1 };
+
+static unsigned char triangleTable_122_0[11] =  { 9, 0, 8, 0, 3, 6, 7, 11, 2, 1, 10 };
+static unsigned char triangleTable_122_1[17] =  { 15, 0, 8, 0, 3, 1, 10, 6, 1, 6, 7, 1, 7, 2, 11, 2, 7 };
+static unsigned char triangleTable_122_2[17] =  { 15, 0, 1, 10, 2, 6, 7, 8, 6, 8, 0, 6, 0, 11, 3, 11, 0 };
+static unsigned char triangleTable_122_3[17] =  { 15, 0, 6, 7, 11, 8, 0, 1, 8, 1, 10, 8, 10, 3, 2, 3, 10 };
+static unsigned char triangleTable_122_4[29] =  { 27, 1, 0, 3, 12, 3, 11, 12, 8, 12, 7, 2, 12, 11, 1, 10, 12, 6, 7, 12, 1, 12, 2, 0, 12, 8, 12, 10, 6 };
+static unsigned char triangleTable_122_5[29] =  { 27, 1, 7, 11, 12, 11, 2, 12, 6, 12, 10, 3, 12, 2, 8, 0, 12, 1, 10, 12, 8, 12, 3, 7, 12, 6, 12, 0, 1 };
+static unsigned char triangleTable_122_6[29] =  { 27, 1, 10, 2, 12, 2, 3, 12, 1, 12, 0, 11, 12, 3, 6, 7, 12, 8, 0, 12, 6, 12, 11, 10, 12, 1, 12, 7, 8 };
+static unsigned char triangleTable_122_7[17] =  { 15, 0, 10, 6, 7, 10, 0, 1, 2, 3, 11, 10, 7, 0, 8, 0, 7 };
+static unsigned char * triangleTable_122[8] = 
+{ 
+  triangleTable_122_0, triangleTable_122_1, triangleTable_122_2, triangleTable_122_3, triangleTable_122_4, triangleTable_122_5, triangleTable_122_6, triangleTable_122_7
+};
+
+static unsigned char triangleTable_123_0[8] =  { 6, 0, 1, 10, 2, 11, 6, 7 };
+static unsigned char triangleTable_123_1[14] =  { 12, 0, 1, 6, 7, 11, 2, 1, 1, 7, 11, 6, 1, 10 };
+static unsigned char * triangleTable_123[2] = { triangleTable_123_0, triangleTable_123_1 };
+
+static unsigned char triangleTable_124_0[11] =  { 9, 0, 7, 11, 6, 3, 8, 1, 8, 9, 1 };
+static unsigned char triangleTable_124_1[17] =  { 15, 0, 11, 6, 1, 8, 9, 7, 6, 9, 1, 11, 1, 3, 7, 9, 6 };
+static unsigned char * triangleTable_124[2] = { triangleTable_124_0, triangleTable_124_1 };
+
+static unsigned char triangleTable_125_0[8] =  { 6, 0, 6, 7, 11, 0, 9, 1 };
+static unsigned char triangleTable_125_1[20] =  { 18, 0, 7, 11, 0, 9, 7, 0, 6, 1, 11, 6, 7, 9, 1, 6, 9, 0, 11, 1 };
+static unsigned char * triangleTable_125[2] = { triangleTable_125_0, triangleTable_125_1 };
+
+static unsigned char triangleTable_126_0[8] =  { 6, 0, 0, 3, 8, 7, 11, 6 };
+static unsigned char triangleTable_126_1[14] =  { 12, 0, 0, 11, 6, 7, 8, 0, 0, 6, 7, 11, 0, 3 };
+static unsigned char * triangleTable_126[2] = { triangleTable_126_0, triangleTable_126_1 };
+
+static unsigned char triangleTable_127_0[5] =  { 3, 0, 6, 7, 11 };
+static unsigned char * triangleTable_127[1] = { triangleTable_127_0 };
+
+static unsigned char triangleTable_128_0[5] =  { 3, 0, 11, 7, 6 };
+static unsigned char * triangleTable_128[1] = { triangleTable_128_0 };
+
+static unsigned char triangleTable_129_0[8] =  { 6, 0, 8, 3, 0, 6, 11, 7 };
+static unsigned char triangleTable_129_1[14] =  { 12, 0, 6, 11, 0, 0, 8, 7, 7, 6, 0, 3, 0, 11 };
+static unsigned char * triangleTable_129[2] = { triangleTable_129_0, triangleTable_129_1 };
+
+static unsigned char triangleTable_130_0[8] =  { 6, 0, 11, 7, 6, 1, 9, 0 };
+static unsigned char triangleTable_130_1[20] =  { 18, 0, 0, 11, 7, 0, 7, 9, 11, 1, 6, 9, 7, 6, 9, 6, 1, 1, 11, 0 };
+static unsigned char * triangleTable_130[2] = { triangleTable_130_0, triangleTable_130_1 };
+
+static unsigned char triangleTable_131_0[11] =  { 9, 0, 6, 11, 7, 1, 8, 3, 1, 9, 8 };
+static unsigned char triangleTable_131_1[17] =  { 15, 0, 1, 6, 11, 7, 9, 8, 1, 9, 6, 3, 1, 11, 6, 9, 7 };
+static unsigned char * triangleTable_131[2] = { triangleTable_131_0, triangleTable_131_1 };
+
+static unsigned char triangleTable_132_0[8] =  { 6, 0, 2, 10, 1, 7, 6, 11 };
+static unsigned char triangleTable_132_1[14] =  { 12, 0, 7, 6, 1, 1, 2, 11, 11, 7, 1, 10, 1, 6 };
+static unsigned char * triangleTable_132[2] = { triangleTable_132_0, triangleTable_132_1 };
+
+static unsigned char triangleTable_133_0[11] =  { 9, 0, 3, 0, 8, 11, 7, 6, 10, 1, 2 };
+static unsigned char triangleTable_133_1[17] =  { 15, 0, 3, 0, 8, 6, 10, 1, 7, 6, 1, 2, 7, 1, 7, 2, 11 };
+static unsigned char triangleTable_133_2[17] =  { 15, 0, 2, 10, 1, 8, 7, 6, 0, 8, 6, 11, 0, 6, 0, 11, 3 };
+static unsigned char triangleTable_133_3[17] =  { 15, 0, 11, 7, 6, 1, 0, 8, 10, 1, 8, 3, 10, 8, 10, 3, 2 };
+static unsigned char triangleTable_133_4[29] =  { 27, 1, 12, 3, 0, 12, 11, 3, 7, 12, 8, 11, 12, 2, 12, 10, 1, 12, 7, 6, 2, 12, 1, 8, 12, 0, 6, 10, 12 };
+static unsigned char triangleTable_133_5[29] =  { 27, 1, 12, 11, 7, 12, 2, 11, 10, 12, 6, 2, 12, 3, 12, 0, 8, 12, 10, 1, 3, 12, 8, 6, 12, 7, 1, 0, 12 };
+static unsigned char triangleTable_133_6[29] =  { 27, 1, 12, 2, 10, 12, 3, 2, 0, 12, 1, 3, 12, 11, 12, 7, 6, 12, 0, 8, 11, 12, 6, 1, 12, 10, 8, 7, 12 };
+static unsigned char triangleTable_133_7[17] =  { 15, 0, 7, 6, 10, 1, 0, 10, 11, 3, 2, 0, 7, 10, 7, 0, 8 };
+static unsigned char * triangleTable_133[8] = 
+{ 
+  triangleTable_133_0, triangleTable_133_1, triangleTable_133_2, triangleTable_133_3, triangleTable_133_4, triangleTable_133_5, triangleTable_133_6, triangleTable_133_7
+};
+
+static unsigned char triangleTable_134_0[11] =  { 9, 0, 7, 6, 11, 9, 2, 10, 9, 0, 2 };
+static unsigned char triangleTable_134_1[17] =  { 15, 0, 9, 7, 6, 11, 0, 2, 9, 0, 7, 10, 9, 6, 7, 0, 11 };
+static unsigned char * triangleTable_134[2] = { triangleTable_134_0, triangleTable_134_1 };
+
+static unsigned char triangleTable_135_0[14] =  { 12, 0, 3, 2, 10, 8, 3, 10, 6, 11, 7, 8, 10, 9 };
+static unsigned char triangleTable_135_1[14] =  { 12, 0, 7, 6, 10, 8, 7, 10, 2, 11, 3, 8, 10, 9 };
+static unsigned char triangleTable_135_2[26] =  { 24, 1, 12, 6, 11, 9, 12, 10, 8, 7, 12, 2, 12, 3, 3, 12, 11, 12, 9, 8, 12, 2, 10, 6, 12, 7 };
+static unsigned char triangleTable_135_3[26] =  { 24, 1, 12, 2, 11, 9, 12, 10, 8, 3, 12, 6, 12, 7, 7, 12, 11, 12, 9, 8, 12, 6, 10, 2, 12, 3 };
+static unsigned char * triangleTable_135[4] = { triangleTable_135_0, triangleTable_135_1, triangleTable_135_2, triangleTable_135_3 };
+
+static unsigned char triangleTable_136_0[8] =  { 6, 0, 3, 7, 2, 6, 2, 7 };
+static unsigned char * triangleTable_136[1] = { triangleTable_136_0 };
+
+static unsigned char triangleTable_137_0[11] =  { 9, 0, 0, 8, 7, 6, 0, 7, 6, 2, 0 };
+static unsigned char * triangleTable_137[1] = { triangleTable_137_0 };
+
+static unsigned char triangleTable_138_0[11] =  { 9, 0, 9, 0, 1, 7, 2, 3, 7, 6, 2 };
+static unsigned char triangleTable_138_1[17] =  { 15, 0, 7, 9, 0, 1, 6, 2, 7, 6, 9, 3, 7, 0, 9, 6, 1 };
+static unsigned char * triangleTable_138[2] = { triangleTable_138_0, triangleTable_138_1 };
+
+static unsigned char triangleTable_139_0[14] =  { 12, 0, 7, 6, 8, 1, 9, 8, 6, 2, 1, 6, 1, 8 };
+static unsigned char * triangleTable_139[1] = { triangleTable_139_0 };
+
+static unsigned char triangleTable_140_0[11] =  { 9, 0, 7, 6, 10, 1, 7, 10, 1, 3, 7 };
+static unsigned char * triangleTable_140[1] = { triangleTable_140_0 };
+
+static unsigned char triangleTable_141_0[14] =  { 12, 0, 1, 7, 10, 1, 8, 7, 0, 8, 1, 10, 7, 6 };
+static unsigned char * triangleTable_141[1] = { triangleTable_141_0 };
+
+static unsigned char triangleTable_142_0[14] =  { 12, 0, 3, 7, 0, 7, 6, 10, 7, 10, 9, 0, 7, 9 };
+static unsigned char * triangleTable_142[1] = { triangleTable_142_0 };
+
+static unsigned char triangleTable_143_0[11] =  { 9, 0, 7, 6, 10, 7, 10, 8, 10, 9, 8 };
+static unsigned char * triangleTable_143[1] = { triangleTable_143_0 };
+
+static unsigned char triangleTable_144_0[8] =  { 6, 0, 4, 6, 8, 11, 8, 6 };
+static unsigned char * triangleTable_144[1] = { triangleTable_144_0 };
+
+static unsigned char triangleTable_145_0[11] =  { 9, 0, 6, 11, 3, 0, 6, 3, 0, 4, 6 };
+static unsigned char * triangleTable_145[1] = { triangleTable_145_0 };
+
+static unsigned char triangleTable_146_0[11] =  { 9, 0, 1, 9, 0, 6, 8, 4, 6, 11, 8 };
+static unsigned char triangleTable_146_1[17] =  { 15, 0, 6, 1, 9, 0, 11, 8, 6, 11, 1, 4, 6, 9, 1, 11, 0 };
+static unsigned char * triangleTable_146[2] = { triangleTable_146_0, triangleTable_146_1 };
+
+static unsigned char triangleTable_147_0[14] =  { 12, 0, 4, 6, 9, 6, 11, 3, 6, 3, 1, 9, 6, 1 };
+static unsigned char * triangleTable_147[1] = { triangleTable_147_0 };
+
+static unsigned char triangleTable_148_0[11] =  { 9, 0, 1, 2, 10, 8, 6, 11, 8, 4, 6 };
+static unsigned char triangleTable_148_1[17] =  { 15, 0, 8, 1, 2, 10, 4, 6, 8, 4, 1, 11, 8, 2, 1, 4, 10 };
+static unsigned char * triangleTable_148[2] = { triangleTable_148_0, triangleTable_148_1 };
+
+static unsigned char triangleTable_149_0[14] =  { 12, 0, 11, 3, 0, 6, 11, 0, 1, 2, 10, 6, 0, 4 };
+static unsigned char triangleTable_149_1[14] =  { 12, 0, 10, 1, 0, 6, 10, 0, 3, 2, 11, 6, 0, 4 };
+static unsigned char triangleTable_149_2[26] =  { 24, 1, 12, 1, 2, 4, 12, 0, 6, 10, 12, 3, 12, 11, 11, 12, 2, 12, 4, 6, 12, 3, 0, 1, 12, 10 };
+static unsigned char triangleTable_149_3[26] =  { 24, 1, 12, 3, 2, 4, 12, 0, 6, 11, 12, 1, 12, 10, 10, 12, 2, 12, 4, 6, 12, 1, 0, 3, 12, 11 };
+static unsigned char * triangleTable_149[4] = { triangleTable_149_0, triangleTable_149_1, triangleTable_149_2, triangleTable_149_3 };
+
+static unsigned char triangleTable_150_0[14] =  { 12, 0, 8, 4, 11, 0, 2, 9, 2, 10, 9, 4, 6, 11 };
+static unsigned char triangleTable_150_1[14] =  { 12, 0, 6, 9, 4, 0, 11, 8, 0, 2, 11, 6, 10, 9 };
+static unsigned char triangleTable_150_2[26] =  { 24, 0, 8, 0, 11, 10, 4, 6, 10, 6, 11, 2, 11, 0, 4, 10, 9, 0, 4, 9, 10, 11, 2, 4, 0, 8 };
+static unsigned char triangleTable_150_3[26] =  { 24, 1, 12, 0, 2, 4, 12, 8, 12, 4, 6, 12, 9, 0, 2, 11, 12, 8, 12, 11, 6, 10, 12, 12, 10, 9 };
+static unsigned char triangleTable_150_4[26] =  { 24, 1, 12, 11, 8, 10, 12, 2, 12, 10, 9, 12, 6, 11, 8, 0, 12, 2, 12, 0, 9, 4, 12, 12, 4, 6 };
+static unsigned char * triangleTable_150[5] = { triangleTable_150_0, triangleTable_150_1, triangleTable_150_2, triangleTable_150_3, triangleTable_150_4 };
+
+static unsigned char triangleTable_151_0[11] =  { 9, 0, 2, 11, 3, 6, 10, 4, 10, 9, 4 };
+static unsigned char triangleTable_151_1[17] =  { 15, 0, 11, 3, 4, 10, 9, 2, 3, 9, 4, 11, 4, 6, 2, 9, 3 };
+static unsigned char * triangleTable_151[2] = { triangleTable_151_0, triangleTable_151_1 };
+
+static unsigned char triangleTable_152_0[11] =  { 9, 0, 2, 3, 8, 4, 2, 8, 4, 6, 2 };
+static unsigned char * triangleTable_152[1] = { triangleTable_152_0 };
+
+static unsigned char triangleTable_153_0[8] =  { 6, 0, 2, 0, 4, 4, 6, 2 };
+static unsigned char * triangleTable_153[1] = { triangleTable_153_0 };
+
+static unsigned char triangleTable_154_0[14] =  { 12, 0, 3, 8, 4, 2, 3, 4, 9, 0, 1, 2, 4, 6 };
+static unsigned char triangleTable_154_1[14] =  { 12, 0, 1, 9, 4, 2, 1, 4, 8, 0, 3, 2, 4, 6 };
+static unsigned char triangleTable_154_2[26] =  { 24, 1, 12, 9, 0, 6, 12, 4, 2, 1, 12, 8, 12, 3, 3, 12, 0, 12, 6, 2, 12, 8, 4, 9, 12, 1 };
+static unsigned char triangleTable_154_3[26] =  { 24, 1, 12, 8, 0, 6, 12, 4, 2, 3, 12, 9, 12, 1, 1, 12, 0, 12, 6, 2, 12, 9, 4, 8, 12, 3 };
+static unsigned char * triangleTable_154[4] = { triangleTable_154_0, triangleTable_154_1, triangleTable_154_2, triangleTable_154_3 };
+
+static unsigned char triangleTable_155_0[11] =  { 9, 0, 1, 9, 4, 1, 4, 2, 4, 6, 2 };
+static unsigned char * triangleTable_155[1] = { triangleTable_155_0 };
+
+static unsigned char triangleTable_156_0[14] =  { 12, 0, 10, 1, 6, 8, 4, 6, 1, 3, 8, 1, 8, 6 };
+static unsigned char * triangleTable_156[1] = { triangleTable_156_0 };
+
+static unsigned char triangleTable_157_0[11] =  { 9, 0, 10, 1, 0, 10, 0, 6, 0, 4, 6 };
+static unsigned char * triangleTable_157[1] = { triangleTable_157_0 };
+
+static unsigned char triangleTable_158_0[11] =  { 9, 0, 8, 0, 3, 9, 4, 10, 4, 6, 10 };
+static unsigned char triangleTable_158_1[17] =  { 15, 0, 0, 3, 10, 4, 6, 8, 3, 6, 10, 0, 10, 9, 8, 6, 3 };
+static unsigned char * triangleTable_158[2] = { triangleTable_158_0, triangleTable_158_1 };
+
+static unsigned char triangleTable_159_0[8] =  { 6, 0, 10, 4, 6, 4, 10, 9 };
+static unsigned char * triangleTable_159[1] = { triangleTable_159_0 };
+
+static unsigned char triangleTable_160_0[8] =  { 6, 0, 5, 4, 9, 11, 7, 6 };
+static unsigned char triangleTable_160_1[14] =  { 12, 0, 11, 7, 9, 9, 5, 6, 6, 11, 9, 4, 9, 7 };
+static unsigned char * triangleTable_160[2] = { triangleTable_160_0, triangleTable_160_1 };
+
+static unsigned char triangleTable_161_0[11] =  { 9, 0, 7, 6, 11, 8, 3, 0, 9, 5, 4 };
+static unsigned char triangleTable_161_1[17] =  { 15, 0, 7, 6, 11, 0, 9, 5, 3, 0, 5, 4, 3, 5, 3, 4, 8 };
+static unsigned char triangleTable_161_2[17] =  { 15, 0, 4, 9, 5, 11, 3, 0, 6, 11, 0, 8, 6, 0, 6, 8, 7 };
+static unsigned char triangleTable_161_3[17] =  { 15, 0, 8, 3, 0, 5, 6, 11, 9, 5, 11, 7, 9, 11, 9, 7, 4 };
+static unsigned char triangleTable_161_4[29] =  { 27, 1, 12, 7, 6, 12, 8, 7, 3, 12, 11, 8, 12, 4, 12, 9, 5, 12, 3, 0, 4, 12, 5, 11, 12, 6, 0, 9, 12 };
+static unsigned char triangleTable_161_5[29] =  { 27, 1, 12, 8, 3, 12, 4, 8, 9, 12, 0, 4, 12, 7, 12, 6, 11, 12, 9, 5, 7, 12, 11, 0, 12, 3, 5, 6, 12 };
+static unsigned char triangleTable_161_6[29] =  { 27, 1, 12, 4, 9, 12, 7, 4, 6, 12, 5, 7, 12, 8, 12, 3, 0, 12, 6, 11, 8, 12, 0, 5, 12, 9, 11, 3, 12 };
+static unsigned char triangleTable_161_7[17] =  { 15, 0, 3, 0, 9, 5, 6, 9, 8, 7, 4, 6, 3, 9, 3, 6, 11 };
+static unsigned char * triangleTable_161[8] = 
+{ 
+  triangleTable_161_0, triangleTable_161_1, triangleTable_161_2, triangleTable_161_3, triangleTable_161_4, triangleTable_161_5, triangleTable_161_6, triangleTable_161_7
+};
+
+static unsigned char triangleTable_162_0[11] =  { 9, 0, 11, 7, 6, 0, 5, 4, 0, 1, 5 };
+static unsigned char triangleTable_162_1[17] =  { 15, 0, 0, 11, 7, 6, 1, 5, 0, 1, 11, 4, 0, 7, 11, 1, 6 };
+static unsigned char * triangleTable_162[2] = { triangleTable_162_0, triangleTable_162_1 };
+
+static unsigned char triangleTable_163_0[14] =  { 12, 0, 4, 8, 3, 5, 4, 3, 11, 7, 6, 5, 3, 1 };
+static unsigned char triangleTable_163_1[14] =  { 12, 0, 6, 11, 3, 5, 6, 3, 8, 7, 4, 5, 3, 1 };
+static unsigned char triangleTable_163_2[26] =  { 24, 1, 12, 11, 7, 1, 12, 3, 5, 6, 12, 8, 12, 4, 4, 12, 7, 12, 1, 5, 12, 8, 3, 11, 12, 6 };
+static unsigned char triangleTable_163_3[26] =  { 24, 1, 12, 8, 7, 1, 12, 3, 5, 4, 12, 11, 12, 6, 6, 12, 7, 12, 1, 5, 12, 11, 3, 8, 12, 4 };
+static unsigned char * triangleTable_163[4] = { triangleTable_163_0, triangleTable_163_1, triangleTable_163_2, triangleTable_163_3 };
+
+static unsigned char triangleTable_164_0[11] =  { 9, 0, 5, 4, 9, 10, 1, 2, 11, 7, 6 };
+static unsigned char triangleTable_164_1[17] =  { 15, 0, 5, 4, 9, 2, 11, 7, 1, 2, 7, 6, 1, 7, 1, 6, 10 };
+static unsigned char triangleTable_164_2[17] =  { 15, 0, 6, 11, 7, 9, 1, 2, 4, 9, 2, 10, 4, 2, 4, 10, 5 };
+static unsigned char triangleTable_164_3[17] =  { 15, 0, 10, 1, 2, 7, 4, 9, 11, 7, 9, 5, 11, 9, 11, 5, 6 };
+static unsigned char triangleTable_164_4[29] =  { 27, 1, 12, 5, 4, 12, 10, 5, 1, 12, 9, 10, 12, 6, 12, 11, 7, 12, 1, 2, 6, 12, 7, 9, 12, 4, 2, 11, 12 };
+static unsigned char triangleTable_164_5[29] =  { 27, 1, 12, 10, 1, 12, 6, 10, 11, 12, 2, 6, 12, 5, 12, 4, 9, 12, 11, 7, 5, 12, 9, 2, 12, 1, 7, 4, 12 };
+static unsigned char triangleTable_164_6[29] =  { 27, 1, 12, 6, 11, 12, 5, 6, 4, 12, 7, 5, 12, 10, 12, 1, 2, 12, 4, 9, 10, 12, 2, 7, 12, 11, 9, 1, 12 };
+static unsigned char triangleTable_164_7[17] =  { 15, 0, 1, 2, 11, 7, 4, 11, 10, 5, 6, 4, 1, 11, 1, 4, 9 };
+static unsigned char * triangleTable_164[8] = 
+{ 
+  triangleTable_164_0, triangleTable_164_1, triangleTable_164_2, triangleTable_164_3, triangleTable_164_4, triangleTable_164_5, triangleTable_164_6, triangleTable_164_7
+};
+
+static unsigned char triangleTable_165_0[14] =  { 12, 0, 10, 1, 2, 7, 6, 11, 0, 8, 3, 5, 4, 9 };
+static unsigned char triangleTable_165_1[20] =  { 18, 0, 1, 2, 10, 11, 7, 6, 5, 4, 3, 0, 9, 5, 4, 8, 3, 5, 3, 0 };
+static unsigned char triangleTable_165_2[20] =  { 18, 0, 6, 11, 7, 3, 0, 8, 4, 9, 2, 10, 5, 4, 9, 1, 2, 4, 2, 10 };
+static unsigned char triangleTable_165_3[20] =  { 18, 0, 3, 0, 8, 9, 5, 4, 7, 6, 1, 2, 11, 7, 6, 10, 1, 7, 1, 2 };
+static unsigned char triangleTable_165_4[20] =  { 18, 0, 4, 9, 5, 1, 2, 10, 6, 11, 0, 8, 7, 6, 11, 3, 0, 6, 0, 8 };
+static unsigned char triangleTable_165_5[20] =  { 18, 0, 11, 7, 6, 4, 9, 5, 10, 1, 8, 3, 2, 10, 1, 0, 8, 10, 8, 3 };
+static unsigned char triangleTable_165_6[20] =  { 18, 0, 8, 3, 0, 2, 10, 1, 9, 5, 11, 7, 4, 9, 5, 6, 11, 9, 11, 7 };
+static unsigned char triangleTable_165_7[32] =  { 30, 1, 4, 12, 5, 12, 2, 10, 1, 12, 9, 12, 1, 2, 12, 8, 3, 5, 12, 10, 9, 12, 0, 6, 11, 7, 3, 0, 12, 8, 12, 4 };
+static unsigned char triangleTable_165_8[32] =  { 30, 1, 0, 12, 3, 12, 6, 11, 7, 12, 8, 12, 7, 6, 12, 9, 5, 3, 12, 11, 8, 12, 4, 2, 10, 1, 5, 4, 12, 9, 12, 0 };
+static unsigned char triangleTable_165_9[32] =  { 30, 1, 3, 12, 8, 2, 10, 12, 0, 12, 1, 10, 1, 12, 5, 4, 12, 2, 12, 3, 9, 12, 0, 7, 6, 11, 12, 9, 5, 8, 12, 4 };
+static unsigned char triangleTable_165_10[32] =  { 30, 1, 5, 12, 9, 6, 11, 12, 4, 12, 7, 11, 7, 12, 3, 0, 12, 6, 12, 5, 8, 12, 4, 1, 2, 10, 12, 8, 3, 9, 12, 0 };
+static unsigned char triangleTable_165_11[32] =  { 30, 1, 2, 12, 1, 11, 7, 12, 10, 12, 6, 7, 6, 12, 4, 9, 12, 11, 12, 2, 5, 12, 10, 0, 8, 3, 12, 5, 4, 1, 12, 9 };
+static unsigned char triangleTable_165_12[32] =  { 30, 1, 10, 12, 2, 12, 8, 3, 0, 12, 1, 12, 0, 8, 12, 5, 4, 2, 12, 3, 1, 12, 9, 11, 7, 6, 4, 9, 12, 5, 12, 10 };
+static unsigned char triangleTable_165_13[32] =  { 30, 1, 9, 12, 4, 1, 2, 12, 5, 12, 10, 2, 10, 12, 11, 7, 12, 1, 12, 9, 6, 12, 5, 8, 3, 0, 12, 6, 11, 4, 12, 7 };
+static unsigned char triangleTable_165_14[32] =  { 30, 1, 6, 12, 7, 12, 0, 8, 3, 12, 11, 12, 3, 0, 12, 10, 1, 7, 12, 8, 11, 12, 2, 4, 9, 5, 1, 2, 12, 10, 12, 6 };
+static unsigned char triangleTable_165_15[32] =  { 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+static unsigned char triangleTable_165_16[32] =  { 30, 1, 7, 12, 11, 12, 1, 2, 10, 12, 6, 12, 10, 1, 12, 4, 9, 11, 12, 2, 6, 12, 5, 3, 0, 8, 9, 5, 12, 4, 12, 7 };
+static unsigned char triangleTable_165_17[32] =  { 30, 1, 8, 12, 0, 7, 6, 12, 3, 12, 11, 6, 11, 12, 10, 1, 12, 7, 12, 8, 2, 12, 3, 9, 5, 4, 12, 2, 10, 0, 12, 1 };
+static unsigned char triangleTable_165_18[32] =  { 30, 1, 11, 12, 6, 12, 9, 5, 4, 12, 7, 12, 4, 9, 12, 3, 0, 6, 12, 5, 7, 12, 8, 10, 1, 2, 0, 8, 12, 3, 12, 11 };
+static unsigned char triangleTable_165_19[38] =  { 36, 1, 12, 4, 8, 10, 5, 12, 0, 12, 3, 4, 12, 7, 2, 12, 1, 12, 0, 9, 1, 12, 9, 12, 11, 7, 8, 3, 12, 6, 12, 5, 2, 10, 12, 6, 11, 12 };
+static unsigned char triangleTable_165_20[38] =  { 36, 1, 12, 0, 9, 11, 3, 12, 4, 12, 5, 0, 12, 1, 6, 12, 7, 12, 4, 8, 7, 12, 8, 12, 10, 1, 9, 5, 12, 2, 12, 3, 6, 11, 12, 2, 10, 12 };
+static unsigned char triangleTable_165_21[38] =  { 36, 1, 12, 10, 5, 3, 2, 12, 9, 12, 4, 10, 12, 6, 8, 12, 0, 12, 9, 1, 0, 12, 1, 12, 7, 6, 5, 4, 12, 11, 12, 2, 8, 3, 12, 11, 7, 12 };
+static unsigned char triangleTable_165_22[38] =  { 36, 1, 12, 6, 10, 8, 7, 12, 2, 12, 1, 6, 12, 5, 0, 12, 3, 12, 2, 11, 3, 12, 11, 12, 9, 5, 10, 1, 12, 4, 12, 7, 0, 8, 12, 4, 9, 12 };
+static unsigned char triangleTable_165_23[32] =  { 30, 1, 0, 12, 1, 12, 6, 10, 5, 12, 9, 12, 5, 6, 12, 8, 7, 1, 12, 10, 9, 12, 4, 2, 11, 3, 7, 4, 12, 8, 12, 0 };
+static unsigned char triangleTable_165_24[32] =  { 30, 1, 4, 12, 7, 12, 2, 11, 3, 12, 8, 12, 3, 2, 12, 9, 1, 7, 12, 11, 8, 12, 0, 6, 10, 5, 1, 0, 12, 9, 12, 4 };
+static unsigned char triangleTable_165_25[32] =  { 30, 1, 1, 12, 9, 2, 11, 12, 0, 12, 3, 11, 3, 12, 7, 4, 12, 2, 12, 1, 8, 12, 0, 5, 6, 10, 12, 8, 7, 9, 12, 4 };
+static unsigned char triangleTable_165_26[32] =  { 30, 1, 7, 12, 8, 6, 10, 12, 4, 12, 5, 10, 5, 12, 1, 0, 12, 6, 12, 7, 9, 12, 4, 3, 2, 11, 12, 9, 1, 8, 12, 0 };
+static unsigned char triangleTable_165_27[32] =  { 30, 1, 6, 12, 5, 11, 3, 12, 10, 12, 2, 3, 2, 12, 0, 9, 12, 11, 12, 6, 1, 12, 10, 4, 8, 7, 12, 1, 0, 5, 12, 9 };
+static unsigned char triangleTable_165_28[32] =  { 30, 1, 9, 12, 0, 12, 11, 3, 2, 12, 1, 12, 2, 11, 12, 5, 6, 0, 12, 3, 1, 12, 10, 8, 7, 4, 6, 10, 12, 5, 12, 9 };
+static unsigned char triangleTable_165_29[32] =  { 30, 1, 10, 12, 6, 1, 0, 12, 5, 12, 9, 0, 9, 12, 8, 7, 12, 1, 12, 10, 4, 12, 5, 11, 3, 2, 12, 4, 8, 6, 12, 7 };
+static unsigned char triangleTable_165_30[32] =  { 30, 1, 2, 12, 3, 12, 4, 8, 7, 12, 11, 12, 7, 4, 12, 10, 5, 3, 12, 8, 11, 12, 6, 0, 9, 1, 5, 6, 12, 10, 12, 2 };
+static unsigned char triangleTable_165_31[32] =  { 30, 1, 1, 12, 10, 0, 8, 12, 2, 12, 3, 8, 3, 12, 7, 6, 12, 0, 12, 1, 11, 12, 2, 5, 4, 9, 12, 11, 7, 10, 12, 6 };
+static unsigned char triangleTable_165_32[32] =  { 30, 1, 5, 12, 10, 12, 3, 2, 11, 12, 6, 12, 11, 3, 12, 4, 8, 10, 12, 2, 6, 12, 7, 1, 0, 9, 8, 7, 12, 4, 12, 5 };
+static unsigned char triangleTable_165_33[32] =  { 30, 1, 11, 12, 2, 7, 4, 12, 3, 12, 8, 4, 8, 12, 9, 1, 12, 7, 12, 11, 0, 12, 3, 10, 5, 6, 12, 0, 9, 2, 12, 1 };
+static unsigned char triangleTable_165_34[32] =  { 30, 1, 8, 12, 4, 12, 10, 5, 6, 12, 7, 12, 6, 10, 12, 3, 2, 4, 12, 5, 7, 12, 11, 9, 1, 0, 2, 11, 12, 3, 12, 8 };
+static unsigned char triangleTable_165_35[20] =  { 18, 0, 6, 10, 5, 11, 3, 2, 1, 8, 7, 7, 4, 9, 1, 0, 8, 9, 1, 7 };
+static unsigned char triangleTable_165_36[20] =  { 18, 0, 8, 7, 4, 3, 2, 11, 6, 1, 0, 0, 9, 5, 6, 10, 1, 5, 6, 0 };
+static unsigned char triangleTable_165_37[20] =  { 18, 0, 4, 8, 7, 9, 1, 0, 3, 10, 5, 5, 6, 11, 3, 2, 10, 11, 3, 5 };
+static unsigned char triangleTable_165_38[20] =  { 18, 0, 10, 5, 6, 1, 0, 9, 4, 3, 2, 2, 11, 7, 4, 8, 3, 7, 4, 2 };
+static unsigned char triangleTable_165_39[20] =  { 18, 0, 5, 6, 10, 4, 8, 7, 11, 0, 9, 9, 1, 2, 11, 3, 0, 2, 11, 9 };
+static unsigned char triangleTable_165_40[20] =  { 18, 0, 1, 0, 9, 2, 11, 3, 8, 6, 10, 10, 5, 4, 8, 7, 6, 4, 8, 10 };
+static unsigned char triangleTable_165_41[14] =  { 12, 0, 3, 2, 11, 8, 7, 4, 0, 9, 1, 10, 5, 6 };
+static unsigned char triangleTable_165_42[20] =  { 18, 0, 2, 10, 4, 4, 8, 2, 1, 0, 9, 2, 8, 3, 6, 11, 7, 4, 10, 5 };
+static unsigned char triangleTable_165_43[20] =  { 18, 0, 6, 11, 0, 0, 9, 6, 7, 4, 8, 6, 9, 5, 2, 10, 1, 0, 11, 3 };
+static unsigned char triangleTable_165_44[20] =  { 18, 0, 4, 9, 2, 2, 11, 4, 5, 6, 10, 4, 11, 7, 0, 8, 3, 2, 9, 1 };
+static unsigned char triangleTable_165_45[20] =  { 18, 0, 0, 8, 6, 6, 10, 0, 3, 2, 11, 0, 10, 1, 4, 9, 5, 6, 8, 7 };
+static unsigned char * triangleTable_165[46] = 
+{ 
+  triangleTable_165_0, triangleTable_165_1, triangleTable_165_2, triangleTable_165_3, triangleTable_165_4, triangleTable_165_5, triangleTable_165_6, triangleTable_165_7, 
+  triangleTable_165_8, triangleTable_165_9, triangleTable_165_10, triangleTable_165_11, triangleTable_165_12, triangleTable_165_13, triangleTable_165_14, triangleTable_165_15, 
+  triangleTable_165_16, triangleTable_165_17, triangleTable_165_18, triangleTable_165_19, triangleTable_165_20, triangleTable_165_21, triangleTable_165_22, triangleTable_165_23, 
+  triangleTable_165_24, triangleTable_165_25, triangleTable_165_26, triangleTable_165_27, triangleTable_165_28, triangleTable_165_29, triangleTable_165_30, triangleTable_165_31, 
+  triangleTable_165_32, triangleTable_165_33, triangleTable_165_34, triangleTable_165_35, triangleTable_165_36, triangleTable_165_37, triangleTable_165_38, triangleTable_165_39, 
+  triangleTable_165_40, triangleTable_165_41, triangleTable_165_42, triangleTable_165_43, triangleTable_165_44, triangleTable_165_45
+};
+
+static unsigned char triangleTable_166_0[14] =  { 12, 0, 10, 5, 4, 2, 10, 4, 7, 6, 11, 2, 4, 0 };
+static unsigned char triangleTable_166_1[14] =  { 12, 0, 11, 7, 4, 2, 11, 4, 5, 6, 10, 2, 4, 0 };
+static unsigned char triangleTable_166_2[26] =  { 24, 1, 12, 7, 6, 0, 12, 4, 2, 11, 12, 5, 12, 10, 10, 12, 6, 12, 0, 2, 12, 5, 4, 7, 12, 11 };
+static unsigned char triangleTable_166_3[26] =  { 24, 1, 12, 5, 6, 0, 12, 4, 2, 10, 12, 7, 12, 11, 11, 12, 6, 12, 0, 2, 12, 7, 4, 5, 12, 10 };
+static unsigned char * triangleTable_166[4] = { triangleTable_166_0, triangleTable_166_1, triangleTable_166_2, triangleTable_166_3 };
+
+static unsigned char triangleTable_167_0[11] =  { 9, 0, 3, 2, 11, 4, 8, 7, 6, 10, 5 };
+static unsigned char triangleTable_167_1[17] =  { 15, 0, 3, 2, 11, 10, 5, 4, 10, 4, 8, 10, 8, 6, 7, 6, 8 };
+static unsigned char triangleTable_167_2[17] =  { 15, 0, 10, 5, 6, 4, 8, 3, 4, 3, 2, 4, 2, 7, 11, 7, 2 };
+static unsigned char triangleTable_167_3[17] =  { 15, 0, 4, 8, 7, 3, 2, 10, 3, 10, 5, 3, 5, 11, 6, 11, 5 };
+static unsigned char triangleTable_167_4[29] =  { 27, 1, 2, 11, 12, 11, 7, 12, 3, 12, 8, 6, 12, 7, 10, 5, 12, 4, 8, 12, 10, 12, 6, 2, 12, 3, 12, 5, 4 };
+static unsigned char triangleTable_167_5[29] =  { 27, 1, 8, 7, 12, 7, 6, 12, 4, 12, 5, 11, 12, 6, 3, 2, 12, 10, 5, 12, 3, 12, 11, 8, 12, 4, 12, 2, 10 };
+static unsigned char triangleTable_167_6[29] =  { 27, 1, 5, 6, 12, 6, 11, 12, 10, 12, 2, 7, 12, 11, 4, 8, 12, 3, 2, 12, 4, 12, 7, 5, 12, 10, 12, 8, 3 };
+static unsigned char triangleTable_167_7[17] =  { 15, 0, 5, 4, 8, 5, 2, 10, 6, 11, 7, 5, 8, 2, 3, 2, 8 };
+static unsigned char * triangleTable_167[8] = 
+{ 
+  triangleTable_167_0, triangleTable_167_1, triangleTable_167_2, triangleTable_167_3, triangleTable_167_4, triangleTable_167_5, triangleTable_167_6, triangleTable_167_7
+};
+
+static unsigned char triangleTable_168_0[11] =  { 9, 0, 9, 5, 4, 2, 7, 6, 2, 3, 7 };
+static unsigned char triangleTable_168_1[17] =  { 15, 0, 2, 9, 5, 4, 3, 7, 2, 3, 9, 6, 2, 5, 9, 3, 4 };
+static unsigned char * triangleTable_168[2] = { triangleTable_168_0, triangleTable_168_1 };
+
+static unsigned char triangleTable_169_0[14] =  { 12, 0, 8, 7, 6, 0, 8, 6, 5, 4, 9, 0, 6, 2 };
+static unsigned char triangleTable_169_1[14] =  { 12, 0, 9, 5, 6, 0, 9, 6, 7, 4, 8, 0, 6, 2 };
+static unsigned char triangleTable_169_2[26] =  { 24, 1, 12, 5, 4, 2, 12, 6, 0, 9, 12, 7, 12, 8, 8, 12, 4, 12, 2, 0, 12, 7, 6, 5, 12, 9 };
+static unsigned char triangleTable_169_3[26] =  { 24, 1, 12, 7, 4, 2, 12, 6, 0, 8, 12, 5, 12, 9, 9, 12, 4, 12, 2, 0, 12, 5, 6, 7, 12, 8 };
+static unsigned char * triangleTable_169[4] = { triangleTable_169_0, triangleTable_169_1, triangleTable_169_2, triangleTable_169_3 };
+
+static unsigned char triangleTable_170_0[14] =  { 12, 0, 2, 3, 6, 1, 5, 0, 5, 4, 0, 3, 7, 6 };
+static unsigned char triangleTable_170_1[14] =  { 12, 0, 7, 0, 3, 1, 6, 2, 1, 5, 6, 7, 4, 0 };
+static unsigned char triangleTable_170_2[26] =  { 24, 0, 2, 1, 6, 4, 3, 7, 4, 7, 6, 5, 6, 1, 3, 4, 0, 1, 3, 0, 4, 6, 5, 3, 1, 2 };
+static unsigned char triangleTable_170_3[26] =  { 24, 1, 12, 1, 5, 3, 12, 2, 12, 3, 7, 12, 0, 1, 5, 6, 12, 2, 12, 6, 7, 4, 12, 12, 4, 0 };
+static unsigned char triangleTable_170_4[26] =  { 24, 1, 12, 6, 2, 4, 12, 5, 12, 4, 0, 12, 7, 6, 2, 1, 12, 5, 12, 1, 0, 3, 12, 12, 3, 7 };
+static unsigned char * triangleTable_170[5] = { triangleTable_170_0, triangleTable_170_1, triangleTable_170_2, triangleTable_170_3, triangleTable_170_4 };
+
+static unsigned char triangleTable_171_0[11] =  { 9, 0, 7, 4, 8, 5, 6, 1, 6, 2, 1 };
+static unsigned char triangleTable_171_1[17] =  { 15, 0, 4, 8, 1, 6, 2, 7, 8, 2, 1, 4, 1, 5, 7, 2, 8 };
+static unsigned char * triangleTable_171[2] = { triangleTable_171_0, triangleTable_171_1 };
+
+static unsigned char triangleTable_172_0[14] =  { 12, 0, 6, 10, 1, 7, 6, 1, 9, 5, 4, 7, 1, 3 };
+static unsigned char triangleTable_172_1[14] =  { 12, 0, 4, 9, 1, 7, 4, 1, 10, 5, 6, 7, 1, 3 };
+static unsigned char triangleTable_172_2[26] =  { 24, 1, 12, 9, 5, 3, 12, 1, 7, 4, 12, 10, 12, 6, 6, 12, 5, 12, 3, 7, 12, 10, 1, 9, 12, 4 };
+static unsigned char triangleTable_172_3[26] =  { 24, 1, 12, 10, 5, 3, 12, 1, 7, 6, 12, 9, 12, 4, 4, 12, 5, 12, 3, 7, 12, 9, 1, 10, 12, 6 };
+static unsigned char * triangleTable_172[4] = { triangleTable_172_0, triangleTable_172_1, triangleTable_172_2, triangleTable_172_3 };
+
+static unsigned char triangleTable_173_0[11] =  { 9, 0, 6, 10, 5, 8, 7, 4, 9, 1, 0 };
+static unsigned char triangleTable_173_1[17] =  { 15, 0, 6, 10, 5, 1, 0, 8, 1, 8, 7, 1, 7, 9, 4, 9, 7 };
+static unsigned char triangleTable_173_2[17] =  { 15, 0, 1, 0, 9, 8, 7, 6, 8, 6, 10, 8, 10, 4, 5, 4, 10 };
+static unsigned char triangleTable_173_3[17] =  { 15, 0, 8, 7, 4, 6, 10, 1, 6, 1, 0, 6, 0, 5, 9, 5, 0 };
+static unsigned char triangleTable_173_4[29] =  { 27, 1, 10, 5, 12, 5, 4, 12, 6, 12, 7, 9, 12, 4, 1, 0, 12, 8, 7, 12, 1, 12, 9, 10, 12, 6, 12, 0, 8 };
+static unsigned char triangleTable_173_5[29] =  { 27, 1, 7, 4, 12, 4, 9, 12, 8, 12, 0, 5, 12, 9, 6, 10, 12, 1, 0, 12, 6, 12, 5, 7, 12, 8, 12, 10, 1 };
+static unsigned char triangleTable_173_6[29] =  { 27, 1, 0, 9, 12, 9, 5, 12, 1, 12, 10, 4, 12, 5, 8, 7, 12, 6, 10, 12, 8, 12, 4, 0, 12, 1, 12, 7, 6 };
+static unsigned char triangleTable_173_7[17] =  { 15, 0, 0, 8, 7, 0, 10, 1, 9, 5, 4, 0, 7, 10, 6, 10, 7 };
+static unsigned char * triangleTable_173[8] = 
+{ 
+  triangleTable_173_0, triangleTable_173_1, triangleTable_173_2, triangleTable_173_3, triangleTable_173_4, triangleTable_173_5, triangleTable_173_6, triangleTable_173_7
+};
+
+static unsigned char triangleTable_174_0[11] =  { 9, 0, 5, 6, 10, 7, 4, 3, 4, 0, 3 };
+static unsigned char triangleTable_174_1[17] =  { 15, 0, 6, 10, 3, 4, 0, 5, 10, 0, 3, 6, 3, 7, 5, 0, 10 };
+static unsigned char * triangleTable_174[2] = { triangleTable_174_0, triangleTable_174_1 };
+
+static unsigned char triangleTable_175_0[8] =  { 6, 0, 10, 5, 6, 7, 4, 8 };
+static unsigned char triangleTable_175_1[14] =  { 12, 0, 10, 4, 8, 7, 6, 10, 10, 8, 7, 4, 10, 5 };
+static unsigned char * triangleTable_175[2] = { triangleTable_175_0, triangleTable_175_1 };
+
+static unsigned char triangleTable_176_0[11] =  { 9, 0, 9, 5, 6, 11, 9, 6, 11, 8, 9 };
+static unsigned char * triangleTable_176[1] = { triangleTable_176_0 };
+
+static unsigned char triangleTable_177_0[14] =  { 12, 0, 5, 3, 9, 5, 11, 3, 6, 11, 5, 9, 3, 0 };
+static unsigned char * triangleTable_177[1] = { triangleTable_177_0 };
+
+static unsigned char triangleTable_178_0[14] =  { 12, 0, 6, 11, 5, 0, 1, 5, 11, 8, 0, 11, 0, 5 };
+static unsigned char * triangleTable_178[1] = { triangleTable_178_0 };
+
+static unsigned char triangleTable_179_0[11] =  { 9, 0, 6, 11, 3, 6, 3, 5, 3, 1, 5 };
+static unsigned char * triangleTable_179[1] = { triangleTable_179_0 };
+
+static unsigned char triangleTable_180_0[14] =  { 12, 0, 5, 6, 11, 9, 5, 11, 2, 10, 1, 9, 11, 8 };
+static unsigned char triangleTable_180_1[14] =  { 12, 0, 1, 2, 11, 9, 1, 11, 6, 10, 5, 9, 11, 8 };
+static unsigned char triangleTable_180_2[26] =  { 24, 1, 12, 2, 10, 8, 12, 11, 9, 1, 12, 6, 12, 5, 5, 12, 10, 12, 8, 9, 12, 6, 11, 2, 12, 1 };
+static unsigned char triangleTable_180_3[26] =  { 24, 1, 12, 6, 10, 8, 12, 11, 9, 5, 12, 2, 12, 1, 1, 12, 10, 12, 8, 9, 12, 2, 11, 6, 12, 5 };
+static unsigned char * triangleTable_180[4] = { triangleTable_180_0, triangleTable_180_1, triangleTable_180_2, triangleTable_180_3 };
+
+static unsigned char triangleTable_181_0[11] =  { 9, 0, 5, 6, 10, 0, 9, 1, 2, 11, 3 };
+static unsigned char triangleTable_181_1[17] =  { 15, 0, 5, 6, 10, 11, 3, 0, 11, 0, 9, 11, 9, 2, 1, 2, 9 };
+static unsigned char triangleTable_181_2[17] =  { 15, 0, 11, 3, 2, 0, 9, 5, 0, 5, 6, 0, 6, 1, 10, 1, 6 };
+static unsigned char triangleTable_181_3[17] =  { 15, 0, 0, 9, 1, 5, 6, 11, 5, 11, 3, 5, 3, 10, 2, 10, 3 };
+static unsigned char triangleTable_181_4[29] =  { 27, 1, 6, 10, 12, 10, 1, 12, 5, 12, 9, 2, 12, 1, 11, 3, 12, 0, 9, 12, 11, 12, 2, 6, 12, 5, 12, 3, 0 };
+static unsigned char triangleTable_181_5[29] =  { 27, 1, 9, 1, 12, 1, 2, 12, 0, 12, 3, 10, 12, 2, 5, 6, 12, 11, 3, 12, 5, 12, 10, 9, 12, 0, 12, 6, 11 };
+static unsigned char triangleTable_181_6[29] =  { 27, 1, 3, 2, 12, 2, 10, 12, 11, 12, 6, 1, 12, 10, 0, 9, 12, 5, 6, 12, 0, 12, 1, 3, 12, 11, 12, 9, 5 };
+static unsigned char triangleTable_181_7[17] =  { 15, 0, 3, 0, 9, 3, 6, 11, 2, 10, 1, 3, 9, 6, 5, 6, 9 };
+static unsigned char * triangleTable_181[8] = 
+{ 
+  triangleTable_181_0, triangleTable_181_1, triangleTable_181_2, triangleTable_181_3, triangleTable_181_4, triangleTable_181_5, triangleTable_181_6, triangleTable_181_7
+};
+
+static unsigned char triangleTable_182_0[11] =  { 9, 0, 6, 10, 5, 2, 11, 0, 11, 8, 0 };
+static unsigned char triangleTable_182_1[17] =  { 15, 0, 10, 5, 0, 11, 8, 6, 5, 8, 0, 10, 0, 2, 6, 8, 5 };
+static unsigned char * triangleTable_182[2] = { triangleTable_182_0, triangleTable_182_1 };
+
+static unsigned char triangleTable_183_0[8] =  { 6, 0, 5, 6, 10, 2, 11, 3 };
+static unsigned char triangleTable_183_1[14] =  { 12, 0, 5, 11, 3, 2, 10, 5, 5, 3, 2, 11, 5, 6 };
+static unsigned char * triangleTable_183[2] = { triangleTable_183_0, triangleTable_183_1 };
+
+static unsigned char triangleTable_184_0[14] =  { 12, 0, 6, 2, 5, 2, 3, 8, 2, 8, 9, 5, 2, 9 };
+static unsigned char * triangleTable_184[1] = { triangleTable_184_0 };
+
+static unsigned char triangleTable_185_0[11] =  { 9, 0, 9, 5, 6, 9, 6, 0, 6, 2, 0 };
+static unsigned char * triangleTable_185[1] = { triangleTable_185_0 };
+
+static unsigned char triangleTable_186_0[11] =  { 9, 0, 0, 3, 8, 2, 1, 6, 1, 5, 6 };
+static unsigned char triangleTable_186_1[17] =  { 15, 0, 3, 8, 6, 1, 5, 0, 8, 5, 6, 3, 6, 2, 0, 5, 8 };
+static unsigned char * triangleTable_186[2] = { triangleTable_186_0, triangleTable_186_1 };
+
+static unsigned char triangleTable_187_0[8] =  { 6, 0, 6, 1, 5, 1, 6, 2 };
+static unsigned char * triangleTable_187[1] = { triangleTable_187_0 };
+
+static unsigned char triangleTable_188_0[11] =  { 9, 0, 10, 5, 6, 9, 1, 8, 1, 3, 8 };
+static unsigned char triangleTable_188_1[17] =  { 15, 0, 5, 6, 8, 1, 3, 10, 6, 3, 8, 5, 8, 9, 10, 3, 6 };
+static unsigned char * triangleTable_188[2] = { triangleTable_188_0, triangleTable_188_1 };
+
+static unsigned char triangleTable_189_0[8] =  { 6, 0, 6, 10, 5, 9, 1, 0 };
+static unsigned char triangleTable_189_1[14] =  { 12, 0, 6, 1, 0, 9, 5, 6, 6, 0, 9, 1, 6, 10 };
+static unsigned char * triangleTable_189[2] = { triangleTable_189_0, triangleTable_189_1 };
+
+static unsigned char triangleTable_190_0[8] =  { 6, 0, 10, 5, 6, 8, 0, 3 };
+static unsigned char triangleTable_190_1[20] =  { 18, 0, 5, 6, 8, 0, 5, 8, 10, 3, 6, 10, 5, 0, 3, 10, 0, 8, 6, 3 };
+static unsigned char * triangleTable_190[2] = { triangleTable_190_0, triangleTable_190_1 };
+
+static unsigned char triangleTable_191_0[5] =  { 3, 0, 10, 5, 6 };
+static unsigned char * triangleTable_191[1] = { triangleTable_191_0 };
+
+static unsigned char triangleTable_192_0[8] =  { 6, 0, 7, 5, 11, 10, 11, 5 };
+static unsigned char * triangleTable_192[1] = { triangleTable_192_0 };
+
+static unsigned char triangleTable_193_0[11] =  { 9, 0, 0, 8, 3, 5, 11, 7, 5, 10, 11 };
+static unsigned char triangleTable_193_1[17] =  { 15, 0, 5, 0, 8, 3, 10, 11, 5, 10, 0, 7, 5, 8, 0, 10, 3 };
+static unsigned char * triangleTable_193[2] = { triangleTable_193_0, triangleTable_193_1 };
+
+static unsigned char triangleTable_194_0[11] =  { 9, 0, 0, 1, 9, 11, 5, 10, 11, 7, 5 };
+static unsigned char triangleTable_194_1[17] =  { 15, 0, 11, 0, 1, 9, 7, 5, 11, 7, 0, 10, 11, 1, 0, 7, 9 };
+static unsigned char * triangleTable_194[2] = { triangleTable_194_0, triangleTable_194_1 };
+
+static unsigned char triangleTable_195_0[14] =  { 12, 0, 11, 7, 10, 3, 1, 8, 1, 9, 8, 7, 5, 10 };
+static unsigned char triangleTable_195_1[14] =  { 12, 0, 5, 8, 7, 3, 10, 11, 3, 1, 10, 5, 9, 8 };
+static unsigned char triangleTable_195_2[26] =  { 24, 0, 11, 3, 10, 9, 7, 5, 9, 5, 10, 1, 10, 3, 7, 9, 8, 3, 7, 8, 9, 10, 1, 7, 3, 11 };
+static unsigned char triangleTable_195_3[26] =  { 24, 1, 12, 3, 1, 7, 12, 11, 12, 7, 5, 12, 8, 3, 1, 10, 12, 11, 12, 10, 5, 9, 12, 12, 9, 8 };
+static unsigned char triangleTable_195_4[26] =  { 24, 1, 12, 10, 11, 9, 12, 1, 12, 9, 8, 12, 5, 10, 11, 3, 12, 1, 12, 3, 8, 7, 12, 12, 7, 5 };
+static unsigned char * triangleTable_195[5] = { triangleTable_195_0, triangleTable_195_1, triangleTable_195_2, triangleTable_195_3, triangleTable_195_4 };
+
+static unsigned char triangleTable_196_0[11] =  { 9, 0, 1, 2, 11, 7, 1, 11, 7, 5, 1 };
+static unsigned char * triangleTable_196[1] = { triangleTable_196_0 };
+
+static unsigned char triangleTable_197_0[14] =  { 12, 0, 2, 11, 7, 1, 2, 7, 8, 3, 0, 1, 7, 5 };
+static unsigned char triangleTable_197_1[14] =  { 12, 0, 0, 8, 7, 1, 0, 7, 11, 3, 2, 1, 7, 5 };
+static unsigned char triangleTable_197_2[26] =  { 24, 1, 12, 8, 3, 5, 12, 7, 1, 0, 12, 11, 12, 2, 2, 12, 3, 12, 5, 1, 12, 11, 7, 8, 12, 0 };
+static unsigned char triangleTable_197_3[26] =  { 24, 1, 12, 11, 3, 5, 12, 7, 1, 2, 12, 8, 12, 0, 0, 12, 3, 12, 5, 1, 12, 8, 7, 11, 12, 2 };
+static unsigned char * triangleTable_197[4] = { triangleTable_197_0, triangleTable_197_1, triangleTable_197_2, triangleTable_197_3 };
+
+static unsigned char triangleTable_198_0[14] =  { 12, 0, 11, 7, 2, 9, 0, 2, 7, 5, 9, 7, 9, 2 };
+static unsigned char * triangleTable_198[1] = { triangleTable_198_0 };
+
+static unsigned char triangleTable_199_0[11] =  { 9, 0, 11, 3, 2, 8, 7, 9, 7, 5, 9 };
+static unsigned char triangleTable_199_1[17] =  { 15, 0, 3, 2, 9, 7, 5, 11, 2, 5, 9, 3, 9, 8, 11, 5, 2 };
+static unsigned char * triangleTable_199[2] = { triangleTable_199_0, triangleTable_199_1 };
+
+static unsigned char triangleTable_200_0[11] =  { 9, 0, 5, 10, 2, 3, 5, 2, 3, 7, 5 };
+static unsigned char * triangleTable_200[1] = { triangleTable_200_0 };
+
+static unsigned char triangleTable_201_0[14] =  { 12, 0, 2, 0, 10, 0, 8, 7, 0, 7, 5, 10, 0, 5 };
+static unsigned char * triangleTable_201[1] = { triangleTable_201_0 };
+
+static unsigned char triangleTable_202_0[14] =  { 12, 0, 10, 2, 3, 5, 10, 3, 0, 1, 9, 5, 3, 7 };
+static unsigned char triangleTable_202_1[14] =  { 12, 0, 9, 0, 3, 5, 9, 3, 2, 1, 10, 5, 3, 7 };
+static unsigned char triangleTable_202_2[26] =  { 24, 1, 12, 0, 1, 7, 12, 3, 5, 9, 12, 2, 12, 10, 10, 12, 1, 12, 7, 5, 12, 2, 3, 0, 12, 9 };
+static unsigned char triangleTable_202_3[26] =  { 24, 1, 12, 2, 1, 7, 12, 3, 5, 10, 12, 0, 12, 9, 9, 12, 1, 12, 7, 5, 12, 0, 3, 2, 12, 10 };
+static unsigned char * triangleTable_202[4] = { triangleTable_202_0, triangleTable_202_1, triangleTable_202_2, triangleTable_202_3 };
+
+static unsigned char triangleTable_203_0[11] =  { 9, 0, 1, 10, 2, 5, 9, 7, 9, 8, 7 };
+static unsigned char triangleTable_203_1[17] =  { 15, 0, 10, 2, 7, 9, 8, 1, 2, 8, 7, 10, 7, 5, 1, 8, 2 };
+static unsigned char * triangleTable_203[2] = { triangleTable_203_0, triangleTable_203_1 };
+
+static unsigned char triangleTable_204_0[8] =  { 6, 0, 5, 1, 3, 3, 7, 5 };
+static unsigned char * triangleTable_204[1] = { triangleTable_204_0 };
+
+static unsigned char triangleTable_205_0[11] =  { 9, 0, 0, 8, 7, 0, 7, 1, 7, 5, 1 };
+static unsigned char * triangleTable_205[1] = { triangleTable_205_0 };
+
+static unsigned char triangleTable_206_0[11] =  { 9, 0, 9, 0, 3, 9, 3, 5, 3, 7, 5 };
+static unsigned char * triangleTable_206[1] = { triangleTable_206_0 };
+
+static unsigned char triangleTable_207_0[8] =  { 6, 0, 7, 9, 8, 9, 7, 5 };
+static unsigned char * triangleTable_207[1] = { triangleTable_207_0 };
+
+static unsigned char triangleTable_208_0[11] =  { 9, 0, 8, 4, 5, 10, 8, 5, 10, 11, 8 };
+static unsigned char * triangleTable_208[1] = { triangleTable_208_0 };
+
+static unsigned char triangleTable_209_0[14] =  { 12, 0, 3, 0, 11, 5, 10, 11, 0, 4, 5, 0, 5, 11 };
+static unsigned char * triangleTable_209[1] = { triangleTable_209_0 };
+
+static unsigned char triangleTable_210_0[14] =  { 12, 0, 4, 5, 10, 8, 4, 10, 1, 9, 0, 8, 10, 11 };
+static unsigned char triangleTable_210_1[14] =  { 12, 0, 0, 1, 10, 8, 0, 10, 5, 9, 4, 8, 10, 11 };
+static unsigned char triangleTable_210_2[26] =  { 24, 1, 12, 1, 9, 11, 12, 10, 8, 0, 12, 5, 12, 4, 4, 12, 9, 12, 11, 8, 12, 5, 10, 1, 12, 0 };
+static unsigned char triangleTable_210_3[26] =  { 24, 1, 12, 5, 9, 11, 12, 10, 8, 4, 12, 1, 12, 0, 0, 12, 9, 12, 11, 8, 12, 1, 10, 5, 12, 4 };
+static unsigned char * triangleTable_210[4] = { triangleTable_210_0, triangleTable_210_1, triangleTable_210_2, triangleTable_210_3 };
+
+static unsigned char triangleTable_211_0[11] =  { 9, 0, 5, 9, 4, 1, 10, 3, 10, 11, 3 };
+static unsigned char triangleTable_211_1[17] =  { 15, 0, 9, 4, 3, 10, 11, 5, 4, 11, 3, 9, 3, 1, 5, 11, 4 };
+static unsigned char * triangleTable_211[2] = { triangleTable_211_0, triangleTable_211_1 };
+
+static unsigned char triangleTable_212_0[14] =  { 12, 0, 5, 1, 4, 1, 2, 11, 1, 11, 8, 4, 1, 8 };
+static unsigned char * triangleTable_212[1] = { triangleTable_212_0 };
+
+static unsigned char triangleTable_213_0[11] =  { 9, 0, 3, 2, 11, 1, 0, 5, 0, 4, 5 };
+static unsigned char triangleTable_213_1[17] =  { 15, 0, 2, 11, 5, 0, 4, 3, 11, 4, 5, 2, 5, 1, 3, 4, 11 };
+static unsigned char * triangleTable_213[2] = { triangleTable_213_0, triangleTable_213_1 };
+
+static unsigned char triangleTable_214_0[11] =  { 9, 0, 9, 4, 5, 8, 0, 11, 0, 2, 11 };
+static unsigned char triangleTable_214_1[17] =  { 15, 0, 4, 5, 11, 0, 2, 9, 5, 2, 11, 4, 11, 8, 9, 2, 5 };
+static unsigned char * triangleTable_214[2] = { triangleTable_214_0, triangleTable_214_1 };
+
+static unsigned char triangleTable_215_0[8] =  { 6, 0, 5, 9, 4, 3, 2, 11 };
+static unsigned char triangleTable_215_1[20] =  { 18, 0, 9, 4, 3, 2, 9, 3, 5, 11, 4, 5, 9, 2, 11, 5, 2, 3, 4, 11 };
+static unsigned char * triangleTable_215[2] = { triangleTable_215_0, triangleTable_215_1 };
+
+static unsigned char triangleTable_216_0[14] =  { 12, 0, 10, 8, 5, 10, 3, 8, 2, 3, 10, 5, 8, 4 };
+static unsigned char * triangleTable_216[1] = { triangleTable_216_0 };
+
+static unsigned char triangleTable_217_0[11] =  { 9, 0, 5, 10, 2, 5, 2, 4, 2, 0, 4 };
+static unsigned char * triangleTable_217[1] = { triangleTable_217_0 };
+
+static unsigned char triangleTable_218_0[11] =  { 9, 0, 10, 2, 1, 4, 5, 9, 0, 3, 8 };
+static unsigned char triangleTable_218_1[17] =  { 15, 0, 10, 2, 1, 3, 8, 4, 3, 4, 5, 3, 5, 0, 9, 0, 5 };
+static unsigned char triangleTable_218_2[17] =  { 15, 0, 3, 8, 0, 4, 5, 10, 4, 10, 2, 4, 2, 9, 1, 9, 2 };
+static unsigned char triangleTable_218_3[17] =  { 15, 0, 4, 5, 9, 10, 2, 3, 10, 3, 8, 10, 8, 1, 0, 1, 8 };
+static unsigned char triangleTable_218_4[29] =  { 27, 1, 2, 1, 12, 1, 9, 12, 10, 12, 5, 0, 12, 9, 3, 8, 12, 4, 5, 12, 3, 12, 0, 2, 12, 10, 12, 8, 4 };
+static unsigned char triangleTable_218_5[29] =  { 27, 1, 5, 9, 12, 9, 0, 12, 4, 12, 8, 1, 12, 0, 10, 2, 12, 3, 8, 12, 10, 12, 1, 5, 12, 4, 12, 2, 3 };
+static unsigned char triangleTable_218_6[29] =  { 27, 1, 8, 0, 12, 0, 1, 12, 3, 12, 2, 9, 12, 1, 4, 5, 12, 10, 2, 12, 4, 12, 9, 8, 12, 3, 12, 5, 10 };
+static unsigned char triangleTable_218_7[17] =  { 15, 0, 8, 4, 5, 8, 2, 3, 0, 1, 9, 8, 5, 2, 10, 2, 5 };
+static unsigned char * triangleTable_218[8] = 
+{ 
+  triangleTable_218_0, triangleTable_218_1, triangleTable_218_2, triangleTable_218_3, triangleTable_218_4, triangleTable_218_5, triangleTable_218_6, triangleTable_218_7
+};
+
+static unsigned char triangleTable_219_0[8] =  { 6, 0, 2, 1, 10, 5, 9, 4 };
+static unsigned char triangleTable_219_1[14] =  { 12, 0, 2, 9, 4, 5, 10, 2, 2, 4, 5, 9, 2, 1 };
+static unsigned char * triangleTable_219[2] = { triangleTable_219_0, triangleTable_219_1 };
+
+static unsigned char triangleTable_220_0[11] =  { 9, 0, 8, 4, 5, 8, 5, 3, 5, 1, 3 };
+static unsigned char * triangleTable_220[1] = { triangleTable_220_0 };
+
+static unsigned char triangleTable_221_0[8] =  { 6, 0, 0, 5, 1, 5, 0, 4 };
+static unsigned char * triangleTable_221[1] = { triangleTable_221_0 };
+
+static unsigned char triangleTable_222_0[8] =  { 6, 0, 5, 9, 4, 8, 0, 3 };
+static unsigned char triangleTable_222_1[14] =  { 12, 0, 5, 0, 3, 8, 4, 5, 5, 3, 8, 0, 5, 9 };
+static unsigned char * triangleTable_222[2] = { triangleTable_222_0, triangleTable_222_1 };
+
+static unsigned char triangleTable_223_0[5] =  { 3, 0, 4, 5, 9 };
+static unsigned char * triangleTable_223[1] = { triangleTable_223_0 };
+
+static unsigned char triangleTable_224_0[11] =  { 9, 0, 11, 7, 4, 9, 11, 4, 9, 10, 11 };
+static unsigned char * triangleTable_224[1] = { triangleTable_224_0 };
+
+static unsigned char triangleTable_225_0[14] =  { 12, 0, 7, 4, 9, 11, 7, 9, 0, 8, 3, 11, 9, 10 };
+static unsigned char triangleTable_225_1[14] =  { 12, 0, 3, 0, 9, 11, 3, 9, 4, 8, 7, 11, 9, 10 };
+static unsigned char triangleTable_225_2[26] =  { 24, 1, 12, 0, 8, 10, 12, 9, 11, 3, 12, 4, 12, 7, 7, 12, 8, 12, 10, 11, 12, 4, 9, 0, 12, 3 };
+static unsigned char triangleTable_225_3[26] =  { 24, 1, 12, 4, 8, 10, 12, 9, 11, 7, 12, 0, 12, 3, 3, 12, 8, 12, 10, 11, 12, 0, 9, 4, 12, 7 };
+static unsigned char * triangleTable_225[4] = { triangleTable_225_0, triangleTable_225_1, triangleTable_225_2, triangleTable_225_3 };
+
+static unsigned char triangleTable_226_0[14] =  { 12, 0, 10, 11, 1, 11, 7, 4, 11, 4, 0, 1, 11, 0 };
+static unsigned char * triangleTable_226[1] = { triangleTable_226_0 };
+
+static unsigned char triangleTable_227_0[11] =  { 9, 0, 8, 7, 4, 11, 3, 10, 3, 1, 10 };
+static unsigned char triangleTable_227_1[17] =  { 15, 0, 7, 4, 10, 3, 1, 8, 4, 1, 10, 7, 10, 11, 8, 1, 4 };
+static unsigned char * triangleTable_227[2] = { triangleTable_227_0, triangleTable_227_1 };
+
+static unsigned char triangleTable_228_0[14] =  { 12, 0, 7, 1, 11, 7, 9, 1, 4, 9, 7, 11, 1, 2 };
+static unsigned char * triangleTable_228[1] = { triangleTable_228_0 };
+
+static unsigned char triangleTable_229_0[11] =  { 9, 0, 2, 11, 3, 9, 1, 0, 8, 7, 4 };
+static unsigned char triangleTable_229_1[17] =  { 15, 0, 2, 11, 3, 7, 4, 9, 7, 9, 1, 7, 1, 8, 0, 8, 1 };
+static unsigned char triangleTable_229_2[17] =  { 15, 0, 7, 4, 8, 9, 1, 2, 9, 2, 11, 9, 11, 0, 3, 0, 11 };
+static unsigned char triangleTable_229_3[17] =  { 15, 0, 9, 1, 0, 2, 11, 7, 2, 7, 4, 2, 4, 3, 8, 3, 4 };
+static unsigned char triangleTable_229_4[29] =  { 27, 1, 11, 3, 12, 3, 0, 12, 2, 12, 1, 8, 12, 0, 7, 4, 12, 9, 1, 12, 7, 12, 8, 11, 12, 2, 12, 4, 9 };
+static unsigned char triangleTable_229_5[29] =  { 27, 1, 1, 0, 12, 0, 8, 12, 9, 12, 4, 3, 12, 8, 2, 11, 12, 7, 4, 12, 2, 12, 3, 1, 12, 9, 12, 11, 7 };
+static unsigned char triangleTable_229_6[29] =  { 27, 1, 4, 8, 12, 8, 3, 12, 7, 12, 11, 0, 12, 3, 9, 1, 12, 2, 11, 12, 9, 12, 0, 4, 12, 7, 12, 1, 2 };
+static unsigned char triangleTable_229_7[17] =  { 15, 0, 4, 9, 1, 4, 11, 7, 8, 3, 0, 4, 1, 11, 2, 11, 1 };
+static unsigned char * triangleTable_229[8] = 
+{ 
+  triangleTable_229_0, triangleTable_229_1, triangleTable_229_2, triangleTable_229_3, triangleTable_229_4, triangleTable_229_5, triangleTable_229_6, triangleTable_229_7
+};
+
+static unsigned char triangleTable_230_0[11] =  { 9, 0, 11, 7, 4, 11, 4, 2, 4, 0, 2 };
+static unsigned char * triangleTable_230[1] = { triangleTable_230_0 };
+
+static unsigned char triangleTable_231_0[8] =  { 6, 0, 2, 11, 3, 8, 7, 4 };
+static unsigned char triangleTable_231_1[14] =  { 12, 0, 2, 7, 4, 8, 3, 2, 2, 4, 8, 7, 2, 11 };
+static unsigned char * triangleTable_231[2] = { triangleTable_231_0, triangleTable_231_1 };
+
+static unsigned char triangleTable_232_0[14] =  { 12, 0, 2, 3, 10, 4, 9, 10, 3, 7, 4, 3, 4, 10 };
+static unsigned char * triangleTable_232[1] = { triangleTable_232_0 };
+
+static unsigned char triangleTable_233_0[11] =  { 9, 0, 4, 8, 7, 0, 9, 2, 9, 10, 2 };
+static unsigned char triangleTable_233_1[17] =  { 15, 0, 8, 7, 2, 9, 10, 4, 7, 10, 2, 8, 2, 0, 4, 10, 7 };
+static unsigned char * triangleTable_233[2] = { triangleTable_233_0, triangleTable_233_1 };
+
+static unsigned char triangleTable_234_0[11] =  { 9, 0, 2, 1, 10, 0, 3, 4, 3, 7, 4 };
+static unsigned char triangleTable_234_1[17] =  { 15, 0, 1, 10, 4, 3, 7, 2, 10, 7, 4, 1, 4, 0, 2, 7, 10 };
+static unsigned char * triangleTable_234[2] = { triangleTable_234_0, triangleTable_234_1 };
+
+static unsigned char triangleTable_235_0[8] =  { 6, 0, 1, 10, 2, 7, 4, 8 };
+static unsigned char triangleTable_235_1[20] =  { 18, 0, 10, 2, 7, 4, 10, 7, 1, 8, 2, 1, 10, 4, 8, 1, 4, 7, 2, 8 };
+static unsigned char * triangleTable_235[2] = { triangleTable_235_0, triangleTable_235_1 };
+
+static unsigned char triangleTable_236_0[11] =  { 9, 0, 4, 9, 1, 4, 1, 7, 1, 3, 7 };
+static unsigned char * triangleTable_236[1] = { triangleTable_236_0 };
+
+static unsigned char triangleTable_237_0[8] =  { 6, 0, 1, 0, 9, 4, 8, 7 };
+static unsigned char triangleTable_237_1[14] =  { 12, 0, 1, 8, 7, 4, 9, 1, 1, 7, 4, 8, 1, 0 };
+static unsigned char * triangleTable_237[2] = { triangleTable_237_0, triangleTable_237_1 };
+
+static unsigned char triangleTable_238_0[8] =  { 6, 0, 4, 3, 7, 3, 4, 0 };
+static unsigned char * triangleTable_238[1] = { triangleTable_238_0 };
+
+static unsigned char triangleTable_239_0[5] =  { 3, 0, 4, 8, 7 };
+static unsigned char * triangleTable_239[1] = { triangleTable_239_0 };
+
+static unsigned char triangleTable_240_0[8] =  { 6, 0, 9, 10, 11, 11, 8, 9 };
+static unsigned char * triangleTable_240[1] = { triangleTable_240_0 };
+
+static unsigned char triangleTable_241_0[11] =  { 9, 0, 3, 0, 9, 3, 9, 11, 9, 10, 11 };
+static unsigned char * triangleTable_241[1] = { triangleTable_241_0 };
+
+static unsigned char triangleTable_242_0[11] =  { 9, 0, 0, 1, 10, 0, 10, 8, 10, 11, 8 };
+static unsigned char * triangleTable_242[1] = { triangleTable_242_0 };
+
+static unsigned char triangleTable_243_0[8] =  { 6, 0, 3, 10, 11, 10, 3, 1 };
+static unsigned char * triangleTable_243[1] = { triangleTable_243_0 };
+
+static unsigned char triangleTable_244_0[11] =  { 9, 0, 1, 2, 11, 1, 11, 9, 11, 8, 9 };
+static unsigned char * triangleTable_244[1] = { triangleTable_244_0 };
+
+static unsigned char triangleTable_245_0[8] =  { 6, 0, 11, 3, 2, 1, 0, 9 };
+static unsigned char triangleTable_245_1[14] =  { 12, 0, 11, 0, 9, 1, 2, 11, 11, 9, 1, 0, 11, 3 };
+static unsigned char * triangleTable_245[2] = { triangleTable_245_0, triangleTable_245_1 };
+
+static unsigned char triangleTable_246_0[8] =  { 6, 0, 11, 0, 2, 0, 11, 8 };
+static unsigned char * triangleTable_246[1] = { triangleTable_246_0 };
+
+static unsigned char triangleTable_247_0[5] =  { 3, 0, 2, 11, 3 };
+static unsigned char * triangleTable_247[1] = { triangleTable_247_0 };
+
+static unsigned char triangleTable_248_0[11] =  { 9, 0, 2, 3, 8, 2, 8, 10, 8, 9, 10 };
+static unsigned char * triangleTable_248[1] = { triangleTable_248_0 };
+
+static unsigned char triangleTable_249_0[8] =  { 6, 0, 9, 2, 0, 2, 9, 10 };
+static unsigned char * triangleTable_249[1] = { triangleTable_249_0 };
+
+static unsigned char triangleTable_250_0[8] =  { 6, 0, 10, 2, 1, 0, 3, 8 };
+static unsigned char triangleTable_250_1[14] =  { 12, 0, 10, 3, 8, 0, 1, 10, 10, 8, 0, 3, 10, 2 };
+static unsigned char * triangleTable_250[2] = { triangleTable_250_0, triangleTable_250_1 };
+
+static unsigned char triangleTable_251_0[5] =  { 3, 0, 2, 1, 10 };
+static unsigned char * triangleTable_251[1] = { triangleTable_251_0 };
+
+static unsigned char triangleTable_252_0[8] =  { 6, 0, 8, 1, 3, 1, 8, 9 };
+static unsigned char * triangleTable_252[1] = { triangleTable_252_0 };
+
+static unsigned char triangleTable_253_0[5] =  { 3, 0, 9, 1, 0 };
+static unsigned char * triangleTable_253[1] = { triangleTable_253_0 };
+
+static unsigned char triangleTable_254_0[5] =  { 3, 0, 0, 3, 8 };
+static unsigned char * triangleTable_254[1] = { triangleTable_254_0 };
+
+static unsigned char ** triangleTable[256] = 
+{ 
+  NULL, triangleTable_1, triangleTable_2, triangleTable_3, triangleTable_4, triangleTable_5, triangleTable_6, triangleTable_7, 
+  triangleTable_8, triangleTable_9, triangleTable_10, triangleTable_11, triangleTable_12, triangleTable_13, triangleTable_14, triangleTable_15, 
+  triangleTable_16, triangleTable_17, triangleTable_18, triangleTable_19, triangleTable_20, triangleTable_21, triangleTable_22, triangleTable_23, 
+  triangleTable_24, triangleTable_25, triangleTable_26, triangleTable_27, triangleTable_28, triangleTable_29, triangleTable_30, triangleTable_31, 
+  triangleTable_32, triangleTable_33, triangleTable_34, triangleTable_35, triangleTable_36, triangleTable_37, triangleTable_38, triangleTable_39, 
+  triangleTable_40, triangleTable_41, triangleTable_42, triangleTable_43, triangleTable_44, triangleTable_45, triangleTable_46, triangleTable_47, 
+  triangleTable_48, triangleTable_49, triangleTable_50, triangleTable_51, triangleTable_52, triangleTable_53, triangleTable_54, triangleTable_55, 
+  triangleTable_56, triangleTable_57, triangleTable_58, triangleTable_59, triangleTable_60, triangleTable_61, triangleTable_62, triangleTable_63, 
+  triangleTable_64, triangleTable_65, triangleTable_66, triangleTable_67, triangleTable_68, triangleTable_69, triangleTable_70, triangleTable_71, 
+  triangleTable_72, triangleTable_73, triangleTable_74, triangleTable_75, triangleTable_76, triangleTable_77, triangleTable_78, triangleTable_79, 
+  triangleTable_80, triangleTable_81, triangleTable_82, triangleTable_83, triangleTable_84, triangleTable_85, triangleTable_86, triangleTable_87, 
+  triangleTable_88, triangleTable_89, triangleTable_90, triangleTable_91, triangleTable_92, triangleTable_93, triangleTable_94, triangleTable_95, 
+  triangleTable_96, triangleTable_97, triangleTable_98, triangleTable_99, triangleTable_100, triangleTable_101, triangleTable_102, triangleTable_103, 
+  triangleTable_104, triangleTable_105, triangleTable_106, triangleTable_107, triangleTable_108, triangleTable_109, triangleTable_110, triangleTable_111, 
+  triangleTable_112, triangleTable_113, triangleTable_114, triangleTable_115, triangleTable_116, triangleTable_117, triangleTable_118, triangleTable_119, 
+  triangleTable_120, triangleTable_121, triangleTable_122, triangleTable_123, triangleTable_124, triangleTable_125, triangleTable_126, triangleTable_127, 
+  triangleTable_128, triangleTable_129, triangleTable_130, triangleTable_131, triangleTable_132, triangleTable_133, triangleTable_134, triangleTable_135, 
+  triangleTable_136, triangleTable_137, triangleTable_138, triangleTable_139, triangleTable_140, triangleTable_141, triangleTable_142, triangleTable_143, 
+  triangleTable_144, triangleTable_145, triangleTable_146, triangleTable_147, triangleTable_148, triangleTable_149, triangleTable_150, triangleTable_151, 
+  triangleTable_152, triangleTable_153, triangleTable_154, triangleTable_155, triangleTable_156, triangleTable_157, triangleTable_158, triangleTable_159, 
+  triangleTable_160, triangleTable_161, triangleTable_162, triangleTable_163, triangleTable_164, triangleTable_165, triangleTable_166, triangleTable_167, 
+  triangleTable_168, triangleTable_169, triangleTable_170, triangleTable_171, triangleTable_172, triangleTable_173, triangleTable_174, triangleTable_175, 
+  triangleTable_176, triangleTable_177, triangleTable_178, triangleTable_179, triangleTable_180, triangleTable_181, triangleTable_182, triangleTable_183, 
+  triangleTable_184, triangleTable_185, triangleTable_186, triangleTable_187, triangleTable_188, triangleTable_189, triangleTable_190, triangleTable_191, 
+  triangleTable_192, triangleTable_193, triangleTable_194, triangleTable_195, triangleTable_196, triangleTable_197, triangleTable_198, triangleTable_199, 
+  triangleTable_200, triangleTable_201, triangleTable_202, triangleTable_203, triangleTable_204, triangleTable_205, triangleTable_206, triangleTable_207, 
+  triangleTable_208, triangleTable_209, triangleTable_210, triangleTable_211, triangleTable_212, triangleTable_213, triangleTable_214, triangleTable_215, 
+  triangleTable_216, triangleTable_217, triangleTable_218, triangleTable_219, triangleTable_220, triangleTable_221, triangleTable_222, triangleTable_223, 
+  triangleTable_224, triangleTable_225, triangleTable_226, triangleTable_227, triangleTable_228, triangleTable_229, triangleTable_230, triangleTable_231, 
+  triangleTable_232, triangleTable_233, triangleTable_234, triangleTable_235, triangleTable_236, triangleTable_237, triangleTable_238, triangleTable_239, 
+  triangleTable_240, triangleTable_241, triangleTable_242, triangleTable_243, triangleTable_244, triangleTable_245, triangleTable_246, triangleTable_247, 
+  triangleTable_248, triangleTable_249, triangleTable_250, triangleTable_251, triangleTable_252, triangleTable_253, triangleTable_254, NULL
+};
+
diff --git a/libraries/include/massSpringStencilForceModel.h b/libraries/include/massSpringStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..9d41ed1fb5e17d8edd3110364d8734c8caaf67d1
--- /dev/null
+++ b/libraries/include/massSpringStencilForceModel.h
@@ -0,0 +1,55 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _MASS_SPRING_STENCIL_FORCEMODEL_H_
+#define _MASS_SPRING_STENCIL_FORCEMODEL_H_
+
+#include "massSpringSystem.h"
+#include "stencilForceModel.h"
+
+// Stencils for mass-spring systems.
+// A stencil is one spring.
+// See comments in the parent class.
+class MassSpringStencilForceModel : public StencilForceModel
+{
+public:
+  MassSpringStencilForceModel(MassSpringSystem *system);
+  virtual ~MassSpringStencilForceModel() {}
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+protected:
+  MassSpringSystem *massSpringSystem;
+};
+
+#endif
+
diff --git a/libraries/include/massSpringSystemForceModel.h b/libraries/include/massSpringSystemForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..4d5a6aa07adadc18efb3a90f070501ef667660d1
--- /dev/null
+++ b/libraries/include/massSpringSystemForceModel.h
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "forceModel" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Force model for the mass-spring system material.
+*/
+
+#ifndef _MASSSPRINGSYSTEMFORCEMODEL_H_
+#define _MASSSPRINGSYSTEMFORCEMODEL_H_
+
+#include "massSpringSystem.h"
+#include "forceModel.h"
+
+class MassSpringSystemForceModel : public ForceModel
+{
+public:
+  MassSpringSystemForceModel(MassSpringSystem * massSpringSystem);
+
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override; 
+
+protected:
+  MassSpringSystem * massSpringSystem;
+};
+
+#endif
+
diff --git a/src/libminivector/mat3d.h b/libraries/include/mat3d.h
similarity index 60%
rename from src/libminivector/mat3d.h
rename to libraries/include/mat3d.h
index a621896753621de6308af6ed3f450b5943cd4c3f..feca236c902cd90a8f2d41465c15b6c045be6630 100644
--- a/src/libminivector/mat3d.h
+++ b/libraries/include/mat3d.h
@@ -30,27 +30,18 @@
   This class implements a 3x3 matrix, with elementary algebra,
   including the computation of eigenvalues and eigenvectors of a *symmetric* 3x3 matrix.
   (using a public domain external routine; see mat3d.cpp and eig3.h)
+*/
 
-  Note: this code was inspired by Andrew Willmott's VL and SVL Libraries:
-    http://www.cs.cmu.edu/afs/cs/user/ajw/www/software/index.html#VL
-    (these two libraries contain a lot of useful functionality and
-     are highly recommended)
-    My library offers just the basic functionality, hence "minivector".
-    It was written from scratch for a course project at CMU.
 
-  Version: 1.2
-*/
 
 #ifndef _MINIMAT3D_H_
 #define _MINIMAT3D_H_
 
 #include "vec3d.h"
 
-namespace vega
-{
 class Mat3d {
 public:
-
+  
   inline Mat3d() {}
 
   /*
@@ -61,9 +52,9 @@ public:
   inline Mat3d(double x0, double x1, double x2,
                double x3, double x4, double x5,
                double x6, double x7, double x8);
-  inline Mat3d(double mat[9]); // "mat" must be given in row-major order
-  inline Mat3d(Vec3d rows[3]);
-  inline Mat3d(Vec3d row0, Vec3d row1, Vec3d row2);
+  inline Mat3d(const double mat[9]); // "mat" must be given in row-major order
+  inline Mat3d(const Vec3d rows[3]); 
+  inline Mat3d(const Vec3d & row0, const Vec3d & row1, const Vec3d & row2);
   inline Mat3d(double diag); // create a diagonal matrix with all entries "diag" (can create zero matrix by passing 0.0)
 
   inline void set(double x0, double x1, double x2,
@@ -71,19 +62,22 @@ public:
 		  double x6, double x7, double x8);
   inline void set(double value); // set all elements to value
 
-  inline Mat3d & operator=(const Mat3d & source);
+  inline Mat3d & operator=(const Mat3d & source); 
 
-  inline Mat3d operator+ (const Mat3d & );
+  inline Mat3d operator+ (const Mat3d & ) const;
   inline Mat3d & operator+= (const Mat3d & );
 
-  inline Mat3d operator- (const Mat3d & );
+  inline Mat3d operator- (const Mat3d & ) const;
   inline Mat3d & operator-= (const Mat3d & );
 
   inline Mat3d & operator*= (double scalar);
   inline Mat3d & operator/= (double scalar);
 
+  inline bool operator== (const Mat3d &) const;
+  inline bool operator!= (const Mat3d &) const;
+
   friend inline Mat3d operator* (double scalar, const Mat3d & mat2);
-  friend inline Mat3d operator/ (double scalar, const Mat3d & mat2);
+  friend inline Mat3d operator/ (const Mat3d & mat2, double scalar);
 
   inline Mat3d & multiplyDiagRight(Vec3d & v); // M = M * diag(v)
   inline Mat3d & multiplyDiagLeft(Vec3d & v); // M = diag(v) * M
@@ -94,14 +88,20 @@ public:
   friend inline double det(const Mat3d & mat); // determinant
   friend inline Mat3d trans(const Mat3d & mat); // transpose
 
+  inline double maxAbsEntry() const;
+  inline bool hasNaN() const;
   friend inline std::ostream &operator << (std::ostream &s, const Mat3d &v);
-  inline void print();
+  inline void print() const;
 
   inline Vec3d & operator[] (int index); // M[i] returns i-th row
   inline const Vec3d & operator[] (int index) const; // M[i] returns i-th row
 
-  // converts the matrix into a C array of length 9
-  inline void convertToArray(double * array) const; // in row-major order
+  // converts the matrix into a C array of length 9 in row-major order
+  inline void convertToArray(double array[9]) const;
+  inline void addToArray(double array[9]) const;
+
+  const double * data() const { return &elt[0][0]; }
+  double * data() { return &elt[0][0]; }
 
   // matrix-vector multiply
   inline const Vec3d operator* (const Vec3d & vec) const;
@@ -109,28 +109,72 @@ public:
   // matrix-matrix multiply
   inline const Mat3d operator* (const Mat3d & mat2) const;
 
-  // Computes eigenvalues and eigenvectors of a 3x3 matrix
-  // Assumes symmetric matrix; contents of matrix "a" are not modified by the routine
-  // Eigenvalues are sorted in decreasing order (not decreasing absolute order)
+  // Computes eigenvalues and eigenvectors of a 3x3 matrix M
+  // Assumes symmetric matrix; contents of matrix "M" are not modified by the routine
+  // Eigenvalues are sorted in decreasing order (not decreasing absolute-value order)
   // Returned eigenvectors are unit length
-  friend void eigen_sym(Mat3d & a, Vec3d & eig_val, Vec3d eig_vec[3]);
+  friend void eigen_sym(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3]);
 
   // NOTE: This particular routine is not publicly released, as its implementation
   // was taken from Numerical Recipes, which is not free software.
   // (you can use the eigen_sym routine above which is public domain)
   // Computes eigenvalues and eigenvectors of a 3x3 matrix, using Jacobi Iteration
-  // Assumes symmetric matrix; contents of matrix "a" are overwritten (destroyed)
+  // Assumes symmetric matrix; contents of matrix "M" are overwritten (destroyed)
   // Returns true if iteration succeeded in making the sum of abs values of non-diagonal values below epsilon, and false otherwise
   // Default epsilon is machine precision (which always converged with our matrices)
   // Eigenvalues are sorted in decreasing absolute order
   // Returned eigenvectors are unit length
-  friend bool eigen_sym_NR(Mat3d & a, Vec3d & eig_val, Vec3d eig_vec[3],
-                           int maxIterations, double epsilon);
+  friend bool eigen_sym_NR(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3], int maxIterations, double epsilon);
+
+  /*
+    Standard SVD (modifiedSVD == 0):
+
+    Given a 3x3 matrix M, decomposes it using SVD so
+    that M = U Sigma V^T where U is a 3x3 rotation, 
+    V is 3x3 orthonormal (V^T V = V V^T = I), and
+    Sigma is a diagonal matrix with non-negative entries,
+    in descending order, Sigma[0] >= Sigma[1] >= Sigma[2] >= 0.
+    Note that the matrix V may have a determinant equaling 
+    to -1 (i.e., reflection).
+
+    singularValue_eps is a threshold to determine
+    when a singular value is deemed zero, and special handling is then invoked
+    to improve numerical robustness.
+
+    Modified SVD (modifiedSVD == 1):
 
+    The SVD is modified so that it gives the 
+    following properties (useful in solid mechanics applications) :
+
+    1) Not just the determinant of U, but also the determinant of V is 1 
+      (i.e., both U and V are rotations, not reflections). 
+
+    2) The smallest diagonal value Sigma[2] may be negative. We have:
+       Sigma[0] >= Sigma[1] >= abs(Sigma[2]) >= 0 .
+  */
+  friend int SVD(Mat3d & M, Mat3d & U, Vec3d & Sigma, Mat3d & V, double singularValue_eps, int modifiedSVD);
+
+  const static Mat3d Identity;
+  const static Mat3d Zero;
 protected:
   Vec3d elt[3]; // the three rows of the matrix
 };
 
+// Computes eigenvalues and eigenvectors of a 3x3 matrix M
+// Assumes symmetric matrix; contents of matrix "M" are not modified by the routine
+// Eigenvalues are sorted in decreasing order (not decreasing absolute-value order)
+// Returned eigenvectors are unit length
+void eigen_sym(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3]=NULL);
+
+// inverse of a 3x3 matrix (suitable when A is given by a pointer to the 9 entries)
+// input: A (row-major)
+// output: AInv
+void inverse3x3(const double * A, double * AInv);
+
+// these two declarations are repeated here, with default arguments, to conform to the C++ standard; this makes it possible to compile the code on Apple's clang compiler
+bool eigen_sym_NR(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3], int maxIterations=50, double epsilon=0.0);
+int SVD(Mat3d & M, Mat3d & U, Vec3d & Sigma, Mat3d & V, double singularValue_eps=1e-8, int modifiedSVD=0);
+
 // ===== below is the implementation =====
 
 inline Mat3d::Mat3d(double x0_g, double x1_g, double x2_g,
@@ -142,21 +186,21 @@ inline Mat3d::Mat3d(double x0_g, double x1_g, double x2_g,
   elt[2] = Vec3d(x6_g,x7_g,x8_g);
 }
 
-inline Mat3d::Mat3d(double mat[9])
+inline Mat3d::Mat3d(const double mat[9])
 {
   elt[0] = Vec3d(mat[0],mat[1],mat[2]);
   elt[1] = Vec3d(mat[3],mat[4],mat[5]);
   elt[2] = Vec3d(mat[6],mat[7],mat[8]);
 }
 
-inline Mat3d::Mat3d(Vec3d rows[3])
+inline Mat3d::Mat3d(const Vec3d rows[3]) 
 {
   elt[0] = rows[0];
   elt[1] = rows[1];
   elt[2] = rows[2];
 }
 
-inline Mat3d::Mat3d(Vec3d row0, Vec3d row1, Vec3d row2)
+inline Mat3d::Mat3d(const Vec3d & row0, const Vec3d & row1, const Vec3d & row2)
 {
   elt[0] = row0;
   elt[1] = row1;
@@ -189,7 +233,7 @@ inline void Mat3d::set(double x0, double x1, double x2,
 
 inline void Mat3d::set(double value)
 {
-  elt[0][0] = elt[0][1] = elt[0][2] =
+  elt[0][0] = elt[0][1] = elt[0][2] = 
   elt[1][0] = elt[1][1] = elt[1][2] =
   elt[2][0] = elt[2][1] = elt[2][2] = value;
 }
@@ -203,7 +247,7 @@ inline Mat3d & Mat3d::operator=(const Mat3d & source)
   return *this;
 }
 
-inline Mat3d Mat3d::operator+ (const Mat3d & mat2)
+inline Mat3d Mat3d::operator+ (const Mat3d & mat2) const
 {
   Mat3d sum = *this;
   sum.elt[0] += mat2.elt[0];
@@ -221,7 +265,7 @@ inline Mat3d & Mat3d::operator+= (const Mat3d & mat2)
   return *this;
 }
 
-inline Mat3d Mat3d::operator- (const Mat3d & mat2)
+inline Mat3d Mat3d::operator- (const Mat3d & mat2) const
 {
   Mat3d sum = *this;
   sum.elt[0] -= mat2.elt[0];
@@ -259,6 +303,16 @@ inline Mat3d & Mat3d::operator*= (double scalar)
   return *this;
 }
 
+inline bool Mat3d::operator== (const Mat3d & mat2) const
+{
+  return elt[0] == mat2.elt[0] && elt[1] == mat2.elt[1] && elt[2] == mat2.elt[2]; 
+}
+
+inline bool Mat3d::operator!= (const Mat3d & mat2) const
+{
+  return elt[0] != mat2.elt[0] || elt[1] != mat2.elt[1] || elt[2] != mat2.elt[2];
+}
+
 inline Mat3d & Mat3d::multiplyDiagRight(Vec3d & v) // M = M * diag(v)
 {
   elt[0][0] *= v[0];
@@ -295,7 +349,7 @@ inline Mat3d operator* (double scalar, const Mat3d & mat2)
   return result;
 }
 
-inline Mat3d operator/ (double scalar, const Mat3d & mat2)
+inline Mat3d operator/ (const Mat3d & mat2, double scalar)
 {
   Mat3d result = mat2;
   result.elt[0] /= scalar;
@@ -304,8 +358,8 @@ inline Mat3d operator/ (double scalar, const Mat3d & mat2)
 
   return result;
 }
-
-inline Mat3d tensorProduct(Vec3d & vecA, Vec3d & vecB)
+ 
+inline Mat3d tensorProduct(const Vec3d & vecA, const Vec3d & vecB)
 {
   Mat3d result(vecA[0]*vecB[0],vecA[0]*vecB[1],vecA[0]*vecB[2],
 	       vecA[1]*vecB[0],vecA[1]*vecB[1],vecA[1]*vecB[2],
@@ -349,35 +403,43 @@ inline const Mat3d Mat3d::operator* (const Mat3d & mat2) const
 
 inline Mat3d inv(const Mat3d & mat)
 {
-  double invDeterminant = 1.0 /
-    (-mat[0][2] * mat[1][1] * mat[2][0] +
-      mat[0][1] * mat[1][2] * mat[2][0] +
-      mat[0][2] * mat[1][0] * mat[2][1] -
-      mat[0][0] * mat[1][2] * mat[2][1] -
-      mat[0][1] * mat[1][0] * mat[2][2] +
-      mat[0][0] * mat[1][1] * mat[2][2] );
-
-  return Mat3d(
-    invDeterminant * (-mat[1][2] * mat[2][1] + mat[1][1] * mat[2][2]),
-    invDeterminant * (mat[0][2] * mat[2][1] - mat[0][1] * mat[2][2]),
-    invDeterminant * (-mat[0][2] * mat[1][1] + mat[0][1] * mat[1][2]),
-    invDeterminant * (mat[1][2] * mat[2][0] - mat[1][0] * mat[2][2]),
-    invDeterminant * (-mat[0][2] * mat[2][0] + mat[0][0] * mat[2][2]),
-    invDeterminant * (mat[0][2] * mat[1][0] - mat[0][0] * mat[1][2]),
-    invDeterminant * (-mat[1][1] * mat[2][0] + mat[1][0] * mat[2][1]),
-    invDeterminant * (mat[0][1] * mat[2][0] - mat[0][0] * mat[2][1]),
-    invDeterminant * (-mat[0][1] * mat[1][0] + mat[0][0] * mat[1][1])
-  );
+  double A[9];
+  mat.convertToArray(A);
+  double AInv[9];
+  inverse3x3(A, AInv);
+
+  return Mat3d(AInv);
 }
 
-inline double det(const Mat3d & mat)
+// inverse of a 3x3 matrix
+// row-major format
+inline void inverse3x3(const double * A, double * AInv)
+{
+  // converted to C from Mathematica output   
+  AInv[0] = -A[5] * A[7] + A[4] * A[8];
+  AInv[1] = A[2] * A[7] - A[1] * A[8];
+  AInv[2] = -A[2] * A[4] + A[1] * A[5];
+  AInv[3] = A[5] * A[6] - A[3] * A[8];
+  AInv[4] = -A[2] * A[6] + A[0] * A[8];
+  AInv[5] = A[2] * A[3] - A[0] * A[5];
+  AInv[6] = -A[4] * A[6] + A[3] * A[7];
+  AInv[7] = A[1] * A[6] - A[0] * A[7];
+  AInv[8] = -A[1] * A[3] + A[0] * A[4];
+
+  double invDet = 1.0 / (A[0] * AInv[0] + A[1] * AInv[3] + A[2] * AInv[6]);
+
+  for(int i=0; i<9; i++)
+    AInv[i] *= invDet;
+}
+
+inline double det(const Mat3d & mat) 
 {
   return
-   (-mat[0][2] * mat[1][1] * mat[2][0] +
-     mat[0][1] * mat[1][2] * mat[2][0] +
-     mat[0][2] * mat[1][0] * mat[2][1] -
-     mat[0][0] * mat[1][2] * mat[2][1] -
-     mat[0][1] * mat[1][0] * mat[2][2] +
+   (-mat[0][2] * mat[1][1] * mat[2][0] + 
+     mat[0][1] * mat[1][2] * mat[2][0] + 
+     mat[0][2] * mat[1][0] * mat[2][1] - 
+     mat[0][0] * mat[1][2] * mat[2][1] - 
+     mat[0][1] * mat[1][0] * mat[2][2] + 
      mat[0][0] * mat[1][1] * mat[2][2] );
 }
 
@@ -389,13 +451,30 @@ inline Mat3d trans(const Mat3d & mat)
               mat[0][2], mat[1][2], mat[2][2] );
 }
 
-inline void Mat3d::convertToArray(double * array) const // in row-major order
+// compute [  0  -v2  v1 ]
+//         [  v2  0  -v0 ]
+//         [ -v1  v0  0  ]
+inline Mat3d skewSymmetricMatrix(const Vec3d & vec)
+{
+  return Mat3d(0, -vec[2], vec[1], 
+               vec[2], 0, -vec[0],
+               -vec[1], vec[0], 0);
+}
+
+inline void Mat3d::convertToArray(double array[9]) const // in row-major order
 {
   array[0] = elt[0][0]; array[1] = elt[0][1]; array[2] = elt[0][2];
   array[3] = elt[1][0]; array[4] = elt[1][1]; array[5] = elt[1][2];
   array[6] = elt[2][0]; array[7] = elt[2][1]; array[8] = elt[2][2];
 }
 
+inline void Mat3d::addToArray(double array[9]) const // in row-major order
+{
+  array[0] += elt[0][0]; array[1] += elt[0][1]; array[2] += elt[0][2];
+  array[3] += elt[1][0]; array[4] += elt[1][1]; array[5] += elt[1][2];
+  array[6] += elt[2][0]; array[7] += elt[2][1]; array[8] += elt[2][2];
+}
+
 inline std::ostream &operator << (std::ostream &s, const Mat3d &v)
 {
   double a00 = v.elt[0][0]; double a01 = v.elt[0][1]; double a02 = v.elt[0][2];
@@ -403,22 +482,39 @@ inline std::ostream &operator << (std::ostream &s, const Mat3d &v)
   double a20 = v.elt[2][0]; double a21 = v.elt[2][1]; double a22 = v.elt[2][2];
 
   return(
-    s << '[' << a00 << ' ' << a01 << ' ' << a02 << ']' << std::endl <<
-    '[' << a10 << ' ' << a11 << ' ' << a12 << ']' << std::endl <<
-    '[' << a20 << ' ' << a21 << ' ' << a22 << ']'
+    s << '[' << a00 << ' ' << a01 << ' ' << a02 << ']' << '\n'
+      << '[' << a10 << ' ' << a11 << ' ' << a12 << ']' << '\n'
+      << '[' << a20 << ' ' << a21 << ' ' << a22 << ']'
   );
 }
 
-inline void Mat3d::print()
+inline void Mat3d::print() const
 {
   double a00 = elt[0][0]; double a01 = elt[0][1]; double a02 = elt[0][2];
   double a10 = elt[1][0]; double a11 = elt[1][1]; double a12 = elt[1][2];
   double a20 = elt[2][0]; double a21 = elt[2][1]; double a22 = elt[2][2];
-
+  
   printf("[%G %G %G;\n", a00, a01, a02);
   printf(" %G %G %G;\n", a10, a11, a12);
   printf(" %G %G %G]\n", a20, a21, a22);
 }
+
+inline double Mat3d::maxAbsEntry() const
+{
+  double maxAbsEntry = 0.0;
+  for(int i = 0; i < 3; i++)
+    for(int j = 0; j < 3; j++)
+    {
+       if (fabs(elt[i][j]) > maxAbsEntry)
+         maxAbsEntry = fabs(elt[i][j]);
+    }
+  return maxAbsEntry;
+}
+
+inline bool Mat3d::hasNaN() const
+{
+  return elt[0].hasNaN() || elt[1].hasNaN() || elt[2].hasNaN();
 }
+
 #endif
 
diff --git a/src/libmatrixIO/matrixIO.h b/libraries/include/matrixIO.h
similarity index 60%
rename from src/libmatrixIO/matrixIO.h
rename to libraries/include/matrixIO.h
index a6dd0b0323afcb909383747e6041917f3c5e17f8..be67be71b9302aeabcc78428e76467fcd6f59506 100644
--- a/src/libmatrixIO/matrixIO.h
+++ b/libraries/include/matrixIO.h
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrixIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -49,36 +58,56 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <vector>
 #include "matrixMacros.h"
 
-namespace vega
-{
 // === matrix input/output routines ===
 
+// function ending with "_" will abort if fails to read/write
 template <class real>
 int ReadMatrixFromDisk(const char * filename, int * m, int * n, real ** matrix);
+template <class real>
+int ReadMatrixFromDisk(const char * filename, int * m, int * n, std::vector<real> & matrix);
+template <class real>
+int ReadMatrixFromDisk(const char * filename, int & m, int & n, std::vector<real> & matrix) { return ReadMatrixFromDisk(filename, &m, &n, matrix); }
+
 template <class real>
 int ReadMatrixFromDisk_(const char * filename, int * m, int * n, real ** matrix);
+template <class real>
+int ReadMatrixFromDisk_(const char * filename, int * m, int * n, std::vector<real> & matrix);
+template <class real>
+int ReadMatrixFromDisk_(const char * filename, int & m, int & n, std::vector<real> & matrix) { return ReadMatrixFromDisk_(filename, &m, &n, matrix); }
+
+template <class real>
+int ReadVectorFromDisk(const char * filename, int & m, std::vector<real> & vec);
+template <class real>
+int ReadVectorFromDisk(const char * filename, std::vector<real> & vec) { int n = 0; return ReadVectorFromDisk(filename, n, vec); }
 
 int ReadMatrixSizeFromDisk(const char * filename, int * m, int * n);
 void ReadMatrixSizeFromDisk_(const char * filename, int * m, int * n);
 void ChangeMatrixHeader(const char * filename, int numRows, int numColumns); // overwrites any previous header; keeps data intact
 
 template <class real>
-int WriteMatrixToDisk(const char * filename, int m, int n, real * matrix);
+int WriteMatrixToDisk(const char * filename, int m, int n, const real * matrix);
 template <class real>
-int WriteMatrixToDisk_(const char * filename, int m, int n, real * matrix);
+int WriteMatrixToDisk_(const char * filename, int m, int n, const real * matrix);
 
 template <class real>
-int AppendMatrixToDisk(const char * filename, int m, int n, real * matrix);
+int WriteVectorToDisk(const char * filename, int m, const real * vector) { return WriteMatrixToDisk(filename, m, 1, vector); }
 template <class real>
-int AppendMatrixToDisk_(const char * filename, int m, int n, real * matrix);
+int WriteVectorToDisk(const char * filename, const std::vector<real> & vec) { return WriteVectorToDisk(filename, vec.size(), vec.data()); }
+
+// assert m is the same as the matrix already stored in filename
+template <class real>
+int AppendMatrixToDisk(const char * filename, int m, int n, const real * matrix);
+template <class real>
+int AppendMatrixToDisk_(const char * filename, int m, int n, const real * matrix);
 
 template <class real>
 int ReadMatrixFromDiskTextFile(const char * filename, int * m, int * n, real ** matrix);
 
 template <class real>
-int WriteMatrixToDiskTextFile(const char * filename, int m, int n, real * matrix);
+int WriteMatrixToDiskTextFile(const char * filename, int m, int n, const real * matrix);
 
 // "fileList" is a filename of a text file that contains the matrices to be loaded (concatenated column-wise) into the matrix, one text entry per line of "fileList"
 template <class real>
@@ -100,10 +129,10 @@ void Abort_(const char * reason, int exitCode);
 
 // === stream routines (they operate on a file that is already open, in binary mode) ===
 
-int OpenFile_(const char * filename, FILE ** fin, char * mode);
+int OpenFile_(const char * filename, FILE ** fin, const char * mode);
 
 template <class real>
-int WriteMatrixToStream(FILE * file, int m, int n, real * matrix);
+int WriteMatrixToStream(FILE * file, int m, int n, const real * matrix);
 
 int WriteMatrixHeaderToStream(FILE * file, int m, int n);
 
@@ -133,6 +162,20 @@ int ReadModeInfoFromDisk(const char * filename, int * n, int * r);
 
 template <class real>
 int WriteModesToDisk(const char * filename, int n, int r, real * frequencies, real * modes);
+
+// =================================================================
+// IMPLEMENTATION
+// =================================================================
+
+template <class real>
+int ReadVectorFromDisk(const char * filename, int & m, std::vector<real> & vec)
+{
+  int n = 0;
+  int ret = ReadMatrixFromDisk(filename, m, n, vec);
+  if (ret != 0) return ret;
+  if (n != 1) return 2;
+  return ret;
 }
+
 #endif
 
diff --git a/libraries/include/matrixMacros.h b/libraries/include/matrixMacros.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd28180c25cc4974104c4e7c1e724db2fb5ad8d3
--- /dev/null
+++ b/libraries/include/matrixMacros.h
@@ -0,0 +1,56 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "matrixIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Commonly used macros in the matrix library.
+*/
+
+#ifndef _MATRIXMACROS_H_
+#define _MATRIXMACROS_H_
+
+/*
+   Returns the array offset index of element located in row i and column j (both 0-indexed). The matrix has "numRows" rows, and is stored in LAPACK-style column-major order.
+*/
+#ifndef ELT
+  #define ELT(numRows,i,j) (((long)j)*((long)numRows)+((long)i))
+#endif
+
+#ifndef MIN
+  #define MIN(x,y) ((x)<(y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+  #define MAX(x,y) ((x)>(y) ? (x) : (y))
+#endif
+
+
+#endif
diff --git a/include/matrixMultiplyMacros.h b/libraries/include/matrixMultiplyMacros.h
similarity index 55%
rename from include/matrixMultiplyMacros.h
rename to libraries/include/matrixMultiplyMacros.h
index 6b4b8681c81356de7bbcbbc2ce75df366bf9aded..c4a584eb4f045b1450d4858df079c61f53f4f464 100644
--- a/include/matrixMultiplyMacros.h
+++ b/libraries/include/matrixMultiplyMacros.h
@@ -1,30 +1,36 @@
-/*
-
-* Copyright (c) 2011, Jernej Barbic, Yili Zhao, University of Southern California
-* All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are met:
-*     * Redistributions of source code must retain the above copyright
-*       notice, this list of conditions and the following disclaimer.
-*     * Redistributions in binary form must reproduce the above copyright
-*       notice, this list of conditions and the following disclaimer in the
-*       documentation and/or other materials provided with the distribution.
-*     * Neither the name of University of Southern California, nor the
-*       names of its contributors may be used to endorse or promote products
-*       derived from this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY JERNEJ BARBIC, YILI ZHAO AND UNIVERSITY OF SOUTHERN CALIFORNIA 
-* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
-* IN NO EVENT SHALL JERNEJ BARBIC, YILI ZHAO OR UNIVERSITY OF SOUTHERN CALIFORNIA BE 
-* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "macros" include files, Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    * 
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yili Zhao                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
 
+/*
   3x3 and 4x4 matrix multiplication macros
 
   This code was used in the following publication:
@@ -56,6 +62,10 @@
   #define MAX(x,y) ((x)>(y) ? (x) : (y))
 #endif
 
+#ifndef FABS
+  #define FABS(x) ((x)>=0 ? (x) : -(x))
+#endif
+
 #ifndef ELT
   #define ELT(rows,i,j) (((long)j)*((long)rows)+((long)i))
 #endif
@@ -208,6 +218,30 @@
   (a)[7] += (b)[7];\
   (a)[8] += (b)[8];
 
+// a -= b
+#define MATRIX_SUBTRACTEQUAL3X3(a,b)\
+  (a)[0] -= (b)[0];\
+  (a)[1] -= (b)[1];\
+  (a)[2] -= (b)[2];\
+  (a)[3] -= (b)[3];\
+  (a)[4] -= (b)[4];\
+  (a)[5] -= (b)[5];\
+  (a)[6] -= (b)[6];\
+  (a)[7] -= (b)[7];\
+  (a)[8] -= (b)[8];
+
+// c = a - b
+#define MATRIX_SUBTRACT3X3(a,b,c)\
+  (c)[0] = (a)[0] - (b)[0];\
+  (c)[1] = (a)[1] - (b)[1];\
+  (c)[2] = (a)[2] - (b)[2];\
+  (c)[3] = (a)[3] - (b)[3];\
+  (c)[4] = (a)[4] - (b)[4];\
+  (c)[5] = (a)[5] - (b)[5];\
+  (c)[6] = (a)[6] - (b)[6];\
+  (c)[7] = (a)[7] - (b)[7];\
+  (c)[8] = (a)[8] - (b)[8];
+
 // c = a + b
 #define MATRIX_ADD3X3(a,b,c)\
   (c)[0] = (a)[0] + (b)[0];\
@@ -220,6 +254,30 @@
   (c)[7] = (a)[7] + (b)[7];\
   (c)[8] = (a)[8] + (b)[8];
 
+// c = a - b
+#define MATRIX_SUBTRACT3X3(a,b,c)\
+  (c)[0] = (a)[0] - (b)[0];\
+  (c)[1] = (a)[1] - (b)[1];\
+  (c)[2] = (a)[2] - (b)[2];\
+  (c)[3] = (a)[3] - (b)[3];\
+  (c)[4] = (a)[4] - (b)[4];\
+  (c)[5] = (a)[5] - (b)[5];\
+  (c)[6] = (a)[6] - (b)[6];\
+  (c)[7] = (a)[7] - (b)[7];\
+  (c)[8] = (a)[8] - (b)[8];
+
+// c = alpha * a + beta * b
+#define MATRIX_ADDWEIGHTED3X3(alpha,a,beta,b,c)\
+  (c)[0] = (alpha) * (a)[0] + (beta) * (b)[0];\
+  (c)[1] = (alpha) * (a)[1] + (beta) * (b)[1];\
+  (c)[2] = (alpha) * (a)[2] + (beta) * (b)[2];\
+  (c)[3] = (alpha) * (a)[3] + (beta) * (b)[3];\
+  (c)[4] = (alpha) * (a)[4] + (beta) * (b)[4];\
+  (c)[5] = (alpha) * (a)[5] + (beta) * (b)[5];\
+  (c)[6] = (alpha) * (a)[6] + (beta) * (b)[6];\
+  (c)[7] = (alpha) * (a)[7] + (beta) * (b)[7];\
+  (c)[8] = (alpha) * (a)[8] + (beta) * (b)[8];
+
 // c = a * b
 #define MATRIX_MULTIPLY3X3(a,b,c)\
 (c)[0]=(a)[0]*(b)[0]+(a)[1]*(b)[3]+(a)[2]*(b)[6];\
@@ -288,6 +346,12 @@
 (B)[3] = (A)[1]; (B)[4] = (A)[4]; (B)[5] = (A)[7];\
 (B)[6] = (A)[2]; (B)[7] = (A)[5]; (B)[8] = (A)[8];
 
+#define MATRIX_TRANSPOSE4X4(A,B)\
+(B)[0] = (A)[0]; (B)[1] = (A)[4]; (B)[2] = (A)[8]; (B)[3]=(A)[12];\
+(B)[4] = (A)[1]; (B)[5] = (A)[5]; (B)[6] = (A)[9]; (B)[7]=(A)[13];\
+(B)[8] = (A)[2]; (B)[9] = (A)[6]; (B)[10] = (A)[10]; (B)[11]=(A)[14];\
+(B)[12] = (A)[3]; (B)[13] = (A)[7]; (B)[14] = (A)[11]; (B)[15]=(A)[15];
+
 // A = u * v^T
 #define VECTOR_TENSOR_PRODUCT3X3(u,v,A)\
 (A)[0] = (u)[0] * (v)[0]; (A)[1] = (u)[0] * (v)[1]; (A)[2] = (u)[0] * (v)[2];\
@@ -327,6 +391,12 @@
 (a)[1] = 0.5 * ((A)[2] - (A)[6]);\
 (a)[2] = 0.5 * ((A)[3] - (A)[1]);
 
+// a = skew( A - A^T )
+#define SKEW_PART_NO_DIV2(A, a)\
+(a)[0] = ((A)[7] - (A)[5]);\
+(a)[1] = ((A)[2] - (A)[6]);\
+(a)[2] = ((A)[3] - (A)[1]);
+
 // a = upper-triangle( 0.5 * (A + A^T) )
 #define SYM_PART(A, a)\
 (a)[0] = ((A)[0]);\
@@ -356,5 +426,80 @@
   (c)[1] += ((b)[0]) * ((a)[2]) - ((a)[0]) * ((b)[2]);\
   (c)[2] += ((a)[0]) * ((b)[1]) - ((b)[0]) * ((a)[1])
 
+// b = a^T, a is 4x3, b is 3x4
+#define MATRIX_TRANSPOSE4X3(a,b)\
+  (b)[0] = (a)[0]; (b)[1] = (a)[3]; (b)[2] = (a)[6]; (b)[3]=(a)[9];\
+  (b)[4] = (a)[1]; (b)[5] = (a)[4]; (b)[6] = (a)[7]; (b)[7]=(a)[10];\
+  (b)[8] = (a)[2]; (b)[9] = (a)[5]; (b)[10] = (a)[8]; (b)[11]=(a)[11];
+
+// b = a
+#define MATRIX_SET4X3(a,b)\
+  (a)[0] = (b)[0]; (a)[1] = (b)[1]; (a)[2] = (b)[2];  (a)[3] = (b)[3];\
+  (a)[4] = (b)[4]; (a)[5] = (b)[5]; (a)[6] = (b)[6];  (a)[7] = (b)[7];\
+  (a)[8] = (b)[8]; (a)[9] = (b)[9]; (a)[10] = (b)[10]; (a)[11] = (b)[11];
+
+//c = a * b, a is 3x4, b is 4x3
+#define MATRIX_MULTIPLY3X4X3(a,b,c)\
+  (c)[0]=(a)[0]*(b)[0]+(a)[1]*(b)[3]+(a)[2]*(b)[6]+(a)[3]*(b)[9];\
+  (c)[1]=(a)[0]*(b)[1]+(a)[1]*(b)[4]+(a)[2]*(b)[7]+(a)[3]*(b)[10];\
+  (c)[2]=(a)[0]*(b)[2]+(a)[1]*(b)[5]+(a)[2]*(b)[8]+(a)[3]*(b)[11];\
+  (c)[3]=(a)[4]*(b)[0]+(a)[5]*(b)[3]+(a)[6]*(b)[6]+(a)[7]*(b)[9];\
+  (c)[4]=(a)[4]*(b)[1]+(a)[5]*(b)[4]+(a)[6]*(b)[7]+(a)[7]*(b)[10];\
+  (c)[5]=(a)[4]*(b)[2]+(a)[5]*(b)[5]+(a)[6]*(b)[8]+(a)[7]*(b)[11];\
+  (c)[6]=(a)[8]*(b)[0]+(a)[9]*(b)[3]+(a)[10]*(b)[6]+(a)[11]*(b)[9];\
+  (c)[7]=(a)[8]*(b)[1]+(a)[9]*(b)[4]+(a)[10]*(b)[7]+(a)[11]*(b)[10];\
+  (c)[8]=(a)[8]*(b)[2]+(a)[9]*(b)[5]+(a)[10]*(b)[8]+(a)[11]*(b)[11];
+
+// a *= scalar
+#define MATRIX_SCALE4X4(a,scalar)\
+  (a)[0] *= (scalar); (a)[1] *= (scalar); (a)[2] *= (scalar); (a)[3] *= (scalar);\
+  (a)[4] *= (scalar); (a)[5] *= (scalar); (a)[6] *= (scalar); (a)[7] *= (scalar);\
+  (a)[8] *= (scalar); (a)[9] *= (scalar); (a)[10] *= (scalar); (a)[11] *= (scalar);\
+  (a)[12] *= (scalar); (a)[13] *= (scalar); (a)[14] *= (scalar); (a)[15] *= (scalar);
+
+// a -= b
+#define MATRIX_SUBTRACTEQUAL4X4(a,b)\
+  (a)[0] -= (b)[0]; (a)[1] -= (b)[1]; (a)[2] -= (b)[2]; (a)[3] -= (b)[3];\
+  (a)[4] -= (b)[4]; (a)[5] -= (b)[5]; (a)[6] -= (b)[6]; (a)[7] -= (b)[7];\
+  (a)[8] -= (b)[8]; (a)[9] -= (b)[9]; (a)[10] -= (b)[10]; (a)[11] -= (b)[11];\
+  (a)[12] -= (b)[12]; (a)[13] -= (b)[13]; (a)[14] -= (b)[14]; (a)[15] -= (b)[15];
+
+// c = a - b
+#define MATRIX_SUBTRACT4X4(a,b,c)\
+  (c)[0] = (a)[0] - (b)[0];\
+  (c)[1] = (a)[1] - (b)[1];\
+  (c)[2] = (a)[2] - (b)[2];\
+  (c)[3] = (a)[3] - (b)[3];\
+  (c)[4] = (a)[4] - (b)[4];\
+  (c)[5] = (a)[5] - (b)[5];\
+  (c)[6] = (a)[6] - (b)[6];\
+  (c)[7] = (a)[7] - (b)[7];\
+  (c)[8] = (a)[8] - (b)[8];\
+  (c)[9] = (a)[9] - (b)[9];\
+  (c)[10] = (a)[10] - (b)[10];\
+  (c)[11] = (a)[11] - (b)[11];\
+  (c)[12] = (a)[12] - (b)[12];\
+  (c)[13] = (a)[13] - (b)[13];\
+  (c)[14] = (a)[14] - (b)[14];\
+  (c)[15] = (a)[15] - (b)[15];
+
+//c = a * b, a is 4x4, b is 4x3
+#define MATRIX_MULTIPLY4X4X3(a,b,c)\
+  (c)[0]=(a)[0]*(b)[0]+(a)[1]*(b)[3]+(a)[2]*(b)[6]+(a)[3]*(b)[9];\
+  (c)[1]=(a)[0]*(b)[1]+(a)[1]*(b)[4]+(a)[2]*(b)[7]+(a)[3]*(b)[10];\
+  (c)[2]=(a)[0]*(b)[2]+(a)[1]*(b)[5]+(a)[2]*(b)[8]+(a)[3]*(b)[11];\
+  (c)[3]=(a)[4]*(b)[0]+(a)[5]*(b)[3]+(a)[6]*(b)[6]+(a)[7]*(b)[9];\
+  (c)[4]=(a)[4]*(b)[1]+(a)[5]*(b)[4]+(a)[6]*(b)[7]+(a)[7]*(b)[10];\
+  (c)[5]=(a)[4]*(b)[2]+(a)[5]*(b)[5]+(a)[6]*(b)[8]+(a)[7]*(b)[11];\
+  (c)[6]=(a)[8]*(b)[0]+(a)[9]*(b)[3]+(a)[10]*(b)[6]+(a)[11]*(b)[9];\
+  (c)[7]=(a)[8]*(b)[1]+(a)[9]*(b)[4]+(a)[10]*(b)[7]+(a)[11]*(b)[10];\
+  (c)[8]=(a)[8]*(b)[2]+(a)[9]*(b)[5]+(a)[10]*(b)[8]+(a)[11]*(b)[11];\
+  (c)[9]=(a)[12]*(b)[0]+(a)[13]*(b)[3]+(a)[14]*(b)[6]+(a)[15]*(b)[9];\
+  (c)[10]=(a)[12]*(b)[1]+(a)[13]*(b)[4]+(a)[14]*(b)[7]+(a)[15]*(b)[10];\
+  (c)[11]=(a)[12]*(b)[2]+(a)[13]*(b)[5]+(a)[14]*(b)[8]+(a)[15]*(b)[11];
+
+#define VECTOR_LENGTH(a)\
+    sqrt((a)[0] * (a)[0] + (a)[1] * (a)[1] + (a)[2] * (a)[2])
+
 #endif
 
diff --git a/src/libminivector/minivector.h b/libraries/include/minivector.h
similarity index 96%
rename from src/libminivector/minivector.h
rename to libraries/include/minivector.h
index a6463828d4c185df79c52395256f7402d1c4c1fd..11208a2c14a095dcb7630dce922fe7e7999537b5 100644
--- a/src/libminivector/minivector.h
+++ b/libraries/include/minivector.h
@@ -27,8 +27,6 @@
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-  Version 1.2
 */
 
 #ifndef _MINIVECTOR_H_
@@ -36,7 +34,10 @@
 
 #include "vec2d.h"
 #include "vec3d.h"
+#include "vec4d.h"
 #include "mat3d.h"
+#include "vec3i.h"
+#include "vec4i.h"
 
 #endif
 
diff --git a/libraries/include/multiMatrixIO.h b/libraries/include/multiMatrixIO.h
new file mode 100644
index 0000000000000000000000000000000000000000..5a3f20e5efa72e0978bcd2b0bc16c8a5d0d2f14f
--- /dev/null
+++ b/libraries/include/multiMatrixIO.h
@@ -0,0 +1,57 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "matrixIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Load/save multiple binary matrices from a single file.
+*/
+
+#ifndef _MULTIMATRIXIO_H_
+#define _MULTIMATRIXIO_H_
+
+class MultiMatrixIO
+{
+public:
+  // input: filename
+  // output: numMatrices, m, n, matrices
+  // returns 0 on success, non-zero otherwise
+  static int Load(const char * filename, int * numMatrices, int ** m, int ** n, double *** matrices);
+
+  // input: filename, numMatrices, m, n, matrices
+  // output: none
+  // returns 0 on success, non-zero otherwise
+  static int Save(const char * filename, int numMatrices, int * m, int * n, double ** matrices);
+
+protected:
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/neoHookeanIsotropicMaterial.h b/libraries/include/neoHookeanIsotropicMaterial.h
old mode 100755
new mode 100644
similarity index 83%
rename from src/libisotropicHyperelasticFEM/neoHookeanIsotropicMaterial.h
rename to libraries/include/neoHookeanIsotropicMaterial.h
index 7265e0c1f27ac186ec37e2a32950419f3b7180e5..204973e4c6b2146be7dc91a47c0ebd2d4c387838
--- a/src/libisotropicHyperelasticFEM/neoHookeanIsotropicMaterial.h
+++ b/libraries/include/neoHookeanIsotropicMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 #include "tetMesh.h"
 #include "isotropicMaterialWithCompressionResistance.h"
 
-namespace vega
-{
 /*
    Neo-Hookean material. Material properties are read from the tet mesh, and can be heterogeneous.
 
@@ -61,6 +63,6 @@ protected:
   double * EdivNuFactor;
   virtual double GetCompressionResistanceFactor(int elementIndex);
 };
-}
+
 #endif
 
diff --git a/libraries/include/openGL-headers.h b/libraries/include/openGL-headers.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd300c6064fc6820c2c12a3424e462a2daef35c6
--- /dev/null
+++ b/libraries/include/openGL-headers.h
@@ -0,0 +1,14 @@
+#if defined(_WIN32) || defined(WIN32)
+  #include <Windows.h>
+#endif
+
+#if defined(_WIN32) || defined(WIN32) || defined(linux) || defined (__linux__)
+  #include <GL/gl.h> 
+  #include <GL/glu.h> 
+  #include <GL/glut.h>
+#elif defined(__APPLE__)
+  #include <OpenGL/gl.h>
+  #include <OpenGL/glu.h>
+  #include <GLUT/glut.h>
+#endif
+
diff --git a/src/libperformanceCounter/performanceCounter.h b/libraries/include/performanceCounter.h
similarity index 93%
rename from src/libperformanceCounter/performanceCounter.h
rename to libraries/include/performanceCounter.h
index c3ee321f126b5826f5fb230aa65cfcb80b8e6786..a64d8f0f9b710b0d2ff94e6808766fbbb40dec21 100644
--- a/src/libperformanceCounter/performanceCounter.h
+++ b/libraries/include/performanceCounter.h
@@ -62,8 +62,6 @@ Version: 1.0
 #include "stdlib.h"
 #include "sys/time.h"
 
-namespace vega
-{
 class PerformanceCounter
 {
 public:
@@ -73,7 +71,7 @@ public:
   void StartCounter(); // call this before your code block
   void StopCounter(); // call this after your code block
 
-  // read elapsed time (units are seconds, accuracy is up to microseconds)
+  // read the elapsed time (units are seconds, accuracy is up to microseconds)
   double GetElapsedTime();
 
 protected:
@@ -88,6 +86,8 @@ inline void PerformanceCounter::StartCounter()
 
   startCountSec = tv.tv_sec;
   startCountMicroSec = tv.tv_usec;
+  stopCountSec = startCountSec;
+  stopCountMicroSec = startCountMicroSec;
 }
 
 inline void PerformanceCounter::StopCounter()
@@ -103,20 +103,18 @@ inline void PerformanceCounter::StopCounter()
 
 inline double PerformanceCounter::GetElapsedTime()
 {
-  float elapsedTime = 1.0 * (stopCountSec-startCountSec) + 1E-6 * (stopCountMicroSec - startCountMicroSec);
+  double elapsedTime = 1.0 * (stopCountSec-startCountSec) + 1E-6 * (stopCountMicroSec - startCountMicroSec);
   return elapsedTime;
 }
-}
+
 #endif
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
 
 /**************** WINDOWS COUNTER *******************/
 
 #include <windows.h>
 
-namespace vega
-{
 class PerformanceCounter
 {
 public:
@@ -155,7 +153,7 @@ inline double PerformanceCounter::GetElapsedTime()
   return ((double)(stopCount.QuadPart - startCount.QuadPart))
     / ((double)timerFrequency.QuadPart);
 }
-}
+
 #endif
 #endif
 
diff --git a/src/libpolarDecomposition/polarDecomposition.h b/libraries/include/polarDecomposition.h
similarity index 91%
rename from src/libpolarDecomposition/polarDecomposition.h
rename to libraries/include/polarDecomposition.h
index f0c96c5d552ab6f1e11a7a21785d85cfb3fb6338..ac70bdad57acc475561aca526974be3eb0eca9d0 100644
--- a/src/libpolarDecomposition/polarDecomposition.h
+++ b/libraries/include/polarDecomposition.h
@@ -1,8 +1,6 @@
 #ifndef _POLARDECOMPOSITION_H_
 #define _POLARDECOMPOSITION_H_
 
-namespace vega
-{
 /*
   Polar decomposition of a general 3x3 matrix
 
@@ -28,11 +26,12 @@ public:
   // M = Q * S
   // M is 3x3 input matrix
   // Q is 3x3 orthogonal output matrix, Q Q^T = Q^T Q = I 
-  // S is 3x3 symmetric output matrix
+  // S is 3x3 symmetric positive-definite output matrix
   // Note: det(Q)=sgn(det(M)); this sign can be 1 or -1, depending on M
+  // if forceRotation is 1, the matrix Q will be a rotation, S will be symmetric, but not necessarily positive-definite
   // M is not modified
   // All matrices are row-major
-  static double Compute(const double * M, double * Q, double * S, double tolerance = 1E-6);
+  static double Compute(const double * M, double * Q, double * S, double tolerance=1E-6, int forceRotation=0);
 
 protected:
 
@@ -54,6 +53,6 @@ inline void PolarDecomposition::crossProduct(const double * a, const double * b,
   c[1] = a[2] * b[0] - a[0] * b[2];
   c[2] = a[0] * b[1] - a[1] * b[0];
 }
-}
+
 #endif
 
diff --git a/src/libpolarDecomposition/polarDecompositionGradient.h b/libraries/include/polarDecompositionGradient.h
similarity index 99%
rename from src/libpolarDecomposition/polarDecompositionGradient.h
rename to libraries/include/polarDecompositionGradient.h
index ca84b4264241f63f2a2b55deff2fd32f092cc3f3..f61a569c9e707df8fd0a849973f6e709158c7895 100644
--- a/src/libpolarDecomposition/polarDecompositionGradient.h
+++ b/libraries/include/polarDecompositionGradient.h
@@ -54,8 +54,6 @@
 
 #include <stdlib.h>
 
-namespace vega
-{
 class PolarDecompositionGradient
 {
 public:
@@ -87,6 +85,6 @@ public:
 protected:
 
 };
-}
+
 #endif
 
diff --git a/libraries/include/profiler.h b/libraries/include/profiler.h
new file mode 100644
index 0000000000000000000000000000000000000000..850a8aa7666b3d264648ba0af41a638d55ddf037
--- /dev/null
+++ b/libraries/include/profiler.h
@@ -0,0 +1,160 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "performanceCounter" library , Copyright (C) 2018 USC                 *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef PROFILER_H
+#define PROFILER_H
+
+#include "stopWatch.h"
+#include "averagingBuffer.h"
+#include <map>
+#include <string>
+
+// a profiling tool that can record timings in multiple parts of the code
+class Profiler
+{
+public:
+  Profiler();
+  virtual ~Profiler() {}
+
+  // start the timer on one section
+  // it does nothing if the timer has already started
+  void startTimer(const std::string & sectionName);
+
+  // stop the timer on one section
+  // it does nothing if there is no such section or the timer has already stopped
+  // when you stop the timer and start it again, new time is added onto the previous time measured on the last stopTimer call()
+  void stopTimer(const std::string & sectionName);
+  // stop the timer that started last
+  void stopLastTimer();
+
+  // return the elapsed time on one section
+  // return 0.0 if that section does not exist
+  double getTime(const std::string & sectionName);
+
+  void startExtraTimer(const std::string & sectionName);
+  void stopExtraTimer(const std::string & sectionName);
+
+  // clear all stored timers
+  void clear();
+
+  // return a string that writes the timing
+  // the format is:
+  // =============== PROFILING ================
+  // <SECTION A>: 10.00 s                 5.00%
+  // <SECTION B>: 20.00 s                10.00%
+  // <SECTION C>: 170.00 s               85.00%
+  // Overall: 200.00 s
+  // ==========================================
+  std::string toString();
+
+protected:
+  std::map<std::string, StopWatch> timer;
+  std::map<std::string, StopWatch>::iterator lastTimer;
+  std::map<std::string, StopWatch> extraTimer;
+};
+
+// a profiling tool that records timing on multiple sections in a iterative program
+class IterationProfiler : public Profiler
+{
+public:
+  IterationProfiler(int averageBufferSize = 20, bool keepHistory = false);
+  virtual ~IterationProfiler() {}
+
+  // tell the profiler that one iteration is finished
+  // calling this function will reset all timers and their time is stored in 
+  // corresponding averaging buffer and history buffer (if keepHistory is on) and maxTime is updated
+  virtual void finishOneIteration();
+  // set the number of recent iterations used to calculate average timing; default is 20
+  void setAverageBufferSize(int bufferSize);
+
+  // clear all stored data
+  void clear();
+
+  // return a string that writes the timing
+  // the format is:
+  // =============== PROFILING ================
+  // <SECTION A>: 10.00 ms                 5.00%
+  // <SECTION B>: 20.00 ms                10.00%
+  // <SECTION C>: 170.00 ms               85.00%
+  // Overall: 200.00 ms
+  // ==========================================
+  std::string toString();
+
+  bool saveHistory(const std::string & filename) const;
+
+protected:
+  int bufferSize;
+  struct ProfilingData
+  {
+    ProfilingData(int bufferSize) : avgBuffer(bufferSize), maxTime(0) {}
+    AveragingBuffer avgBuffer;
+    double maxTime;
+    std::vector<double> history;
+  };
+  std::map<std::string, ProfilingData> profilingData;
+
+  bool keepHistory;
+  int numIterInHistory;
+  int numIterRecorded;
+};
+
+// helper profiler measure timing on a section
+// by using destructor function, it stops the timer whenever it goes out of the code block
+
+class ProfilerSection
+{
+public:
+  ProfilerSection(Profiler * profiler, const std::string & sectionName) : p(profiler) { if (p) { name = sectionName; } }
+  virtual ~ProfilerSection() { stop(); }
+
+  void start() { if (p) p->startTimer(name); }
+  void stop() { if (p) p->stopTimer(name); }
+
+protected:
+  Profiler * p;
+  std::string name;
+};
+
+class ProfilerExtraSection
+{
+public:
+  ProfilerExtraSection(Profiler * profiler, const std::string & sectionName) : p(profiler) { if (p) { name = sectionName; } }
+  virtual ~ProfilerExtraSection() { stop(); }
+
+  void start() { if (p) p->startExtraTimer(name); }
+  void stop() { if (p) p->stopExtraTimer(name); }
+
+protected:
+  Profiler * p;
+  std::string name;
+};
+#endif
diff --git a/src/libquaternion/quaternion.h b/libraries/include/quaternion.h
similarity index 84%
rename from src/libquaternion/quaternion.h
rename to libraries/include/quaternion.h
index b6392d2bdf3a46b74fa4d50aa98c3ac44032375a..3dcb4e5bbd44d415d464dfecea69d1fd9bf3bd6d 100644
--- a/src/libquaternion/quaternion.h
+++ b/libraries/include/quaternion.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "quaternion" library , Copyright (C) 2007 CMU                         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -50,8 +54,6 @@
 #include <stdio.h>
 #include <math.h>
 
-namespace vega
-{
 // forward declarations for the friend template
 template <typename real> class Quaternion;
 template <typename real> Quaternion<real> operator* (real alpha, Quaternion<real> q2);
@@ -76,23 +78,17 @@ public:
   inline real Gety() const;
   inline real Getz() const;
 
-  inline Quaternion operator+ (const Quaternion q2) const; // q3 = q1+q2
-  inline Quaternion operator- (const Quaternion q2) const; // q3 = q1-q2
-  inline Quaternion operator* (const Quaternion q2) const; // q3 = q1 * q2
-  inline Quaternion operator/ (const Quaternion q2) const; // q3 = q1 / q2
-
-  // Multiply quaternion with a scalar; e.g. q1 = alpha * q2;
-  friend Quaternion<real> operator* (real alpha, const Quaternion<real> q2)
-  {
-    return Quaternion<real>(alpha * q2.s, alpha * q2.x, alpha * q2.y, alpha * q2.z);
-  }
+  inline Quaternion operator+ (const Quaternion & q2) const; // q3 = q1+q2
+  inline Quaternion operator- (const Quaternion & q2) const; // q3 = q1-q2
+  inline Quaternion operator* (const Quaternion & q2) const; // q3 = q1 * q2
+  inline Quaternion operator/ (const Quaternion & q2) const; // q3 = q1 / q2
 
   inline Quaternion conj(); // q2 = q1.conj()
 
-  inline Quaternion & operator= (const Quaternion rhs); // q2 = q1;
+  inline Quaternion & operator= (const Quaternion & rhs); // q2 = q1;
   inline Quaternion & operator= (real s); // sets quaternion equal to the scalar quaternion s
-  inline int operator== (const Quaternion rhs) const; // q2 == q1
-  inline int operator!= (const Quaternion rhs) const; // q2 != q1
+  inline bool operator== (const Quaternion & rhs) const; // q2 == q1
+  inline bool operator!= (const Quaternion & rhs) const; // q2 != q1
 
   void Normalize(); // q.Normalize() scales q such that it is unit size
 
@@ -111,7 +107,7 @@ public:
   // There are two quaternions corresponding to a rotation (and they have opposite signs). You can't directly control which one you get, but you can force the real part to be non-negative by a subsequent call to MoveToRightHalfSphere() .
   // This implementation follows David Baraff's SIGGRAPH course notes:
   // http://www.cs.cmu.edu/~baraff/pbm/pbm.html
-  static Quaternion Matrix2Quaternion(real * R);
+  static Quaternion Matrix2Quaternion(const real R[9]);
   
   // Returns the angle of rotation (in radians), and the unit rotation axis corresponding to the quaternion.
   // Assumes a unit quaternion (use Normalize() to remove any noise due to floating point errors).
@@ -128,6 +124,35 @@ public:
   // Prints the quaternion to stdout.
   inline void Print() const;
 
+  // Multiply quaternion with a scalar; e.g. q1 = alpha * q2;
+  friend Quaternion operator* (real alpha, const Quaternion & q2)
+  {
+    return Quaternion(alpha * q2.s, alpha * q2.x, alpha * q2.y, alpha * q2.z);
+  }
+
+  // Dot product of two quaternions
+  friend double dot(const Quaternion & q1, const Quaternion & q2) 
+  {
+    return q1.s * q2.s + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+  }
+
+  // Negate a quaternion
+  friend Quaternion operator- (const Quaternion & q) 
+  {
+    return (-1) * q;
+  }
+
+  // Inverse quaternion
+  friend Quaternion inv(const Quaternion & q) 
+  {
+    Quaternion nq;
+    nq.s = q.s;
+    nq.x = -q.x;
+    nq.y = -q.y;
+    nq.z = -q.z;
+    return nq / q.Norm2();
+  }
+
 protected:
   real s,x,y,z; 
 
@@ -187,7 +212,7 @@ template <typename real>
 inline real Quaternion<real>::Getz() const { return z; }
 
 template <typename real>
-inline Quaternion<real> & Quaternion<real>::operator= (const Quaternion<real> rhs)
+inline Quaternion<real> & Quaternion<real>::operator= (const Quaternion<real> & rhs)
 {
   s = rhs.s;
   x = rhs.x;
@@ -209,14 +234,14 @@ inline Quaternion<real> & Quaternion<real>::operator= (real s_g)
 }
 
 template <typename real>
-inline int Quaternion<real>::operator== (const Quaternion<real> rhs) const
+inline bool Quaternion<real>::operator== (const Quaternion<real> & rhs) const
 {
   return ((s == rhs.s) && (x == rhs.x) &&
           (y == rhs.y) && (z == rhs.z));
 }
 
 template <typename real>
-inline int Quaternion<real>::operator!= (const Quaternion<real> rhs) const
+inline bool Quaternion<real>::operator!= (const Quaternion<real> & rhs) const
 {
   return ((s != rhs.s) || (x != rhs.x) ||
           (y != rhs.y) || (z != rhs.z));
@@ -242,7 +267,7 @@ inline real Quaternion<real>::Norm2() const
 }
 
 template <typename real>
-inline Quaternion<real> Quaternion<real>::operator+ (const Quaternion<real> q2) const
+inline Quaternion<real> Quaternion<real>::operator+ (const Quaternion<real> & q2) const
 {
   Quaternion<real> w(s + q2.s, x + q2.x, y + q2.y, z + q2.z);
 
@@ -250,14 +275,14 @@ inline Quaternion<real> Quaternion<real>::operator+ (const Quaternion<real> q2)
 }
 
 template <typename real>
-inline Quaternion<real> Quaternion<real>::operator- (const Quaternion<real> q2) const
+inline Quaternion<real> Quaternion<real>::operator- (const Quaternion<real> & q2) const
 {
   Quaternion<real> w(s - q2.s, x - q2.x, y - q2.y, z - q2.z);
   return w;  
 }
 
 template <typename real>
-inline Quaternion<real> Quaternion<real>::operator* (const Quaternion<real> q2) const 
+inline Quaternion<real> Quaternion<real>::operator* (const Quaternion<real> & q2) const 
 {
   Quaternion<real> w(
         s * q2.s - x * q2.x - y    * q2.y - z * q2.z,
@@ -269,7 +294,7 @@ inline Quaternion<real> Quaternion<real>::operator* (const Quaternion<real> q2)
 }
 
 template <typename real>
-inline Quaternion<real> Quaternion<real>::operator/ (const Quaternion<real> q2) const
+inline Quaternion<real> Quaternion<real>::operator/ (const Quaternion<real> & q2) const
 {
   // compute invQ2 = q2^{-1}
   Quaternion<real> invQ2; 
@@ -371,6 +396,6 @@ inline void Quaternion<real>::Print() const
 {
   printf("%f + %fi + %fj + %fk\n",s,x,y,z);
 }
-}
+
 #endif
 
diff --git a/libraries/include/range.h b/libraries/include/range.h
new file mode 100644
index 0000000000000000000000000000000000000000..692611a67ea1207ee5502058c89d72d5a472d78b
--- /dev/null
+++ b/libraries/include/range.h
@@ -0,0 +1,52 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef RANGE_H_
+#define RANGE_H_
+
+// used when you don't have a container but begin(), end() are needed for a range for loop
+template<class InputIt>
+class Range
+{
+public:
+  Range(InputIt a, InputIt b) : a(a), b(b) {}
+  const InputIt & begin() const { return a; }
+  const InputIt & end() const { return b; }
+
+protected:
+  InputIt a, b;
+};
+
+template<class InputIt>
+Range<InputIt> makeRange(InputIt a, InputIt b) { return Range<InputIt>(a, b); }
+
+#endif /* RANGE_H_ */
diff --git a/libraries/include/reversibleHeap.h b/libraries/include/reversibleHeap.h
new file mode 100644
index 0000000000000000000000000000000000000000..65183c2ac28700f8523c69b4ae196e6bf8e2c48a
--- /dev/null
+++ b/libraries/include/reversibleHeap.h
@@ -0,0 +1,151 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef REVERSIBLEHEAP_H
+#define REVERSIBLEHEAP_H
+
+#include <map>
+#include <set>
+#include <cassert>
+#include <cstddef>
+
+// a heap that finds the Key with the smallest Value
+// the input Key must be unique
+// has the ability to find the Key inside the heap (therefore named Reversible)
+template<class Value, class Key>
+class ReversibleHeap
+{
+public:
+  ReversibleHeap() {}
+  virtual ~ReversibleHeap() {}
+
+  typedef std::pair<Value, Key> ValueKeyPair;
+
+  // add (value, key) into the heap, each key must be unique
+  void push(const Value & value, const Key & key);
+
+  void pop();
+
+  // get the top (value, key) pair
+  const ValueKeyPair & top() const { return *setQueue.begin(); }
+
+  void erase(const Key & key);
+
+  // add or update (value, key)
+  void update(const Value & value, const Key & key);
+
+  void clear();
+
+  // get the value associated with key; return NULL if not found
+  const Value * find(const Key & key) const;
+
+  size_t size() const { return setQueue.size(); }
+
+protected:
+
+  typedef std::set<ValueKeyPair> ValueKeySet;
+  typedef typename ValueKeySet::iterator ValueKeySetIter;
+  typedef std::map<Key, ValueKeySetIter> ReverseMap;
+  typedef typename ReverseMap::iterator ReverseMapIter;
+
+  ValueKeySet setQueue;
+  ReverseMap reverseMap;
+};
+
+
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::push(const Value & value, const Key & key)
+{
+  std::pair<ValueKeySetIter, bool> p = setQueue.insert(ValueKeyPair(value, key));
+  assert(p.second == true); // assert no same ValueKeyPair stored before
+  ReverseMapIter it =  reverseMap.find(key);
+  assert(it == reverseMap.end());
+  reverseMap.insert(std::pair<Key, ValueKeySetIter>(key, p.first));
+}
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::update(const Value & value, const Key & key)
+{
+  ReverseMapIter it =  reverseMap.find(key);
+  if (it != reverseMap.end()) // this key has been added before
+  {
+    setQueue.erase(it->second);
+    std::pair<ValueKeySetIter, bool> p = setQueue.insert(ValueKeyPair(value, key));
+    assert(p.second == true); // assert no same ValueKeyPair stored before
+    it->second = p.first; // update the ValueKeySetIter
+  }
+  else
+  {
+    std::pair<ValueKeySetIter, bool> p = setQueue.insert(ValueKeyPair(value, key));
+    assert(p.second == true); // assert no same ValueKeyPair stored before
+    reverseMap.insert(std::pair<Key, ValueKeySetIter>(key, p.first));
+  }
+}
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::pop()
+{
+  ValueKeySetIter it = setQueue.begin();
+  reverseMap.erase(it->second);
+  setQueue.erase(it);
+}
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::erase(const Key & key)
+{
+  ReverseMapIter it =  reverseMap.find(key);
+  if (it != reverseMap.end())
+  {
+    setQueue.erase(it->second);
+    reverseMap.erase(it);
+  }
+}
+
+template<class Value, class Key>
+void ReversibleHeap<Value, Key>::clear()
+{
+  reverseMap.clear();
+  setQueue.clear();
+}
+
+template<class Value, class Key>
+const Value * ReversibleHeap<Value, Key>::find(const Key & key) const
+{
+  typename std::map<Key, ValueKeySetIter>::const_iterator it =  reverseMap.find(key);
+  if (it != reverseMap.end())
+    return &it->second->first; // it->second is a ValueKeySetIter, it->second->first is the value pointed by the ValueKeySetIter
+  return NULL;
+}
+
+
+#endif /* REVERSIBLEHEAP_H_ */
diff --git a/src/librigidBodyDynamics/rigidBody.h b/libraries/include/rigidBody.h
similarity index 97%
rename from src/librigidBodyDynamics/rigidBody.h
rename to libraries/include/rigidBody.h
index 0be34d4327fe346a35fe1b17fb8591c740c9d73f..f1096a63fc7c38d318575ae5375b01f7364d2102 100644
--- a/src/librigidBodyDynamics/rigidBody.h
+++ b/libraries/include/rigidBody.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "rigidBodyDynamics" library , Copyright (C) 2007 CMU                  *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -151,8 +155,6 @@ or 4th order. Add support for impulses.
 #include <string.h>
 #include "quaternion.h"
 
-namespace vega
-{
 class RigidBody
 {
 public:
@@ -483,6 +485,6 @@ inline void RigidBody::ResetBodyToRest()
   angularVelocityX = angularVelocityY = angularVelocityZ = 0;
   angularMomentumX = angularMomentumY = angularMomentumZ = 0;
 }
-}
+
 #endif
 
diff --git a/src/librigidBodyDynamics/rigidBody_generalTensor.h b/libraries/include/rigidBody_generalTensor.h
similarity index 86%
rename from src/librigidBodyDynamics/rigidBody_generalTensor.h
rename to libraries/include/rigidBody_generalTensor.h
index 676f1b4b5e0edf5dd5e609623abcc50a36c7dc70..8914c00588f907bca05b3a1330f7a0abbc7d689a 100644
--- a/src/librigidBodyDynamics/rigidBody_generalTensor.h
+++ b/libraries/include/rigidBody_generalTensor.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "rigidBodyDynamics" library , Copyright (C) 2007 CMU                  *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -44,8 +48,6 @@
 
 #include "rigidBody.h"
 
-namespace vega
-{
 class RigidBody_GeneralTensor : public RigidBody
 {
 public:
@@ -76,6 +78,6 @@ protected:
   double inverseInertiaTensorAtRest[9]; 
 
 };
-}
+
 #endif
 
diff --git a/libraries/include/signedDistanceFieldFromPolygonSoup.h b/libraries/include/signedDistanceFieldFromPolygonSoup.h
new file mode 100644
index 0000000000000000000000000000000000000000..ddd048e3a7a5db62978b86fb169b613ccf32d86b
--- /dev/null
+++ b/libraries/include/signedDistanceFieldFromPolygonSoup.h
@@ -0,0 +1,112 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This class computes a signed distance field from polygon soup geometry, as described in:
+
+  Hongyi Xu, Jernej Barbic: 
+  Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014, Montreal, Canada
+*/
+
+
+#ifndef _SIGNEDDISTANCEFIELDFROMPOLYGONSOUP_H_
+#define _SIGNEDDISTANCEFIELDFROMPOLYGONSOUP_H_
+
+#include <vector>
+#include "distanceField.h"
+#include "distanceFieldNarrowBand.h"
+#include "closestPointField.h"
+#include "objMesh.h"
+
+class SignedDistanceFieldFromPolygonSoup
+{
+public:
+  // if useCubicBox=0, the bounding box will not be a cube, but the aspect ratios of the bounding box will be set so that voxels are cubes, by following the resolutionX, resolutionY, resolutionZ parameters in the Compute* routines below
+  // if useCubicBox=1, the bounding box will be a cube, but the voxels will not necessarily be cubes
+  SignedDistanceFieldFromPolygonSoup(ObjMesh * objMesh, double expansionRatio = 1.5, bool useCubicBox = 1,
+      Vec3d * bbmin = NULL, Vec3d * bbmax = NULL);
+
+  SignedDistanceFieldFromPolygonSoup() {}
+
+  ~SignedDistanceFieldFromPolygonSoup() {}
+
+  // Compute a signed distance field from polygon soup geometry, as described in:
+  // Hongyi Xu, Jernej Barbic: 
+  // Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014, Montreal, Canada
+  // 
+  // An unsigned field will be created first (or loaded from precomputedUnsignedFieldFilename if not NULL). 
+  // Then, a new mesh is created around the sigma isosurface. Next, a signed field is computed based on this new mesh. Finally, this signed field is offset by -sigma, to approximate the isosurface at the original mesh.
+  // "sigma" is given in absolute units (not grid units)
+  // subtractSigma: whether to subtract sigma from the computed signed distance field (to match the "zero" isosurface) 
+  // computeVoronoiDiagram: whether to compute Voronoi diagram (stored in DistanceField)
+  // closestPointFlag: whether to return a ClosestPointField or not (default: return DistanceField)
+  DistanceField * ComputeDistanceField(int resolutionX, int resolutionY, int resolutionZ, double sigma, int subtractSigma = 1,
+      bool computeVoronoiDiagram = false, int maxTriCount = 15, int maxDepth = 10, int closestPointFlag = 0,
+      const char * precomputedUnsignedFieldFilename = NULL);
+
+  DistanceField * ComputeDistanceField(const ObjMesh *mesh, DistanceField *field, double sigma, int subtractSigma = 1, bool computeVoronoiDiagram = false, int maxTriCount = 15, int maxDepth = 10, int closestPointFlag = 0);
+
+  // Compute a distance field in a narrow band around the geometry
+  // bandwidth: the width of the band (in either direction) around the geometry where the distance field is computed
+  // "bandwidth" and "sigma" are given in absolute units (not grid units)
+  DistanceFieldNarrowBand * ComputeDistanceFieldNarrowBand(int resolutionX, int resolutionY, int resolutionZ, double bandwidth,
+      double sigma, int subtractSigma = 1, int maxTriCount = 15, int maxDepth = 10, const char * precomputedUnsignedFieldFilename = NULL);
+
+protected:
+  ObjMesh * objMesh;
+  DistanceField *unsignedField;
+
+  double expansionRatio;
+  bool useCubicBox;
+  Vec3d bbmin, bbmax;
+  bool autoBoundingBox;
+
+  //remove the interior components inside the isosurface 
+  ObjMesh * RemoveInteriorComponents(ObjMesh * isoMesh);
+
+  //construct the pseudo-normal for each component for pseudo-normal test
+  bool ConstructPseudoNormalComponents(ObjMesh * isoMesh, std::vector<TriangleWithCollisionInfoAndPseudoNormals*>* triangleList);
+
+  //pseudo-normal test to determine the relation between two components
+  bool CheckInAndOutViaNormalTest(std::vector<TriangleWithCollisionInfoAndPseudoNormals*>* triangleList, int compIndex1, int compIndex2);
+
+  //pre-removal via bounding box test. If a bounding box is completely inside another bounding box, the smaller one can be removed.
+  bool CheckInAndOutViaBoundingBox(std::vector<Vec3d> & bmin, std::vector<Vec3d> & bmax, int compIndex1, int compIndex2);
+
+  //extract isosurface using marching cubes from unsigned distance field. 
+  ObjMesh * ComputeIsosurface(DistanceFieldBase * distanceField, double sigma);
+
+  bool setBoundingBox(DistanceFieldBase* field, int resolutionX, int resolutionY, int resolutionZ);
+};
+
+#endif
+
diff --git a/src/libsparseMatrix/sparseMatrix.h b/libraries/include/sparseMatrix.h
similarity index 67%
rename from src/libsparseMatrix/sparseMatrix.h
rename to libraries/include/sparseMatrix.h
index 6a632e80b0f01ec0964c43ea5a5ab21af79258ec..c8e49c08652efc08fb35bdfc33f940c7a698df54 100644
--- a/src/libsparseMatrix/sparseMatrix.h
+++ b/libraries/include/sparseMatrix.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseMatrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseMatrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -101,8 +105,6 @@
 #include <vector>
 #include <map>
 
-namespace vega
-{
 class SparseMatrix;
 
 class SparseMatrixOutline
@@ -110,26 +112,33 @@ class SparseMatrixOutline
 public:
   // makes an empty sparse matrix with numRows rows
   SparseMatrixOutline(int numRows);
-  ~SparseMatrixOutline();
+  virtual ~SparseMatrixOutline();
 
   // makes a diagonal numRows x numRows sparse matrix; with a constant diagonal
   SparseMatrixOutline(int numRows, double diagonal);
   // makes a diagonal numRows x numRows sparse matrix; diagonal is a vector of n numbers
-  SparseMatrixOutline(int numRows, double * diagonal);
+  SparseMatrixOutline(int numRows, const double * diagonal);
 
   // loads the sparse matrix from a text file
   // if expand is greater than 1, the routine also expands each element into a diagonal block of size expand x expand... 
   //   (expand option is useful for loading the mass matrix in structural mechanics (with expand=3 in 3D))
-  SparseMatrixOutline(char * filename, int expand=1); 
+  SparseMatrixOutline(const char * filename, int expand=1); 
 
   // save matrix to a text file
-  int Save(char * filename, int oneIndexed=0) const;
+  int Save(const char * filename, int oneIndexed=0) const;
 
+  // initialize the matrix to be an empty matrix with numRows rows
+  void Initialize(int numRows);
   // add entry at location (i,j) in the matrix
   void AddEntry(int i, int j, double value=0.0);
-  void AddBlock3x3Entry(int i, int j, double * matrix3x3); // matrix3x3 should be given in row-major order
-  // add a block (sparse) matrix (optionally multiplied with "scalarFactor"), starting at row i, and column j
-  void AddBlockMatrix(int i, int j, const SparseMatrix * block, double scalarFactor=1.0);
+  // remove entry at location (i,j)
+  void RemoveEntry(int i, int j);
+  // add a 3x3 matrix at location (3*i, 3*j)
+  void AddBlock3x3Entry(int i, int j, const double * matrix3x3); // matrix3x3 should be given in row-major order
+  void AddBlock3x3Entry(int i, int j, double value=0.0); // matrix is assigned the value at all 3x3 elements
+  // add a block (sparse) matrix (optionally multiplied with "scalarFactor"), starting at row i, and column j, can expand each element of block into a diagonal block of size "expand"
+  void AddBlockMatrix(int i, int j, const SparseMatrix * block, double scalarFactor=1.0, int expand=1);
+  void AddBlockMatrix(int i, int j, const SparseMatrixOutline & block, double scalarFactor=1.0);
   void IncreaseNumRows(int numAddedRows); // increases the number of matrix rows (new rows are added at the bottom of the matrix, and are all empty)
 
   void MultiplyRow(int row, double scalar); // multiplies all elements in row 'row' with scalar 'scalar'
@@ -143,6 +152,7 @@ public:
 
   // low-level routine which is rarely used
   inline const std::map<int,double> & GetRow(int i) const { return columnEntries[i]; }
+  inline std::map<int,double> & GetRow(int i) { return columnEntries[i]; }
   friend class SparseMatrix;
 
 protected:
@@ -155,14 +165,25 @@ class SparseMatrix
 {
 public:
 
-  SparseMatrix(char * filename); // load from text file (same text file format as SparseMatrixOutline)
+  SparseMatrix(const char * filename); // load from text file (same text file format as SparseMatrixOutline)
   SparseMatrix(SparseMatrixOutline * sparseMatrixOutline); // create it from the outline
+  // create it by specifying all entries: number of rows, length of each row, indices of columns of non-zero entries in each row, values of non-zero entries in each row
+  // column indices in each row must be sorted (ascending)
+  // if shallowCopy=1, the class will not allocate its own internal buffers, but will assume ownership of the input rowLength, columnIndices and columnEntries parameters
+  SparseMatrix(int numRows, int * rowLength, int ** columnIndices, double ** columnEntries, int shallowCopy=0); 
+  SparseMatrix(int numSubMatrices, const SparseMatrix ** subMatrices, const int * numColumns); // construct a diagonal block matrix from submatrices
+
   SparseMatrix(const SparseMatrix & source); // copy constructor
-  ~SparseMatrix();
+  virtual ~SparseMatrix();
 
-  int Save(char * filename, int oneIndexed=0) const; // save matrix to a disk text file 
+  int Save(const char * filename, int oneIndexed=0) const; // save matrix to a disk text file 
 
-  int SaveToMatlabFormat(char * filename) const; // save matrix to a text file that can be imported into Matlab
+  // save matrix to a text file that can be imported into Matlab; Related Matlab commands: 
+  // load matname.dat;       # read the stored sparse matrix from disk
+  // M = spconvert(matname); # convert the loaded matrix to standard Matlab sparse format
+  // spy(M);                 # check matrix sparsity
+  // Mf = full(M);           # convert the sparse matrix to a full matrix
+  int SaveToMatlabFormat(const char * filename) const; 
 
   // set/add value to the j-th sparse entry in the given row (NOT to matrix element at (row,j))
   inline void SetEntry(int row, int j, double value) { columnEntries[row][j] = value; }
@@ -190,9 +211,13 @@ public:
   double SumEntries() const; // returns the sum of all matrix entries
   void SumRowEntries(double * rowSums) const; // returns the sum of all entries in each row
   double GetMaxAbsEntry() const; // max abs value of a matrix entry
-  double GetInfinityNorm() const; // matrix infinity norm
+  double GetInfinityNorm() const; // matrix infinity norm: maximum absolute row sum of the matrix
+  double GetFrobeniusNorm() const; // matrix Frobenius norm: sqrt of the sum of absolute squares of its elements
   void Print(int sparsePrint=0) const; // prints the matrix out to standard output
   double GetRowNorm2(int row) const;
+  bool HasInf() const; // has positive infinity or negative infinity
+  bool HasNaN() const; // has not-a-number
+  bool HasInfOrNaN() const; // has positive infinity, negative infinity or not-a-number
 
   // matrix algebra (all involved matrices must have the same pattern of non-zero entries)
   SparseMatrix operator+(const SparseMatrix & mat2) const;
@@ -202,6 +227,11 @@ public:
   SparseMatrix & operator*=(const double alpha);
   SparseMatrix & operator+=(const SparseMatrix & mat2);
   SparseMatrix & operator-=(const SparseMatrix & mat2);
+  bool operator==(const SparseMatrix & mat2) const;
+  bool operator!=(const SparseMatrix & mat2) const;
+  // check whether two sparse matrices share the same size and locations of non-zero entries
+  bool SameStructure(const SparseMatrix & mat2) const;
+  
   void ScalarMultiply(const double alpha, SparseMatrix * dest=NULL); // dest = alpha * dest (if dest=NULL, operation is applied to this object)
   void ScalarMultiplyAdd(const double alpha, SparseMatrix * dest=NULL); // dest += alpha * dest (if dest=NULL, operation is applied to this object)
   void MultiplyRow(int row, double scalar); // multiplies all elements in row 'row' with scalar 'scalar'
@@ -220,8 +250,8 @@ public:
   double QuadraticForm(const double * vector) const;
   // normalizes vector in the M-norm: vector := vector / sqrt(<M * vector, vector>)  (assumes symmetric M)
   void NormalizeVector(double * vector) const;
-  void ConjugateMatrix(double * U, int r, double * MTilde); // computes MTilde = U^T M U (M can be a general square matrix, U need not be a square matrix; number of columns of U is r; sizes of M and U must be such that product is defined; output matrix will have size r x r, stored column-major)
-  SparseMatrix ConjugateMatrix(SparseMatrix & U, int verbose=0); // computes U^T M U (M is this matrix, and can be a general square matrix, U need not be a square matrix; sizes of M and U must be such that product is defined)
+  void ConjugateMatrix(const double * U, int r, double * MTilde) const; // computes MTilde = U^T M U (M can be a general square matrix, U need not be a square matrix; number of columns of U is r; sizes of M and U must be such that product is defined; output matrix will have size r x r, stored column-major)
+  SparseMatrix ConjugateMatrix(const SparseMatrix & U, int verbose=0, int numColumns=-1) const; // computes U^T M U (M is this matrix, and can be a general square matrix, U need not be a square matrix; sizes of M and U must be such that product is defined); numColumns is the number of columns of the result (U^T M U); this is important because there could be empty columns in U at the right border of the U matrix; if numColumns=-1, the routine will use U->GetNumColumns()
 
   // builds indices for subsequent faster product computation (below)
   // input: U, MTilde; MTilde must equal U^T M U, computed using the "ConjugateMatrix" routine above
@@ -245,36 +275,39 @@ public:
   void MakeLinearColumnIndexArray(int * indices) const;
   void MakeLinearColumnIndexArray(double * indices) const;
 
-  // make a dense matrix (column-major LAPACK style storage)
+  // Make a dense matrix (column-major storage).
   // (this can be a huge matrix for large sparse matrices)
-  // storage in denseMatrix must be pre-allocated
-  void MakeDenseMatrix(double * denseMatrix) const;
+  // Storage in denseMatrix must be pre-allocated.
+  // The size of the denseMatrix is: this->GetNumRows() x (numColumns == -1 ? this->GetNumColumns() : numColumns).
+  void MakeDenseMatrix(double * denseMatrix, int numColumns=-1) const;
   // also transposes the matrix:
   void MakeDenseMatrixTranspose(int numColumns, double * denseMatrix) const;
 
   // removes row(s) and column(s) from the matrix
   void RemoveRowColumn(int rowColumn); // 0-indexed
-  void RemoveRowsColumns(int numRemovedRowColumns, int * removedRowColumns, int oneIndexed=0); // the rowColumns must be sorted (ascending)
-  void RemoveRowsColumnsSlow(int numRemovedRowColumns, int * removedRowColumns, int oneIndexed=0); // the rowColumns need not be sorted
+  void RemoveRowsColumns(int numRemovedRowColumns, const int * removedRowColumns, int oneIndexed=0); // the rowColumns must be sorted (ascending)
+  void RemoveRowsColumnsSlow(int numRemovedRowColumns, const int * removedRowColumns, int oneIndexed=0); // the rowColumns need not be sorted
 
   // removes row(s) from the matrix
   void RemoveRow(int row); // 0-indexed
-  void RemoveRows(int numRemovedRows, int * removedRows, int oneIndexed=0); // rows must be sorted (ascending)
-  void RemoveRowsSlow(int numRemovedRows, int * removedRows, int oneIndexed=0); // the rows need not be sorted
+  void RemoveRows(int numRemovedRows, const int * removedRows, int oneIndexed=0); // rows must be sorted (ascending)
+  void RemoveRowsSlow(int numRemovedRows, const int * removedRows, int oneIndexed=0); // the rows need not be sorted
 
   // removes column(s) from the matrix
   void RemoveColumn(int column); // 0-indexed
-  void RemoveColumns(int numRemovedColumns, int * removedColumns, int oneIndexed=0); // columns must be sorted (ascending)
-  void RemoveColumnsSlow(int numRemovedColumns, int * removedColumns, int oneIndexed=0); // columns need not be sorted 
+  void RemoveColumns(int numRemovedColumns, const int * removedColumns, int oneIndexed=0); // columns must be sorted (ascending)
+  void RemoveColumnsSlow(int numRemovedColumns, const int * removedColumns, int oneIndexed=0); // columns need not be sorted 
 
   void IncreaseNumRows(int numAddedRows); // increases the number of matrix rows (new rows are added at the bottom of the matrix, and are all empty)
-  void SetRows(SparseMatrix * source, int startRow, int startColumn=0); // starting with startRow, overwrites the rows with those of matrix "source"; data is written into columns starting at startColumn
-  void AppendRowsColumns(SparseMatrix * source); // appends the matrix "source" at the bottom of matrix, and trans(source) to the right of the matrix
+  void SetRows(const SparseMatrix * source, int startRow, int startColumn=0); // starting with startRow, overwrites the rows with those of matrix "source"; data is written into columns starting at startColumn
+  void AppendRowsColumns(const SparseMatrix * source); // appends the matrix "source" at the bottom of matrix, and trans(source) to the right of the matrix
+  void AppendRows(const SparseMatrix * source);
 
   // transposition (note: the matrix need not be symmetric)
   void BuildTranspositionIndices();
   void FreeTranspositionIndices();
   // returns the list position of the transposed element (row, list position j)
+  // must first call BuildTranspositionIndices()
   inline int TransposedIndex(int row, int j) const { return transposedIndices[row][j]; }
 
   // returns the transposed matrix
@@ -282,43 +315,55 @@ public:
   // this is important in case there are zero columns at the end of the matrix
   // if numColumns=-1 (default), GetNumColumns() will be called; however, this will lead to a transposed matrix with a fewer number of rows in case of empty columns at the end of the original matrix
   SparseMatrix * Transpose(int numColumns=-1);
+  // assign a transposed matrix AT to this matrix; topology of AT must be transpose of topology of this matrix
+  // note: this function calls BuildTranspositionIndices internally, so you don't need to call BuildTranspositionIndices first
+  void AssignTransposedMatrix(SparseMatrix & AT);
 
   // checks if the matrix is skew-symmetric
   // the non-zero entry locations must form a symmetric pattern
   // returns max ( abs ( A^T + A ) ) = || A^T + A ||_{\infty}
+  // note: this function calls BuildTranspositionIndices internally, so you don't need to call BuildTranspositionIndices first
   double SkewSymmetricCheck(); 
   // makes matrix symmetric by copying upper triangle + diagonal into the lower triangle
   // the non-zero entry locations must form a symmetric pattern
+  // note: this function calls BuildTranspositionIndices internally, so you don't need to call BuildTranspositionIndices first
   void SymmetrizeMatrix();
 
   // pre-computes the sparse columns of diagonal matrix entries
   // this routine will accelerate subsequent GetDiagonal or AddDiagonalMatrix calls, but is not necessary for GetDiagonal or AddDiagonalMatrix
   void BuildDiagonalIndices();
   void FreeDiagonalIndices();
-  void GetDiagonal(double * diagonal);
+  void GetDiagonal(double * diagonal) const;
   void AddDiagonalMatrix(double * diagonalMatrix);
   void AddDiagonalMatrix(double constDiagonalElement);
 
-  //
-  // Build sub matrix indices is used for pair of matrices where the sparsity of one matrix is a subset of another matrix (i.e. mass matrix and 
-  // stiffness matrix). Also, the two matrics have to have the same dimension (i.e. number of rows and columns).
-  // Build super matrix indices is used for pair of matrices with rows/columns removed
-  //
-  // add a matrix to the current matrix, whose elements are a subset of the elements of the current matrix
-  // call this once to establish the correspondence
-  void BuildSubMatrixIndices(SparseMatrix & mat2, int subMatrixID=0);
+  // Build submatrix indices is used for pair of matrices where the sparsity of one matrix is a subset of another matrix (for example, mass matrix and stiffness matrix). 
+  // It allows you to assign/add a submatrix to the current matrix.
+  // The submatrix begins at row "startRow" and column "startColumn" of this matrix.
+  // submatrixID allows you to keep several submatrices at once
+  // Call this once to establish the correspondence:
+  void BuildSubMatrixIndices(const SparseMatrix & submatrix, int subMatrixID=0, int startRow=0, int startColumn=0);
   void FreeSubMatrixIndices(int subMatrixID=0);
+  // assign a submatrix to the current matrix, whose elements are a subset of the elements of the current matrix
+  // note: the other entries of the current matrix are unmodified
+  void AssignSubMatrix(const SparseMatrix & submatrix, int subMatrixID=0);
+  // assign the current matrix to a submatrix
+  void AssignToSubMatrix(SparseMatrix & submatrix, int subMatrixID=0) const;
+  // add a matrix to the current matrix, whose elements are a subset of the elements of the current matrix
   // += factor * mat2
   // returns *this
-  SparseMatrix & AddSubMatrix(double factor, SparseMatrix & mat2, int subMatrixID=0);
+  SparseMatrix & AddSubMatrix(double factor, SparseMatrix & submatrix, int subMatrixID=0);
 
-  // copy data from a matrix into a submatrix obtained by a previous call to RemoveRowColumns
+  // Build supermatrix indices is used for pair of matrices with rows/columns removed.
+  // It allows you to assign a super matrix to the current matrix.
   // oneIndexed: tells whether the fixed rows and columns are specified 1-indexed or 0-indexed
   // First, call BuildSuperMatrixIndices once to inialize (all fixed rows and columns are indexed with respect the superMatrix):
-  void BuildSuperMatrixIndices(int numFixedRowColumns, int * fixedRowColumns, SparseMatrix * superMatrix, int oneIndexed=0); // use this version if the indices of removed rows and columns are the same
-  void BuildSuperMatrixIndices(int numFixedRows, int * fixedRows, int numFixedColumns, int * fixedColumns, SparseMatrix * superMatrix, int oneIndexed=0); // allows arbitrary row and column indices
-  // Then, call this (potentially many times) to quickly assign the values at the appropriate places in the submatrix:
-  void AssignSuperMatrix(SparseMatrix * superMatrix);
+  void BuildSuperMatrixIndices(int numFixedRowColumns, const int * fixedRowColumns, const SparseMatrix * superMatrix, int oneIndexed=0); // use this version if the indices of removed rows and columns are the same
+  void BuildSuperMatrixIndices(int numFixedRows, const int * fixedRows, int numFixedColumns, const int * fixedColumns, const SparseMatrix * superMatrix, int oneIndexed=0); // allows arbitrary row and column indices
+  // Then, call this (potentially many times) to quickly assign the values at the appropriate places in the submatrix.
+  // For example, you can use this to copy data from a matrix into a submatrix obtained by a previous call to RemoveRowColumns.
+  void AssignSuperMatrix(const SparseMatrix & superMatrix);
+  void FreeSuperMatrixIndices();
 
   // returns the total number of non-zero entries in the lower triangle (including diagonal)
   int GetNumLowerTriangleEntries() const;
@@ -366,25 +411,23 @@ protected:
 
   /*
     numSubMatrixIDs specifies how many sub-matrix relationships we have
-    length(subMatrixIndices) == length(subMatrixIndexLengths) == (numSubMatrixIDs + 1)
-
-    length(subMatrixIndexLengths[subMatrixID]) ==
-    length(subMatrixIndices[subMatrixID]) == number of rows = numRows
-
-    length(subMatrixIndices[subMatrixID][rowIndex]) ==
-    subMatrixIndexLengths[subMatrixID][rowIndex]
-   */
+    length(subMatrixIndices) == length(subMatrixIndexLengths) == length(subMatrixStartRow) == (numSubMatrixIDs + 1)
+    length(subMatrixIndexLengths[subMatrixID]) == length(subMatrixIndices[subMatrixID]) == number of rows = numRows
+    length(subMatrixIndices[subMatrixID][rowIndex]) == subMatrixIndexLengths[subMatrixID][rowIndex]
+  */
   int numSubMatrixIDs;
   int *** subMatrixIndices;
   int ** subMatrixIndexLengths;
+  int * subMatrixStartRow;
+  int * subMatrixNumRows;
 
   int ** superMatrixIndices;
   int * superRows;
 
   void InitFromOutline(SparseMatrixOutline * sparseMatrixOutline);
   void Allocate();
-  void BuildRenumberingVector(int nConstrained, int nSuper, int numFixedDOFs, int * fixedDOFs, int ** superDOFs, int oneIndexed=0);
+  void FreeAuxiliaryData();
 };
-}
+
 #endif
 
diff --git a/src/libsparseSolver/sparseSolverAvailability.h b/libraries/include/sparseSolverAvailability.h
similarity index 100%
rename from src/libsparseSolver/sparseSolverAvailability.h
rename to libraries/include/sparseSolverAvailability.h
diff --git a/src/libsparseSolver/sparseSolvers.h b/libraries/include/sparseSolvers.h
similarity index 79%
rename from src/libsparseSolver/sparseSolvers.h
rename to libraries/include/sparseSolvers.h
index 782360fb11af7497ce490b0b96e41881ca92ed6c..0dfe921cbd2eddc61fd6e974a96beb899450f805 100644
--- a/src/libsparseSolver/sparseSolvers.h
+++ b/libraries/include/sparseSolvers.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 1.9.8                             *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
  * http://www.jernejbarbic.com/code                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/libraries/include/stencilForceModel.h b/libraries/include/stencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..76b4f73014ca45a7a12b5ffb1dd3c117aaeffb58
--- /dev/null
+++ b/libraries/include/stencilForceModel.h
@@ -0,0 +1,98 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STENCILFORCEMODEL_H_
+#define _STENCILFORCEMODEL_H_
+
+#include <vector>
+
+/*
+  To simulate a deformable object, we usually discretize it into elements.
+  For example, a 3D solid can be represented by a set of tetrahedra,
+  and a cloth can be represented by a set of triangles. When we simulate 
+  an object, we need to evaluate the elastic energy, internal forces
+  and sometimes the tangent stiffness matrix. These quantities are usually
+  evaluated by computing them for each individual element
+  and then assembled into a single global per-object vector.
+  For example, when we compute the elastic energy of a tetrahedral mesh,
+  we can compute the energy of each tetrahedron first and then add them together.
+  In summary, the entire evaluation process can be considered as
+  an assembling of quantities at the individual units. We call the units "stencils".
+  In cloth simulation, a stencil is a single triangle for in-plane stretch and shear.
+  Another stencil type is an edge between two triangles to model the bending energy.
+  In FEM solid simulation, each element can be treated as a stencil.
+  In mass-spring systems, each spring can be treated as a stencil.
+  In this file, we define a class called StencilForceModel that provides an 
+  abstract interface to stencils.
+*/
+
+class StencilForceModel
+{
+public:
+  StencilForceModel() {}
+  virtual ~StencilForceModel() {}
+
+  // Get the number of degrees of freedom (DOF) of the object (= # vertices x 3).
+  int Getn3() const { return n3; }
+  // Return the number of different types of stencils within a single object.
+  int GetNumStencilTypes() const { return (int)numStencilsInDifferentTypes.size(); }
+  // Return the number of stencils for each type.
+  int GetNumStencils(int stencilType) const { return numStencilsInDifferentTypes[stencilType]; }
+  // Return the number of vertices involved in a single stencil.
+  int GetNumStencilVertices(int stencilType) const { return numStencilVerticesInDifferentTypes[stencilType]; }
+  // Return the number of DOFs of the internal forces in a single stencil.
+  int GetStencilInternalForceSize(int stencilType) const { return numStencilVerticesInDifferentTypes[stencilType] * 3; }
+  // Return the size of the stiffness matrix of a single stencil.
+  int GetStencilStiffnessMatrixSize(int stencilType) const { return GetStencilInternalForceSize(stencilType) * GetStencilInternalForceSize(stencilType); }
+  // Compute the energy, internal forces, tangent stiffness matrix of stencil stencilId in type stencilType.
+  // Parameter u is the displacement vector of object vertices in R^n3.
+  // Energy points to a double value.
+  // internalForces points to a dense vector.
+  // tangentStiffnessMatrix points to a dense column-major matrix
+  // The pointers energy, internalForces and tangentStiffnessMatrix can be nullptr, in which case the corresponding quantity will not be computed.
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) = 0;
+  
+  // Return an array of vertex indices that a stencil 'stencilId' in type 'stencilType' involves.
+  // Typically, a tetrahedron involves 4 vertices. The return pointer will point to an array with 4 integers.
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const = 0;
+
+  // Vertex gravity.
+  virtual void GetVertexGravityForce(int vertexId, double gravity[3]) { gravity[0] = gravity[1] = gravity[2] = 0.0; }
+
+protected:
+  std::vector<int> numStencilsInDifferentTypes;
+  std::vector<int> numStencilVerticesInDifferentTypes;
+  int n3;
+};
+
+#endif
+
diff --git a/libraries/include/stopWatch.h b/libraries/include/stopWatch.h
new file mode 100644
index 0000000000000000000000000000000000000000..5574bcd3e43678a88002b0459f45057841e87f94
--- /dev/null
+++ b/libraries/include/stopWatch.h
@@ -0,0 +1,202 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "performanceCounter" library , Copyright (C) 2018 USC                 *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef STOPWATCH_H
+#define STOPWATCH_H
+
+/*
+  === A stopwatch to measure code execution time. ===
+  Modified from performanceCounter.h
+
+  Designed for real-time system (e.g., real-time computer animation, haptics),
+  but useful in general. You can time arbitrary segments of your code.
+  Same interface under Windows, Linux and Mac OS X.
+
+  Under Linux/MAC OS X, accuracy is that of gettimeofday function,
+  which gives time in seconds and microseconds.
+  In practice, it has been accurate down to microsecond range.
+
+  Under Windows, the counter uses the QueryPerformanceCounter Windows API call.
+  Again, accuracy has been in the microsecond range in practice.
+*/
+
+#if defined(_WIN32) || defined(WIN32)
+#include <windows.h>
+#include <cstdlib>
+#include <cstdio>
+#else
+#include <cstdlib>
+#include <cstdio>
+#include "sys/time.h"
+#endif
+
+class StopWatch
+{
+public:
+  // initialize a stopped watch 
+  inline StopWatch();
+
+  // start counting
+  // have no effect if the watch has already started
+  inline void start();
+
+  // stop counting and store the elapsed time
+  // can be resumed by calling start() again
+  // have no effect if the watch has already stopped
+  inline void stop();
+
+  // stop the watch and reset internal timer
+  inline void reset();
+
+  // If the watch has stopped, return the stored time
+  // otherwise, return the stored time + the time from the recent start() to this getElapsedTime() call
+  inline double getElapsedTime();
+
+  inline bool hasStarted() const { return stopped == 0; }
+  inline bool hasStopped() const { return stopped; }
+
+protected:
+  inline double getTime();
+
+#if defined(_WIN32) || defined(WIN32)
+  LARGE_INTEGER timerFrequency;
+  LARGE_INTEGER startCount,stopCount;
+#else
+  long startCountSec,stopCountSec,startCountMicroSec,stopCountMicroSec;
+#endif
+  double savedTime;
+  int stopped;
+};
+
+// helper class for measuring the time of a scope
+class FunctionTimer
+{
+public:
+  FunctionTimer(StopWatch & w) : watch(&w) { watch->start(); }
+  ~FunctionTimer() { watch->stop(); }
+protected:
+  StopWatch * watch;
+};
+
+// helper class to print time of a function
+class ScopeTimeReporter
+{
+public:
+  ScopeTimeReporter(const char * scopeName) : scopeName(scopeName) { watch.start(); }
+  ~ScopeTimeReporter() { watch.stop(); printf("%s: %lfs\n", scopeName, watch.getElapsedTime());}
+protected:
+  const char * scopeName;
+  StopWatch watch;
+};
+
+#define REPORT_FUNCTION_TIME ScopeTimeReporter __function_timer(__func__)
+
+
+//////////////////////////////////////////////////////////
+//                   IMPLEMENTATION
+//////////////////////////////////////////////////////////
+
+inline StopWatch::StopWatch()
+{
+#if defined(_WIN32) || defined(WIN32)
+  QueryPerformanceFrequency(&timerFrequency);
+  startCount.QuadPart = stopCount.QuadPart = 0;
+#else
+  startCountSec = stopCountSec = startCountMicroSec = stopCountMicroSec = 0.0;
+#endif
+  savedTime = 0.0;
+  stopped = 1;
+}
+
+inline void StopWatch::start()
+{
+  if (stopped == 0)
+    return;
+  stopped = 0;
+
+#if defined(_WIN32) || defined(WIN32)
+  LARGE_INTEGER count;
+  QueryPerformanceCounter(&count);
+#else
+  struct timeval tv;
+  gettimeofday(&tv,NULL);
+#endif
+#if defined(_WIN32) || defined(WIN32)
+  startCount = count;
+#else
+  startCountSec = tv.tv_sec;
+  startCountMicroSec = tv.tv_usec;
+#endif
+}
+
+inline double StopWatch::getElapsedTime() 
+{
+  if (stopped)
+    return savedTime;
+
+  double currentTime = getTime();
+  return savedTime + currentTime;
+}
+
+inline void StopWatch::stop() 
+{
+  double currentTime = getTime();
+  if (stopped)
+    return;
+
+  stopped = 1;
+  savedTime += currentTime;
+}
+
+inline void StopWatch::reset()
+{
+  savedTime = 0.0;
+  stopped = 1;
+}
+
+inline double StopWatch::getTime() 
+{
+#if defined(_WIN32) || defined(WIN32)
+  QueryPerformanceCounter(&stopCount);
+  return ((double)(stopCount.QuadPart - startCount.QuadPart))
+        / ((double)timerFrequency.QuadPart);
+#else
+  struct timeval tv;
+  gettimeofday(&tv,NULL);
+  stopCountSec = tv.tv_sec;
+  stopCountMicroSec = tv.tv_usec;
+  return 1.0 * (stopCountSec-startCountSec) + 1E-6 * (stopCountMicroSec - startCountMicroSec);
+#endif
+}
+
+
+#endif /* STOPWATCH_H */
diff --git a/libraries/include/stringHelper.h b/libraries/include/stringHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..8694a332be58c6d509834f5fc94766eb366702cf
--- /dev/null
+++ b/libraries/include/stringHelper.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef STRINGHELPER_H
+#define STRINGHELPER_H
+
+#include <string>
+
+// check with str ends with substr
+bool endWith(const std::string & str, const std::string & substr);
+// version with case-insensitive comparison
+bool iendWith(const std::string & str, const std::string & substr);
+bool iendWith(const std::string & str, const char * substr);
+
+// strip a string, removing white-space characters at the beginning and end of the string
+// if the string contains only white-space characters, return an empty string
+void stripSelf(std::string & s);
+std::string strip(const std::string & s);
+
+// strip a c-string s with minimal modification to the memory
+// remove the end white-space and return the beginning of the stripped s
+char * stripLight(char * s);
+
+inline void skipSpace(char * & s)
+{
+  while(isspace(*s))
+    s++;
+}
+
+// converts a string to upper case
+void upperCase(char * s);
+void upperCase(std::string & s);
+
+#endif
diff --git a/src/libvolumetricMesh/tetMesh.h b/libraries/include/tetMesh.h
old mode 100755
new mode 100644
similarity index 65%
rename from src/libvolumetricMesh/tetMesh.h
rename to libraries/include/tetMesh.h
index 758ce17d9c5e8de593e66d897c01634328f78f09..c8c1a004413ad1259c1a00cf6dd39a82a7a1428b
--- a/src/libvolumetricMesh/tetMesh.h
+++ b/libraries/include/tetMesh.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,17 +40,23 @@
 #define _TETMESH_H_
 
 #include "volumetricMesh.h"
+#include "vec4i.h"
 
 // see also volumetricMesh.h for a description of the routines
 
-namespace vega
-{
 class TetMesh : public VolumetricMesh
 {
 public:
-  // loads the mesh from a text file 
-  // (.veg input formut, see documentation and the provided examples)
-  TetMesh(char * filename, int verbose=1);
+  // loads the mesh from a file 
+  // ASCII: .veg text input format, see documentation and the provided examples
+  // BINARY: .vegb binary input format
+  TetMesh(const char * filename, fileFormatType fileFormat = ASCII, int verbose=1);
+
+  // load from a stream
+  // if memoryLoad is 0, binaryStream is FILE* (load from a file), otherwise, it is char* (load from a memory buffer)
+  TetMesh(void * binaryStream, int memoryLoad = 0);
+  
+  TetMesh(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2, const Vec3d & p3);
 
   // constructs a tet mesh from the given vertices and elements, 
   // with a single region and material ("E, nu" material)
@@ -55,6 +65,8 @@ public:
   TetMesh(int numVertices, double * vertices,
          int numElements, int * elements,
          double E=1E6, double nu=0.45, double density=1000);
+  TetMesh(const std::vector<Vec3d> & vertices, const std::vector<Vec4i> & elements, 
+    double E=1E6, double nu=0.45, double density=1000);
 
   // constructs a tet mesh from the given vertices and elements, 
   // with an arbitrary number of sets, regions and materials
@@ -74,7 +86,7 @@ public:
   //   the ".ele" and ".node" format, used by TetGen, 
   //   "filename" is the basename, e.g., passing "mesh" will load the mesh from "mesh.ele" and "mesh.node" 
   // default material parameters will be used
-  TetMesh(char * filename, int specialFileType, int verbose); 
+  TetMesh(const char * filename, int specialFileType, int verbose); 
 
   // creates a mesh consisting of the specified element subset of the given TetMesh
   TetMesh(const TetMesh & mesh, int numElements, int * elements, std::map<int,int> * vertexMap = NULL);
@@ -83,15 +95,25 @@ public:
   virtual VolumetricMesh * clone();
   virtual ~TetMesh();
 
-  virtual int save(char * filename) const;
+  virtual int saveToAscii(const char * filename) const;
+  // saves the mesh to binary format
+  // returns: 0 = success, non-zero = error
+  // output: if bytesWritten is non-NULL, it will contain the number of bytes written 
+  virtual int saveToBinary(const char * filename, unsigned int * bytesWritten = NULL) const;
+  virtual int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten = NULL, bool countBytesOnly = false) const;
+
+  using VolumetricMesh::exportMeshGeometry;
+  void exportMeshGeometry(std::vector<Vec3d> & vertices, std::vector<Vec4i> & tets) const;
 
  // === misc queries ===
 
-  static const VolumetricMesh::elementType elementType() { return elementType_; }
+  static VolumetricMesh::elementType elementType() { return elementType_; }
   virtual VolumetricMesh::elementType getElementType() const { return elementType(); }
 
-  static double getTetVolume(Vec3d * a, Vec3d * b, Vec3d * c, Vec3d * d);
-  static double getTetDeterminant(Vec3d * a, Vec3d * b, Vec3d * c, Vec3d * d);
+  static double getSignedTetVolume(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+  static double getTetVolume(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+  static double getTetDeterminant(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+
   virtual double getElementVolume(int el) const;
   virtual void getElementInertiaTensor(int el, Mat3d & inertiaTensor) const;
   virtual void computeElementMassMatrix(int element, double * massMatrix) const;
@@ -104,7 +126,10 @@ public:
 
  // === interpolation ===
 
-  virtual void computeBarycentricWeights(int el, Vec3d pos, double * weights) const;
+  static void computeBarycentricWeights(const Vec3d tetVertexPos[4], const Vec3d & pos, double weights[4]);
+  static void computeBarycentricWeights(const Vec3d & tetVtxPos0, const Vec3d & tetVtxPos1, const Vec3d & tetVtxPos2, const Vec3d & tetVtxPos3, 
+    const Vec3d & pos, double weights[4]);
+  virtual void computeBarycentricWeights(int el, const Vec3d & pos, double * weights) const;
   void computeGradient(int element, const double * U, int numFields, double * grad) const; // for tet meshes, gradient is constant inside each tet, hence no need to specify position
   virtual void interpolateGradient(int element, const double * U, int numFields, Vec3d pos, double * grad) const; // conforms to the virtual function in the base class, "pos" does not affect the computation
 
@@ -113,12 +138,11 @@ public:
   void orient(); // orients the tets (re-orders vertices within each tet), so that each tet has positive orientation: ((v1 - v0) x (v2 - v0)) dot (v3 - v0) >= 0
 
 protected:
-  void computeElementMassMatrixHelper(Vec3d a, Vec3d b, Vec3d c, Vec3d d, double * buffer);
   static const VolumetricMesh::elementType elementType_;
   TetMesh(int numElementVertices): VolumetricMesh(numElementVertices) {}
 
   friend class VolumetricMeshExtensions;
 };
-}
+
 #endif
 
diff --git a/libraries/include/tetMesher.h b/libraries/include/tetMesher.h
new file mode 100644
index 0000000000000000000000000000000000000000..50df5b875bdb6315a19c17de7538cf58d101cab1
--- /dev/null
+++ b/libraries/include/tetMesher.h
@@ -0,0 +1,253 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Implements a constrained 3D Delaunay tet mesher with refinement,
+  using the methods from:
+
+  Hang Si. Adaptive tetrahedral mesh generation by constrained Delaunay refinement. 
+  International Journal for Numerical Methods in Engineering, 75:856–880, 2008.
+
+  Hang Si and Klaus Gartner. 3d boundary recovery by constrained delaunay tetrahedralization. 
+  International Journal for Numerical Methods in Engineering, 85(11):1341–1364, 2011.
+
+  The input is a manifold and self-intersection-free triangle mesh,
+  and the output is a quality tet mesh of the volume enclosed by the input mesh
+  that conforms to the input mesh.
+  This mesh is generated using constrained Delaunay tetrahedralization,
+  and by inserting new vertices (``Steiner vertices'') into the mesh as needed
+  to satisy the quality conditions.
+
+  Note: The tet mesher is experimental. It may generate sliver tets with some inputs.
+*/
+
+#ifndef _TETMESHER_H_
+#define _TETMESHER_H_
+
+#include <vector>
+#include <set>
+#include <queue>
+
+#include "vec3d.h"
+#include "objMesh.h"
+#include "tetMesh.h"
+#include "delaunayMesher.h"
+
+class TetMesher
+{
+public:
+
+  TetMesher();
+  virtual ~TetMesher();
+
+  // compute the tet mesh, using constrained 3D Delaunay tetrahedralization, with refinement
+  // refinementQuality is a scalar; one must use refinementQuality >= 1
+  // the smaller the value, the more the mesh will be refined
+  // minDihedralAngle: the minimal dihedral angle in a tet on the result tet mesh should not be larger than "minDihedralAngle"
+  // maxSteinerVertices controls how many vertices are added in the refinement process. < 0 means no limitation.
+  // alpha: the smallest distance between a new Steiner vertex and the existing vertices should be larger than alpha times 
+  // the average edge length of the input mesh
+  // maxTimeSeconds: the routine will terminate if this computation time (in seconds) is exceeded. < 0 means no limitation.
+  TetMesh * compute(ObjMesh * surfaceMesh, double refinementQuality = 1.1, double alpha = 2.0, double minDihedralAngle = 0.0, 
+      int maxSteinerVertices = -1, double maxTimeSeconds = -1.0);
+
+  inline int getNumAddedSteinerVertices() const { return numSteinerVertices; }
+
+protected:
+
+  typedef DelaunayMesher::DelaunayBall DelaunayBall;
+  typedef DelaunayMesher::BallCIter BallCIter;
+
+
+  // Delaunay ball with information of radius/minEdge and minimal dihedral angle
+  struct DelaunayBallWithRefineInfo : public DelaunayBall
+  {
+    double minDihedral;       // Not valid for infinite ball. The minimal dihedral angel of the tetrahedral. Lazy compute
+    double edgeQuality;       // Not valid for infinite ball. Square of the minimal edge of the tetrahedral. Lazy compute
+    DelaunayBallWithRefineInfo(const DelaunayBall & parent);
+  protected:
+    DelaunayBallWithRefineInfo(const DelaunayBallWithRefineInfo &);
+  };
+
+  // A set of delaunay balls with information of radius/minEdge and minimal dihedral angle
+  struct TetMeshWithRefineInfo
+  {
+    static DelaunayMesher::DelaunayBallCompare defaultComparor;
+    TetMeshWithRefineInfo(const DelaunayMesher & delaunayMesher);
+    ~TetMeshWithRefineInfo();
+
+    struct DelaunayBallEdgeRefineCMP           // sort radius/minEdge descend
+    {
+      bool operator () (const DelaunayBallWithRefineInfo * const & p1, const DelaunayBallWithRefineInfo * const & p2);
+    };
+
+
+    struct DelaunayBallAngleRefineCMP         // sort dihedral angel ascend
+    {
+      bool operator () (const DelaunayBallWithRefineInfo * const & p1, const DelaunayBallWithRefineInfo * const & p2);
+    };
+
+    typedef std::set<const DelaunayBallWithRefineInfo *, DelaunayBallEdgeRefineCMP>::const_iterator EdgeRefineIterator;
+    typedef std::set<const DelaunayBallWithRefineInfo *, DelaunayBallAngleRefineCMP>::const_iterator AngleRefineIterator;
+    inline EdgeRefineIterator getEdgeRefineBegin() { return edgeRefineBalls.begin(); }
+    inline EdgeRefineIterator getEdgeRefineEnd() { return edgeRefineBalls.end(); }
+    inline EdgeRefineIterator getAngleRefineBegin() { return angleRefineBalls.begin(); }
+    inline EdgeRefineIterator getAngleRefineEnd() { return angleRefineBalls.end(); }
+
+    // Get Tetmesh from the set of delaunayBall.
+    // Callee will alloc the memory and caller is responsible to free the memory
+    TetMesh * getTetMesh();
+
+    // Compute the refine information such as edge quality and dihedral angle
+    // Alloc memory and deep copy inside, free memory in deconstruc function and remove function
+    void insert(const DelaunayMesher::DelaunayBall * delaunayBall);
+
+    // Remove the delaunay ball that is removed in the last refinement. Free memory inside
+    void remove(const DelaunayMesher::DelaunayBall * delaunayBall);
+
+    // Enable the minimal dihedral angle refinement
+    inline void enableAngleRefine() { enabledAngleRefine = true; }
+    //friend class TetMesher;
+  protected:
+
+    bool enabledAngleRefine; // Whether the angle refinement is enabled
+    const DelaunayMesher & delaunayMesher; // Reference to the delaunayMesher, used to get the positions of vertices
+    std::set<const DelaunayBallWithRefineInfo *, DelaunayBallEdgeRefineCMP> edgeRefineBalls; // Sort the current delaunay ball by radius/minEdge descendingly
+    std::set<const DelaunayBallWithRefineInfo *, DelaunayBallAngleRefineCMP> angleRefineBalls; // Sort the current delaunay ball by diheral angle  aescendingly
+    std::map<const DelaunayMesher::DelaunayBall *, EdgeRefineIterator, DelaunayMesher::DelaunayBallCompare> edgeRefineMap; // Look up the iterator in edgeRefineSet by pointer
+    std::map<const DelaunayMesher::DelaunayBall *, AngleRefineIterator, DelaunayMesher::DelaunayBallCompare> angleRefineMap;     // Look up the iterator in angleRefineSet by pointer
+  } resultTetMesh;
+
+  // construct a constrained delaunay mesh, if recovery is true, the face recovery process will be done
+  int initializeCDT(bool recovery = true);
+  // refine a constrained delaunay mesh
+
+  /*
+    Refine one tet with bad edge quality, radius/minEdge
+    Ret value
+    0: refined
+    1: No tet to refine due to enough steiner points
+    2: No bad tet to refine
+  */
+  int refineEdge(double refinementQuality, const double minmimalDist);
+
+  /*
+    Refine one tet with bad dihedral angle, too small dihedral angle
+    Ret value
+    0: refined
+    1: No tet to refine due to enough steiner points
+    2: No bad tet to refine
+  */
+  int refineAngle(const double angleBound, const double minmimalDist);
+
+  // Make sure the first four vertices of the triangle mesh are not on the same plane
+  static bool renumberInitialVertices(ObjMesh * surfaceMesh);
+
+  //remove all tetrahedrons that are outside the constrained delaunay mesh
+  int removeOutside();
+
+  // insert a tet into the tetmesh
+  void insertTet(const int * v);
+  // remove a tet from the tetmesh
+  void removeTet(const int * v);
+
+  // insert a face into the tetmesh
+  void insertFace(int f1, int f2, int f3);
+  // remove a face from the tetmesh
+  void removeFace(int f1, int f2, int f3);
+  // insert an edge into the tetmesh
+  void insertEdge(int e1, int e2);
+  // remove an edge from the tetmesh
+  void removeEdge(int e1, int e2);
+
+  // get a face from the input triangular mesh
+  UTriKey getFace(int index);
+
+  // Do face recovery process
+  void faceRecovery();
+
+  // form a missing region by one face, the boundary of the missing face is on the current tet, the triangles in the region are all missing
+  void formRegion(int face, std::set<int> &region);
+  // calculate the boundary of a tet mesh, triangles that has only one neighbor tetrahedron
+  void calculateTetBoundary(std::vector<DelaunayMesher::DelaunayBall*> &tet, std::vector<UTriKey> &boundary);
+  // calculate the boundary of a missing region, the region consists of connected triangles, boundary is a set of edges that have only one neighbor triangle
+  void calculateTriangleBoundary(std::set<int> &missingRegion, std::set<std::pair<int, int> > &boundary);
+  // build a map for triangular mesh, which two triangles are the neigbhor of an edge
+  void buildTriangleNeighbor(std::vector<UTriKey> &mesh, std::map<std::pair<int, int>, std::pair<int, int> > &neighbor);
+  // insert the missing region into the cavity formed by removing all tetrahedrons that intersect the missing region, and then form two cavities
+  bool formTwoCavities(std::vector<UTriKey> &missingRegion, std::set<std::pair<int, int> >& boundary, std::map<std::pair<int, int>, std::pair<int, int> > &neighbor);
+  // fill a hole defined by a set of boundary triangles, compute what tetrahedrons need adding
+  bool fillHole(std::vector<UTriKey> &holeBoundary);
+
+  // determine whether the point is too close to any existent vertices in the current mesh
+  bool isTooCloseToOtherVertices(const Vec3d & r, const double minimalDist) const;
+
+  // Get the steiner point in segement recovery.
+  Vec3d getSteinerPoint(const UEdgeKey & edge, const bool isAcute1, const bool isAcute2, const double lfs1, const double lfs2, const double c = 2) const;
+
+  // segment recovery
+  int segmentRecovery();
+
+  // Used to compute delaunay mesh
+  DelaunayMesher delaunay;
+  // the input triangular mesh
+  ObjMesh * objMesh;
+
+  // the set of triangles that are in the input triangular mesh but not in the volumetric mesh
+  std::vector<UTriKey> lost;
+  // the indices of triangles that are in a missing region
+  std::set<int> region;
+
+  // for the input triangular mesh, which three triangles are the neigbhors of a triangle. All triangles are described by their indices
+  std::vector<std::vector<int> > neighborSurface;
+
+  // Tetradedrons added into the tet mesh to fill a hole
+  std::vector <std::vector<int> > toAdd;
+
+  // Triangles in the volumetric mesh, key is the triangle and value is how many tets are associated with the triangle
+  std::map<UTriKey, unsigned> trianglesInTet;
+
+  // flip the input objmesh to fit the delaunay mesh in case failure in the segment recovery
+  int flipSurface();
+
+  // edges in the volumetric mesh, key is the edge and value is how many tets are associated with the edge
+  std::map<UEdgeKey, unsigned> edgesInTet;
+
+  // the number of steiner points inserted (refinement)
+  int numSteinerVertices;
+
+  // recursion depth for face recovery;
+  int faceRecoveryDepth;
+};
+
+#endif /* TETMESHER_H_ */
+
diff --git a/libraries/include/triangleTetIntersection.h b/libraries/include/triangleTetIntersection.h
new file mode 100644
index 0000000000000000000000000000000000000000..d1ad7b4ec47d56f33cc57294209ab3b11b599453
--- /dev/null
+++ b/libraries/include/triangleTetIntersection.h
@@ -0,0 +1,94 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Intersection between a tet and a triangle.
+*/
+
+#ifndef _TRIANGLETETINTERSECTION_H_
+#define _TRIANGLETETINTERSECTION_H_
+
+#include "vec3d.h"
+
+// This class computes whether a tetrahedron intersects a triangle, excluding just touching.
+class TriangleTetIntersection
+{
+public:
+
+  // does the tetrahedron intersects the triangle, excluding just touching
+  static bool tetrahedronIntersectTriangle(const Vec3d & tet1, const Vec3d & tet2, const Vec3d & tet3, const Vec3d & tet4, const Vec3d & t1, const Vec3d & t2, const Vec3d & t3);
+
+protected:
+  class Tetrahedron
+  {
+  protected:
+    // The positions of the four vertices
+    const Vec3d v0, v1, v2, v3;
+
+    // The normals of the four faces
+    Vec3d n0, n1, n2, n3;
+    // The positions of the four faces
+    double d0, d1, d2, d3;
+  public:
+    Tetrahedron(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2, const Vec3d & v3);
+    // Whether a point is inside the tetrahedron, excluding on the tetrahedron
+    bool contains(const Vec3d & p);
+  };
+
+  class Triangle 
+  {
+  protected:
+    // The positions of the three vertices
+    const Vec3d v0, v1, v2;
+    // The normal
+    Vec3d n;
+    // The position of the face
+    double d;
+    // The two base vectors of the face
+    Vec3d base1, base2;
+  public:
+    Triangle(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2);
+    // Whether a point is inside the triangle, excluding on the triangle
+    bool contains(const Vec3d & p);
+    // Whether the segment starting from l0 and ending at l1 intersects the triangle, excluding just touching
+    bool intersect(const Vec3d & l0, const Vec3d & l1);
+  };
+
+  //compute the determinant of a 3x3 matrix defined by three vectors whose dimensions are all three
+  static inline double det(const Vec3d & a, const Vec3d & b, const Vec3d & c) { return dot(a, cross(b, c)); }
+
+private:
+  TriangleTetIntersection();
+};
+
+#endif /* INTERSECTION_H_ */
+
diff --git a/libraries/include/trilinearInterpolation.h b/libraries/include/trilinearInterpolation.h
new file mode 100644
index 0000000000000000000000000000000000000000..4acd61265a7749e158242df4fc00b2cbcfb40fa8
--- /dev/null
+++ b/libraries/include/trilinearInterpolation.h
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" library , Copyright (C) 2007 CMU, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Trilinear interpolation.
+*/
+
+#define TRILINEAR_INTERPOLATION(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) \
+( (wx) * (wy) * (wz) *             (v111) + \
+  (wx) * (wy) * (1-(wz)) *         (v110) + \
+  (wx) * (1-(wy)) * (wz) *         (v101) + \
+  (wx) * (1-(wy)) * (1-(wz)) *     (v100) + \
+  (1-(wx)) * (wy) * (wz) *         (v011) + \
+  (1-(wx)) * (wy) * (1-(wz)) *     (v010) + \
+  (1-(wx)) * (1-(wy)) * (wz) *     (v001) + \
+  (1-(wx)) * (1-(wy)) * (1-(wz)) * (v000))
+
+#define GRADIENT_COMPONENT_X(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) \
+  (((wy) * (wz) *             (v111) + \
+    (wy) * (1-(wz)) *         (v110) + \
+    (1-(wy)) * (wz) *         (v101) + \
+    (1-(wy)) * (1-(wz)) *     (v100) + \
+    (-1) * (wy) * (wz) *         (v011) + \
+    (-1) * (wy) * (1-(wz)) *     (v010) + \
+    (-1) * (1-(wy)) * (wz) *     (v001) + \
+    (-1) * (1-(wy)) * (1-(wz)) * (v000) ) / gridX)
+
+#define GRADIENT_COMPONENT_Y(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) \
+  (((wx) * (wz) *             (v111) + \
+    (wx) * (1-(wz)) *         (v110) + \
+    (wx) * (-1) * (wz) *         (v101) + \
+    (wx) * (-1) * (1-(wz)) *     (v100) + \
+    (1-(wx)) * (wz) *         (v011) + \
+    (1-(wx)) * (1-(wz)) *     (v010) + \
+    (1-(wx)) * (-1) * (wz) *     (v001) + \
+    (1-(wx)) * (-1) * (1-(wz)) * (v000)) / gridY)
+  
+#define GRADIENT_COMPONENT_Z(wx,wy,wz,v000,v100,v110,v010,v001,v101,v111,v011) \
+  (((wx) * (wy) *                (v111) + \
+    (wx) * (wy) * (-1) *         (v110) + \
+    (wx) * (1-(wy)) *            (v101) + \
+    (wx) * (1-(wy)) * (-1) *     (v100) + \
+    (1-(wx)) * (wy) *            (v011) + \
+    (1-(wx)) * (wy) * (-1) *     (v010) + \
+    (1-(wx)) * (1-(wy)) *        (v001) + \
+    (1-(wx)) * (1-(wy)) * (-1) * (v000)) / gridZ)
+
diff --git a/include/triple.h b/libraries/include/triple.h
similarity index 83%
rename from include/triple.h
rename to libraries/include/triple.h
index 59e8b935d5edfdb505c86f8bdbd250be7fbe0853..b78440133208b6ef709d4c57a316e1a5ad4f780a 100644
--- a/include/triple.h
+++ b/libraries/include/triple.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "triple" include file, Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "triple" include file, Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/libraries/include/uniqueIntegerID.h b/libraries/include/uniqueIntegerID.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4123e0e6d7f1bef3f32ec33996b73b4b983f102
--- /dev/null
+++ b/libraries/include/uniqueIntegerID.h
@@ -0,0 +1,67 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+   Generates unique integer IDs. Makes it possible to release IDs.
+*/
+
+#ifndef _UNIQUEINTEGERID_
+#define _UNIQUEINTEGERID_
+
+#include <set>
+
+class UniqueIntegerID
+{
+public:
+  UniqueIntegerID(unsigned int startID=0);
+
+  // get a unique ID
+  unsigned int Get();
+
+  // registers an already existing ID
+  void Register(unsigned int ID);
+
+  // returns 0 on success, 1 if ID does not exist
+  int Release(unsigned int ID);
+
+  void GetIDs(std::set<unsigned int> & IDs);
+
+  void Clear(unsigned int startID=0); // clears all IDs
+
+protected:
+  unsigned int maxID;
+  std::set<unsigned int> activeIDs;
+  std::set<unsigned int> deletedIDs;
+};
+
+#endif
+
diff --git a/libraries/include/uniqueRecentQueue.h b/libraries/include/uniqueRecentQueue.h
new file mode 100644
index 0000000000000000000000000000000000000000..4bfc7d373eba4d20b3aae1fbaaaf8b5be2cecdda
--- /dev/null
+++ b/libraries/include/uniqueRecentQueue.h
@@ -0,0 +1,106 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef UNIQUERECENTQUEUE_H_
+#define UNIQUERECENTQUEUE_H_
+
+#include "range.h"
+#include <list>
+
+template<class T>
+class UniqueRecentQueue
+{
+public:
+  UniqueRecentQueue(int maxSize) : maxSize(maxSize) {}
+
+  void push(const T & v);
+  void push(T && v);
+  template<typename... _Args>
+  void emplace(_Args &&... __args);
+
+  typename std::list<T>::const_iterator begin() const { return l.begin(); }
+  typename std::list<T>::const_iterator end() const { return l.end(); }
+
+  std::size_t size() const { return l.size(); }
+  Range<typename std::list<T>::const_iterator> last(int numElements) const;
+
+protected:
+  std::list<T> l;
+  int maxSize = 0;
+};
+
+template<class T>
+void UniqueRecentQueue<T>::push(const T & v)
+{
+  if (l.empty())
+  {
+    l.push_back(v);
+  }
+  else if (l.back() != v)
+  {
+    l.push_back(v);
+    if (l.size() > size_t(maxSize))
+      l.pop_front();
+  }
+}
+
+template<class T>
+void UniqueRecentQueue<T>::push(T && v)
+{
+  if (l.empty())
+  {
+    l.push_back(std::move(v));
+  }
+  else if (l.back() != v)
+  {
+    l.push_back(std::move(v));
+    if (l.size() > maxSize)
+      l.pop_front();
+  }
+}
+
+template<class T> template<typename... _Args>
+void UniqueRecentQueue<T>::emplace(_Args &&... __args)
+{
+  l.emplace_back(std::forward<_Args>(__args)...);
+}
+
+template<class T>
+Range<typename std::list<T>::const_iterator> UniqueRecentQueue<T>::last(int numElements) const
+{
+  int n = l.size() - numElements;
+  typename std::list<T>::const_iterator it = l.begin();
+  for(int i = 0; i < n; i++, it++) {}
+  return { it, l.end() };
+}
+
+#endif /* UNIQUERECENTQUEUE_H_ */
diff --git a/libraries/include/valueIndex.h b/libraries/include/valueIndex.h
new file mode 100644
index 0000000000000000000000000000000000000000..21bbc916c2a17edda16580e43b75770e44eab81e
--- /dev/null
+++ b/libraries/include/valueIndex.h
@@ -0,0 +1,82 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "macros" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef VALUEINDEX_H
+#define VALUEINDEX_H
+
+#include <cfloat>
+
+// a simple struct to find the index of an entry with min/max value in a search algorithm
+
+struct MaxValueIndex
+{
+  MaxValueIndex() : value(-DBL_MAX), index(-1) {}
+  // record the new index if the newer value is larger
+  void update(double newValue, int newIndex) { if (newValue > value) { value = newValue; index = newIndex; } }
+  void reset() { value = -DBL_MAX; index = -1; }
+  double value;
+  int index;
+};
+
+struct MinValueIndex
+{
+  MinValueIndex() : value(DBL_MAX), index(-1) {}
+  // record the new index if the newer value is smaller
+  void update(double newValue, int newIndex) { if (newValue < value) { value = newValue; index = newIndex; } }
+  void reset() { value = DBL_MAX; index = -1; }
+  double value;
+  int index;
+};
+
+template<class T>
+struct MaxValueKey
+{
+  MaxValueKey() : value(-DBL_MAX) {} 
+  MaxValueKey(const T & initialKey) : value(-DBL_MAX), key(initialKey) {} 
+  void update(double newValue, const T & newKey) { if (newValue > value) { value = newValue; key = newKey; } }
+  void reset(const T & initialKey) { value = -DBL_MAX; key = initialKey; }
+  double value;
+  T key;
+};
+
+template<class T>
+struct MinValueKey
+{
+  MinValueKey() : value(DBL_MAX) {} 
+  MinValueKey(const T & initialKey) : value(DBL_MAX), key(initialKey) {} 
+  void update(double newValue, const T & newKey) { if (newValue < value) { value = newValue; key = newKey; } }
+  void reset(const T & initialKey) { value = DBL_MAX; key = initialKey; }
+  double value;
+  T key;
+};
+
+#endif
diff --git a/src/libminivector/vec2d.h b/libraries/include/vec2d.h
similarity index 86%
rename from src/libminivector/vec2d.h
rename to libraries/include/vec2d.h
index 467e388b1b68e307638675abbab1265f454a2be3..8ce76c62c8c24be779f68535a10f86ee64329854 100644
--- a/src/libminivector/vec2d.h
+++ b/libraries/include/vec2d.h
@@ -30,15 +30,6 @@
 
   A simple class for vector algebra on 2D vectors 
   (summation, normalization, dot product, etc.).
-
-  Note: this code was inspired by Andrew Willmott's VL and SVL Libraries:
-    http://www.cs.cmu.edu/afs/cs/user/ajw/www/software/index.html#VL
-    (these two libraries contain a lot of useful functionality and
-     are highly recommended)
-  My library offers just the basic vector functionality, hence "minivector".
-  It was written from scratch for a course project at CMU.
-
-  Version 1.2
 */
 
 #ifndef _MINIVEC2D_H_
@@ -47,17 +38,17 @@
 #include <math.h>
 #include <ostream>
 
-namespace vega
-{
 class Vec2d {
 public:
 
   inline Vec2d() {}
-  inline Vec2d(double x, double y) {elt[0]=x; elt[1]=y;}
-  inline Vec2d(double entry); // // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+  inline Vec2d(double x, double y) { elt[0]=x; elt[1]=y; }
+  inline Vec2d(const double v[2]) { elt[0]=v[0]; elt[1]=v[1]; }
+  inline explicit Vec2d(double entry); // // create a vector with all entries "entry" (can create zero vector for entry=0.0)
 
   inline Vec2d & operator=(const Vec2d & source);
-  inline bool operator==(const Vec2d & vec2);
+  inline bool operator==(const Vec2d & vec2) const;
+  inline bool operator!=(const Vec2d & vec2) const;
 
   inline Vec2d operator+ (const Vec2d & vec2);
   inline Vec2d & operator+= (const Vec2d & vec2);
@@ -73,10 +64,12 @@ public:
 
   friend inline Vec2d operator* (double scalar, const Vec2d & vec2);
   friend inline Vec2d operator/ (double scalar, const Vec2d & vec2);
+  friend inline Vec2d operator- (const Vec2d & vec1);
 
   friend inline double dot(const Vec2d & vec1, const Vec2d & vec2); // dot product
 
   friend inline Vec2d norm(const Vec2d & vec1); // returns normalized vector (unit length)
+  inline void normalize(); // normalize itself without returning anything
   friend inline std::ostream &operator << (std::ostream &s, const Vec2d &v);
 
   friend class Mat3d;
@@ -104,12 +97,19 @@ inline Vec2d & Vec2d::operator=(const Vec2d & source)
   return *this;
 }
 
-inline bool Vec2d::operator==(const Vec2d & vec2)
+inline bool Vec2d::operator==(const Vec2d & vec2) const
 {
   return ((elt[0] == vec2[0]) &&
           (elt[1] == vec2[1]));
 }
 
+inline bool Vec2d::operator!=(const Vec2d & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]));
+}
+
+
 inline Vec2d operator* (double scalar, const Vec2d & vec2)
 {
   Vec2d result = vec2;
@@ -128,6 +128,11 @@ inline Vec2d operator/ (double scalar, const Vec2d & vec2)
   return result;
 }
 
+inline Vec2d operator- (const Vec2d & vec1)
+{
+  return vec1 * (-1);
+}
+
 inline Vec2d Vec2d::operator+ (const Vec2d & vec2)
 {
   Vec2d sum = *this;
@@ -215,6 +220,13 @@ inline double len(const Vec2d & vec1)
   return(sqrt(dot(vec1,vec1)));
 }
 
+inline void Vec2d::normalize()
+{
+  double invMag = 1.0 / len(*this);
+  (*this) *= invMag;
+}
+
+
 inline std::ostream &operator << (std::ostream &s, const Vec2d &v)
 {
   double a = v[0];
@@ -222,6 +234,6 @@ inline std::ostream &operator << (std::ostream &s, const Vec2d &v)
   
   return(s << '[' << a << ' ' << b << ']');
 }
-}
+
 #endif
 
diff --git a/libraries/include/vec2i.h b/libraries/include/vec2i.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b2842a0d150d8fb5c328fbcc52ec5aed1bbb0dc
--- /dev/null
+++ b/libraries/include/vec2i.h
@@ -0,0 +1,307 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "minivector" library , Copyright (C) 2018 USC                         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Funding: National Science Foundation                                  *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef VEC2I_H
+#define VEC2I_H
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+
+class Vec2i
+{
+public:
+  inline Vec2i() {}
+  inline Vec2i(int x, int y) {elt[0]=x; elt[1]=y;}
+  inline Vec2i(int entry); // create a vector with all entries "entry" (can create zero vector for entry=0)
+  inline Vec2i(const int vec[2]); // create a vector from the array of three ints pointed to by "vec"
+  inline Vec2i(const Vec2i & vec);
+
+  inline void set(int x0, int x1); // assign vector [x0, x1]
+  inline void set(int value); // set all elements to value
+
+  inline Vec2i & operator=(const Vec2i & source);
+  inline bool operator==(const Vec2i & vec2) const;
+  inline bool operator!=(const Vec2i & vec2) const;
+
+  inline const Vec2i operator+ (const Vec2i & vec2) const;
+  inline Vec2i & operator+= (const Vec2i & vec2);
+
+  inline const Vec2i operator- (const Vec2i & vec2) const;
+  inline Vec2i & operator-= (const Vec2i & vec2);
+
+  inline const Vec2i operator* (int scalar) const;
+  inline Vec2i & operator*= (int scalar);
+
+  inline Vec2i operator/ (int scalar) const;
+  inline Vec2i & operator/= (int scalar);
+
+  // operator for Vec2i to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec2i & vec2) const;
+
+  friend inline Vec2i operator* (int scalar, const Vec2i & vec2);
+  friend inline Vec2i operator/ (int scalar, const Vec2i & vec2);
+  friend inline Vec2i operator- (const Vec2i & vec1);
+
+  friend inline int dot(const Vec2i & vec1, const Vec2i & vec2); // dot product
+
+  friend inline Vec2i cross(const Vec2i & vec1, const Vec2i & vec2); // cross product
+
+  friend inline std::ostream &operator << (std::ostream & s, const Vec2i & v);
+  void print() const;
+
+  inline int & operator[] (int index); // v[i] returns i-th entry of v
+  inline const int & operator[] (int index) const;
+
+  // copy the vector into an array of length 2
+  inline void convertToArray(int vecArray[2]) const;
+  // add the vector into an array of length 2
+  inline void addToArray(int vecArray[2]) const;
+
+  // find the first index in elt which equals to value; return -1 if not found
+  inline int getInvertedIndex(int value) const;
+ 
+  // rotate elt[0] to elt[1] so that elt[newStartIndex] is moved to elt[0]
+  inline void rotate(int newStartIndex);
+
+  // do set intersection; return whether this and vec2 share at least one element
+  inline bool intersect(const Vec2i & vec2) const;
+
+  const int * begin() const { return elt; }
+  int * begin() { return elt; }
+  const int * end() const { return elt + 2; }
+  int * end() { return elt + 2; }
+
+protected:
+  int elt[2];
+};
+
+// === below is the implementation ===
+
+inline Vec2i::Vec2i(int entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+}
+
+inline Vec2i::Vec2i(const int vec[2])
+{
+  elt[0] = vec[0];
+  elt[1] = vec[1];
+}
+
+inline Vec2i::Vec2i(const Vec2i & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+}
+
+inline Vec2i & Vec2i::operator=(const Vec2i & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+
+  return *this;
+}
+
+inline bool Vec2i::operator==(const Vec2i & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]));
+}
+
+inline bool Vec2i::operator!=(const Vec2i & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]));
+}
+
+inline bool Vec2i::operator<(const Vec2i & vec2) const
+{
+  if(elt[0] < vec2[0])
+    return true;
+  if(elt[0] > vec2[0])
+    return false;
+  return elt[1] < vec2[1];
+}
+
+inline Vec2i operator* (int scalar, const Vec2i & vec2)
+{
+  Vec2i result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+
+  return result;
+}
+
+inline Vec2i operator/ (int scalar, const Vec2i & vec2)
+{
+  Vec2i result = vec2;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+
+  return result;
+}
+
+inline Vec2i operator- (const Vec2i & vec1)
+{
+  Vec2i result = vec1;
+  result.elt[0] *= -1;
+  result.elt[1] *= -1;
+
+  return result;
+}
+
+inline const Vec2i Vec2i::operator+ (const Vec2i & vec2) const
+{
+  Vec2i sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+
+  return sum;
+}
+
+inline Vec2i & Vec2i::operator+= (const Vec2i & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+
+  return *this;
+}
+
+inline const Vec2i Vec2i::operator- (const Vec2i & vec2) const
+{
+  Vec2i sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+
+  return sum;
+}
+
+inline Vec2i & Vec2i::operator-= (const Vec2i & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+
+  return *this;
+}
+
+inline int & Vec2i::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const int & Vec2i::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline int dot(const Vec2i & vec1, const Vec2i & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1]);
+}
+
+inline Vec2i & Vec2i::operator*= (int scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  return *this;
+}
+
+inline const Vec2i Vec2i::operator* (int scalar) const
+{
+  return (Vec2i(elt[0] * scalar, elt[1] * scalar));
+}
+
+inline Vec2i Vec2i::operator/ (int scalar) const
+{
+  return (Vec2i(elt[0] / scalar, elt[1] / scalar));
+}
+
+inline Vec2i & Vec2i::operator/= (int scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  return *this;
+}
+
+inline std::ostream &operator << (std::ostream &s, const Vec2i &v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ']');
+}
+
+inline void Vec2i::convertToArray(int vecArray[2]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+}
+
+inline void Vec2i::addToArray(int vecArray[2]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+}
+
+inline void Vec2i::print() const
+{
+  printf("[%d %d]\n", elt[0], elt[1]);
+}
+
+inline void Vec2i::set(int x0, int x1) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+}
+
+inline void Vec2i::set(int value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+}
+
+inline int Vec2i::getInvertedIndex(int value) const
+{
+  if (value == elt[0]) return 0;
+  if (value == elt[1]) return 1;
+  return -1;
+}
+
+inline void Vec2i::rotate(int newStartIndex)
+{
+  if (newStartIndex == 1) // rotate left one time
+  {
+    std::swap(elt[0], elt[1]);
+  }
+}
+
+inline bool Vec2i::intersect(const Vec2i & vec2) const
+{
+  for(int i = 0; i < 2; i++)
+    for(int j = 0; j < 2; j++)
+      if (elt[i] == vec2.elt[j])
+        return true;
+  return false;
+}
+
+#endif
diff --git a/libraries/include/vec3.h b/libraries/include/vec3.h
new file mode 100644
index 0000000000000000000000000000000000000000..7c2c661869867befc6e7698f46a18c21cc5b0bf4
--- /dev/null
+++ b/libraries/include/vec3.h
@@ -0,0 +1,360 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  A simple class for vector algebra on 3D vectors 
+  (summation, normalization, dot product, cross product, etc.).
+*/
+
+#ifndef _MINIVEC3_H_
+#define _MINIVEC3_H_
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+
+// forward declarations for external helper functions
+template <typename real> class Vec3;
+template <typename real> inline Vec3<real> operator* (real alpha, Vec3<real> vec);
+template <typename real> inline Vec3<real> operator/ (real alpha, Vec3<real> vec);
+template <typename real> inline real dot(const Vec3<real> & vec1, const Vec3<real> & vec2); 
+template <typename real> inline Vec3<real> cross(const Vec3<real> & vec1, const Vec3<real> & vec2); // cross product
+template <typename real> inline real len(const Vec3<real> & vec); // length of vector
+template <typename real> inline real len2(const Vec3<real> & vec); // length^2 of vector
+template <typename real> inline Vec3<real> norm(const Vec3<real> & vec); // returns normalized vector (unit length)
+template <typename real> inline std::ostream & operator << (std::ostream & s, const Vec3<real> & v);
+
+typedef Vec3<double> Vec3d;
+typedef Vec3<float> Vec3f;
+
+template <class real>
+class Vec3
+{
+public:
+  inline Vec3() {}
+  inline Vec3(real x, real y, real z) { elt[0]=x; elt[1]=y; elt[2]=z; }
+  inline Vec3(real entry); // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+  inline Vec3(const real * vec); // create a vector from the array of three reals pointed to by "vec"
+  inline Vec3(const Vec3 & vec);
+
+  inline void set(real x0, real x1, real x2); // assign vector [x0, x1, x2]
+  inline void set(real value); // set all elements to value
+
+  inline Vec3 & operator=(const Vec3 & source);
+  inline bool operator==(const Vec3 & vec) const;
+
+  inline const Vec3 operator+ (const Vec3 & vec) const;
+  inline Vec3 & operator+= (const Vec3 & vec);
+
+  inline const Vec3 operator- (const Vec3 & vec) const;
+  inline Vec3 & operator-= (const Vec3 & vec);
+
+  inline const Vec3 operator* (real scalar) const;
+  inline Vec3 & operator*= (real scalar);
+
+  inline Vec3 operator/ (real scalar) const;
+  inline Vec3 & operator/= (real scalar);
+
+  inline void normalize(); // normalize itself 
+
+  void print() const; // print the vector out
+
+  inline real & operator[] (int index); // v[i] returns i-th entry of v
+  inline const real & operator[] (int index) const;
+
+  // finds a unit vector orthogonal to this vector
+  Vec3 findOrthonormalVector() const;
+
+  // copies the vector into an array of length 3
+  inline void convertToArray(real * vecArray) const;
+  
+protected:
+  real elt[3];
+};
+
+// === below is the implementation ===
+
+template <class real>
+inline Vec3<real>::Vec3(real entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+  elt[2] = entry;
+}
+
+template <class real>
+inline Vec3<real>::Vec3(const real * vec)
+{
+  elt[0] = vec[0];
+  elt[1] = vec[1];
+  elt[2] = vec[2];
+}
+
+template <class real>
+inline Vec3<real>::Vec3(const Vec3<real> & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+  elt[2] = vec.elt[2];
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator=(const Vec3<real> & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+  elt[2] = source.elt[2];
+
+  return *this;
+}
+
+template <class real>
+inline bool Vec3<real>::operator==(const Vec3<real> & vec) const
+{
+  return ((elt[0] == vec[0]) &&
+          (elt[1] == vec[1]) &&
+          (elt[2] == vec[2]));
+}
+
+template <class real>
+inline Vec3<real> operator* (real scalar, const Vec3<real> & vec)
+{
+  Vec3<real> result = vec;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+
+  return result;
+}
+
+template <class real>
+inline Vec3<real> operator/ (real scalar, const Vec3<real> & vec)
+{
+  Vec3<real> result = vec;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+  result.elt[2] /= scalar;
+
+  return result;
+}
+
+template <class real>
+inline const Vec3<real> Vec3<real>::operator+ (const Vec3<real> & vec) const
+{
+  Vec3<real> sum = *this;
+  sum.elt[0] += vec.elt[0];
+  sum.elt[1] += vec.elt[1];
+  sum.elt[2] += vec.elt[2];
+
+  return sum;
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator+= (const Vec3<real> & vec)
+{
+  elt[0] += vec.elt[0];
+  elt[1] += vec.elt[1];
+  elt[2] += vec.elt[2];
+
+  return *this;
+}
+
+template <class real>
+inline const Vec3<real> Vec3<real>::operator- (const Vec3<real> & vec) const
+{
+  Vec3<real> sum = *this;
+  sum.elt[0] -= vec.elt[0];
+  sum.elt[1] -= vec.elt[1];
+  sum.elt[2] -= vec.elt[2];
+
+  return sum;
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator-= (const Vec3<real> & vec)
+{
+  elt[0] -= vec.elt[0];
+  elt[1] -= vec.elt[1];
+  elt[2] -= vec.elt[2];
+
+  return *this;
+}
+
+template <class real>
+inline real & Vec3<real>::operator[] (int index)
+{
+  return elt[index];
+}
+
+template <class real>
+inline const real & Vec3<real>::operator[] (int index) const
+{
+  return elt[index];
+}
+
+template <class real>
+inline real dot(const Vec3<real> & vec1, const Vec3<real> & vec2)
+{
+  return (vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2]);
+}
+
+template <class real>
+inline Vec3<real> cross(const Vec3<real> & vec1, const Vec3<real> & vec2)
+{
+  Vec3<real> result(vec1[1] * vec2[2] - vec2[1] * vec1[2],
+                   -vec1[0] * vec2[2] + vec2[0] * vec1[2],
+                    vec1[0] * vec2[1] - vec2[0] * vec1[1]);
+
+  return result;
+}
+
+template <class real>
+inline Vec3<real> norm(const Vec3<real> & vec)
+{
+  real norm2 = dot(vec,vec);
+  Vec3<real> result = vec;
+  result *= 1.0 / sqrt(norm2);
+  
+  return result;
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator*= (real scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  elt[2] *= scalar;
+  return *this;
+}
+
+template <class real>
+inline const Vec3<real> Vec3<real>::operator* (real scalar) const
+{
+  return (Vec3<real>(elt[0]*scalar, elt[1]*scalar, elt[2]*scalar));
+}
+
+template <class real>
+inline Vec3<real> Vec3<real>::operator/ (real scalar) const
+{
+  return (Vec3<real>(elt[0]/scalar, elt[1]/scalar, elt[2]/scalar));
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator/= (real scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  elt[2] /= scalar;
+  return *this;
+}
+
+template <class real>
+inline real len(const Vec3<real> & vec)
+{
+  return(sqrt(dot(vec,vec)));
+}
+
+template <class real>
+inline real len2(const Vec3<real> & vec)
+{
+  return(dot(vec,vec));
+}
+
+template <class real>
+inline std::ostream & operator << (std::ostream & s, const Vec3<real> & v)
+{
+  real a = v[0];
+  real b = v[1];
+  real c = v[2];
+  
+  return(s << '[' << a << ' ' << b << ' ' << c << ']');
+}
+
+template <class real>
+inline void Vec3<real>::convertToArray(real * vecArray) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+}
+
+template <class real>
+inline void Vec3<real>::normalize()
+{
+  real invMag = 1.0 / sqrt(elt[0]*elt[0] + elt[1]*elt[1] + elt[2]*elt[2]);
+  elt[0] *= invMag;
+  elt[1] *= invMag;
+  elt[2] *= invMag;
+}
+
+template <class real>
+inline void Vec3<real>::print() const
+{
+  real a = elt[0];
+  real b = elt[1];
+  real c = elt[2];
+  
+  printf("[%G %G %G]\n", a, b, c);
+}
+
+template <class real>
+inline void Vec3<real>::set(real x0, real x1, real x2) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+}
+
+template <class real>
+inline void Vec3<real>::set(real value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+  elt[2] = value;
+}
+
+// Given an input vector v, find a unit vector that is orthogonal to it 
+template <class real>
+Vec3<real> Vec3<real>::findOrthonormalVector() const
+{
+  // find smallest abs component of v
+  int smallestIndex = 0;
+  for(int dim=1; dim<3; dim++)
+    if (fabs(elt[dim]) < fabs(elt[smallestIndex]))
+      smallestIndex = dim;
+
+  Vec3<real> axis(0.0, 0.0, 0.0);
+  axis[smallestIndex] = 1.0;
+
+  // this cross-product will be non-zero (as long as v is not zero)
+  Vec3<real> result = norm(cross(elt, axis));
+  return result;
+}
+
+#endif
+
diff --git a/src/libminivector/vec3d.h b/libraries/include/vec3d.h
similarity index 63%
rename from src/libminivector/vec3d.h
rename to libraries/include/vec3d.h
index 3d7de01df9645837995ff6a0bd4329923d17ee70..74f63d3091dac8460cc3d77a0e65535bcdec6b75 100644
--- a/src/libminivector/vec3d.h
+++ b/libraries/include/vec3d.h
@@ -29,15 +29,6 @@
 
   A simple class for vector algebra on 3D vectors 
   (summation, normalization, dot product, cross product, etc.).
-
-  Note: this code was inspired by Andrew Willmott's VL and SVL Libraries:
-    http://www.cs.cmu.edu/afs/cs/user/ajw/www/software/index.html#VL
-    (these two libraries contain a lot of useful functionality and
-     are highly recommended)
-  My library offers just the basic vector functionality, hence "minivector".
-  It was written from scratch for a course project at CMU.
-
-  Version 1.2
 */
 
 #ifndef _MINIVEC3D_H_
@@ -46,20 +37,23 @@
 #include <stdio.h>
 #include <math.h>
 #include <ostream>
+#include <cstring>
 
-namespace vega
+class Vec3d
 {
-class Vec3d {
-
 public:
   inline Vec3d() {}
   inline Vec3d(double x, double y, double z) {elt[0]=x; elt[1]=y; elt[2]=z;}
-  inline Vec3d(double entry); // create a vector with all entries "entry" (can create zero vector for entry=0.0)
-  inline Vec3d(const double * vec); // create a vector from the array of three doubles pointed to by "vec"
+  inline explicit Vec3d(double entry); // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+  inline Vec3d(const double vec[3]); // create a vector from the array of three doubles pointed to by "vec"
   inline Vec3d(const Vec3d & vec);
 
+  inline void set(double x0, double x1, double x2); // assign vector [x0, x1, x2]
+  inline void set(double value); // set all elements to value
+
   inline Vec3d & operator=(const Vec3d & source);
   inline bool operator==(const Vec3d & vec2) const;
+  inline bool operator!=(const Vec3d & vec2) const;
 
   inline const Vec3d operator+ (const Vec3d & vec2) const;
   inline Vec3d & operator+= (const Vec3d & vec2);
@@ -73,8 +67,11 @@ public:
   inline Vec3d operator/ (double scalar) const;
   inline Vec3d & operator/= (double scalar);
 
+  // operator for Vec3d to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec3d & vec2) const;
+
   friend inline Vec3d operator* (double scalar, const Vec3d & vec2);
-  friend inline Vec3d operator/ (double scalar, const Vec3d & vec2);
+  friend inline Vec3d operator- (const Vec3d & vec1);
 
   friend inline double dot(const Vec3d & vec1, const Vec3d & vec2); // dot product
 
@@ -84,15 +81,30 @@ public:
   inline void normalize(); // normalize itself without returning anything
 
   friend inline std::ostream &operator << (std::ostream &s, const Vec3d &v);
-  void print();
+  void print() const;
 
   friend class Mat3d;
 
-  inline double & operator[] (int index); // v[i] returns i-th entry of v
-  inline const double & operator[] (int index) const;
+  inline double & operator[] (int i) { return elt[i]; } // v[i] returns i-th entry of v
+  inline const double & operator[] (int i) const { return elt[i]; }
+
+  // allow implicit conversion from Vec3d to const double *
+  operator const double * () const { return &elt[0]; }
+
+  const double * data() const { return &elt[0]; }
+  double * data() { return &elt[0]; }
+
+  // finds a unit vector orthogonal to this vector
+  Vec3d findOrthonormalVector() const;
 
   // copies the vector into an array of length 3
-  inline void convertToArray(double * vecArray);
+  inline void convertToArray(double vecArray[3]) const;
+  // adds the vector into an array of length 3
+  inline void addToArray(double vecArray[3]) const;
+
+  inline bool hasNaN() const;
+
+  inline static bool isNaN(double x);
   
 protected:
   double elt[3];
@@ -102,31 +114,22 @@ protected:
 
 inline Vec3d::Vec3d(double entry)
 {
-  elt[0] = entry;
-  elt[1] = entry;
-  elt[2] = entry;
+  set(entry);
 }
 
-inline Vec3d::Vec3d(const double * vec)
+inline Vec3d::Vec3d(const double vec[3])
 {
-  elt[0] = vec[0];
-  elt[1] = vec[1];
-  elt[2] = vec[2];
+  memcpy(elt, vec, sizeof(double) * 3);
 }
 
 inline Vec3d::Vec3d(const Vec3d & vec)
 {
-  elt[0] = vec.elt[0];
-  elt[1] = vec.elt[1];
-  elt[2] = vec.elt[2];
+  memcpy(elt, vec.elt, sizeof(double) * 3);
 }
 
 inline Vec3d & Vec3d::operator=(const Vec3d & source)
 {
-  elt[0] = source.elt[0];
-  elt[1] = source.elt[1];
-  elt[2] = source.elt[2];
-
+  memcpy(elt, source.elt, sizeof(double) * 3);
   return *this;
 }
 
@@ -137,6 +140,26 @@ inline bool Vec3d::operator==(const Vec3d & vec2) const
           (elt[2] == vec2[2]));
 }
 
+inline bool Vec3d::operator!=(const Vec3d & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]));
+}
+
+inline bool Vec3d::operator<(const Vec3d & vec2) const
+{
+  if(elt[0] < vec2[0]) 
+    return true;
+  if(elt[0] > vec2[0]) 
+    return false;
+  if(elt[1] < vec2[1]) 
+    return true;
+  if(elt[1] > vec2[1]) 
+    return false;
+  return elt[2] < vec2[2];
+}
+
 inline Vec3d operator* (double scalar, const Vec3d & vec2)
 {
   Vec3d result = vec2;
@@ -147,14 +170,9 @@ inline Vec3d operator* (double scalar, const Vec3d & vec2)
   return result;
 }
 
-inline Vec3d operator/ (double scalar, const Vec3d & vec2)
+inline Vec3d operator- (const Vec3d & vec1)
 {
-  Vec3d result = vec2;
-  result.elt[0] /= scalar;
-  result.elt[1] /= scalar;
-  result.elt[2] /= scalar;
-
-  return result;
+  return vec1 * (-1);
 }
 
 inline const Vec3d Vec3d::operator+ (const Vec3d & vec2) const
@@ -195,16 +213,6 @@ inline Vec3d & Vec3d::operator-= (const Vec3d & vec2)
   return *this;
 }
 
-inline double & Vec3d::operator[] (int index)
-{
-  return elt[index];
-}
-
-inline const double & Vec3d::operator[] (int index) const
-{
-  return elt[index];
-}
-
 inline double dot(const Vec3d & vec1, const Vec3d & vec2)
 {
   return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1] + vec1.elt[2] * vec2.elt[2]);
@@ -219,38 +227,25 @@ inline Vec3d cross(const Vec3d & vec1, const Vec3d & vec2)
   return result;
 }
 
-inline Vec3d norm(const Vec3d & vec1)
-{
-  double norm2 = dot(vec1,vec1);
-  Vec3d result = vec1;
-  result *= 1.0 / sqrt(norm2);
-  
-  return result;
-}
-
-inline Vec3d & Vec3d::operator*= (double scalar)
+inline const Vec3d Vec3d::operator* (double scalar) const
 {
-  elt[0] *= scalar;
-  elt[1] *= scalar;
-  elt[2] *= scalar;
-  return *this;
+  return (Vec3d(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar));
 }
 
-inline const Vec3d Vec3d::operator* (double scalar) const
+inline Vec3d Vec3d::operator/ (double scalar) const
 {
-  return (Vec3d(elt[0]*scalar,elt[1]*scalar,elt[2]*scalar));
+  return (Vec3d(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar));
 }
 
-inline Vec3d Vec3d::operator/ (double scalar) const
+inline Vec3d & Vec3d::operator*= (double scalar)
 {
-  return (Vec3d(elt[0]/scalar,elt[1]/scalar,elt[2]/scalar));
+  *this = (*this) * scalar;
+  return *this;
 }
 
 inline Vec3d & Vec3d::operator/= (double scalar)
 {
-  elt[0] /= scalar;
-  elt[1] /= scalar;
-  elt[2] /= scalar;
+  *this = (*this) / scalar;
   return *this;
 }
 
@@ -259,43 +254,79 @@ inline double len(const Vec3d & vec1)
   return(sqrt(dot(vec1,vec1)));
 }
 
+inline Vec3d norm(const Vec3d & vec1)
+{
+  // return vec1 / len(vec1); 
+
+  double norm2 = dot(vec1,vec1);
+  Vec3d result = vec1;
+  result *= 1.0 / sqrt(norm2);
+  
+  return result;
+}
+
 inline double len2(const Vec3d & vec1)
 {
   return(dot(vec1,vec1));
 }
 
-inline std::ostream &operator << (std::ostream &s, const Vec3d &v)
+inline std::ostream &operator << (std::ostream & s, const Vec3d & v)
 {
-  double a = v[0];
-  double b = v[1];
-  double c = v[2];
-  
-  return(s << '[' << a << ' ' << b << ' ' << c << ']');
+  return(s << '[' << v[0] << ' ' << v[1] << ' ' << v[2] << ']');
 }
 
-inline void Vec3d::convertToArray(double * vecArray)
+inline void Vec3d::convertToArray(double vecArray[3]) const
 {
   vecArray[0] = elt[0];
   vecArray[1] = elt[1];
   vecArray[2] = elt[2];
 }
 
+inline void Vec3d::addToArray(double vecArray[3]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+}
+
 inline void Vec3d::normalize()
 {
-  double invMag = 1.0 / sqrt(elt[0]*elt[0] + elt[1]*elt[1] + elt[2]*elt[2]);
-  elt[0] *= invMag;
-  elt[1] *= invMag;
-  elt[2] *= invMag;
+  double invMag = 1.0 / len(*this);
+  (*this) *= invMag;
 }
 
-inline void Vec3d::print()
+inline void Vec3d::print() const
 {
-  double a = elt[0];
-  double b = elt[1];
-  double c = elt[2];
-  
-  printf("[%G %G %G]\n", a, b, c);
+  printf("[%G %G %G]\n", elt[0], elt[1], elt[2]);
 }
+
+inline void Vec3d::set(double x0, double x1, double x2) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
 }
+
+inline void Vec3d::set(double value) // set all elements to value
+{
+  elt[0] = elt[1] = elt[2] = value;
+}
+
+inline bool Vec3d::hasNaN() const
+{
+  return (isNaN(elt[0]) || isNaN(elt[1]) || isNaN(elt[2]));
+}
+
+inline bool Vec3d::isNaN(double x) 
+{ 
+  #ifdef isnan
+    return (isnan(x) != 0);
+  #elif defined(_WIN32)
+    return (_isnan(x) != 0);
+  #else
+    return (x != x); 
+  #endif
+}
+
 #endif
 
diff --git a/libraries/include/vec3i.h b/libraries/include/vec3i.h
new file mode 100644
index 0000000000000000000000000000000000000000..9f3d806a6cae4b0fef54a5364e3e89511eda4ac7
--- /dev/null
+++ b/libraries/include/vec3i.h
@@ -0,0 +1,363 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "minivector" library , Copyright (C) 2018 USC                         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef VEC3I_H
+#define VEC3I_H
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+#include "vec3d.h"
+
+class Vec3i
+{
+public:
+  inline Vec3i() {}
+  inline Vec3i(int x, int y, int z) {elt[0]=x; elt[1]=y; elt[2]=z;}
+  inline Vec3i(int entry); // create a vector with all entries "entry" (can create zero vector for entry=0)
+  inline Vec3i(const int vec[3]); // create a vector from the array of three ints pointed to by "vec"
+  inline Vec3i(const Vec3i & vec);
+
+  inline void set(int x0, int x1, int x2); // assign vector [x0, x1, x2]
+  inline void set(int value); // set all elements to value
+
+  inline Vec3i & operator=(const Vec3i & source);
+  inline bool operator==(const Vec3i & vec2) const;
+  inline bool operator!=(const Vec3i & vec2) const;
+
+  inline const Vec3i operator+ (const Vec3i & vec2) const;
+  inline Vec3i & operator+= (const Vec3i & vec2);
+
+  inline const Vec3i operator- (const Vec3i & vec2) const;
+  inline Vec3i & operator-= (const Vec3i & vec2);
+
+  inline const Vec3i operator* (int scalar) const;
+  inline Vec3i & operator*= (int scalar);
+
+  inline const Vec3i operator/ (int scalar) const;
+  inline Vec3i & operator/= (int scalar);
+
+  inline const Vec3d operator* (double scalar) const;
+  inline const Vec3d operator/ (double scalar) const;
+
+  // operator for Vec3i to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec3i & vec2) const;
+
+  friend inline Vec3i operator* (int scalar, const Vec3i & vec2);
+  friend inline Vec3i operator- (const Vec3i & vec1);
+
+  friend inline int dot(const Vec3i & vec1, const Vec3i & vec2); // dot product
+
+  friend inline Vec3i cross(const Vec3i & vec1, const Vec3i & vec2); // cross product
+
+  friend inline std::ostream &operator << (std::ostream & s, const Vec3i & v);
+  void print() const;
+
+  inline int & operator[] (int index); // v[i] returns i-th entry of v
+  inline const int & operator[] (int index) const;
+
+  // copy the vector into an array of length 3
+  inline void convertToArray(int vecArray[3]) const;
+  // add the vector into an array of length 3
+  inline void addToArray(int vecArray[3]) const;
+
+  // find the first index in elt which equals to value; return -1 if not found
+  inline int getInvertedIndex(int value) const;
+ 
+  // rotate elt[0] to elt[3] so that elt[newStartIndex] is moved to elt[0]
+  inline void rotate(int newStartIndex);
+
+  // do set intersection; return whether this and vec2 share at least one element
+  inline bool intersect(const Vec3i & vec2) const;
+
+  const int * begin() const { return elt; }
+  int * begin() { return elt; }
+  const int * end() const { return elt + 3; }
+  int * end() { return elt + 3; }
+
+protected:
+  int elt[3];
+};
+
+// === below is the implementation ===
+
+inline Vec3i::Vec3i(int entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+  elt[2] = entry;
+}
+
+inline Vec3i::Vec3i(const int vec[3])
+{
+  elt[0] = vec[0];
+  elt[1] = vec[1];
+  elt[2] = vec[2];
+}
+
+inline Vec3i::Vec3i(const Vec3i & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+  elt[2] = vec.elt[2];
+}
+
+inline Vec3i & Vec3i::operator=(const Vec3i & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+  elt[2] = source.elt[2];
+
+  return *this;
+}
+
+inline bool Vec3i::operator==(const Vec3i & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]) &&
+          (elt[2] == vec2[2]));
+}
+
+inline bool Vec3i::operator!=(const Vec3i & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]));
+}
+
+inline bool Vec3i::operator<(const Vec3i & vec2) const
+{
+  if(elt[0] < vec2[0])
+    return true;
+  if(elt[0] > vec2[0])
+    return false;
+  if(elt[1] < vec2[1])
+    return true;
+  if(elt[1] > vec2[1])
+    return false;
+  return elt[2] < vec2[2];
+}
+
+inline Vec3i operator* (int scalar, const Vec3i & vec2)
+{
+  Vec3i result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+
+  return result;
+}
+
+inline Vec3i operator- (const Vec3i & vec1)
+{
+  Vec3i result = vec1;
+  result.elt[0] *= -1;
+  result.elt[1] *= -1;
+  result.elt[2] *= -1;
+
+  return result;
+}
+
+inline const Vec3i Vec3i::operator+ (const Vec3i & vec2) const
+{
+  Vec3i sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+  sum.elt[2] += vec2.elt[2];
+
+  return sum;
+}
+
+inline Vec3i & Vec3i::operator+= (const Vec3i & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+  elt[2] += vec2.elt[2];
+
+  return *this;
+}
+
+inline const Vec3i Vec3i::operator- (const Vec3i & vec2) const
+{
+  Vec3i sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+  sum.elt[2] -= vec2.elt[2];
+
+  return sum;
+}
+
+inline Vec3i & Vec3i::operator-= (const Vec3i & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+  elt[2] -= vec2.elt[2];
+
+  return *this;
+}
+
+inline int & Vec3i::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const int & Vec3i::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline int dot(const Vec3i & vec1, const Vec3i & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1] + vec1.elt[2] * vec2.elt[2]);
+}
+
+inline Vec3i cross(const Vec3i & vec1, const Vec3i & vec2)
+{
+  Vec3i result(vec1.elt[1] * vec2.elt[2] - vec2.elt[1] * vec1.elt[2],
+              -vec1.elt[0] * vec2.elt[2] + vec2.elt[0] * vec1.elt[2],
+               vec1.elt[0] * vec2.elt[1] - vec2.elt[0] * vec1.elt[1]);
+
+  return result;
+}
+
+inline Vec3i & Vec3i::operator*= (int scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  elt[2] *= scalar;
+  return *this;
+}
+
+inline const Vec3i Vec3i::operator* (int scalar) const
+{
+  return (Vec3i(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar));
+}
+
+inline const Vec3i Vec3i::operator/ (int scalar) const
+{
+  return (Vec3i(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar));
+}
+
+inline const Vec3d Vec3i::operator* (double scalar) const
+{
+  return (Vec3d(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar));
+}
+
+inline const Vec3d Vec3i::operator/ (double scalar) const
+{
+  return (Vec3d(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar));
+}
+
+
+inline Vec3i & Vec3i::operator/= (int scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  elt[2] /= scalar;
+  return *this;
+}
+
+inline std::ostream &operator << (std::ostream &s, const Vec3i &v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ' ' << v[2] << ']');
+}
+
+inline void Vec3i::convertToArray(int vecArray[3]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+}
+
+inline void Vec3i::addToArray(int vecArray[3]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+}
+
+inline void Vec3i::print() const
+{
+  printf("[%d %d %d]\n", elt[0], elt[1], elt[2]);
+}
+
+inline void Vec3i::set(int x0, int x1, int x2) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+}
+
+inline void Vec3i::set(int value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+  elt[2] = value;
+}
+
+inline int Vec3i::getInvertedIndex(int value) const
+{
+  if (value == elt[0]) return 0;
+  if (value == elt[1]) return 1;
+  if (value == elt[2]) return 2;
+  return -1;
+}
+
+inline void Vec3i::rotate(int newStartIndex)
+{
+  if (newStartIndex == 1) // rotate left one time
+  {
+    int tmp = elt[0];
+    elt[0] = elt[1];
+    elt[1] = elt[2];
+    elt[2] = tmp;
+  }
+  else if (newStartIndex == 2) // rotate right one time
+  {
+    int tmp = elt[2];
+    elt[2] = elt[1];
+    elt[1] = elt[0];
+    elt[0] = tmp;
+  }
+}
+
+inline bool Vec3i::intersect(const Vec3i & vec2) const
+{
+  for(int i = 0; i < 3; i++)
+    for(int j = 0; j < 3; j++)
+      if (elt[i] == vec2.elt[j])
+        return true;
+  return false;
+}
+
+#endif
diff --git a/libraries/include/vec4d.h b/libraries/include/vec4d.h
new file mode 100644
index 0000000000000000000000000000000000000000..accea7dc8ef301e6cef106b55f82c42455d9f819
--- /dev/null
+++ b/libraries/include/vec4d.h
@@ -0,0 +1,308 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  A simple class for vector algebra on 4D vectors 
+  (summation, normalization, dot product, cross product, etc.).
+*/
+
+#ifndef _MINIVEC4D_H_
+#define _MINIVEC4D_H_
+
+#include <stdio.h>
+#include <cmath>
+#include <ostream>
+#include <cstring>
+
+class Vec4d
+{
+public:
+  inline Vec4d() {}
+  inline Vec4d(double x, double y, double z, double w) {elt[0]=x; elt[1]=y; elt[2]=z; elt[3]=w;}
+  inline explicit Vec4d(double entry); // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+  inline Vec4d(const double vec[4]); // create a vector from the array of four doubles pointed to by "vec"
+  inline Vec4d(const Vec4d & vec);
+
+  inline void set(double x0, double x1, double x2, double x3); // assign vector [x0, x1, x2, x3]
+  inline void set(double value); // set all elements to value
+
+  inline Vec4d & operator=(const Vec4d & source);
+  inline bool operator==(const Vec4d & vec2) const;
+  inline bool operator!=(const Vec4d & vec2) const;
+
+  inline const Vec4d operator+ (const Vec4d & vec2) const;
+  inline Vec4d & operator+= (const Vec4d & vec2);
+
+  inline const Vec4d operator- (const Vec4d & vec2) const;
+  inline Vec4d & operator-= (const Vec4d & vec2);
+
+  inline const Vec4d operator* (double scalar) const;
+  inline Vec4d & operator*= (double scalar);
+
+  inline Vec4d operator/ (double scalar) const;
+  inline Vec4d & operator/= (double scalar);
+
+  // operator for Vec4d to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec4d & vec2) const;
+
+  friend inline Vec4d operator* (double scalar, const Vec4d & vec2);
+  friend inline Vec4d operator/ (double scalar, const Vec4d & vec2);
+  friend inline Vec4d operator- (const Vec4d & vec1);
+
+  friend inline double dot(const Vec4d & vec1, const Vec4d & vec2); // dot product
+
+  friend inline Vec4d norm(const Vec4d & vec1); // returns normalized vector (unit length)
+  inline void normalize(); // normalize itself without returning anything
+
+  friend inline std::ostream &operator << (std::ostream &s, const Vec4d &v);
+  void print() const;
+
+  inline double & operator[] (int i) { return elt[i]; } // v[i] returns i-th entry of v
+  inline const double & operator[] (int i) const { return elt[i]; }
+
+  operator const double * () const { return &elt[0]; }
+
+  // copies the vector into an array of length 4
+  inline void convertToArray(double vecArray[4]) const;
+  // adds the vector into an array of length 4
+  inline void addToArray(double vecArray[4]) const;
+
+  inline bool hasNaN() const;
+  
+protected:
+  double elt[4];
+};
+
+// === below is the implementation ===
+
+inline Vec4d::Vec4d(double entry)
+{
+  set(entry);
+}
+
+inline Vec4d::Vec4d(const double vec[4])
+{
+  memcpy(elt, vec, sizeof(double) * 4);
+}
+
+inline Vec4d::Vec4d(const Vec4d & vec)
+{
+  memcpy(elt, vec.elt, sizeof(double) * 4);
+}
+
+inline Vec4d & Vec4d::operator=(const Vec4d & source)
+{
+  memcpy(elt, source.elt, sizeof(double) * 4);
+  return *this;
+}
+
+inline bool Vec4d::operator==(const Vec4d & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]) &&
+          (elt[2] == vec2[2]) &&
+          (elt[3] == vec2[3]));
+}
+
+inline bool Vec4d::operator!=(const Vec4d & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]) ||
+          (elt[3] != vec2[3]));
+}
+
+inline bool Vec4d::operator<(const Vec4d & vec2) const
+{
+  if(elt[0] < vec2[0]) 
+    return true;
+  if(elt[0] > vec2[0]) 
+    return false;
+  if(elt[1] < vec2[1]) 
+    return true;
+  if(elt[1] > vec2[1]) 
+    return false;
+  if(elt[2] < vec2[2]) 
+    return true;
+  if(elt[2] > vec2[2]) 
+    return false;
+  return elt[3] < vec2[3];
+}
+
+inline Vec4d operator* (double scalar, const Vec4d & vec2)
+{
+  Vec4d result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+  result.elt[3] *= scalar;
+
+  return result;
+}
+
+inline Vec4d operator- (const Vec4d & vec1)
+{
+  return vec1 * (-1);
+}
+
+inline const Vec4d Vec4d::operator+ (const Vec4d & vec2) const
+{
+  Vec4d sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+  sum.elt[2] += vec2.elt[2];
+  sum.elt[3] += vec2.elt[3];
+
+  return sum;
+}
+
+inline Vec4d & Vec4d::operator+= (const Vec4d & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+  elt[2] += vec2.elt[2];
+  elt[3] += vec2.elt[3];
+
+  return *this;
+}
+
+inline const Vec4d Vec4d::operator- (const Vec4d & vec2) const
+{
+  Vec4d sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+  sum.elt[2] -= vec2.elt[2];
+  sum.elt[3] -= vec2.elt[3];
+
+  return sum;
+}
+
+inline Vec4d & Vec4d::operator-= (const Vec4d & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+  elt[2] -= vec2.elt[2];
+  elt[3] -= vec2.elt[3];
+
+  return *this;
+}
+
+inline double dot(const Vec4d & vec1, const Vec4d & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1] + vec1.elt[2] * vec2.elt[2] + vec1.elt[3] * vec2.elt[3]);
+}
+
+inline const Vec4d Vec4d::operator* (double scalar) const
+{
+  return (Vec4d(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar, elt[3] * scalar));
+}
+
+inline Vec4d Vec4d::operator/ (double scalar) const
+{
+  return (Vec4d(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar, elt[3] / scalar));
+}
+
+inline Vec4d & Vec4d::operator*= (double scalar)
+{
+  *this = (*this) * scalar;
+  return *this;
+}
+
+inline Vec4d & Vec4d::operator/= (double scalar)
+{
+  *this = (*this) / scalar;
+  return *this;
+}
+
+inline double len(const Vec4d & vec1)
+{
+  return(sqrt(dot(vec1,vec1)));
+}
+
+inline Vec4d norm(const Vec4d & vec1)
+{
+  return vec1 / len(vec1); 
+}
+
+inline double len2(const Vec4d & vec1)
+{
+  return(dot(vec1,vec1));
+}
+
+inline std::ostream &operator << (std::ostream & s, const Vec4d & v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ' ' << v[2] << ' ' << v[3] << ']');
+}
+
+inline void Vec4d::convertToArray(double vecArray[4]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+  vecArray[3] = elt[3];
+}
+
+inline void Vec4d::addToArray(double vecArray[4]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+  vecArray[3] += elt[3];
+}
+
+inline void Vec4d::normalize()
+{
+  double invMag = 1.0 / len(*this);
+  (*this) *= invMag;
+}
+
+inline void Vec4d::print() const
+{
+  printf("[%G %G %G %G]\n", elt[0], elt[1], elt[2], elt[3]);
+}
+
+inline void Vec4d::set(double x0, double x1, double x2, double x3)
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+  elt[3] = x3;
+}
+
+inline void Vec4d::set(double value)
+{
+  elt[0] = elt[1] = elt[2] = elt[3] = value;
+}
+
+inline bool Vec4d::hasNaN() const
+{
+  return (std::isnan(elt[0]) || std::isnan(elt[1]) || std::isnan(elt[2]) || std::isnan(elt[3]));
+}
+
+#endif
+
diff --git a/libraries/include/vec4i.h b/libraries/include/vec4i.h
new file mode 100644
index 0000000000000000000000000000000000000000..8c206a402077d0786d1fc7c1180cf8ea92fbabd3
--- /dev/null
+++ b/libraries/include/vec4i.h
@@ -0,0 +1,388 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "minivector" library , Copyright (C) 2018 USC                         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef VEC4I_H
+#define VEC4I_H
+
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+#include <algorithm>
+
+class Vec4i
+{
+public:
+  inline Vec4i() {}
+  inline Vec4i(int x, int y, int z, int w) {elt[0]=x; elt[1]=y; elt[2]=z; elt[3]=w; }
+  inline Vec4i(int entry); // create a vector with all entries "entry" (can create zero vector for entry=0)
+  inline Vec4i(const int vec[4]); // create a vector from the array of four ints pointed to by "vec"
+  inline Vec4i(const Vec4i & vec);
+
+  inline void set(int x0, int x1, int x2, int x3); // assign vector [x0, x1, x2, x3]
+  inline void set(int value); // set all elements to value
+
+  inline Vec4i & operator=(const Vec4i & source);
+  inline bool operator==(const Vec4i & vec2) const;
+  inline bool operator!=(const Vec4i & vec2) const;
+
+  inline const Vec4i operator+ (const Vec4i & vec2) const;
+  inline Vec4i & operator+= (const Vec4i & vec2);
+
+  inline const Vec4i operator- (const Vec4i & vec2) const;
+  inline Vec4i & operator-= (const Vec4i & vec2);
+
+  inline const Vec4i operator* (int scalar) const;
+  inline Vec4i & operator*= (int scalar);
+
+  inline Vec4i operator/ (int scalar) const;
+  inline Vec4i & operator/= (int scalar);
+
+  // operator for Vec4i to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec4i & vec2) const;
+
+  friend inline Vec4i operator* (int scalar, const Vec4i & vec2);
+  friend inline Vec4i operator/ (int scalar, const Vec4i & vec2);
+  friend inline Vec4i operator- (const Vec4i & vec1);
+
+  friend inline int dot(const Vec4i & vec1, const Vec4i & vec2); // dot product
+
+  friend inline std::ostream &operator << (std::ostream & s, const Vec4i & v);
+  void print() const;
+
+  inline int & operator[] (int index); // v[i] returns i-th entry of v
+  inline const int & operator[] (int index) const;
+
+  const int * data() const { return &elt[0]; }
+  int * data() { return &elt[0]; }
+
+  // copy the vector into an array of length 4
+  inline void convertToArray(int vecArray[4]) const;
+  // add the vector into an array of length 4
+  inline void addToArray(int vecArray[4]) const;
+
+  // find the first index in elt which equals to value; return -1 if not found
+  inline int getInvertedIndex(int value) const;
+  // rotate elt[0] to elt[4] so that elt[newStartIndex] is moved to elt[0]
+  inline void rotate(int newStartIndex);
+
+  // do set intersection; return whether this and vec2 share at least one element
+  inline bool intersect(const Vec4i & vec2) const;
+
+  const int * begin() const { return elt; }
+  int * begin() { return elt; }
+  const int * end() const { return elt + 4; }
+  int * end() { return elt + 4; }
+
+protected:
+  int elt[4];
+};
+
+// === below is the implementation ===
+
+inline Vec4i::Vec4i(int entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+  elt[2] = entry;
+  elt[3] = entry;
+}
+
+inline Vec4i::Vec4i(const int vec[4])
+{
+  elt[0] = vec[0];
+  elt[1] = vec[1];
+  elt[2] = vec[2];
+  elt[3] = vec[3];
+}
+
+inline Vec4i::Vec4i(const Vec4i & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+  elt[2] = vec.elt[2];
+  elt[3] = vec.elt[3];
+}
+
+inline Vec4i & Vec4i::operator=(const Vec4i & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+  elt[2] = source.elt[2];
+  elt[3] = source.elt[3];
+
+  return *this;
+}
+
+inline bool Vec4i::operator==(const Vec4i & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]) &&
+          (elt[2] == vec2[2]) &&
+          (elt[3] == vec2[3]));
+}
+
+inline bool Vec4i::operator!=(const Vec4i & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]) ||
+          (elt[3] != vec2[3]));
+}
+
+inline bool Vec4i::operator<(const Vec4i & vec2) const
+{
+  if(elt[0] < vec2[0])
+    return true;
+  if(elt[0] > vec2[0])
+    return false;
+  if(elt[1] < vec2[1])
+    return true;
+  if(elt[1] > vec2[1])
+    return false;
+  if(elt[2] < vec2[2])
+      return true;
+  if(elt[2] > vec2[2])
+      return false;
+  return elt[3] < vec2[3];
+}
+
+inline Vec4i operator* (int scalar, const Vec4i & vec2)
+{
+  Vec4i result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+  result.elt[3] *= scalar;
+
+  return result;
+}
+
+inline Vec4i operator/ (int scalar, const Vec4i & vec2)
+{
+  Vec4i result = vec2;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+  result.elt[2] /= scalar;
+  result.elt[3] /= scalar;
+
+  return result;
+}
+
+inline Vec4i operator- (const Vec4i & vec1)
+{
+  Vec4i result = vec1;
+  result.elt[0] *= -1;
+  result.elt[1] *= -1;
+  result.elt[2] *= -1;
+  result.elt[3] *= -1;
+
+  return result;
+}
+
+inline const Vec4i Vec4i::operator+ (const Vec4i & vec2) const
+{
+  Vec4i sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+  sum.elt[2] += vec2.elt[2];
+  sum.elt[3] += vec2.elt[3];
+
+  return sum;
+}
+
+inline Vec4i & Vec4i::operator+= (const Vec4i & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+  elt[2] += vec2.elt[2];
+  elt[3] += vec2.elt[3];
+
+  return *this;
+}
+
+inline const Vec4i Vec4i::operator- (const Vec4i & vec2) const
+{
+  Vec4i sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+  sum.elt[2] -= vec2.elt[2];
+  sum.elt[3] -= vec2.elt[3];
+
+  return sum;
+}
+
+inline Vec4i & Vec4i::operator-= (const Vec4i & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+  elt[2] -= vec2.elt[2];
+  elt[3] -= vec2.elt[3];
+
+  return *this;
+}
+
+inline int & Vec4i::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const int & Vec4i::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline int dot(const Vec4i & vec1, const Vec4i & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1] + vec1.elt[2] * vec2.elt[2] + vec1.elt[3] * vec2.elt[3]);
+}
+
+inline Vec4i & Vec4i::operator*= (int scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  elt[2] *= scalar;
+  elt[3] *= scalar;
+  return *this;
+}
+
+inline const Vec4i Vec4i::operator* (int scalar) const
+{
+  return (Vec4i(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar, elt[3] * scalar));
+}
+
+inline Vec4i Vec4i::operator/ (int scalar) const
+{
+  return (Vec4i(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar, elt[3] / scalar));
+}
+
+inline Vec4i & Vec4i::operator/= (int scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  elt[2] /= scalar;
+  elt[3] /= scalar;
+  return *this;
+}
+
+inline std::ostream &operator << (std::ostream &s, const Vec4i &v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ' ' << v[2] << ' ' << v[3] << ']');
+}
+
+inline void Vec4i::convertToArray(int vecArray[4]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+  vecArray[3] = elt[3];
+}
+
+inline void Vec4i::addToArray(int vecArray[4]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+  vecArray[3] += elt[3];
+}
+
+inline void Vec4i::print() const
+{
+  int a = elt[0];
+  int b = elt[1];
+  int c = elt[2];
+  int d = elt[3];
+
+  printf("[%d %d %d %d]\n", a, b, c, d);
+}
+
+inline void Vec4i::set(int x0, int x1, int x2, int x3) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+  elt[3] = x3;
+}
+
+inline void Vec4i::set(int value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+  elt[2] = value;
+  elt[3] = value;
+}
+
+inline int Vec4i::getInvertedIndex(int value) const
+{
+  if (value == elt[0]) return 0;
+  if (value == elt[1]) return 1;
+  if (value == elt[2]) return 2;
+  if (value == elt[3]) return 3;
+  return -1;
+}
+
+inline void Vec4i::rotate(int newStartIndex)
+{
+  if (newStartIndex == 1) // rotate left one time, (1, 2, 3, 0)
+  {
+    int tmp = elt[0];
+    elt[0] = elt[1];
+    elt[1] = elt[2];
+    elt[2] = elt[3];
+    elt[3] = tmp;
+  }
+  else if (newStartIndex == 2) // rotate left two times (2, 3, 0, 1)
+  {
+    std::swap(elt[0], elt[2]);
+    std::swap(elt[1], elt[3]);
+  }
+  else if (newStartIndex == 3) // rotate right one time (3, 0, 1, 2)
+  {
+    int tmp = elt[3];
+    elt[3] = elt[2];
+    elt[2] = elt[1];
+    elt[1] = elt[0];
+    elt[0] = tmp;
+  }
+}
+
+inline bool Vec4i::intersect(const Vec4i & vec2) const
+{
+  for(int i = 0; i < 4; i++)
+    for(int j = 0; j < 4; j++)
+      if (elt[i] == vec2.elt[j])
+        return true;
+  return false;
+}
+
+#endif
diff --git a/libraries/include/vectorStack.h b/libraries/include/vectorStack.h
new file mode 100644
index 0000000000000000000000000000000000000000..647c67e80d026c3a93c7934f03fde69995e3f8e4
--- /dev/null
+++ b/libraries/include/vectorStack.h
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "basicAlgorithms" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef VECTORSTACK_H
+#define VECTORSTACK_H
+
+#include <vector>
+
+// a stack/vector combination that holds multiple items in order and has an index
+// that points to the current item
+// useful for displaying history of an editing process
+template <class T>
+class VectorStack
+{
+public:
+  VectorStack() : i(-1) {}
+  void clear() { vec.clear(); i = -1; }
+
+  void push(const T & newItem) { i = vec.size(); vec.push_back(newItem); }
+  void pop() { vec.pop_back(); i = int(vec.size()) - 1; }
+
+  void toBegin() { i = 0; }
+  void toEnd() { i = int(vec.size()) - 1; }
+
+  int size() const { return vec.size(); }
+  bool empty() const { return vec.empty(); }
+
+  const T & current() const { assert(i >= 0 && i < (int)vec.size()); return vec[i]; }
+  T & current() { assert(i >= 0 && i < (int)vec.size()); return vec[i]; }
+  int index() const { return this->i; }
+
+  const T & top() const { assert(vec.size() > 0); return vec.back(); }
+  T & top() { assert(vec.size() > 0); return vec.back(); }
+
+  // ++index
+  int & operator++() { if (i+1 == (int)vec.size()) return i; return ++i; }
+  // --index
+  int & operator--() { if (i <= 0) return i; return --i; }
+  // index++
+  int operator++(int) { if (i+1 == (int)vec.size()) return i; return i++; }
+  // index--
+  int operator--(int) { if (i <= 0) return i; return i--; }
+
+protected:
+  std::vector<T> vec;
+  int i;
+};
+
+
+#endif
diff --git a/libraries/include/vegalong.h b/libraries/include/vegalong.h
new file mode 100644
index 0000000000000000000000000000000000000000..da4fff2e4b54d68828593da57984d4bc2f3e3d62
--- /dev/null
+++ b/libraries/include/vegalong.h
@@ -0,0 +1,8 @@
+#ifndef vegalong
+
+#include <stdint.h>
+#define vegalong int64_t
+#define vegaunsignedlong uint64_t
+
+#endif
+
diff --git a/src/libvolumetricMesh/volumetricMesh.h b/libraries/include/volumetricMesh.h
old mode 100755
new mode 100644
similarity index 60%
rename from src/libvolumetricMesh/volumetricMesh.h
rename to libraries/include/volumetricMesh.h
index aed8e3b653b55e5827736cdb4502b83c42b8f33f..dcbcc13fbf546da80b20443fe3c2715e35142a58
--- a/src/libvolumetricMesh/volumetricMesh.h
+++ b/libraries/include/volumetricMesh.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -54,14 +58,13 @@
 #include <string>
 #include <map>
 #include "minivector.h"
+#include "boundingBox.h"
 
-namespace vega
-{
 class VolumetricMesh
 {
 public:
 
-  // Note: This class is abstract and cannot be instantiated; use the constructors in the derived classes (TetMesh, CubicMesh) to initialize a mesh
+  // Note: This class is abstract and cannot be instantiated; use the constructors in the derived classes (TetMesh, CubicMesh) to initialize a mesh, or use the load routine in volumetricMeshLoader.h
 
   // copy constructor, destructor
   VolumetricMesh(const VolumetricMesh & volumetricMesh);
@@ -76,44 +79,69 @@ public:
   // === save/export ===
 
   // saves the mesh to a text file (.veg file format, see examples and documentation)
-  virtual int save(char * filename) const = 0; 
+  virtual int saveToAscii(const char * filename) const = 0; 
+  virtual int save(const char * filename) const; // for backward compatibility (just calls saveToAscii)
+
+  // saves the mesh to binary format
+  // returns: 0 = success, non-zero = error
+  // output: if bytesWritten is non-NULL, it will contain the number of bytes written 
+  virtual int saveToBinary(const char * filename, unsigned int * bytesWritten = NULL) const = 0;
+  // if countBytesOnly = true, user can pass NULL to binaryOutputStream
+  virtual int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten = NULL, bool countBytesOnly = false) const = 0;
+
   // exports the mesh geometry to an .ele and .node file (TetGen and Stellar format)
   // if includeRegions=1, an extra column is added to output, identifying the region of each element
-  int exportToEle(char * baseFilename, int includeRegions=0) const;
+  int exportToEle(const char * baseFilename, int includeRegions=0) const;
   // exports the mesh geometry to memory arrays (say, for external usage)
   // all parameters are output parameters
   // vertices and elements will be allocated inside the routine
-  void exportMeshGeometry(int * numVertices, double ** vertices, int * numElements, int * numElementVertices, int ** elements) const;
+  void exportMeshGeometry(int * numVertices, double ** vertices, int * numElements = NULL, int * numElementVertices = NULL, int ** elements = NULL) const;
+  void exportMeshGeometry(std::vector<Vec3d> & vertices) const;
 
   // === vertex and element access ===
 
   typedef enum { INVALID, TET, CUBIC } elementType;
+  typedef enum { ASCII, BINARY, NUM_FILE_FORMATS } fileFormatType; // ASCII is the text .veg format, BINARY is the binary .vegb format
   // opens the file and returns the element type of the volumetric mesh in the file; returns INVALID if no type information found
-  static elementType getElementType(char * filename); 
+  static elementType getElementType(const char * filename, fileFormatType fileFormat = ASCII); 
   virtual elementType getElementType() const = 0; // calls the derived class to identify itself
+  // advanced usage: returns the element type of the volumetric mesh from a BINARY stream (does not modify fin)
+  //static elementType getElementType(FILE * fin);
+  static elementType getElementType(void * fin, int memoryLoad = 0);
 
   inline int getNumVertices() const { return numVertices; }
-  inline Vec3d * getVertex(int i) const { return vertices[i]; }
-  inline Vec3d * getVertex(int element, int vertex) const { return vertices[elements[element][vertex]]; }
+  inline Vec3d & getVertex(int i) { return vertices[i]; }
+  inline const Vec3d & getVertex(int i) const { return vertices[i]; }
+  inline Vec3d & getVertex(int element, int vertex) { return vertices[elements[element][vertex]]; }
+  inline const Vec3d & getVertex(int element, int vertex) const { return vertices[elements[element][vertex]]; }
   inline int getVertexIndex(int element, int vertex) const { return elements[element][vertex]; }
-  inline Vec3d ** getVertices() const { return vertices;} // advanced, internal datastructure
+  inline const int * getVertexIndices(int element) const { return elements[element]; }
+  inline Vec3d * getVertices() { return vertices; } // advanced, internal datastructure
+  inline const Vec3d * getVertices() const { return vertices; }
   inline int getNumElements() const { return numElements; }
   inline int getNumElementVertices() const { return numElementVertices; } 
+  void renumberVertices(const std::vector<int> & permutation); // renumbers the vertices using the provided permutation
+  inline void setVertex(int i, const Vec3d & pos) { vertices[i] = pos; } // set the position of a vertex
 
   // === materials access === 
 
   inline int getNumMaterials() const { return numMaterials; }
-  inline Material * getMaterial(int i) const { return materials[i]; }
-  inline Material * getElementMaterial(int el) const { return materials[elementMaterial[el]]; }
-  void setMaterial(int i, const Material * material); // sets i-th material to "material"
-  void setSingleMaterial(double E, double nu, double density); // erases all materials and creates a single material for the entire mesh
+  inline const Material * getMaterial(int i) const { return materials[i]; }
+  inline const Material * getElementMaterial(int el) const { return materials[elementMaterial[el]]; }
   static void getDefaultMaterial(double * E, double * nu, double * density);
 
   inline int getNumSets() const { return numSets; }
-  inline Set * getSet(int i) const { return sets[i]; }
+  inline const Set * getSet(int i) const { return sets[i]; }
 
   inline int getNumRegions() const { return numRegions; }
-  inline Region * getRegion(int i) const { return regions[i]; }
+  inline const Region * getRegion(int i) const { return regions[i]; }
+
+  // === materials editing ===
+  inline Material * getMaterial(int i) { return materials[i]; }
+  inline Material * getElementMaterial(int el) { return materials[elementMaterial[el]]; }
+  void setMaterial(int i, const Material * material); // sets i-th material to "material"
+  void setSingleMaterial(double E, double nu, double density); // erases all materials and creates a single material for the entire mesh
+  void addMaterial(const Material * material, const Set & newSet, bool removeEmptySets, bool removeEmptyMaterials);
 
   // mass density of an element
   double getElementDensity(int el) const { return materials[elementMaterial[el]]->getDensity(); }
@@ -130,10 +158,12 @@ public:
   virtual double getElementVolume(int el) const = 0;
   void getVertexVolumes(double * vertexVolumes) const; // compute the volume "belonging" to each vertex
   virtual void getElementInertiaTensor(int el, Mat3d & inertiaTensor) const = 0; // returns the inertia tensor of a single element, around its center of mass, with unit density
-  void getInertiaParameters(double & mass, Vec3d & centerOfMass, Mat3d & inertiaTensor) const ; // center of mass and inertia tensor for the entire mesh
+  double getMass() const; // compute the total mass of the mesh, using the mass density material information
+  void getInertiaParameters(double & mass, Vec3d & centerOfMass, Mat3d & inertiaTensor) const ; // mass, center of mass and inertia tensor for the entire mesh
 
   // centroid is the geometric center of all vertices; radius is the tightest fitting sphere centered at the centroid
   void getMeshGeometricParameters(Vec3d & centroid, double * radius) const;
+  BoundingBox getBoundingBox() const;
 
   // mesh 1-neighborhood queries
   void getVerticesInElements(std::vector<int> & elements, std::vector<int> & vertices) const;
@@ -155,7 +185,7 @@ public:
   virtual void getElementEdges(int el, int * edgeBuffer) const = 0; // edgeBuffer must be pre-allocated, of size 2 x numElementEdges()
 
   // (permanently) applies the deformation to the vertices of the mesh
-  void applyDeformation(double * u);
+  void applyDeformation(const double * u);
   void applyLinearTransformation(double * pos, double * R); // transforms every vertex as X |--> pos + R * X (R must be given row-major)
 
   // === submesh creation ===
@@ -167,35 +197,45 @@ public:
   // === interpolation ===
 
   // the interpolant is a triple (numTargetLocations, vertices, weights)
-
   // Generates interpolation weights to transfer quantities from volumetric mesh to (embedded) surface meshes.
   // Input is a list of 3D target locations where the interpolant will be computed,
   // e.g., those could be vertices of a triangle mesh embedded into the volumetric mesh.
-  // Each location is a 3-vector, i.e., 3 consecutive double-precision vectors.
+  // Each location is a 3-vector, i.e., 3 consecutive double-precision values.
   // If zeroThreshold is set positive, than for any target location that is 
-  //   more than zeroThreshold away from the closest voxel, 
+  //   more than zeroThreshold away from the closest element, 
   //   all weights will be set to zero; this is useful, e.g. to 
-  //   stabilize locations far away from your mesh.
+  //   fix locations far away from your mesh.
   // Output: vertices and weights arrays
-  // vertices: gives a list of integer indicies of the vertices of the element
+  // vertices: gives a list of integer indices of the vertices of the element
   //   closest to the target location (numElementVertices entries per target location, one for each element vertex)
   //   note: if target location is inside a voxel, that voxel will be chosen as closest
   // weights: a list of numElementVertices_ weights, as per the numElementVertices_ vertices of each element (weights sum to 1)
   // If zeroThreshold >= 0, then the points that are further than zeroThreshold away from any volumetric mesh vertex, are assigned weights of 0.
-  // If closestElementList is not NULL, the closest elements will be returned in the vector "closestElementList".
-  // Returns the number of target points which do not lie inside any element.
-  int generateInterpolationWeights(int numTargetLocations, double * targetLocations, int ** vertices, double ** weights, 
-	double zeroThreshold = -1.0, std::vector<int> * closestElementList = NULL, int verbose=0) const;  
-  static int getNumInterpolationElementVertices(char * filename); // looks at the first line of "filename" to determine "numElementVertices" for this particular interpolant
-  static int loadInterpolationWeights(char * filename, int numTargetLocations, int numElementVertices, int ** vertices, double ** weights); // returns 0 on success
-  static int saveInterpolationWeights(char * filename, int numTargetLocations, int numElementVertices, int * vertices, double * weights);
+  // If elements is not NULL, the closest elements for each target location will be returned in the integer list "*elements" (allocated inside the function)
+  // If elements is not NULL, the function will allocate an integer array *elements, and return the closest element to each target location in it.
+  // Returns the number of target points that do not lie inside any element.
+  int generateInterpolationWeights(int numTargetLocations, const double * targetLocations, int ** vertices, double ** weights, double zeroThreshold = -1.0, int ** elements = NULL, int verbose=0) const; // this is the "master" function, meant to be typically used to create the interpolant
+
   // interpolates 3D vector data from vertices of the 
   //   volumetric mesh (data given in u) to the target locations (output goes into uTarget)
   //   e.g., use this to interpolate deformation from the volumetric mesh to a triangle mesh
-  static void interpolate(double * u, double * uTarget, int numTargetLocations, int numElementVertices, int * vertices, double * weights);
+  static void interpolate(const double * u, double * uTarget, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights);
+
+  // the following are less often used, more specialized functions
+  // same as "generateInterpolationWeights" above, except here the elements that contain the target locations are assumed to be known, and are provided in array "elements"; returns 0 on success, 1 otherwise
+  int generateInterpolationWeights(int numTargetLocations, const double * targetLocations, int * elements, int ** vertices, double ** weights, double zeroThreshold = -1.0, int verbose=0) const; 
+  // generates the integer list "elements" of the elements that contain given vertices; if closestElementIfOutside==1, then vertices outside of the mesh are assigned the closest element, otherwise -1 is assigned; returns the number of target locations outside of the mesh
+  int generateContainingElements(int numTargetLocations, const double * targetLocations, int ** elements, int useClosestElementIfOutside=1, int verbose=0) const; 
+  static int getNumInterpolationElementVertices(const char * filename); // looks at the first line of "filename" to determine "numElementVertices" for this particular interpolant
+  static int loadInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices, int ** vertices, double ** weights); // ASCII version; returns 0 on success
+  static int saveInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // ASCII version
+  static int loadInterpolationWeightsBinary(const char * filename, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights); // binary version; returns 0 on success
+  static int saveInterpolationWeightsBinary(const char * filename, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // binary version
+  static int loadInterpolationWeightsBinary(FILE * fin, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights); // binary version; returns 0 on success
+  static int saveInterpolationWeightsBinary(FILE * fout, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // binary version
 
   // computes barycentric weights of the given position with respect to the given element
-  virtual void computeBarycentricWeights(int element, Vec3d pos, double * weights) const = 0;
+  virtual void computeBarycentricWeights(int element, const Vec3d & pos, double * weights) const = 0;
 
   // computes the gradient of a 3D vector field (specified at the volumetric mesh vertices), at the location "pos"
   // "numFields" fields can be interpolated simultaneously; each is given as one column of the U matrix
@@ -213,14 +253,17 @@ public:
   {
   public:
 
-    Set(const std::string name);
+    Set(const std::string & name);
     Set(const Set & set);
+    Set(const std::string & name, const std::set<int> & elements);
 
     inline std::string getName() const;
     inline int getNumElements() const;
     inline void getElements(std::set<int> & elements) const;
+    inline const std::set<int> & getElements() const;
     inline bool isMember(int element) const;
 
+    inline std::set<int> & getElements();
     inline void insert(int element);
     inline void clear();
 
@@ -243,11 +286,16 @@ public:
     inline void setName(const std::string name);
     inline void setDensity(double density);
 
-    // ENU = any material parameterized by E (Young's modulus), nu (Poisson's ratio)
+    // ENU = any isotropic material parameterized by E (Young's modulus), nu (Poisson's ratio)
+    // ORTHOTROPIC = orthotropic anisotropic material
     // MOONEYRIVLIN = Mooney-Rivlin material
-    typedef enum { INVALID, ENU, MOONEYRIVLIN } materialType;
+    typedef enum { INVALID, ENU, ORTHOTROPIC, MOONEYRIVLIN } materialType;
     virtual materialType getType() = 0;
 
+    typedef enum { ENU_DENSITY, ENU_E, ENU_NU, ENU_NUM_PROPERTIES } enuMaterialProperties;
+    typedef enum { ORTHOTROPIC_DENSITY, ORTHOTROPIC_E1, ORTHOTROPIC_E2, ORTHOTROPIC_E3, ORTHOTROPIC_NU12, ORTHOTROPIC_NU23, ORTHOTROPIC_NU31, ORTHOTROPIC_G12, ORTHOTROPIC_G23, ORTHOTROPIC_G31, ORTHOTROPIC_NUM_PROPERTIES } orthotropicMaterialProperties;
+    typedef enum { MOONEYRIVLIN_DENSITY, MOONEYRIVLIN_MU01, MOONEYRIVLIN_MU10, MOONEYRIVLIN_V1, MOONEYRIVLIN_NUM_PROPERTIES } mooneyrivlinMaterialProperties;
+
   protected:
     std::string name;
     double density;
@@ -257,6 +305,8 @@ public:
   class ENuMaterial;
   // Mooney-Rivlin material (defined in volumetricMeshMooneyRivlinMaterial.h)
   class MooneyRivlinMaterial;
+  // Orthotropic material (defined in volumetricMeshOrthotropicMaterial.h)
+  class OrthotropicMaterial;
 
   // a volumetric mesh region, i.e., a set of elements sharing the same material
   class Region
@@ -265,6 +315,8 @@ public:
     Region(int materialIndex, int setIndex);
     inline int getMaterialIndex() const;
     inline int getSetIndex() const;
+    inline void setMaterialIndex(int index);
+    inline void setSetIndex(int index);
 
   protected:
     int setIndex, materialIndex;
@@ -276,7 +328,7 @@ public:
 
 protected:
   int numVertices;
-  Vec3d ** vertices;
+  Vec3d * vertices;
 
   int numElementVertices;
   int numElements;
@@ -291,9 +343,12 @@ protected:
   int * elementMaterial;  // material index of each element
 
   // parses the mesh, and returns the mesh element type
-  VolumetricMesh(char * filename, int numElementVertices, int verbose, elementType * elementType_);
+  VolumetricMesh(const char * filename, fileFormatType fileFormat, int numElementVertices, elementType * elementType_, int verbose);
+  // if memoryLoad is 0, binaryInputStream is FILE* (load from a file, via a stream), otherwise, it is char* (load from a memory buffer)
+  VolumetricMesh(void * binaryInputStream, int numElementVertices, elementType * elementType_, int memoryLoad = 0);
   VolumetricMesh(int numElementVertices_) { numElementVertices = numElementVertices_; }
-  void PropagateRegionsToElements();
+  void propagateRegionsToElements();
+  void loadFromBinaryGeneric(void * binaryInputStream, elementType * elementType_, int memoryLoad);
 
   // constructs a mesh from the given vertices and elements, 
   // with a single region and material ("E, nu" material)
@@ -319,20 +374,36 @@ protected:
   // if vertexMap is non-null, it also returns a renaming datastructure: vertexMap[big mesh vertex] is the vertex index in the subset mesh
   VolumetricMesh(const VolumetricMesh & mesh, int numElements, int * elements, std::map<int,int> * vertexMap = NULL); 
 
-  int save(char * filename, elementType elementType_) const;
+  int saveToAscii(const char * filename, elementType elementType_) const;
+  int saveToBinary(const char * filename, unsigned int * bytesWritten, elementType elementType_) const;
+  int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten, elementType elementType_, bool countBytesOnly = false) const;
+
+  void loadFromAscii(const char * filename, elementType * elementType_, int verbose = 0);
+  void loadFromBinary(const char * filename, elementType * elementType_);
+  void loadFromBinary(FILE * binaryInputStream, elementType * elementType_);
+  void loadFromMemory(unsigned char * binaryInputStream, elementType * elementType_);
+  void assignMaterialsToElements(int verbose);
+
+  static elementType getElementTypeASCII(const char * filename); 
+  static elementType getElementTypeBinary(const char * filename);
 
   elementType temp; // auxiliary
 
   friend class VolumetricMeshExtensions;
+  friend class VolumetricMeshLoader;
 
-  static void nop(); // no operation
+  static unsigned int readFromFile(void * buf, unsigned int elementSize, unsigned int numElements, void * fin);
+  static unsigned int readFromMemory(void * buf, unsigned int elementSize, unsigned int numElements, void * memoryLocation);
 };
 
-inline VolumetricMesh::Set::Set(const std::string name_) { name = name_; }
+inline VolumetricMesh::Set::Set(const std::string & name_) { name = name_; }
 inline VolumetricMesh::Set::Set(const Set & set) { elements = set.elements; name = set.getName(); }
+inline VolumetricMesh::Set::Set(const std::string & name_, const std::set<int> & elements_) : name(name_), elements(elements_) {}
 inline std::string VolumetricMesh::Set::getName() const { return name; }
 inline int VolumetricMesh::Set::getNumElements() const { return (int)(this->elements.size()); }
 inline void VolumetricMesh::Set::getElements(std::set<int> & elements) const { elements = this->elements; }
+inline const std::set<int> & VolumetricMesh::Set::getElements() const { return elements; }
+inline std::set<int> & VolumetricMesh::Set::getElements() { return elements; }
 inline bool VolumetricMesh::Set::isMember(int element) const {return (elements.find(element) != elements.end());}
 inline void VolumetricMesh::Set::insert(int element) { elements.insert(element); }
 inline void VolumetricMesh::Set::clear() { elements.clear(); }
@@ -347,6 +418,8 @@ inline void VolumetricMesh::Material::setDensity(double density_) { density = de
 inline VolumetricMesh::Region::Region(int materialIndex_, int setIndex_): setIndex(setIndex_), materialIndex(materialIndex_) {}
 inline int VolumetricMesh::Region::getMaterialIndex() const { return materialIndex; }
 inline int VolumetricMesh::Region::getSetIndex() const { return setIndex; }
-}
+inline void VolumetricMesh::Region::setMaterialIndex(int index) { materialIndex = index; }
+inline void VolumetricMesh::Region::setSetIndex(int index) { setIndex = index; }
+
 #endif
 
diff --git a/libraries/include/volumetricMeshDeformationGradient.h b/libraries/include/volumetricMeshDeformationGradient.h
new file mode 100644
index 0000000000000000000000000000000000000000..3b07b8a28e9ec81cca5b0f0e15063ddec8fafe85
--- /dev/null
+++ b/libraries/include/volumetricMeshDeformationGradient.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+   Given a volumetric mesh and its deformations,
+   computes the deformation gradient, and other related quantities.
+*/
+
+#ifndef _VOLUMETRICMESHDEFORMATIONGRADIENT_H_
+#define _VOLUMETRICMESHDEFORMATIONGRADIENT_H_
+
+#include "volumetricMesh.h"
+
+class VolumetricMeshDeformationGradient
+{
+public:
+  // computes the deformation gradient for the deformation given by the displacement vector u (length 3n, where n=#vertices), for the element with index "elementIndex"
+  // F is a 3x3 matrix, stored row-major into a vector of length 9
+  static void ComputeDeformationGradient(VolumetricMesh * mesh, double * u, int elementIndex, double * F);
+
+  // computes the three principal stretches (singular values of F)
+  // lambdas are returned in descending order
+  // returns 0 on success, and non-zero on SVD failure
+  static int ComputePrincipalStretches(double * F, double * lambda);
+
+  // maximal stretch in the entire mesh
+  static double ComputeMaximalStretch(VolumetricMesh * mesh, double * u);
+
+protected:
+
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/volumetricMeshENuMaterial.h b/libraries/include/volumetricMeshENuMaterial.h
similarity index 84%
rename from src/libvolumetricMesh/volumetricMeshENuMaterial.h
rename to libraries/include/volumetricMeshENuMaterial.h
index b9127040c724e3b2dfd6eb579add00d54968fc62..47f440c0cffa5718938202e11fb3a7cd0dc76a9c 100644
--- a/src/libvolumetricMesh/volumetricMeshENuMaterial.h
+++ b/libraries/include/volumetricMeshENuMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,9 +35,7 @@
 
 #include "volumetricMesh.h"
 
-namespace vega
-{
-// stores a material specified by E (Young's modulus), nu (Poisson's ratio), and density
+// stores an isotropic material specified by E (Young's modulus), nu (Poisson's ratio), and density
 // such a material specification is very common: (corotational) linear FEM, StVK, etc.
 class VolumetricMesh::ENuMaterial : public VolumetricMesh::Material
 {
@@ -66,6 +68,6 @@ inline void VolumetricMesh::ENuMaterial::setNu(double nu) { nu_ = nu; }
 
 // obtain pointer to ENuMaterial (necessary inside classes that assume ENu material)
 VolumetricMesh::ENuMaterial * downcastENuMaterial(VolumetricMesh::Material * material); // performs a check via getType and returns NULL if material is not ENU
-}
+
 #endif
 
diff --git a/libraries/include/volumetricMeshLoader.h b/libraries/include/volumetricMeshLoader.h
new file mode 100644
index 0000000000000000000000000000000000000000..cbfb6dea1623de98ad4d5ea40c550dc690593c63
--- /dev/null
+++ b/libraries/include/volumetricMeshLoader.h
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Loads a volumetric mesh from a text file or a binary file. It automatically determines 
+  the type of the mesh (tet mesh, cube mesh).
+*/
+
+#ifndef _VOLUMETRICMESHLOADER_H_
+#define _VOLUMETRICMESHLOADER_H_
+
+#include "volumetricMesh.h"
+
+class VolumetricMeshLoader
+{
+public:
+  // loads a volumetric mesh (ASCII (.veg format), or BINARY)
+  static VolumetricMesh * load(const char * filename, VolumetricMesh::fileFormatType fileFormat = VolumetricMesh::ASCII, int verbose=1);
+
+  // loads several volumetric meshes from a single binary file
+  static int load(const char * filename, int * numVolumetricMeshes, VolumetricMesh *** volumetricMeshes, int verbose=1);
+
+  // saves several volumetric meshes to a single binary file
+  // if you do not want to use "saveVolumetricMeshFlag", set it to NULL
+  // if saveVolumetricMeshFlag is not NULL, then:
+  // saveVolumetricMeshFlag[i] = 0: skip (do not save) volumetric mesh i
+  // saveVolumetricMeshFlag[i] != 0: save volumetric mesh i to disk
+  static int save(const char * filename, int numVolumetricMeshes, VolumetricMesh ** volumetricMeshes, int * saveVolumetricMeshFlag, int verbose=0);
+
+  // advanced usage: loads a volumetric mesh from the current position of the file stream (binary mode).
+  // if memoryLoad is 0, binaryStream is FILE* (load from a file), otherwise, it is char* (load from a memory buffer)
+  static VolumetricMesh * load(void * fin, int memoryLoad = 0);
+
+protected:
+  static int load(FILE * fin, int * numVolumetricMeshes, VolumetricMesh *** volumetricMeshes, int verbose=1);
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/volumetricMeshMooneyRivlinMaterial.h b/libraries/include/volumetricMeshMooneyRivlinMaterial.h
similarity index 88%
rename from src/libvolumetricMesh/volumetricMeshMooneyRivlinMaterial.h
rename to libraries/include/volumetricMeshMooneyRivlinMaterial.h
index 10bb6c391e2de13b442178b31604b1c56c1a7371..962023deee6a6e45cc1514deb34dd1dbd2040d64 100644
--- a/src/libvolumetricMesh/volumetricMeshMooneyRivlinMaterial.h
+++ b/libraries/include/volumetricMeshMooneyRivlinMaterial.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,8 +35,6 @@
 
 #include "volumetricMesh.h"
 
-namespace vega
-{
 // Mooney-Rivlin material
 
 /*
@@ -76,6 +78,6 @@ inline void VolumetricMesh::MooneyRivlinMaterial::setv1(double v1) { v1_ = v1; }
 
 // obtain pointer to MooneyRivlinMaterial (necessary inside classes that implement the Mooney-Rivlin material)
 VolumetricMesh::MooneyRivlinMaterial * downcastMooneyRivlinMaterial(VolumetricMesh::Material * material); // performs a check via getType and returns NULL if material is not Mooney Rivlin
-}
+
 #endif
 
diff --git a/libraries/include/volumetricMeshOrthotropicMaterial.h b/libraries/include/volumetricMeshOrthotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..5feceba9b81c791522f21419066f8c221ee5ad7a
--- /dev/null
+++ b/libraries/include/volumetricMeshOrthotropicMaterial.h
@@ -0,0 +1,104 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Stores an orthotropic material.
+*/
+
+#ifndef _VOLUMETRICMESHORTHOTROPICMATERIAL_H_
+#define _VOLUMETRICMESHORTHOTROPICMATERIAL_H_
+
+#include "volumetricMesh.h"
+
+// stores an orthotropic material
+class VolumetricMesh::OrthotropicMaterial : public VolumetricMesh::Material
+{
+public:
+  OrthotropicMaterial(std::string name, double density, double E1, double E2, double E3, double nu12, double nu23, double nu31, double G12, double G23, double G31, double * R);
+  OrthotropicMaterial(const OrthotropicMaterial & orthotropicMaterial);
+  virtual ~OrthotropicMaterial() {}
+  virtual VolumetricMesh::Material * clone() const;
+  virtual VolumetricMesh::Material::materialType getType();
+
+  // Es, Nus and Gs are all given in the local coordinate system specified by R
+  inline double getE1() const; // Young's modulus in the x-direction  
+  inline double getE2() const; // Young's modulus in the y-direction  
+  inline double getE3() const; // Young's modulus in the z-direction  
+  inline double getNu12() const; // Poisson's ratio when pulling in x-direction, contraction in y
+  inline double getNu23() const; // Poisson's ratio when pulling in y-direction, contraction in z
+  inline double getNu31() const; // Poisson's ratio when pulling in z-direction, contraction in x
+  inline double getG12() const; // shear modulus in xy
+  inline double getG23() const; // shear modulus in yz
+  inline double getG31() const; // shear modulus in zx
+  void getR(double * R) const; // rotation (3x3 matrix) of the local coordinate system, in row-major order
+
+  inline void setE1(double E1); 
+  inline void setE2(double E2); 
+  inline void setE3(double E3); 
+  inline void setNu12(double nu12); 
+  inline void setNu23(double nu23); 
+  inline void setNu31(double nu31); 
+  inline void setG12(double G12); 
+  inline void setG23(double G23); 
+  inline void setG31(double G31); 
+  void setR(double * R); 
+
+protected:
+  double E1_, E2_, E3_, nu12_, nu23_, nu31_, G12_, G23_, G31_; 
+  double R_[9];
+};
+
+inline double VolumetricMesh::OrthotropicMaterial::getE1() const { return E1_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getE2() const { return E2_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getE3() const { return E3_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getNu12() const { return nu12_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getNu23() const { return nu23_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getNu31() const { return nu31_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getG12() const { return G12_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getG23() const { return G23_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getG31() const { return G31_; } 
+
+inline void VolumetricMesh::OrthotropicMaterial::setE1(double E1) { E1_ = E1; }
+inline void VolumetricMesh::OrthotropicMaterial::setE2(double E2) { E2_ = E2; }
+inline void VolumetricMesh::OrthotropicMaterial::setE3(double E3) { E3_ = E3; }
+inline void VolumetricMesh::OrthotropicMaterial::setNu12(double nu12) { nu12_ = nu12; }
+inline void VolumetricMesh::OrthotropicMaterial::setNu23(double nu23) { nu23_ = nu23; }
+inline void VolumetricMesh::OrthotropicMaterial::setNu31(double nu31) { nu31_ = nu31; }
+inline void VolumetricMesh::OrthotropicMaterial::setG12(double G12) { G12_ = G12; }
+inline void VolumetricMesh::OrthotropicMaterial::setG23(double G23) { G23_ = G23; }
+inline void VolumetricMesh::OrthotropicMaterial::setG31(double G31) { G31_ = G31; }
+
+// obtain pointer to OrthotropicMaterial (necessary inside classes that assume ENu material)
+VolumetricMesh::OrthotropicMaterial * downcastOrthotropicMaterial(VolumetricMesh::Material * material); // performs a check via getType and returns NULL if material is not Orthotropic
+
+#endif
+
diff --git a/src/libvolumetricMesh/volumetricMeshParser.h b/libraries/include/volumetricMeshParser.h
old mode 100755
new mode 100644
similarity index 80%
rename from src/libvolumetricMesh/volumetricMeshParser.h
rename to libraries/include/volumetricMeshParser.h
index 6bed884cc51c1ff1d4275e4079b1a2a5fc4ddc69..b63940eda11afba1d66254a66f32cbc64079d5fa
--- a/src/libvolumetricMesh/volumetricMeshParser.h
+++ b/libraries/include/volumetricMeshParser.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -34,8 +38,6 @@
 #include <vector>
 using namespace std;
 
-namespace vega
-{
 /*
   A parser for the volumetric mesh text file format.
   Note: the end user never needs to use this class directly.
@@ -46,10 +48,10 @@ class VolumetricMeshParser
 {
 public:
 
-  VolumetricMeshParser(char * includeToken = NULL); // pass NULL for normal usage
+  VolumetricMeshParser(const char * includeToken = NULL); // pass NULL for normal usage
   ~VolumetricMeshParser();
 
-  int open(char * filename);
+  int open(const char * filename);
 
   // return the next line, s must be externally allocated string
   // if last line, return will be NULL
@@ -65,13 +67,13 @@ public:
 protected:
   FILE * fin;
   vector< FILE* > fileStack;
-  int fileStackDepth;
+  //int fileStackDepth;
 
   char directoryName[4096];
 
   char includeToken[96]; // normally "*INCLUDE "
   int includeTokenLength; // normally 9
 };
-}
+
 #endif
 
diff --git a/libraries/include/windingNumber.h b/libraries/include/windingNumber.h
new file mode 100644
index 0000000000000000000000000000000000000000..a8cb83d0e6e0ad5edba8f7f965f90f0dc8bdef77
--- /dev/null
+++ b/libraries/include/windingNumber.h
@@ -0,0 +1,59 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "windingNumber" library , Copyright (C) 2018 USC                      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Computes the winding number of a 3D point with respect to a given 
+  closed manifold triangle mesh, as described in:
+
+  Alec Jacobson, Ladislav Kavan, Olga Sorkine-Hornung:
+  Robust Inside-Outside Segmentation using Generalized Winding Numbers, SIGGRAPH 2013
+
+  This implementation does not use the hierarchical acceleration presented
+  in the paper above; instead, it loops over all the triangles.
+*/
+
+#ifndef _WINDINGNUMBER_H_
+#define _WINDINGNUMBER_H_
+
+#include "objMesh.h"
+#include "minivector.h"
+
+class WindingNumber 
+{
+public:
+
+  // computes the winding number of point p with respect to the given mesh
+  static double computeWindingNumber(ObjMesh * objMesh, const Vec3d & p);
+};
+
+#endif
+
diff --git a/src/libintegrator/getIntegratorSolver.cpp b/libraries/integrator/getIntegratorSolver.cpp
similarity index 95%
rename from src/libintegrator/getIntegratorSolver.cpp
rename to libraries/integrator/getIntegratorSolver.cpp
index 49f1fffb145b72f5ad7469329ba72d44e8d5a582..a6fdcea8c0fc61ee9f3361a3378a4a3a19e5162e 100644
--- a/src/libintegrator/getIntegratorSolver.cpp
+++ b/libraries/integrator/getIntegratorSolver.cpp
@@ -1,8 +1,6 @@
 #include <string.h>
 #include "integratorSolverSelection.h"
 
-namespace vega
-{
 // returns the string corresponding to the selected integrator solver
 void GetIntegratorSolver(char * solver)
 {
@@ -21,4 +19,3 @@ void GetIntegratorSolver(char * solver)
   #endif
 }
 
-}
diff --git a/libraries/integrator/getIntegratorSolver.h b/libraries/integrator/getIntegratorSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ea45aafefe9fa40114163172ea767157265d9e1
--- /dev/null
+++ b/libraries/integrator/getIntegratorSolver.h
@@ -0,0 +1,10 @@
+#ifndef _GETINTEGRATORSOLVER_H_
+#define _GETINTEGRATORSOLVER_H_
+
+// returns the string corresponding to the selected integrator solver
+// "solver" must be pre-allocated
+// result: PARDISO, SPOOLES or PCG
+void GetIntegratorSolver(char * solver);
+ 
+#endif
+
diff --git a/src/libintegrator/integratorBase.cpp b/libraries/integrator/integratorBase.cpp
similarity index 81%
rename from src/libintegrator/integratorBase.cpp
rename to libraries/integrator/integratorBase.cpp
index 4c1396070906bca5a357359a85379c9314cf8df0..1fcae5c52890df859b968e1d30c9a6f2f7663391 100644
--- a/src/libintegrator/integratorBase.cpp
+++ b/libraries/integrator/integratorBase.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,8 +37,6 @@
 #include <string.h>
 #include <float.h>
 
-namespace vega
-{
 IntegratorBase::IntegratorBase(int r, double timestep, double dampingMassCoef, double dampingStiffnessCoef)
 {
   this->r = r;
@@ -131,4 +133,24 @@ void IntegratorBase::ResetToRest()
   memset(qaccel_1,0,sizeof(double)*r);
 }
 
+void IntegratorBase::ConstrainToSphere(double R2)
+{
+  double norm2 = 0;
+  for(int i=0; i<r; i++)
+    norm2 += q[i] * q[i];
+
+  if (norm2 > R2)
+  {
+    double beta = sqrt(R2 / norm2);
+    for(int dim=0; dim<r; dim++)
+    {
+      q[dim] *= beta;
+      q_1[dim] = q[dim];
+      qvel[dim] = 0;
+      qvel_1[dim] = 0;
+      qaccel[dim] = 0;
+      qaccel_1[dim] = 0;
+    }
+  }
 }
+
diff --git a/libraries/integrator/integratorBase.h b/libraries/integrator/integratorBase.h
new file mode 100644
index 0000000000000000000000000000000000000000..cfb5a8be289ae8d2c6d55925487245ff869d2237
--- /dev/null
+++ b/libraries/integrator/integratorBase.h
@@ -0,0 +1,216 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _INTEGRATORBASE_H_
+#define _INTEGRATORBASE_H_
+
+/*
+
+This code can numerically timestep a system of ODEs of the form:
+
+M * q'' + (alpha * M + beta * K(q)) q' + R(q) = fext(t) , where
+
+q is an unknown vector function, M is an arbitrary symmetric positive-definite 
+matrix, fext(t) are arbitrary user-provided external forces, alpha and beta 
+are arbitrary non-negative scalar constants (Rayleigh damping parameters), 
+R(q) is an arbitrary user-provided vector function, and K(q) = d R / d q is 
+its user-provided gradient. 
+
+Such a system arises, for example, when simulating a nonlinear
+deformable object using the Finite Element Method (FEM), or also using
+mass-spring systems.  This code has been used for simulations of large 
+deformations of 3D solid deformable objects, with dynamics (see [1]).
+
+The code supports several numerical integrators (see derived classes).
+
+The code can handle both large sparse systems and dense reduced systems.
+For example, the large sparse version can be used to simulate a general 
+deforming tetrahedral mesh. The dense version can be used for simulations 
+that employ model reduction (see [1]).
+
+The class in this file (IntegratorBase) is the abstract base class. In practice, 
+you need to use one of the provided derived classes, depending on whether 
+you want to simulate large sparse systems or dense reduced systems.
+
+All these classes are generic in that you can provide your own arbitrary 
+internal forces R(q) and their gradients K(q). You do so by deriving from the 
+InternalForceModel class, and passing that class to the appropriate integrator 
+class constructor. Several force model classes are provided, including 
+the cubic polynomial reduced StVK model from [1], and a linearized version of 
+that model.
+
+For dense simulations, you need a BLAS and LAPACK library. This is necessary 
+to solve the r x r linear systems inside the implicit Newmark solver.
+We have successfully used the following BLAS and LAPACK libraries:
+1. Windows: Intel Math Kernel Library for Windows
+2. Red Hat Linux: Intel Math Kernel Library for Linux
+3. Mac OS X: both BLAS and LAPACK are already included with Mac OS X 
+   (the vecLib framework)
+You can probably also download the generic BLAS and LAPACK implementations 
+from www.netlib.org. All dense matrices are stored in column-major format.
+
+For large sparse simulations, you need a large sparse linear system solver. 
+The code supports the following solvers:
+1. SPOOLES (a free solver), 
+2. PARDISO (we used the commercial version that comes with Intel MKL; this 
+solver is multi-threaded and can be executed across multiple cores of a CPU),
+3. our own Jacobi-preconditioned Conjugate Solver (from our "sparseMatrix" library).
+We were able to run sparse simulations on Windows, Linux and Mac OS X.
+
+The code also supports static simulations, i.e., simulations where the dynamic
+terms are neglected, and the system only computes the static equilibrium
+under the currently applied external forces.
+
+References:
+[1] Jernej Barbic, Doug L. James: Real-Time Subspace Integration for 
+St.Venant-Kirchhoff Deformable Models, ACM Transactions on Graphics 24(3) 
+(SIGGRAPH 2005), p. 982-990, Los Angeles, CA, August 2005
+[2] Jernej Barbic: Real-time Reduced Large-Deformation Models and Distributed 
+Contact for Computer Graphics and Haptics, PhD Thesis, Carnegie Mellon University, 
+August 2007
+
+Both publications are available online at www.jernejbarbic.com .
+
+*/
+
+#include <stdlib.h>
+
+// This abstract class is derived into: IntegratorBaseDense (dense systems)
+// and ImplicitNewmarkSparse ((large) sparse systems).
+class IntegratorBase
+{
+public:
+  // r is the dimension of the simulation; it equals 3*n for unreduced systems, where n is the number of vertices in the simulation mesh; with reduction, r equals the size of the simulation basis 
+  // the damping coefficients are tangential Rayleigh damping coefficients, see [2]
+  IntegratorBase(int r, double timestep, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0);
+
+  virtual ~IntegratorBase();
+
+  // === set/get the state (position,velocity and acceleration) ===
+
+  // set integrator to zero (q, qvel, and qaccel are set to zero)
+  virtual void ResetToRest(); 
+
+  // sets the position and the velocity
+  // this routine will internally automatically compute proper acceleration 
+  // returns 0 on success, 1 if solver fails to converge
+  virtual int SetState(double * q, double * qvel=NULL) = 0;
+
+  // sets the position, velocity, and acceleration
+  // note: if you don't set all three at once, old values will persist, which may not be what you want
+  virtual void SetqState(const double * q, const double * qvel=NULL, const double * qaccel=NULL);
+
+  // copies the state into spaces provided by q,qvel,qaccel (each a vector of length r; if NULL is provided for either of q,qvel,qccel, that part of the state is not copied)
+  virtual void GetqState(double * q, double * qvel=NULL, double * qaccel=NULL);
+
+  // set/get individual position components:
+  inline virtual void SetQ(int index, double qIndex) { q[index] = qIndex; } 
+  inline virtual double GetQ(int index) { return q[index]; } 
+
+  // obtain pointers to the internally stored position, velocity, and acceleration
+  // (advanced usage)
+  inline virtual double * Getq() { return q; }
+  inline virtual double * Getqvel() { return qvel; }
+  inline virtual double * Getqaccel() { return qaccel; }
+
+  // == set external forces (a vector of r numbers) ===
+
+  // external forces remain in force until explicity changed
+  void SetExternalForces(double * externalForces); 
+  void AddExternalForces(double * externalForces); 
+  void GetExternalForces(double * externalForces);
+  double * GetExternalForces() { return externalForces; };
+  void SetExternalForcesToZero(); 
+
+  // === set integration and simulation parameters ===
+
+  inline virtual void SetTimestep(double timestep) { this->timestep = timestep; }
+  inline double GetTimeStep() { return timestep; }
+
+  // scale all internal forces and stiffness matrix entries by internalForceScalingFactor (e.g., to make the model softer or stiffer; default: 1.0)
+  // note: frequency spectrum is scaled by sqrt(internalForceScalingFactor)
+  virtual void SetInternalForceScalingFactor(double internalForceScalingFactor) { this->internalForceScalingFactor = internalForceScalingFactor; }
+
+  // tangential Rayleigh damping parameters
+  inline virtual void SetDampingMassCoef(double dampingMassCoef) { this->dampingMassCoef = dampingMassCoef; }
+  inline virtual void SetDampingStiffnessCoef(double dampingStiffnessCoef) { this->dampingStiffnessCoef = dampingStiffnessCoef;}
+  inline double GetDampingMassCoef() { return dampingMassCoef; }
+  inline double GetDampingStiffnessCoef() { return dampingStiffnessCoef; }
+
+  // === perform one timestep of the simulation ===
+  // (the choice of integrator depends on the derived class)
+
+  virtual int DoTimestep() = 0;
+
+  // === misc ===
+
+  inline int GetNumDOFs() { return r; }
+  inline int Getr() { return r; }
+
+  virtual double GetKineticEnergy() = 0;
+  virtual double GetTotalMass() = 0; // sum of mass matrix entries
+
+  virtual double GetForceAssemblyTime() = 0;
+  virtual double GetSystemSolveTime() = 0;
+
+  // constrain the system to ||q||^2 < R2
+  // useful to prevent large values from occuring
+  virtual void ConstrainToSphere(double R2);
+
+protected:
+
+  double * q; // current deformation amplitudes
+  double * qvel; // current velocities of deformation amplitudes
+  double * qaccel; // current acceleration (used inside implicit newmark integration)
+  double * qresidual, * qdelta; // aux integration variables 
+  double * q_1; // deformation amplitudes at previous time-step
+  double * qvel_1;
+  double * qaccel_1;
+
+  double * internalForces; // current internal force amplitudes
+  double * externalForces; // current external force amplitudes
+
+  double internalForceScalingFactor; 
+
+  double * buffer;
+
+  // these two store the damping parameters 
+  double dampingMassCoef;
+  double dampingStiffnessCoef;
+
+  int r; // number of reduced DOFs 
+
+  double timestep; 
+};
+
+#endif
+
diff --git a/libraries/integrator/integratorSolverSelection.h b/libraries/integrator/integratorSolverSelection.h
new file mode 100644
index 0000000000000000000000000000000000000000..40dd1e0ee50bc0f46c6b42cc37a971899fe8dbd2
--- /dev/null
+++ b/libraries/integrator/integratorSolverSelection.h
@@ -0,0 +1,43 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+// Selects which solver is used by the integrator library.
+// Exactly one of PARDISO, SPOOLES, PCG should be enabled.
+// Note: for PARDISO or SPOOLES, the selected solver must be installed and its
+//       availability also set in libraries/sparseSolvers/sparseSolverAvailability.h .
+//       The PCG solver (Jacobi-preconditioned Conjugate Gradients) is included 
+//       with Vega and therefore always available.
+
+//#define PARDISO
+//#define SPOOLES
+#define PCG
+
diff --git a/src/libintegratorDense/IPIVC.h b/libraries/integratorDense/IPIVC.h
similarity index 76%
rename from src/libintegratorDense/IPIVC.h
rename to libraries/integratorDense/IPIVC.h
index 22976313b1e34b62a790009ca40b622b56ab6ee5..22c1658ee49dd05e574b5c142f38259e70510978 100644
--- a/src/libintegratorDense/IPIVC.h
+++ b/libraries/integratorDense/IPIVC.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,6 +32,7 @@
 
 #ifndef _IPIVC_H_
 #define _IPIVC_H_
+#include <cstdlib>
 
 // a buffer to hold r integers
 // a special buffer is necessary because INTEGER can be architecture-dependent
diff --git a/src/libintegratorDense/centralDifferencesDense.cpp b/libraries/integratorDense/centralDifferencesDense.cpp
similarity index 86%
rename from src/libintegratorDense/centralDifferencesDense.cpp
rename to libraries/integratorDense/centralDifferencesDense.cpp
index 15404b1e2e9803be4798143f62eb36fd545e1623..d20957cc83fe5d5b0d15c1437e0bb57f04aec147 100644
--- a/src/libintegratorDense/centralDifferencesDense.cpp
+++ b/libraries/integratorDense/centralDifferencesDense.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -38,11 +42,11 @@
 #include "IPIVC.h"
 
 #ifdef __APPLE__
-   #define DGETRF dgetrf_
-   #define DGETRS dgetrs_
+  #define DGETRF dgetrf_
+  #define DGETRS dgetrs_
 #else
-//   #define DGETRF dgetrf
-//   #define DGETRS dgetrs
+  #define DGETRF dgetrf
+  #define DGETRS dgetrs
 #endif
 
 CentralDifferencesDense::CentralDifferencesDense(int numDOFs, double timestep, double * massMatrix, ReducedForceModel * reducedForceModel, double dampingMassCoef, double dampingStiffnessCoef, int tangentialDampingMode_): IntegratorBaseDense(numDOFs, timestep, massMatrix, reducedForceModel, dampingMassCoef, dampingStiffnessCoef), tangentialDampingMode(tangentialDampingMode_)
@@ -104,6 +108,13 @@ int CentralDifferencesDense::DoTimestep()
   counterForceAssemblyTime.StopCounter();
   forceAssemblyTime = counterForceAssemblyTime.GetElapsedTime();
 
+  if (plasticfq != NULL)
+  {
+    SetTotalForces(internalForces);
+    for(int i=0; i<r; i++)
+      internalForces[i] -= plasticfq[i];
+  }
+
   PerformanceCounter counterSystemSolveTime;
 
   for (int i=0; i<r; i++)
@@ -172,6 +183,8 @@ int CentralDifferencesDense::DoTimestep()
     q[i] = rhs[i];
   }
 
+  ProcessPlasticDeformations();
+
   counterSystemSolveTime.StopCounter();
   systemSolveTime = counterSystemSolveTime.GetElapsedTime();
 
diff --git a/src/libintegratorDense/centralDifferencesDense.h b/libraries/integratorDense/centralDifferencesDense.h
similarity index 85%
rename from src/libintegratorDense/centralDifferencesDense.h
rename to libraries/integratorDense/centralDifferencesDense.h
index f7f70b9652b0b17df33046b9cc16795b1ccfc2bc..542adc0e36b426dd304c213d40c9b79c7619fd73 100644
--- a/src/libintegratorDense/centralDifferencesDense.h
+++ b/libraries/integratorDense/centralDifferencesDense.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -64,7 +68,7 @@ take very small explicit timesteps, or use the ImplicitNewmark class.
 
 #include "integratorBaseDense.h"
 
-class CentralDifferencesDense : public IntegratorBaseDense
+class CentralDifferencesDense : public virtual IntegratorBaseDense
 {
 public:
   CentralDifferencesDense(int numDOFs, double timestep, double * massMatrix, ReducedForceModel * reducedForceModel, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, int tangentialDampingMode = 1);
diff --git a/src/libintegratorDense/implicitBackwardEulerDense.cpp b/libraries/integratorDense/implicitBackwardEulerDense.cpp
similarity index 90%
rename from src/libintegratorDense/implicitBackwardEulerDense.cpp
rename to libraries/integratorDense/implicitBackwardEulerDense.cpp
index 951a166c688ea374152411633f8e1b0051318b81..41dbe0b09d2102a0323ddfad1f561cbb85e7f7cf 100644
--- a/src/libintegratorDense/implicitBackwardEulerDense.cpp
+++ b/libraries/integratorDense/implicitBackwardEulerDense.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -71,6 +75,10 @@ int ImplicitBackwardEulerDense::DoTimestep()
     counterForceAssemblyTime.StopCounter();
     forceAssemblyTime = counterForceAssemblyTime.GetElapsedTime();
 
+    if (plasticfq != NULL)
+      for(int i=0; i<r; i++)
+        internalForces[i] -= plasticfq[i];
+
     // scale internal forces
     for(int i=0; i<r; i++)
       internalForces[i] *= internalForceScalingFactor;
@@ -145,6 +153,7 @@ int ImplicitBackwardEulerDense::DoTimestep()
       for(int i=0; i<r2; i++)
       {
         dampingMatrix[i] = dampingMassCoef * massMatrix[i] + dampingStiffnessCoef * tangentStiffnessMatrix[i];
+        dampingMatrix[i] += dampingMatrixOffset[i];
 
         tangentStiffnessMatrix[i] *= timestep;
         tangentStiffnessMatrix[i] += dampingMatrix[i];
@@ -251,9 +260,11 @@ int ImplicitBackwardEulerDense::DoTimestep()
         INTEGER LDB = r;
         INTEGER INFO;
 
-         #ifdef __APPLE__
-           #define DGESV dgesv_
-         #endif
+        #ifdef __APPLE__
+          #define DGESV dgesv_
+        #else
+          #define DGESV dgesv
+        #endif
 
         DGESV ( &N, &NRHS, A, &LDA, IPIV->GetBuf(), B, &LDB, &INFO );
 
@@ -268,10 +279,12 @@ int ImplicitBackwardEulerDense::DoTimestep()
       case symmetricMatrixSolver:
       {
         // call dsysv ( uplo, n, nrhs, a, lda, ipiv, b, ldb, work, lwork, info)
-
-         #ifdef __APPLE__
-           #define DSYSV dsysv_
-         #endif
+  
+        #ifdef __APPLE__
+          #define DSYSV dsysv_
+        #else
+          #define DSYSV dsysv
+        #endif
 
         char uplo = 'U';
         INTEGER nrhs = 1;
@@ -293,10 +306,12 @@ int ImplicitBackwardEulerDense::DoTimestep()
       {
         // call dposv ( uplo, n, nrhs, a, lda, b, ldb, info)
 
-         #ifdef __APPLE__
-           #define DPOSV dposv_
-         #endif
-
+        #ifdef __APPLE__
+          #define DPOSV dposv_
+        #else
+          #define DPOSV dposv
+        #endif
+  
         char uplo = 'U';
         INTEGER nrhs = 1;
         INTEGER info = 0;
@@ -350,6 +365,8 @@ int ImplicitBackwardEulerDense::DoTimestep()
   }
   while (numIter < maxIterations);
 
+  ProcessPlasticDeformations();
+
 /*
   printf("Num iterations performed: %d (maxIterations=%d)\n", numIter, maxIterations);
   if ((numIter >= maxIterations) && (maxIterations > 1))
diff --git a/src/libintegratorDense/implicitBackwardEulerDense.h b/libraries/integratorDense/implicitBackwardEulerDense.h
similarity index 83%
rename from src/libintegratorDense/implicitBackwardEulerDense.h
rename to libraries/integratorDense/implicitBackwardEulerDense.h
index 9d033120300e0531a1828ac28103be92882b1189..9110d28ca45a5d8643b5c8ea7a5612474af710b0 100644
--- a/src/libintegratorDense/implicitBackwardEulerDense.h
+++ b/libraries/integratorDense/implicitBackwardEulerDense.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -42,7 +46,7 @@ values of r, there will be a computational slowdown.
 
 #include "integratorBaseDense.h"
 
-class ImplicitBackwardEulerDense : public IntegratorBaseDense
+class ImplicitBackwardEulerDense : public virtual IntegratorBaseDense
 {
 public:
 
diff --git a/src/libintegratorDense/implicitNewmarkDense.cpp b/libraries/integratorDense/implicitNewmarkDense.cpp
similarity index 84%
rename from src/libintegratorDense/implicitNewmarkDense.cpp
rename to libraries/integratorDense/implicitNewmarkDense.cpp
index 703e812f4bafbbb39f8d954c880aae5398ee8e9e..fa1a09ee1af35e0fde2e86ca6c97859e08b2e9a6 100644
--- a/src/libintegratorDense/implicitNewmarkDense.cpp
+++ b/libraries/integratorDense/implicitNewmarkDense.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -49,6 +53,15 @@ ImplicitNewmarkDense::ImplicitNewmarkDense(int r, double timestep, double * mass
   UpdateAlphas();
 }
 
+ImplicitNewmarkDense::ImplicitNewmarkDense(int r, double timestep, double dampingMassCoef, double dampingStiffnessCoef, double NewmarkBeta, double NewmarkGamma): IntegratorBaseDense(r, timestep, NULL, NULL, dampingMassCoef, dampingStiffnessCoef)
+{
+  this->NewmarkBeta = NewmarkBeta;
+  this->NewmarkGamma = NewmarkGamma;
+
+  UpdateAlphas();
+}
+
+
 ImplicitNewmarkDense::~ImplicitNewmarkDense()
 {
   free(symmetricSolver_work);
@@ -87,12 +100,18 @@ int ImplicitNewmarkDense::DoTimestep()
   {
     int i;
 
-
     PerformanceCounter counterForceAssemblyTime;
     reducedForceModel->GetForceAndMatrix(q, internalForces, tangentStiffnessMatrix);
     counterForceAssemblyTime.StopCounter();
     forceAssemblyTime = counterForceAssemblyTime.GetElapsedTime();
 
+    if (plasticfq != NULL)
+    {
+      SetTotalForces(internalForces);
+      for(int i=0; i<r; i++)
+        internalForces[i] -= plasticfq[i];
+    }
+
     // scale internal forces
     for(i=0; i<r; i++)
       internalForces[i] *= internalForceScalingFactor;
@@ -150,6 +169,8 @@ int ImplicitNewmarkDense::DoTimestep()
       for(i=0; i<r2; i++)
       {
         dampingMatrix[i] = dampingMassCoef * massMatrix[i] + dampingStiffnessCoef * tangentStiffnessMatrix[i];
+        dampingMatrix[i] += dampingMatrixOffset[i];
+
         tangentStiffnessMatrix[i] += alpha4 * dampingMatrix[i];
         //tangentStiffnessMatrix[i] += alpha3 * massMatrix[i] + gamma * alpha1 * dampingMatrix[i]; // static Rayleigh damping
 
@@ -248,9 +269,11 @@ int ImplicitNewmarkDense::DoTimestep()
         INTEGER LDB = r;
         INTEGER INFO;
 
-         #ifdef __APPLE__
-           #define DGESV dgesv_
-         #endif
+        #ifdef __APPLE__
+          #define DGESV dgesv_
+        #else
+          #define DGESV dgesv
+        #endif
 
         DGESV ( &N, &NRHS, A, &LDA, IPIV->GetBuf(), B, &LDB, &INFO );
 
@@ -266,9 +289,11 @@ int ImplicitNewmarkDense::DoTimestep()
       {
         // call dsysv ( uplo, n, nrhs, a, lda, ipiv, b, ldb, work, lwork, info)
   
-         #ifdef __APPLE__
-           #define DSYSV dsysv_
-         #endif
+        #ifdef __APPLE__
+          #define DSYSV dsysv_
+        #else
+          #define DSYSV dsysv
+        #endif
 
         char uplo = 'U';
         INTEGER nrhs = 1;
@@ -290,9 +315,11 @@ int ImplicitNewmarkDense::DoTimestep()
       {
         // call dposv ( uplo, n, nrhs, a, lda, b, ldb, info)
 
-         #ifdef __APPLE__
-           #define DPOSV dposv_
-         #endif
+        #ifdef __APPLE__
+          #define DPOSV dposv_
+        #else
+          #define DPOSV dposv
+        #endif
   
         char uplo = 'U';
         INTEGER nrhs = 1;
@@ -326,17 +353,25 @@ int ImplicitNewmarkDense::DoTimestep()
 */
 
     // update state
+/*
     for(i=0; i<r; i++)
     {
       q[i] += qdelta[i];
       qaccel[i] = alpha1 * (q[i] - q_1[i]) - alpha2 * qvel_1[i] - alpha3 * qaccel_1[i];
       qvel[i] = alpha4 * (q[i] - q_1[i]) + alpha5 * qvel_1[i] + alpha6 * qaccel_1[i];
     }
+*/
+
+    for(i=0; i<r; i++)
+      q[i] += qdelta[i];
+    Setq(q);
 
     numIter++;
   }
   while (numIter < maxIterations);
 
+  ProcessPlasticDeformations();
+
 /*
   printf("Num iterations performed: %d (maxIterations=%d)\n", numIter, maxIterations);
   if ((numIter >= maxIterations) && (maxIterations > 1))
@@ -348,3 +383,15 @@ int ImplicitNewmarkDense::DoTimestep()
   return 0;
 }
 
+void ImplicitNewmarkDense::Setq(double * q)
+{
+  // set the new state, and update velocity and acceleration
+  for(int i=0; i<r; i++)
+  {
+    (this->q)[i] = q[i];
+    qaccel[i] = alpha1 * (q[i] - q_1[i]) - alpha2 * qvel_1[i] - alpha3 * qaccel_1[i];
+    qvel[i] = alpha4 * (q[i] - q_1[i]) + alpha5 * qvel_1[i] + alpha6 * qaccel_1[i];
+  }
+}
+
+
diff --git a/src/libintegratorDense/implicitNewmarkDense.h b/libraries/integratorDense/implicitNewmarkDense.h
similarity index 73%
rename from src/libintegratorDense/implicitNewmarkDense.h
rename to libraries/integratorDense/implicitNewmarkDense.h
index 27ef8f6dedf5b2a0f309e381c1ae69c3812e27b8..cf6e16724b63bd8ee99e6716e95d9c37516b4e11 100644
--- a/src/libintegratorDense/implicitNewmarkDense.h
+++ b/libraries/integratorDense/implicitNewmarkDense.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -42,7 +46,7 @@ values of r, there will be a computational slowdown.
 
 #include "integratorBaseDense.h"
 
-class ImplicitNewmarkDense : public IntegratorBaseDense
+class ImplicitNewmarkDense : public virtual IntegratorBaseDense
 {
 public:
 
@@ -55,18 +59,24 @@ public:
   virtual ~ImplicitNewmarkDense();
 
   inline virtual void SetTimestep(double timestep) { this->timestep = timestep; UpdateAlphas(); }
+  inline void SetSolverType(solverType solver_) { this->solver = solver_; }
 
-  // performs one timestep of simulation (returns 0 on sucess, and 1 on failure)
+  // performs one timestep of simulation (returns 0 on success, and 1 on failure)
   // failure can occur, for example, if you are using the positive definite solver and the system matrix has negative eigenvalues
   virtual int DoTimestep(); 
 
-  inline void SetNewmarkBeta(double NewmarkBeta) { this->NewmarkBeta = NewmarkBeta; UpdateAlphas(); }
-  inline void SetNewmarkGamma(double NewmarkGamma) { this->NewmarkGamma = NewmarkGamma; UpdateAlphas(); }
+  inline virtual void SetNewmarkBeta(double NewmarkBeta) { this->NewmarkBeta = NewmarkBeta; UpdateAlphas(); }
+  inline virtual void SetNewmarkGamma(double NewmarkGamma) { this->NewmarkGamma = NewmarkGamma; UpdateAlphas(); }
   inline void SetMaxIterations(int maxIterations) { this->maxIterations = maxIterations; }
   inline void SetEpsilon(double epsilon) { this->epsilon = epsilon; }
 
+  // sets a new reduced coordinate, and adjusts velocity and acceleration
+  void Setq(double * q);
+
 protected:
 
+  ImplicitNewmarkDense(int r, double timestep, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, double NewmarkBeta=0.25, double NewmarkGamma=0.5);
+
   int symmetricSolver_lwork;
   double * symmetricSolver_work;
 
diff --git a/libraries/integratorDense/implicitNewmarkDenseMulti1D.cpp b/libraries/integratorDense/implicitNewmarkDenseMulti1D.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..373e4f18f5884b5efbf7702a4e0d49c498fa713e
--- /dev/null
+++ b/libraries/integratorDense/implicitNewmarkDenseMulti1D.cpp
@@ -0,0 +1,148 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "lapack-headers.h"
+#include "matrix.h"
+#include "matrixLAPACK.h"
+#include "performanceCounter.h"
+#include "implicitNewmarkDense.h"
+#include "implicitNewmarkDenseMulti1D.h"
+#include "IPIVC.h"
+
+ImplicitNewmarkDenseMulti1D::ImplicitNewmarkDenseMulti1D(int r_, double timestep_, double * massMatrix_, double * tangentStiffnessMatrix_, double dampingMassCoef_, double dampingStiffnessCoef_, double NewmarkBeta_, double NewmarkGamma_):
+  IntegratorBaseDense(r_, timestep_, dampingMassCoef_, dampingStiffnessCoef_),
+  IntegratorMulti1D(r_, timestep_, massMatrix_, tangentStiffnessMatrix_, dampingMassCoef_, dampingStiffnessCoef_),
+  ImplicitNewmarkDense(r_, timestep_, dampingMassCoef_, dampingStiffnessCoef_, 0.25, 0.5)
+{
+  coef_deltaZ = (double *) malloc (sizeof(double) * r);
+  coef_zvel = (double *) malloc (sizeof(double) * r);
+  coef_zaccel = (double *) malloc (sizeof(double) * r);
+
+  UpdateCoefs();
+}
+
+ImplicitNewmarkDenseMulti1D::~ImplicitNewmarkDenseMulti1D()
+{
+  free(coef_deltaZ);
+  free(coef_zvel);
+  free(coef_zaccel);
+}
+
+int ImplicitNewmarkDenseMulti1D::DoTimestep()
+{
+  // rotate the external forces: rotatedForces = Q^T * externalForces
+  bool useRotationTranspose = true;
+  RotateVector(Q, externalForces, rotatedForces, useRotationTranspose);
+
+  for(int dim=0; dim<r; dim++)
+  {
+    // evaluate rhs = (alpha2 - damping * alpha5) * z_vel + (alpha3 - damping * alpha6) * z_accel - stiffness * z + f_ext
+    double rhs = coef_zvel[dim] * zvel[dim] + coef_zaccel[dim] * zaccel[dim] - tangentStiffnessMatrix[dim] * z[dim] + rotatedForces[dim];
+
+    // solve for qDelta
+    qdelta[dim] = rhs / coef_deltaZ[dim];
+  }
+
+  // update z, zvel, and zaccel
+  for(int dim=0; dim<r; dim++)
+  {
+    z[dim] += qdelta[dim];
+    double zvelTemp = zvel[dim];
+    double zaccelTemp = zaccel[dim];
+    zvel[dim] = alpha4 * qdelta[dim] + alpha5 * zvelTemp + alpha6 * zaccelTemp;
+    zaccel[dim] = alpha1 * qdelta[dim] - alpha2 * zvelTemp - alpha3 * zaccelTemp;
+  }
+
+  // update states by rotating z, zvel, zaccel with Q
+  useRotationTranspose = false;
+  RotateVector(Q, z, q, useRotationTranspose); // q = Q z
+  RotateVector(Q, zvel, qvel, useRotationTranspose); // qvel = Q zvel
+  RotateVector(Q, zaccel, qaccel, useRotationTranspose); // qaccel = Q zaccel
+
+  return 0;
+}
+
+void ImplicitNewmarkDenseMulti1D::SetTimestep(double timestep_)
+{
+  ImplicitNewmarkDense::SetTimestep(timestep_);
+  UpdateCoefs();
+}
+
+// this function should be always called after alphas are updated
+void ImplicitNewmarkDenseMulti1D::UpdateCoefs()
+{
+  for(int dim=0; dim<r; dim++)
+  {
+    dampingMatrix[dim] = dampingMassCoef + dampingStiffnessCoef * tangentStiffnessMatrix[dim];
+    coef_deltaZ[dim] = alpha1 + dampingMatrix[dim] * alpha4 + tangentStiffnessMatrix[dim];
+    coef_zvel[dim] = alpha2 - dampingMatrix[dim] * alpha5;
+    coef_zaccel[dim] = alpha3 - dampingMatrix[dim] * alpha6;
+  }
+}
+
+void ImplicitNewmarkDenseMulti1D::SetNewmarkBeta(double NewmarkBeta_)
+{
+  NewmarkBeta = NewmarkBeta_; 
+  UpdateAlphas(); 
+  UpdateCoefs();
+}
+
+void ImplicitNewmarkDenseMulti1D::SetNewmarkGamma(double NewmarkGamma_)
+{
+  NewmarkGamma = NewmarkGamma_; 
+  UpdateAlphas();
+  UpdateCoefs();
+}
+
+void ImplicitNewmarkDenseMulti1D::SetInternalForceScalingFactor(double internalForceScalingFactor_)
+{
+  internalForceScalingFactor = internalForceScalingFactor_;
+  for(int dim=0; dim<r; dim++)
+    tangentStiffnessMatrix[dim] = internalForceScalingFactor * tangentStiffnessMatrixOriginal[dim];
+  UpdateCoefs();
+}
+
+void ImplicitNewmarkDenseMulti1D::SetDampingMassCoef(double dampingMassCoef_)
+{ 
+  dampingMassCoef = dampingMassCoef_;
+  UpdateCoefs();
+}
+
+void ImplicitNewmarkDenseMulti1D::SetDampingStiffnessCoef(double dampingStiffnessCoef_)
+{
+  dampingStiffnessCoef = dampingStiffnessCoef_;
+  UpdateCoefs();
+}
+
diff --git a/libraries/integratorDense/implicitNewmarkDenseMulti1D.h b/libraries/integratorDense/implicitNewmarkDenseMulti1D.h
new file mode 100644
index 0000000000000000000000000000000000000000..e9ac8365e033699ea2c76f20d20364107bb97f5b
--- /dev/null
+++ b/libraries/integratorDense/implicitNewmarkDenseMulti1D.h
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A class to timestep dense diagonal dynamics.
+*/
+
+#ifndef _IMPLICITNEWMARKDENSEMULTI1D_H_
+#define _IMPLICITNEWMARKDENSEMULTI1D_H_
+
+#include "integratorMulti1D.h"
+
+class ImplicitNewmarkDenseMulti1D : public IntegratorMulti1D, public ImplicitNewmarkDense
+{
+public:
+  ImplicitNewmarkDenseMulti1D(int r, double timestep, double * massMatrix, double * tangentStiffnessMatrix, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, double NewmarkBeta=0.25, double NewmarkGamma=0.5);
+
+  virtual ~ImplicitNewmarkDenseMulti1D();
+  
+  virtual void SetTimestep(double timestep);
+  virtual void SetNewmarkBeta(double NewmarkBeta);
+  virtual void SetNewmarkGamma(double NewmarkGamma);
+  virtual void SetInternalForceScalingFactor(double internalForceScalingFactor);
+  virtual void SetDampingMassCoef(double dampingMassCoef);
+  virtual void SetDampingStiffnessCoef(double dampingStiffnessCoef);
+
+  // performs one timestep of simulation (returns 0 on success, and 1 on failure)
+  virtual int DoTimestep(); 
+
+protected:
+  double * coef_deltaZ;     // a r x 1 vector, for dimension i, coef_deltaZ[i] = alpha1 + diagonalizedDampingMatrix[i] * alpha4 + diagonalizedStiffness[i]
+  double * coef_zvel;       // a r x 1 vector, for dimension i, coef_zvel[i] = alpha2 - diagonalizedDampingMatrix[i] * alpha5
+  double * coef_zaccel;     // a r x 1 vector, for dimension i, coef_zaccel[i] = alpha3 - diagonalizedDampingMatrix[i] * alpha6
+  
+  void UpdateCoefs();       // this function should be always called after alphas are updated
+};
+
+#endif
+
diff --git a/src/libintegratorDense/integratorBaseDense.cpp b/libraries/integratorDense/integratorBaseDense.cpp
similarity index 58%
rename from src/libintegratorDense/integratorBaseDense.cpp
rename to libraries/integratorDense/integratorBaseDense.cpp
index 7e472445af0f11d23cde67a262cb0fe58df8b828..460da5f30635032458e9c00eedc68616e8026e45 100644
--- a/src/libintegratorDense/integratorBaseDense.cpp
+++ b/libraries/integratorDense/integratorBaseDense.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,28 +30,30 @@
  *                                                                       *
  *************************************************************************/
 
-#include "lapack-headers.h"
-#include "integratorBaseDense.h"
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <float.h>
+#include <cassert>
+#include "lapack-headers.h"
+#include "integratorBaseDense.h"
 #include "IPIVC.h"
 
-IntegratorBaseDense::IntegratorBaseDense(int r, double timestep, double * massMatrix, ReducedForceModel * reducedForceModel, double dampingMassCoef, double dampingStiffnessCoef) : IntegratorBase(r, timestep, dampingMassCoef, dampingStiffnessCoef), useStaticSolver(0)
+IntegratorBaseDense::IntegratorBaseDense(int r, double timestep, double * massMatrix, ReducedForceModel * reducedForceModel, double dampingMassCoef, double dampingStiffnessCoef) : IntegratorBase(r, timestep, dampingMassCoef, dampingStiffnessCoef), useStaticSolver(0), usePlasticDeformations(0), plasticThreshold2(DBL_MAX), plasticfq(NULL), totalfq(NULL)
 {
   r2 = r * r;
   this->massMatrix = (double*) malloc (sizeof(double) * r2);
   dampingMatrix = (double*) malloc (sizeof(double) * r2);
   tangentStiffnessMatrix = (double*) malloc (sizeof(double) * r2);
-  memcpy(this->massMatrix,massMatrix,sizeof(double) * r2);
+  memcpy(this->massMatrix, massMatrix,sizeof(double) * r2);
 
   forceAssemblyTime = systemSolveTime = 0.0;
 
   this->reducedForceModel = reducedForceModel;
 
   tangentStiffnessMatrixOffset = (double*) calloc (r2, sizeof(double));
+  dampingMatrixOffset = (double*) calloc (r2, sizeof(double));
   IPIV = new IPIVC(r);
 
   ResetToRest();
@@ -56,6 +62,25 @@ IntegratorBaseDense::IntegratorBaseDense(int r, double timestep, double * massMa
   memset(internalForces,0,sizeof(double) * r);
 }
 
+IntegratorBaseDense::IntegratorBaseDense(int r, double timestep, double dampingMassCoef, double dampingStiffnessCoef): IntegratorBase(r, timestep, dampingMassCoef, dampingStiffnessCoef), useStaticSolver(0), usePlasticDeformations(0), plasticThreshold2(DBL_MAX), plasticfq(NULL), totalfq(NULL)
+{
+  this->massMatrix = NULL;
+  dampingMatrix = NULL;
+  tangentStiffnessMatrix = NULL;
+  r2 = 0;
+
+  forceAssemblyTime = systemSolveTime = 0.0;
+
+  this->reducedForceModel = NULL;
+
+  tangentStiffnessMatrixOffset = NULL;
+  dampingMatrixOffset = NULL;
+  IPIV = NULL;
+
+  memset(externalForces,0,sizeof(double) * r);
+  memset(internalForces,0,sizeof(double) * r);
+}
+
 IntegratorBaseDense::~IntegratorBaseDense()
 {
   free(massMatrix);
@@ -63,6 +88,8 @@ IntegratorBaseDense::~IntegratorBaseDense()
   free(tangentStiffnessMatrix);
   delete(IPIV);
   free(tangentStiffnessMatrixOffset);
+  free(plasticfq);
+  free(totalfq);
 }
 
 void IntegratorBaseDense::SetMassMatrix(double * massMatrix)
@@ -72,15 +99,40 @@ void IntegratorBaseDense::SetMassMatrix(double * massMatrix)
 
 void IntegratorBaseDense::SetTangentStiffnessMatrixOffset(double * tangentStiffnessMatrixOffset)
 {
+  assert (this->tangentStiffnessMatrixOffset);
   memcpy(this->tangentStiffnessMatrixOffset, tangentStiffnessMatrixOffset, sizeof(double) * r * r);
 }
 
-void IntegratorBaseDense::ClearTangentStiffnessMatrixOffset()
+void IntegratorBaseDense::AddTangentStiffnessMatrixOffset(double * tangentStiffnessMatrixOffset)
+{
+  assert (this->tangentStiffnessMatrixOffset);
+  int r2 = r * r;
+  for(int i=0; i<r2; i++)
+    (this->tangentStiffnessMatrixOffset)[i] += tangentStiffnessMatrixOffset[i];
+}
 
+void IntegratorBaseDense::ClearTangentStiffnessMatrixOffset()
 {
+  if (this->tangentStiffnessMatrixOffset == NULL) 
+    return;
   memset(this->tangentStiffnessMatrixOffset, 0, sizeof(double) * r * r);
 }
 
+void IntegratorBaseDense::ClearDampingMatrixOffset()
+{
+  if (this->dampingMatrixOffset == NULL) 
+    return;
+  memset(this->dampingMatrixOffset, 0, sizeof(double) * r * r);
+}
+
+void IntegratorBaseDense::AddDampingMatrixOffset(double * dampingMatrixOffset)
+{
+  assert (this->dampingMatrixOffset);
+  int r2 = r * r;
+  for(int i=0; i<r2; i++)
+    (this->dampingMatrixOffset)[i] += dampingMatrixOffset[i];
+}
+
 double IntegratorBaseDense::GetKineticEnergy()
 {
   // Wkin = 0.5 * <massMatrix * vel, vel>
@@ -108,6 +160,8 @@ void IntegratorBaseDense::ResetToRest()
 
   if (reducedForceModel != NULL)
     reducedForceModel->ResetToZero();
+
+  ClearPlasticDeformations();
 }
 
 int IntegratorBaseDense::SetState(double * q_, double * qvel_)
@@ -120,10 +174,10 @@ int IntegratorBaseDense::SetState(double * q_, double * qvel_)
     memset(qvel, 0, sizeof(double)*r);
 
   // M * qaccel + C * qvel + R(q) = P_0 
-  // R(q) = P_0 = 0
+  // assume P_0 = 0
   // i.e. M * qaccel = - C * qvel - R(q)
-
-  reducedForceModel->GetForceAndMatrix(q, internalForces, tangentStiffnessMatrix);
+  if (reducedForceModel != NULL)
+    reducedForceModel->GetForceAndMatrix(q, internalForces, tangentStiffnessMatrix);
 
   int r2 = r * r;
   for(int i=0; i<r2; i++)
@@ -139,9 +193,11 @@ int IntegratorBaseDense::SetState(double * q_, double * qvel_)
   // solve M * x = qaccel
   // call dposv ( uplo, n, nrhs, a, lda, b, ldb, info)
 
-   #ifdef __APPLE__
-     #define DPOSV dposv_  
-   #endif
+  #ifdef __APPLE__
+    #define DPOSV dposv_  
+  #else
+    #define DPOSV dposv
+  #endif
 
   memcpy(tangentStiffnessMatrix, massMatrix, sizeof(double) * r2); // must copy mass matrix to another buffer since DPOSV overwrites the system matrix
   char uplo = 'U';
@@ -173,3 +229,58 @@ void IntegratorBaseDense::UseStaticSolver(bool useStaticSolver_)
   }
 }
 
+void IntegratorBaseDense::UsePlasticDeformations(int usePlasticDeformations_)
+{
+  usePlasticDeformations = usePlasticDeformations_;
+  if (usePlasticDeformations)
+  {
+    if (plasticfq == NULL)
+     plasticfq = (double*) calloc (r, sizeof(double));
+
+    if (totalfq == NULL)
+      totalfq = (double*) calloc (r, sizeof(double));
+  }
+}
+
+void IntegratorBaseDense::SetPlasticThreshold(double plasticThreshold2_)
+{
+  plasticThreshold2 = plasticThreshold2_;
+}
+
+void IntegratorBaseDense::ClearPlasticDeformations()
+{
+  if (!usePlasticDeformations)
+    return;
+
+  for(int i=0; i<r; i++)
+  {
+    plasticfq[i] = 0.0;
+    totalfq[i] = 0.0;
+  }
+}
+
+void IntegratorBaseDense::ProcessPlasticDeformations()
+{
+  if (!usePlasticDeformations)
+    return;
+
+  double norm2 = 0.0;
+  for(int i=0; i<r; i++)
+    norm2 += (totalfq[i] - plasticfq[i]) * (totalfq[i] - plasticfq[i]);
+
+  if (norm2 > plasticThreshold2)
+  {
+    // plastic threshold has been exceeded
+    //printf("Plastic threshold exceeded!\n");
+    double norm = sqrt(norm2);
+    double offset = norm - sqrt(plasticThreshold2);
+    for(int i=0; i<r; i++)
+      plasticfq[i] += offset / norm * (totalfq[i] - plasticfq[i]);
+  }
+}
+
+void IntegratorBaseDense::SetTotalForces(double * totalfq_)
+{
+  memcpy(totalfq, totalfq_, sizeof(double) * r);
+}
+
diff --git a/src/libintegratorDense/integratorBaseDense.h b/libraries/integratorDense/integratorBaseDense.h
similarity index 75%
rename from src/libintegratorDense/integratorBaseDense.h
rename to libraries/integratorDense/integratorBaseDense.h
index c33b160cd91054be4789add8c0a2f4ec4239ee9a..1d0dce86b62fd2b3907329921cea8daa5d8e625f 100644
--- a/src/libintegratorDense/integratorBaseDense.h
+++ b/libraries/integratorDense/integratorBaseDense.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -61,7 +65,11 @@ public:
 
   void SetMassMatrix(double * massMatrix);
   void SetTangentStiffnessMatrixOffset(double * tangentStiffnessMatrixOffset);
+  void AddTangentStiffnessMatrixOffset(double * tangentStiffnessMatrixOffset);
+  void AddDampingMatrixOffset(double * dampingMatrixOffset);
+
   void ClearTangentStiffnessMatrixOffset();
+  void ClearDampingMatrixOffset();
 
   // dynamic solver is default (i.e. useStaticSolver=false)
   // with the static solver, all dynamic terms are neglected, and the system only computes the static equilibrium under the currently applied external forces
@@ -77,7 +85,14 @@ public:
   inline virtual double GetForceAssemblyTime() { return forceAssemblyTime; }
   inline virtual double GetSystemSolveTime() { return systemSolveTime; }
 
+  // plastic deformations
+  void UsePlasticDeformations(int usePlasticDeformations);
+  void SetPlasticThreshold(double plasticThreshold2);
+  void ClearPlasticDeformations();
+
 protected:
+  
+  IntegratorBaseDense(int r, double timestep, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0);
 
   double * massMatrix; // pointer to the reduced mass matrix
   double * dampingMatrix; 
@@ -99,6 +114,15 @@ protected:
 
   bool useStaticSolver;
   double * tangentStiffnessMatrixOffset;
+  double * dampingMatrixOffset;
+
+  // plastic deformations
+  int usePlasticDeformations;
+  double plasticThreshold2;
+  double * plasticfq;
+  double * totalfq;
+  void SetTotalForces(double * fq);
+  void ProcessPlasticDeformations();
 };
 
 #endif
diff --git a/libraries/integratorDense/integratorMulti1D.cpp b/libraries/integratorDense/integratorMulti1D.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..116e966ad23dbb032fea20aa3d19538957d2e550
--- /dev/null
+++ b/libraries/integratorDense/integratorMulti1D.cpp
@@ -0,0 +1,301 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <math.h>
+#include <memory.h>
+#include "matrix.h"
+#include "matrixLAPACK.h"
+#include "integratorMulti1D.h"
+#include "float.h"
+
+IntegratorMulti1D::IntegratorMulti1D(int r, double timestep, double * massMatrix_, double * tangentStiffnessMatrix_, double dampingMassCoef, double dampingStiffnessCoef): IntegratorBaseDense(r, timestep, dampingMassCoef, dampingStiffnessCoef)
+{
+  z = (double *) calloc (r, sizeof(double));
+  zvel = (double *) calloc (r, sizeof(double));
+  zaccel = (double *) calloc (r, sizeof(double));
+  
+  double * lambda = (double*) malloc (sizeof(double) * r);
+  Q = (double *) malloc (sizeof(double) * r * r);
+
+  // must do deep copy since the matrix may be modified inside the SymmetricMatrixGeneralEigenDecomposition
+  double * tempTangentStiffnessMatrix = (double *) malloc (sizeof(double) * r * r);
+  double * tempMassMatrix = (double *) malloc (sizeof(double) * r * r);
+  memcpy(tempTangentStiffnessMatrix, tangentStiffnessMatrix_, sizeof(double) * r * r); 
+  memcpy(tempMassMatrix, massMatrix_, sizeof(double) * r * r);
+
+  SymmetricMatrixGeneralEigenDecomposition(r, tempTangentStiffnessMatrix, tempMassMatrix, Q, lambda);  // rotation is stored in Q after this function call
+
+  // set the tangent stiffness matrix (tangent stiffness matrix is a diagonal matrix; therefore only the diagonal elements are stored.)
+  tangentStiffnessMatrix = (double *) malloc (sizeof(double) * r);
+  for(int dim=0; dim<r; dim++)
+    tangentStiffnessMatrix[dim] = lambda[dim];
+  free(lambda);
+
+  tangentStiffnessMatrixOriginal = (double *) malloc (sizeof(double) * r);
+  memcpy(tangentStiffnessMatrixOriginal, tangentStiffnessMatrix, sizeof(double) * r);
+
+  // mass matrix is not needed because Q^T M Q = I
+  massMatrix = NULL;
+  
+  dampingMatrix = (double *) malloc (sizeof(double) * r);
+
+  // compute Q^T M
+  QTM = NULL;
+  ComputeQTM(massMatrix_);
+ 
+  // testing: Q^T * M * Q = I 
+  /*
+  printf("testing: Q^T * M * Q = I\n");
+  Matrix<double> QTMM(r, r, QTM, false, false);
+  Matrix<double> QM(r, r, Q, false, false);  
+  double * ITemp = (double *) malloc (sizeof(double) * r * r);
+  Matrix<double> ITempM(r, r, ITemp, false, false);
+  ITempM = QTMM * QM;
+  printf("\nQ^T * M * Q = \n");
+  for(int row=0; row<r; row++)
+  {
+    for(int col=0; col<r; col++)
+    {
+      double temp = ITemp[ELT(r, row, col)];
+      if (temp < 1e-8)
+        printf("0, ");
+      else
+        printf("%G, ", temp);
+    }
+    printf("\n");
+  }
+  free(ITemp);
+  */
+
+  rotatedForces = (double *) malloc (sizeof(double) * r);
+
+  free(tempTangentStiffnessMatrix);
+  free(tempMassMatrix);
+}
+
+IntegratorMulti1D::IntegratorMulti1D(int r, double timestep, double * massMatrix_, double * tangentStiffnessMatrixDiagonalElements, double * modeRotationMatrix, double dampingMassCoef, double dampingStiffnessCoef)
+: IntegratorBaseDense(r, timestep, dampingMassCoef, dampingStiffnessCoef)
+{
+  z = (double *) calloc (r, sizeof(double));
+  zvel = (double *) calloc (r, sizeof(double));
+  zaccel = (double *) calloc (r, sizeof(double));
+
+  Q = (double *) malloc (sizeof(double) * r * r);
+  memcpy(Q, modeRotationMatrix, sizeof(double) * r * r);
+  
+  tangentStiffnessMatrix = (double *) malloc (sizeof(double) * r);
+  memcpy(tangentStiffnessMatrix, tangentStiffnessMatrixDiagonalElements, sizeof(double) * r);
+
+  tangentStiffnessMatrixOriginal = (double *) malloc (sizeof(double) * r);
+  memcpy(tangentStiffnessMatrixOriginal, tangentStiffnessMatrix, sizeof(double) * r);
+
+  massMatrix = NULL;
+  dampingMatrix = (double *) malloc (sizeof(double) * r);
+
+  // compute (Q^T)M
+  QTM = NULL;
+  ComputeQTM(massMatrix_);
+
+  rotatedForces = (double *) malloc (sizeof(double) * r);
+}
+
+IntegratorMulti1D::~IntegratorMulti1D()
+{
+  free(z);
+  free(zvel);
+  free(zaccel);
+  free(tangentStiffnessMatrixOriginal);
+  free(Q);
+  free(QTM);
+  free(rotatedForces);
+}
+
+void IntegratorMulti1D::ResetToRest()
+{
+  IntegratorBase::ResetToRest();
+  memset(z, 0, sizeof(double) * r);
+  memset(zvel, 0, sizeof(double) * r);
+  memset(zaccel, 0, sizeof(double) * r);
+}
+
+void IntegratorMulti1D::RotateVector(double * Q, double * source, double * dest, bool useRotationTranspose)
+{  
+  Matrix<double> sourceM(r, 1, source, false, false);
+  double * result = (double *) malloc (sizeof(double) * r);
+  Matrix<double> resultM(r, 1, result, false, false);
+  Matrix<double> QM(r, r, Q, false, false);
+  
+  if (useRotationTranspose)
+    resultM = QM.MultiplyT(sourceM);
+  else
+    resultM = QM * sourceM;
+
+  memcpy(dest, result, sizeof(double) * r);
+  free(result);
+}
+
+int IntegratorMulti1D::SetState(double * q_, double * qvel_)
+{
+  memcpy(q, q_, sizeof(double) * r);
+  RotateVector(QTM, q, z);
+
+  if (qvel_ == NULL)
+  {
+    memset(qvel, 0, sizeof(double) * r);
+    memset(zvel, 0, sizeof(double) * r);
+  }
+  else
+  {
+    memcpy(qvel, qvel_, sizeof(double) * r);
+    RotateVector(QTM, qvel, zvel);
+  }
+
+  // zaccel + (dampingMassCoef + dampingStiffnessCoef * tangentStiffnessMatrixDiagonalElement) * zvel + tangentStiffnessMatrixDiagonalElement * z = Q^T * f_ext
+  // rotate the external forces: rotatedForces = Q^T * externalForces
+  bool useRotationTranspose = true;
+    RotateVector(Q, externalForces, rotatedForces, useRotationTranspose);
+
+  for(int dim=0; dim<r; dim++)
+  {
+    double zvelCoef = dampingMassCoef + dampingStiffnessCoef * tangentStiffnessMatrix[dim];
+    zaccel[dim] = rotatedForces[dim] - tangentStiffnessMatrix[dim] * z[dim] - zvelCoef * zvel[dim];
+  }
+  return 0;
+}
+
+void IntegratorMulti1D::ConstrainToSphere(double R2)
+{
+  double norm2 = 0;
+  for(int i=0; i<r; i++)
+    norm2 += q[i] * q[i];
+
+  if (norm2 > R2)
+  {
+    double beta = sqrt(R2 / norm2);
+    for(int dim=0; dim<r; dim++)
+    {
+      q[dim] *= beta;
+      q_1[dim] = q[dim];
+      z[dim] *= beta;
+
+      qvel[dim] = 0;
+      qvel_1[dim] = 0;
+      zvel[dim] = 0;
+
+      qaccel[dim] = 0;
+      qaccel_1[dim] = 0;
+      zaccel[dim] = 0;
+    }
+  }
+}
+
+void IntegratorMulti1D::SetqState(const double * q_, const double * qvel_, const double * qaccel_)
+{
+  // Since q = Qz and (Q^T)MQ = I, z = (Q^T)Mq
+
+  if (q != q_)
+    memcpy(q, q_, sizeof(double) * r);
+  RotateVector(QTM, q, z);
+  
+  if (qvel_ != NULL)
+  {
+    if (qvel != qvel_)
+      memcpy(qvel, qvel_, sizeof(double) * r);
+    RotateVector(QTM, qvel, zvel);
+  }
+
+  if (qaccel_ != NULL)
+  {
+    if (qaccel != qaccel_)
+      memcpy(qaccel, qaccel_, sizeof(double) * r); 
+    RotateVector(QTM, qaccel, zaccel);
+  }
+}
+
+void IntegratorMulti1D::GetqState(double * q_, double * qvel_, double * qaccel_)
+{
+  // q = Qz
+  RotateVector(Q, z, q);
+  memcpy(q_, q, sizeof(double) * r);
+ 
+  if (qvel_ != NULL)
+  {
+    RotateVector(Q, zvel, qvel);
+    memcpy(qvel_, qvel, sizeof(double) * r);
+  }
+  if (qaccel_ != NULL) 
+  {
+    RotateVector(Q, zaccel, qaccel);
+    memcpy(qaccel_, qaccel, sizeof(double) * r);
+  }
+}
+
+void IntegratorMulti1D::SetQ(int index, double qIndex)
+{
+  q[index] = qIndex;
+  RotateVector(QTM, q, z);
+}
+
+double IntegratorMulti1D::GetQ(int index)
+{
+  RotateVector(Q, z, q);
+  return q[index];
+}
+
+double * IntegratorMulti1D::Getq()
+{
+  RotateVector(Q, z, q);
+  return q;
+}
+
+double * IntegratorMulti1D::Getqvel()
+{
+  RotateVector(Q, zvel, qvel);
+  return qvel;
+}
+
+double * IntegratorMulti1D::Getqaccel()
+{
+  RotateVector(Q, zaccel, qaccel);
+  return qaccel;
+}
+
+void IntegratorMulti1D::ComputeQTM(double * massMatrix_)
+{
+  free(QTM);
+  QTM = (double *) malloc (sizeof(double) * r * r);
+  Matrix<double> QM(r, r, Q, false, false);
+  Matrix<double> QTMM(r, r, QTM, false, false);
+  Matrix<double> massM(r, r, massMatrix_, false, false);
+  QTMM = QM.MultiplyT(massM);
+}
+
diff --git a/libraries/integratorDense/integratorMulti1D.h b/libraries/integratorDense/integratorMulti1D.h
new file mode 100644
index 0000000000000000000000000000000000000000..f225c2f203e32fe2a1380c642f142c819200a7ed
--- /dev/null
+++ b/libraries/integratorDense/integratorMulti1D.h
@@ -0,0 +1,118 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A base abstract class to timestep decoupled dynamics
+  For each dimension, the decoupled equation is as follows,
+  
+  z_accel + d * z_vel + k * z = f_external
+
+  (i.e., usually linear model reduction dynamics).
+
+  It is derived into classes that support decoupled implicit Newmark,
+  and decoupled central differences.
+
+*/
+
+#ifndef _INTEGRATORMULTI1D_H_
+#define _INTEGRATORMULTI1D_H_
+
+#include "integratorBaseDense.h"
+#include "reducedForceModel.h"
+
+class IntegratorMulti1D : public virtual IntegratorBaseDense
+{
+public:
+  // r is the dimension of the simulation basis
+  // the damping coefficients are tangential Rayleigh damping coefficients, see [2]
+
+  // Constructor 1: Mass matrix and tangent stiffness matrix are two general r x r matrices. The constructor will diagonalize the system making it decoupled.
+  IntegratorMulti1D(int r, double timestep, double * massMatrix, double * tangentStiffnessMatrix, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0);
+
+  // Constructor 2:
+  // Besides r and timestep, you need to pass:
+  // massMatrix (general mass matrix, not diagonalized, r x r) which is used to compute Q^T * M;
+  // tangentStiffnessMatrixDiagonalElements (diagonalized, r x 1);
+  // modeRotationMatrix (denoted as Q) is a r x r mode rotation matrix, it is usually obtained by mass-PCA, that is, to solve
+  // K * ksi_i = lambda_i * M * ksi_i; (i = 1, 2, ... r), ksi_i is the i-th eigen vector whose dimension is r.
+  // Q = [ksi_1, ksi_2, ..., ksi_r];
+  // Also in this case make sure Q^T * M * Q = I
+  IntegratorMulti1D(int r, double timestep, double * massMatrix, double * tangentStiffnessMatrixDiagonalElements, double * modeRotationMatrix, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0); 
+
+  virtual ~IntegratorMulti1D();
+ 
+  // sets the position and the velocity
+  // this routine will internally automatically compute proper acceleration 
+  // returns 0 on success, 1 if solver fails to converge
+  virtual int SetState(double * q, double * qvel=NULL);
+ 
+  // sets the position, velocity, and acceleration
+  // note: if you don't set all three at once, old values will persist, which may not be what you want
+  virtual void SetqState(const double * q, const double * qvel=NULL, const double * qaccel=NULL);
+
+  // copies the state into spaces provided by q,qvel,qaccel (each a vector of length r; if NULL is provided for either of q,qvel,qccel, that part of the state is not copied)
+  virtual void GetqState(double * q, double * qvel=NULL, double * qaccel=NULL);
+
+  // set/get invidivual position components:
+  virtual void SetQ(int index, double qIndex);
+  virtual double GetQ(int index);
+
+  // obtain pointers to the internally stored position, velocity, and acceleration
+  // (advanced usage)
+  virtual double * Getq();
+  virtual double * Getqvel();
+  virtual double * Getqaccel();
+   
+  virtual void ResetToRest();
+
+  // projects the current state so that ||q||^2 <= R2
+  virtual void ConstrainToSphere(double R2);
+
+protected: 
+  double * tangentStiffnessMatrixOriginal;
+  // source and dest are r x 1 vectors;
+  // Q is a r x r rotation matrix;
+  // dest = Rotation * source if useRotationTranspose = false
+  // dest = Rotation^T * source otherwise
+  void RotateVector(double * Q, double * source, double * dest, bool useRotationTranspose = false);
+  double * Q;
+  double * z;
+  double * zvel;
+  double * zaccel;
+
+  void ComputeQTM(double * massMatrix);
+  double * QTM;             // a r x r matrix, QTM = (Q^T) * massMatrix (not in pratical use)
+  double * rotatedForces;   // a r x 1 vector, rotatedForces = Q^T * externalForces  
+};
+
+#endif
+
diff --git a/src/libintegratorSparse/centralDifferencesSparse.cpp b/libraries/integratorSparse/centralDifferencesSparse.cpp
similarity index 73%
rename from src/libintegratorSparse/centralDifferencesSparse.cpp
rename to libraries/integratorSparse/centralDifferencesSparse.cpp
index fb60fff01f3383ebde0d138a6339c0c6d1ffdec7..a72c71f05c70337e30c9bc62093b79cb2f530e53 100644
--- a/src/libintegratorSparse/centralDifferencesSparse.cpp
+++ b/libraries/integratorSparse/centralDifferencesSparse.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,18 +30,15 @@
  *                                                                       *
  *************************************************************************/
 
-#include <iostream>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <float.h>
 #include "performanceCounter.h"
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "centralDifferencesSparse.h"
 
-namespace vega
-{
 CentralDifferencesSparse::CentralDifferencesSparse(int numDOFs, double timestep, SparseMatrix * massMatrix_, ForceModel * forceModel_, int numConstrainedDOFs, int * constrainedDOFs, double dampingMassCoef, double dampingStiffnessCoef, int tangentialDampingMode_, int numSolverThreads_): IntegratorBaseSparse(numDOFs, timestep, massMatrix_, forceModel_, numConstrainedDOFs, constrainedDOFs, dampingMassCoef, dampingStiffnessCoef), tangentialDampingMode(tangentialDampingMode_), numSolverThreads(numSolverThreads_), timestepIndex(0)
 {
   rhs = (double*) malloc (sizeof(double) * r);
@@ -58,11 +59,38 @@ CentralDifferencesSparse::CentralDifferencesSparse(int numDOFs, double timestep,
   systemMatrix->RemoveRowsColumns(numConstrainedDOFs, constrainedDOFs);
   systemMatrix->BuildSuperMatrixIndices(numConstrainedDOFs, constrainedDOFs, tangentStiffnessMatrix);
 
+  #ifdef PARDISO
+    printf("Creating Pardiso solver for central differences.\n");
+    pardisoSolver = new PardisoSolver(systemMatrix, numSolverThreads, PardisoSolver::REAL_SYM_INDEFINITE);
+  #endif
+
+  #ifdef SPOOLES
+    spoolesSolver = NULL;
+  #endif
+
+  #ifdef PCG
+    printf("Creating Jacobi solver for central differences.\n");
+    jacobiPreconditionedCGSolver = new CGSolver(systemMatrix);
+  #endif
+
   DecomposeSystemMatrix();
 }
 
 CentralDifferencesSparse::~CentralDifferencesSparse()
 {
+  #ifdef PARDISO
+    delete(pardisoSolver);
+  #endif
+
+  #ifdef SPOOLES
+    if (spoolesSolver != NULL)
+      delete(spoolesSolver);
+  #endif
+
+  #ifdef PCG
+    delete(jacobiPreconditionedCGSolver);
+  #endif
+
   delete(systemMatrix);
   delete(tangentStiffnessMatrix);
   delete(rayleighDampingMatrix);
@@ -84,9 +112,28 @@ void CentralDifferencesSparse::DecomposeSystemMatrix()
   // system matrix = mass matrix + 0.5 * timestep * damping matrix (and remove constrained rows and columns)
   rayleighDampingMatrix->ScalarMultiply(0.5 * timestep, tangentStiffnessMatrix);
   tangentStiffnessMatrix->AddSubMatrix(1.0, *massMatrix);
-  systemMatrix->AssignSuperMatrix(tangentStiffnessMatrix);
+  systemMatrix->AssignSuperMatrix(*tangentStiffnessMatrix);
 
   //systemMatrix->SaveToMatlabFormat("system.mat");
+  
+  #ifdef PARDISO
+    int info = pardisoSolver->FactorMatrix(systemMatrix);
+    if (info != 0)
+    {
+      printf("Error: PARDISO solver returned non-zero exit code %d.\n", info);
+      exit(1);
+    }
+  #endif
+
+  #ifdef SPOOLES
+    printf("Creating SPOOLES solver for central differences.\n");
+    if (spoolesSolver != NULL)
+      delete(spoolesSolver);
+    if (numSolverThreads > 1)
+      spoolesSolver = new SPOOLESSolverMT(systemMatrix, numSolverThreads);
+    else
+      spoolesSolver = new SPOOLESSolver(systemMatrix);
+  #endif
 }
 
 int CentralDifferencesSparse::DoTimestep()
@@ -101,12 +148,12 @@ int CentralDifferencesSparse::DoTimestep()
   if (tangentialDampingMode > 0)
     if (timestepIndex % tangentialDampingMode == 0)
       DecomposeSystemMatrix(); // this routines also updates the damping and system matrices
-
+  
   // update equation is (see WRIGGERS P.: Computational Contact Mechanics. John Wiley & Sons, Ltd., 2002., page 275) :
   //
   // (M + dt / 2 * C) * q(t+1) = (dt)^2 * (fext(t) - fint(q(t))) + dt / 2 * C * q(t-1) + M * (2q(t) - q(t-1))
   //
-  // (M + dt / 2 * C) * (q(t+1) - q(t)) = (dt)^2 * (fext(t) - fint(q(t))) + dt / 2 * C * (q(t-1) - q(t)) + M * (q(t) - q(t-1))
+  // (M + dt / 2 * C) * (q(t+1) - q(t)) = (dt)^2 * (fext(t) - fint(q(t))) + dt / 2 * C * (q(t-1) - q(t)) + M * (q(t) - q(t-1)) 
 
   // fext are the external forces
   // fint is the vector of internal forces
@@ -116,7 +163,7 @@ int CentralDifferencesSparse::DoTimestep()
   for (int i=0; i<r; i++)
     buffer[i] = q[i] - q_1[i];
   massMatrix->MultiplyVector(buffer, rhs);
-
+  
   // rhs += dt / 2 * dampingMatrix * (q_{n-1} - q_n)
   for (int i=0; i<r; i++)
     qdelta[i] = q_1[i] - q[i];
@@ -124,34 +171,44 @@ int CentralDifferencesSparse::DoTimestep()
   for (int i=0; i<r; i++)
     rhs[i] += 0.5 * timestep * buffer[i];
 
-  // rhs += dt * dt * (fext - fint(q(t)))
+  // rhs += dt * dt * (fext - fint(q(t))) 
   double timestep2 = timestep * timestep;
   for (int i=0; i<r; i++)
     rhs[i] += timestep2 * (externalForces[i] - internalForces[i]);
 
   // now rhs contains the correct value
 
-  RemoveRows(r, rhsConstrained, rhs, numConstrainedDOFs, constrainedDOFs);
+  ConstrainedDOFs::RemoveDOFs(r, rhsConstrained, rhs, numConstrainedDOFs, constrainedDOFs);
 
   PerformanceCounter counterSystemSolveTime;
 
   memset(buffer, 0, sizeof(double) * r);
 
-  if(!this->linearSolver)
-  {
-      std::cerr << "Error: The linear solver is not set. Attach a solver before calling this method." << std::endl;
-      return 1;
-  }
-
-  int info = this->linearSolver->SolveLinearSystem(buffer, rhsConstrained);
-  InsertRows(r, buffer, qdelta, numConstrainedDOFs, constrainedDOFs);
+  #ifdef SPOOLES
+    int info = spoolesSolver->SolveLinearSystem(buffer, rhsConstrained);
+    char solverString[16] = "SPOOLES";
+  #endif
+
+  #ifdef PARDISO
+    int info = pardisoSolver->SolveLinearSystem(buffer, rhsConstrained);
+    char solverString[16] = "PARDISO";
+  #endif
+  
+  #ifdef PCG
+    int info = jacobiPreconditionedCGSolver->SolveLinearSystemWithJacobiPreconditioner(buffer, rhsConstrained, 1e-6, 10000);
+    if (info > 0)
+      info = 0;
+    char solverString[16] = "PCG";
+  #endif
+
+  ConstrainedDOFs::InsertDOFs(r, buffer, qdelta, numConstrainedDOFs, constrainedDOFs);
 
   counterSystemSolveTime.StopCounter();
   systemSolveTime = counterSystemSolveTime.GetElapsedTime();
 
   if (info != 0)
   {
-    printf("Error: %s sparse solver returned non-zero exit status %d.\n", this->linearSolver->getSolverType().c_str(), (int)info);
+    printf("Error: %s sparse solver returned non-zero exit status %d.\n", solverString, (int)info);
     return 1;
   }
 
@@ -201,9 +258,4 @@ void CentralDifferencesSparse::ResetToRest()
   IntegratorBaseSparse::ResetToRest();
   timestepIndex = 0;
 }
-SparseMatrix* CentralDifferencesSparse::GetSystemMatrix() const
-{
-  return this->systemMatrix;
-}
 
-}
diff --git a/libraries/integratorSparse/centralDifferencesSparse.h b/libraries/integratorSparse/centralDifferencesSparse.h
new file mode 100644
index 0000000000000000000000000000000000000000..35cd1ef35dc381a64978251b25d25cb6731b9fb3
--- /dev/null
+++ b/libraries/integratorSparse/centralDifferencesSparse.h
@@ -0,0 +1,135 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+
+A class to timestep dynamics (e.g. nonlinear deformable FEM) 
+using the EXPLICIT central differences integrator. Normally, the implicit newmark and
+implicit backward Euler classes are recommended for nonlinear deformable FEM
+due to stability under large deformations.  See also integratorBase.h.
+
+This implementation follows
+WRIGGERS P.: Computational Contact Mechanics. John
+Wiley & Sons, Ltd., 2002., page 275
+
+This class supports two damping modes: 
+1. Standard Rayleigh model
+  D = dampingMassCoef * M + dampingStiffnessCoef * K
+     where K is the stiffness matrix AT THE ORIGIN
+2. Tangential Rayleigh model (default)
+  D = dampingMassCoef * M + dampingStiffnessCoef * K(q)
+     where K is the tangential stiffness matrix in the CURRENT deformed configuration q
+
+Mode 1. is computationally faster as it does not require system updates (i.e., 
+matrix inversion). Mode 1. is useful, for example, if you want to timestep 
+linear modal analysis simulations (stiffness matrix is constant in that case).
+
+Mode 2. gives a better damping model for large deformations, but because the
+system matrix changes, requires factoring a linear system anew at each timestep.
+
+In order to use this class, you need to set the timestep very small, or 
+else the explicit integrator will go unstable. Roughly speaking, the timestep 
+must resolve the highest frequency present in your simulation. 
+
+See also integratorBase.h .
+
+*/
+
+#ifndef _CENTRALDIFFERENCESSPARSE_H_
+#define _CENTRALDIFFERENCESSPARSE_H_
+
+#include "integratorBaseSparse.h"
+#include "integratorSolverSelection.h"
+
+#ifdef PARDISO
+  #include "sparseSolvers.h"
+#endif
+#ifdef SPOOLES
+  #include "sparseSolvers.h"
+#endif
+#ifdef PCG
+  #include "CGSolver.h"
+#endif
+
+class CentralDifferencesSparse : public IntegratorBaseSparse
+{
+public:
+  CentralDifferencesSparse(int numDOFs, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, int tangentialDampingMode=1, int numSolverThreads=0);
+
+  virtual ~CentralDifferencesSparse();
+
+  inline virtual void SetTimestep(double timestep) { this->timestep = timestep; DecomposeSystemMatrix(); }
+
+  // performs one timestep of simulation
+  virtual int DoTimestep(); 
+
+  // sets q, and (optionally) qvel 
+  // returns 0 
+  virtual int SetState(double * q, double * qvel=NULL);
+
+  // tangentialDampingMode: 
+  // 0 = no updates of the damping matrix under deformations (not recommended for large deformations)
+  // 1 = update at every timestep (default)
+  // k>1 = update every kth timestep
+  inline void SetTangentialDampingMode(int tangentialDampingMode) { this->tangentialDampingMode = tangentialDampingMode; }
+
+  virtual void SetInternalForceScalingFactor(double internalForceScalingFactor);
+
+  virtual void ResetToRest();
+
+protected:
+  double * rhs;
+  double * rhsConstrained;
+  SparseMatrix * rayleighDampingMatrix;
+  SparseMatrix * tangentStiffnessMatrix;
+  SparseMatrix * systemMatrix;
+  int tangentialDampingMode;
+  int numSolverThreads;
+  int timestepIndex;
+
+  void DecomposeSystemMatrix();
+
+  #ifdef PARDISO
+    PardisoSolver * pardisoSolver;
+  #endif
+
+  #ifdef SPOOLES
+    LinearSolver * spoolesSolver;
+  #endif
+
+  #ifdef PCG
+    CGSolver * jacobiPreconditionedCGSolver;
+  #endif
+};
+
+#endif
+
diff --git a/src/libintegratorSparse/eulerSparse.cpp b/libraries/integratorSparse/eulerSparse.cpp
similarity index 59%
rename from src/libintegratorSparse/eulerSparse.cpp
rename to libraries/integratorSparse/eulerSparse.cpp
index 1c83d0013ef65bd45965eecabcccd401e94893d6..20743febf5193d2606008b1a8cb32246d7a0e7d1 100644
--- a/src/libintegratorSparse/eulerSparse.cpp
+++ b/libraries/integratorSparse/eulerSparse.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,25 +30,61 @@
  *                                                                       *
  *************************************************************************/
 
-#include <iostream>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "matrixIO.h"
 #include "performanceCounter.h"
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "eulerSparse.h"
 
-namespace vega
+EulerSparse::EulerSparse(int r, double timestep, SparseMatrix * massMatrix_, ForceModel * forceModel_, int symplectic_, int numConstrainedDOFs_, int * constrainedDOFs_, double dampingMassCoef, int numSolverThreads): IntegratorBaseSparse(r, timestep, massMatrix_, forceModel_, numConstrainedDOFs_, constrainedDOFs_, dampingMassCoef, 0.0), symplectic(symplectic_)
 {
-EulerSparse::EulerSparse(int r, double timestep, SparseMatrix * massMatrix_, ForceModel * forceModel_, int symplectic_, int numConstrainedDOFs_, int * constrainedDOFs_, double dampingMassCoef): IntegratorBaseSparse(r, timestep, massMatrix_, forceModel_, numConstrainedDOFs_, constrainedDOFs_, dampingMassCoef, 0.0), symplectic(symplectic_)
-{
-
+  systemMatrix = new SparseMatrix(*massMatrix);
+  systemMatrix->RemoveRowsColumns(numConstrainedDOFs, constrainedDOFs);
+  #ifdef PARDISO
+    printf("Creating Pardiso solver for M.\n");
+    pardisoSolver = new PardisoSolver(systemMatrix, numSolverThreads, PardisoSolver::REAL_SPD);
+    int info = pardisoSolver->FactorMatrix(systemMatrix);
+    if (info != 0)
+    {
+      printf("Error: PARDISO solver returned non-zero exit code %d.\n", info);
+      exit(1);
+    }
+    printf("Solver created.\n");
+  #endif
+
+  #ifdef SPOOLES
+    printf("Creating SPOOLES solver for M.\n");
+    spoolesSolver = new SPOOLESSolver(systemMatrix);
+    printf("Solver created.\n");
+  #endif
+
+  #ifdef PCG
+    printf("Creating Jacobi solver for M.\n");
+    jacobiPreconditionedCGSolver = new CGSolver(systemMatrix);
+    printf("Solver created.\n");
+  #endif
+
+  bufferConstrained = (double*)malloc(sizeof(double) * (r - numConstrainedDOFs));
 }
 
 EulerSparse::~EulerSparse()
 {
+  #ifdef PARDISO
+    delete(pardisoSolver);
+  #endif
+
+  #ifdef SPOOLES
+    delete(spoolesSolver);
+  #endif
+
+  #ifdef PCG
+    delete(jacobiPreconditionedCGSolver);
+  #endif
 
+  delete(systemMatrix);
+  free(bufferConstrained);
 }
 
 // sets the state based on given q, qvel
@@ -67,7 +107,7 @@ int EulerSparse::DoTimestep()
   // store current state
   for(int i=0; i<r; i++)
   {
-    q_1[i] = q[i];
+    q_1[i] = q[i]; 
     qvel_1[i] = qvel[i];
     qaccel_1[i] = qaccel[i];
   }
@@ -98,29 +138,41 @@ int EulerSparse::DoTimestep()
     qresidual[i] = externalForces[i] - internalForces[i] - dampingForces[i];
   }
 
+  ConstrainedDOFs::RemoveDOFs(r, bufferConstrained, qresidual, numConstrainedDOFs, constrainedDOFs);
+
   PerformanceCounter counterSystemSolveTime;
 
   // solve: M * qdelta = qresidual
 
-  memset(qdelta, 0.0, sizeof(double)*r);
+  memset(buffer, 0, sizeof(double)*r);
 
-  if(!this->linearSolver)
-  {
-      std::cerr << "Error: The linear solver is not set. Attach a solver before calling this method." << std::endl;
-      return 1;
-  }
+  #ifdef PARDISO
+    int info = pardisoSolver->SolveLinearSystem(buffer, bufferConstrained);
+    char solverString[16] = "PARDISO";
+  #endif
 
-  int info = this->linearSolver->SolveLinearSystem(buffer, qresidual);
+  #ifdef SPOOLES
+    int info = spoolesSolver->SolveLinearSystem(buffer, bufferConstrained);
+    char solverString[16] = "SPOOLES";
+  #endif
+
+  #ifdef PCG
+    int info = jacobiPreconditionedCGSolver->SolveLinearSystemWithJacobiPreconditioner(buffer, bufferConstrained, 1e-6, 10000);
+    if (info > 0)
+      info = 0;
+    char solverString[16] = "PCG";
+  #endif
 
   if (info != 0)
   {
-    printf("Error: %s sparse solver returned non-zero exit status %d.\n", this->linearSolver->getSolverType().c_str(), (int)info);
+    printf("Error: %s sparse solver returned non-zero exit status %d.\n", solverString, (int)info);
     return 1;
   }
 
   counterSystemSolveTime.StopCounter();
   systemSolveTime = counterSystemSolveTime.GetElapsedTime();
 
+  ConstrainedDOFs::InsertDOFs(r, buffer, qdelta, numConstrainedDOFs, constrainedDOFs);
   // update state
   if (symplectic)
   {
@@ -128,7 +180,7 @@ int EulerSparse::DoTimestep()
     {
       qvel[i] += timestep * qdelta[i];
       q[i] += timestep * qvel[i];
-    }
+    }	
   }
   else
   {
@@ -149,4 +201,3 @@ int EulerSparse::DoTimestep()
   return 0;
 }
 
-}
diff --git a/libraries/integratorSparse/eulerSparse.h b/libraries/integratorSparse/eulerSparse.h
new file mode 100644
index 0000000000000000000000000000000000000000..3313184fa2c0c1c9241bf31e24fbbbdbe2886677
--- /dev/null
+++ b/libraries/integratorSparse/eulerSparse.h
@@ -0,0 +1,98 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A class to timestep large sparse dynamics using standard Euler, or symplectic Euler.
+
+  standard Euler:
+  x_{n+1} = x_n + h * v_n
+  v_{n+1} = v_n + h * (F_n / m)
+
+  symplectic Euler
+  v_{n+1} = v_n + h * (F_n / m)
+  x_{n+1} = x_n + h * v_{n+1}
+*/
+
+#ifndef _EULERSPARSE_H_
+#define _EULERSPARSE_H_
+
+#include "integratorSolverSelection.h"
+#include "integratorBaseSparse.h"
+
+#ifdef PARDISO
+  #include "sparseSolvers.h"
+#endif
+#ifdef SPOOLES
+  #include "sparseSolvers.h"
+#endif
+#ifdef PCG
+  #include "CGSolver.h"
+#endif
+
+
+class EulerSparse : public IntegratorBaseSparse
+{
+public:
+
+  // constrainedDOFs is an integer array of degrees of freedom that are to be fixed to zero (e.g., to permanently fix a vertex in a deformable simulation)
+  // constrainedDOFs are 0-indexed (separate DOFs for x,y,z), and must be pre-sorted (ascending)
+  // dampingMatrix is optional and provides damping (in addition to mass damping)
+  EulerSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int symplectic=0, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, int numSolverThreads=1);
+
+  virtual ~EulerSparse();
+
+  // sets q, and (optionally) qvel 
+  // returns 0 
+  virtual int SetState(double * q, double * qvel=NULL);
+
+  virtual int DoTimestep(); 
+
+protected:
+  int symplectic;
+  SparseMatrix * systemMatrix;
+  double * bufferConstrained;
+  
+  #ifdef PARDISO
+    PardisoSolver * pardisoSolver;
+  #endif
+
+  #ifdef SPOOLES
+    SPOOLESSolver * spoolesSolver;
+  #endif
+
+  #ifdef PCG
+    CGSolver * jacobiPreconditionedCGSolver;
+  #endif
+};
+
+#endif
+
diff --git a/src/libintegratorSparse/implicitBackwardEulerSparse.cpp b/libraries/integratorSparse/implicitBackwardEulerSparse.cpp
similarity index 71%
rename from src/libintegratorSparse/implicitBackwardEulerSparse.cpp
rename to libraries/integratorSparse/implicitBackwardEulerSparse.cpp
index a23985f3cd217f81e1097524f19318ca1c50ffce..dcb5acb58c8a534452fbc07d425e5decb6756cb2 100644
--- a/src/libintegratorSparse/implicitBackwardEulerSparse.cpp
+++ b/libraries/integratorSparse/implicitBackwardEulerSparse.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,24 +30,15 @@
  *                                                                       *
  *************************************************************************/
 
-/*
-  Jernej Barbic
-  A class to timestep large sparse dynamics using implicit backward Euler.
-*/
-
-#include <iostream>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "matrixIO.h"
 #include "performanceCounter.h"
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "implicitBackwardEulerSparse.h"
-#include "linearSolver.h"
 
-namespace vega
-{
-ImplicitBackwardEulerSparse::ImplicitBackwardEulerSparse(int r, double timestep, SparseMatrix * massMatrix_, ForceModel * forceModel_, int positiveDefiniteSolver_, int numConstrainedDOFs_, int * constrainedDOFs_, double dampingMassCoef, double dampingStiffnessCoef, int maxIterations, double epsilon, int numSolverThreads_): ImplicitNewmarkSparse(r, timestep, massMatrix_, forceModel_, positiveDefiniteSolver_, numConstrainedDOFs_, constrainedDOFs_, dampingMassCoef, dampingStiffnessCoef, maxIterations, epsilon, 0.25, 0.5, numSolverThreads_)
+ImplicitBackwardEulerSparse::ImplicitBackwardEulerSparse(int r, double timestep, SparseMatrix * massMatrix_, ForceModel * forceModel_, int numConstrainedDOFs_, int * constrainedDOFs_, double dampingMassCoef, double dampingStiffnessCoef, int maxIterations, double epsilon, int numSolverThreads_): ImplicitNewmarkSparse(r, timestep, massMatrix_, forceModel_, numConstrainedDOFs_, constrainedDOFs_, dampingMassCoef, dampingStiffnessCoef, maxIterations, epsilon, 0.25, 0.5, numSolverThreads_)
 {
 }
 
@@ -74,7 +69,7 @@ int ImplicitBackwardEulerSparse::DoTimestep()
   for(int i=0; i<r; i++)
   {
     qaccel_1[i] = qaccel[i] = 0; // acceleration is actually not used in this integrator
-    q_1[i] = q[i];
+    q_1[i] = q[i]; 
     qvel_1[i] = qvel[i];
   }
 
@@ -122,10 +117,14 @@ int ImplicitBackwardEulerSparse::DoTimestep()
     }
     else
     {
+      // compute D_Rayleigh = dampingStiffnessCoef * tangentStiffnessMatrix + dampingMassCoef * massMatrix
       tangentStiffnessMatrix->ScalarMultiply(dampingStiffnessCoef, rayleighDampingMatrix);
       rayleighDampingMatrix->AddSubMatrix(dampingMassCoef, *massMatrix);
 
-      // build effective stiffness:
+      if (tangentStiffnessMatrixOffset != NULL)
+        tangentStiffnessMatrix->AddSubMatrix(1.0, *tangentStiffnessMatrixOffset, 2);
+
+      // build effective stiffness: 
       // Keff = M + h D + h^2 * K
       // compute force residual, store it into aux variable qresidual
       // qresidual = h * (-D qdot - fint + fext - h * K * qdot)) // this is semi-implicit Euler
@@ -140,13 +139,12 @@ int ImplicitBackwardEulerSparse::DoTimestep()
       }
 
       //add mass matrix and damping matrix to tangentStiffnessMatrix
-      *tangentStiffnessMatrix *= timestep;
-
-      *tangentStiffnessMatrix += *rayleighDampingMatrix;
-      tangentStiffnessMatrix->AddSubMatrix(1.0, *dampingMatrix, 1); // at this point, tangentStiffnessMatrix = h * K + D
+      *tangentStiffnessMatrix *= timestep; // h * K
+      *tangentStiffnessMatrix += *rayleighDampingMatrix; // h * K + D_Rayleigh
+      tangentStiffnessMatrix->AddSubMatrix(1.0, *dampingMatrix, 1); // at this point, tangentStiffnessMatrix = h * K + (D_Rayleigh + D_exteral)
       tangentStiffnessMatrix->MultiplyVectorAdd(qvel, qresidual);
-      *tangentStiffnessMatrix *= timestep;
-      tangentStiffnessMatrix->AddSubMatrix(1.0, *massMatrix);
+      *tangentStiffnessMatrix *= timestep; // h^2 * K + h * (D_Rayleigh + D_externnal)
+      tangentStiffnessMatrix->AddSubMatrix(1.0, *massMatrix); // h^2 * K + h * (D_Rayleigh + D_external) + M
 
       // add externalForces, internalForces
       for(int i=0; i<r; i++)
@@ -187,7 +185,7 @@ int ImplicitBackwardEulerSparse::DoTimestep()
     //tangentStiffnessMatrix->Save("Keff");
 
     // remove rows corresponding to fixed vertices from qdelta, and store the result in bufferConstrained
-    RemoveRows(r, bufferConstrained, qdelta, numConstrainedDOFs, constrainedDOFs);
+    ConstrainedDOFs::RemoveDOFs(r, bufferConstrained, qdelta, numConstrainedDOFs, constrainedDOFs);
 
     double error = 0.0;
     for(int i=0; i<r - numConstrainedDOFs; i++)
@@ -196,7 +194,7 @@ int ImplicitBackwardEulerSparse::DoTimestep()
     //printf("numIter: %d error2: %G\n", numIter, error);
 
     // on the first iteration, compute initial error
-    if (numIter == 0)
+    if (numIter == 0) 
     {
       error0 = error;
       errorQuotient = 1.0;
@@ -204,37 +202,60 @@ int ImplicitBackwardEulerSparse::DoTimestep()
     else
     {
       // error divided by the initial error, before performing this iteration
-      errorQuotient = error / error0;
+      errorQuotient = error / error0; 
     }
 
     if (errorQuotient < epsilon * epsilon)
       break;
 
-    systemMatrix->AssignSuperMatrix(tangentStiffnessMatrix);
+    systemMatrix->AssignSuperMatrix(*tangentStiffnessMatrix);
 
     // solve: systemMatrix * buffer = bufferConstrained
 
     PerformanceCounter counterSystemSolveTime;
     memset(buffer, 0, sizeof(double) * r);
 
-    if(!this->linearSolver)
-    {
-        std::cerr << "Error: The linear solver is not set. Attach a solver before calling this method." << std::endl;
-        return 1;
-    }
+    #ifdef SPOOLES
+      int info;
+      if (numSolverThreads > 1)
+      {
+        SPOOLESSolverMT * solver = new SPOOLESSolverMT(systemMatrix, numSolverThreads);
+        info = solver->SolveLinearSystem(buffer, bufferConstrained);
+        delete(solver);
+      }
+      else
+      {
+        SPOOLESSolver * solver = new SPOOLESSolver(systemMatrix);
+        info = solver->SolveLinearSystem(buffer, bufferConstrained);
+        delete(solver);
+      }
+      char solverString[16] = "SPOOLES";
+    #endif
+
+    #ifdef PARDISO
+      int info = pardisoSolver->FactorMatrix(systemMatrix);
+      if (info == 0)
+        info = pardisoSolver->SolveLinearSystem(buffer, bufferConstrained);
+      char solverString[16] = "PARDISO";
+    #endif
+
+    #ifdef PCG
+      int info = jacobiPreconditionedCGSolver->SolveLinearSystemWithJacobiPreconditioner(buffer, bufferConstrained, 1e-6, 10000);
+      if (info > 0)
+        info = 0;
+      char solverString[16] = "PCG";
+    #endif
 
-    int info = this->linearSolver->SolveLinearSystem(buffer, bufferConstrained);
     if (info != 0)
     {
-      printf("Error: %s sparse solver returned non-zero exit status %d.\n", this->linearSolver->getSolverType().c_str(), (int)info);
-      exit(-1);
+      printf("Error: %s sparse solver returned non-zero exit status %d.\n", solverString, (int)info);
       return 1;
     }
 
     counterSystemSolveTime.StopCounter();
     systemSolveTime = counterSystemSolveTime.GetElapsedTime();
 
-    InsertRows(r, buffer, qdelta, numConstrainedDOFs, constrainedDOFs);
+    ConstrainedDOFs::InsertDOFs(r, buffer, qdelta, numConstrainedDOFs, constrainedDOFs);
 
 /*
     printf("qdelta:\n");
@@ -289,4 +310,3 @@ int ImplicitBackwardEulerSparse::DoTimestep()
   return 0;
 }
 
-}
diff --git a/libraries/integratorSparse/implicitBackwardEulerSparse.h b/libraries/integratorSparse/implicitBackwardEulerSparse.h
new file mode 100644
index 0000000000000000000000000000000000000000..a3abe30d45a77e69df6576137376e76c36f4119c
--- /dev/null
+++ b/libraries/integratorSparse/implicitBackwardEulerSparse.h
@@ -0,0 +1,62 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A class to timestep large sparse dynamics using implicit backward Euler.
+*/
+
+#ifndef _IMPLICITBACKWARDEULERSPARSE_H_
+#define _IMPLICITBACKWARDEULERSPARSE_H_
+
+#include "implicitNewmarkSparse.h"
+
+class ImplicitBackwardEulerSparse : public ImplicitNewmarkSparse
+{
+public:
+
+  // constrainedDOFs is an integer array of degrees of freedom that are to be fixed to zero (e.g., to permanently fix a vertex in a deformable simulation)
+  // constrainedDOFs are 0-indexed (separate DOFs for x,y,z), and must be pre-sorted (ascending)
+  // numThreads applies only to the PARDISO and SPOOLES solvers; if numThreads > 0, the sparse linear solves are multi-threaded; default: 0 (use single-threading)
+  ImplicitBackwardEulerSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, int maxIterations = 1, double epsilon = 1E-6, int numSolverThreads=0); 
+
+  virtual ~ImplicitBackwardEulerSparse();
+
+  // sets q, and (optionally) qvel 
+  // returns 0 
+  virtual int SetState(double * q, double * qvel=NULL);
+  virtual int DoTimestep(); 
+
+protected:
+};
+
+#endif
+
diff --git a/src/libintegratorSparse/implicitNewmarkSparse.cpp b/libraries/integratorSparse/implicitNewmarkSparse.cpp
similarity index 70%
rename from src/libintegratorSparse/implicitNewmarkSparse.cpp
rename to libraries/integratorSparse/implicitNewmarkSparse.cpp
index 93ba3df932245099361591977dd27467caa8b05a..a29b2babfa90d2646a829eb6ce2bc0555b0c1359 100644
--- a/src/libintegratorSparse/implicitNewmarkSparse.cpp
+++ b/libraries/integratorSparse/implicitNewmarkSparse.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,22 +30,18 @@
  *                                                                       *
  *************************************************************************/
 
-#include <iostream>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "matrixIO.h"
 #include "performanceCounter.h"
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "implicitNewmarkSparse.h"
-#include "linearSolver.h"
 
-namespace vega
-{
-ImplicitNewmarkSparse::ImplicitNewmarkSparse(int r, double timestep, SparseMatrix * massMatrix_, ForceModel * forceModel_, int positiveDefiniteSolver_, int numConstrainedDOFs_, int * constrainedDOFs_, double dampingMassCoef, double dampingStiffnessCoef, int maxIterations, double epsilon, double NewmarkBeta, double NewmarkGamma, int numSolverThreads_): IntegratorBaseSparse(r, timestep, massMatrix_, forceModel_, numConstrainedDOFs_, constrainedDOFs_, dampingMassCoef, dampingStiffnessCoef), positiveDefiniteSolver(positiveDefiniteSolver_), numSolverThreads(numSolverThreads_)
+ImplicitNewmarkSparse::ImplicitNewmarkSparse(int r, double timestep, SparseMatrix * massMatrix_, ForceModel * forceModel_, int numConstrainedDOFs_, int * constrainedDOFs_, double dampingMassCoef, double dampingStiffnessCoef, int maxIterations, double epsilon, double NewmarkBeta, double NewmarkGamma, int numSolverThreads_): IntegratorBaseSparse(r, timestep, massMatrix_, forceModel_, numConstrainedDOFs_, constrainedDOFs_, dampingMassCoef, dampingStiffnessCoef), numSolverThreads(numSolverThreads_)
 {
   this->maxIterations = maxIterations; // maxIterations = 1 for semi-implicit
-  this->epsilon = epsilon;
+  this->epsilon = epsilon; 
   this->NewmarkBeta = NewmarkBeta;
   this->NewmarkGamma = NewmarkGamma;
 
@@ -58,6 +58,7 @@ ImplicitNewmarkSparse::ImplicitNewmarkSparse(int r, double timestep, SparseMatri
   }
 
   rayleighDampingMatrix = new SparseMatrix(*tangentStiffnessMatrix);
+
   rayleighDampingMatrix->BuildSubMatrixIndices(*massMatrix);
   tangentStiffnessMatrix->BuildSubMatrixIndices(*massMatrix);
   tangentStiffnessMatrix->BuildSubMatrixIndices(*dampingMatrix, 1);
@@ -74,13 +75,25 @@ ImplicitNewmarkSparse::ImplicitNewmarkSparse(int r, double timestep, SparseMatri
   systemMatrix->RemoveRowsColumns(numConstrainedDOFs, constrainedDOFs);
   systemMatrix->BuildSuperMatrixIndices(numConstrainedDOFs, constrainedDOFs, tangentStiffnessMatrix);
 
+  #ifdef PARDISO
+    printf("Creating Pardiso solver. Num threads: %d\n", numSolverThreads);
+    pardisoSolver = new PardisoSolver(systemMatrix, numSolverThreads, PardisoSolver::REAL_SYM_INDEFINITE);
+  #endif
+
+  #ifdef PCG
+    jacobiPreconditionedCGSolver = new CGSolver(systemMatrix);
+  #endif
 }
 
 ImplicitNewmarkSparse::~ImplicitNewmarkSparse()
 {
   delete(tangentStiffnessMatrix);
   delete(rayleighDampingMatrix);
+  delete(systemMatrix);
   free(bufferConstrained);
+  #ifdef PARDISO
+    delete(pardisoSolver);
+  #endif
 }
 
 void ImplicitNewmarkSparse::SetDampingMatrix(SparseMatrix * dampingMatrix)
@@ -113,7 +126,7 @@ int ImplicitNewmarkSparse::SetState(double * q_, double * qvel_)
   for(int i=0; i<numConstrainedDOFs; i++)
     q[constrainedDOFs[i]] = qvel[constrainedDOFs[i]] = 0.0;
 
-  // M * qaccel + C * qvel + R(q) = P_0
+  // M * qaccel + C * qvel + R(q) = P_0 
   // R(q) = P_0 = 0
   // i.e. M * qaccel = - C * qvel - R(q)
 
@@ -130,33 +143,49 @@ int ImplicitNewmarkSparse::SetState(double * q_, double * qvel_)
     buffer[i] = -buffer[i] - internalForces[i];
 
   // solve M * qaccel = buffer
-  RemoveRows(r, bufferConstrained, buffer, numConstrainedDOFs, constrainedDOFs);
+  ConstrainedDOFs::RemoveDOFs(r, bufferConstrained, buffer, numConstrainedDOFs, constrainedDOFs);
 
   // use tangentStiffnessMatrix as the buffer place
   tangentStiffnessMatrix->ResetToZero();
   tangentStiffnessMatrix->AddSubMatrix(1.0, *massMatrix);
   tangentStiffnessMatrix->AddSubMatrix(1.0, *dampingMatrix, 1);
-  systemMatrix->AssignSuperMatrix(tangentStiffnessMatrix); // must go via a matrix with tangentStiffnessMatrix's topology, because the AssignSuperMatrix indices were computed with respect to such topology
+  systemMatrix->AssignSuperMatrix(*tangentStiffnessMatrix); // must go via a matrix with tangentStiffnessMatrix's topology, because the AssignSuperMatrix indices were computed with respect to such topology
 
   memset(buffer, 0, sizeof(double) * r);
 
-  if(!this->linearSolver)
-  {
-    std::cerr << "Error: The linear solver is not set. Attach a solver before calling this method." << std::endl;
-    return 1;
-  }
-  int info = this->linearSolver->SolveLinearSystem(buffer, bufferConstrained);
+  #ifdef SPOOLES
+    SPOOLESSolver solver(systemMatrix);
+    int info = solver.SolveLinearSystem(buffer, bufferConstrained);
+    char solverString[16] = "SPOOLES";
+  #endif
+
+  //massMatrix->Save("M");
+  //systemMatrix->Save("A");
+
+  #ifdef PARDISO
+    pardisoSolver->FactorMatrix(systemMatrix);
+    int info = pardisoSolver->SolveLinearSystem(buffer, bufferConstrained);
+    char solverString[16] = "PARDISO";
+  #endif
+
+  #ifdef PCG
+    int info = jacobiPreconditionedCGSolver->SolveLinearSystemWithJacobiPreconditioner(buffer, bufferConstrained, 1e-6, 10000);
+    if (info > 0)
+      info = 0;
+    char solverString[16] = "PCG";
+  #endif
+
   if (info != 0)
   {
-    printf("Error: %s sparse solver returned non-zero exit status %d.\n", this->linearSolver->getSolverType().c_str(), (int)info);
+    printf("Error: %s sparse solver returned non-zero exit status %d.\n", solverString, (int)info);
     return 1;
   }
-
-  InsertRows(r, buffer, qaccel, numConstrainedDOFs, constrainedDOFs);
+  
+  ConstrainedDOFs::InsertDOFs(r, buffer, qaccel, numConstrainedDOFs, constrainedDOFs);
 
   return 0;
 }
-
+ 
 int ImplicitNewmarkSparse::DoTimestep()
 {
   int numIter = 0;
@@ -167,7 +196,7 @@ int ImplicitNewmarkSparse::DoTimestep()
   // store current amplitudes and set initial guesses for qaccel, qvel
   for(int i=0; i<r; i++)
   {
-    q_1[i] = q[i];
+    q_1[i] = q[i]; 
     qvel_1[i] = qvel[i];
     qaccel_1[i] = qaccel[i];
 
@@ -218,6 +247,12 @@ int ImplicitNewmarkSparse::DoTimestep()
       rayleighDampingMatrix->AddSubMatrix(dampingMassCoef, *massMatrix);
 
       rayleighDampingMatrix->ScalarMultiplyAdd(alpha4, tangentStiffnessMatrix);
+
+      if (tangentStiffnessMatrixOffset != NULL)
+        tangentStiffnessMatrix->AddSubMatrix(1.0, *tangentStiffnessMatrixOffset, 2);
+
+      // form the system matrix
+
       //*tangentStiffnessMatrix += alpha4 * *rayleighDampingMatrix;
       tangentStiffnessMatrix->AddSubMatrix(alpha4, *dampingMatrix, 1);
 
@@ -261,7 +296,7 @@ int ImplicitNewmarkSparse::DoTimestep()
       error += qresidual[i] * qresidual[i];
 
     // on the first iteration, compute initial error
-    if (numIter == 0)
+    if (numIter == 0) 
     {
       error0 = error;
       errorQuotient = 1.0;
@@ -269,7 +304,7 @@ int ImplicitNewmarkSparse::DoTimestep()
     else
     {
       // error divided by the initial error, before performing this iteration
-      errorQuotient = error / error0;
+      errorQuotient = error / error0; 
     }
 
     if (errorQuotient < epsilon * epsilon)
@@ -278,32 +313,44 @@ int ImplicitNewmarkSparse::DoTimestep()
     }
 
     //tangentStiffnessMatrix->Save("Keff");
-    RemoveRows(r, bufferConstrained, qdelta, numConstrainedDOFs, constrainedDOFs);
-    systemMatrix->AssignSuperMatrix(tangentStiffnessMatrix);
+    ConstrainedDOFs::RemoveDOFs(r, bufferConstrained, qdelta, numConstrainedDOFs, constrainedDOFs);
+    systemMatrix->AssignSuperMatrix(*tangentStiffnessMatrix);
 
     // solve: systemMatrix * buffer = bufferConstrained
 
     PerformanceCounter counterSystemSolveTime;
     memset(buffer, 0, sizeof(double) * r);
 
-    if(!this->linearSolver)
-    {
-        std::cerr << "Error: The linear solver is not set. Attach a solver before calling this method." << std::endl;
-        return 1;
-    }
-
-    int info = this->linearSolver->SolveLinearSystem(buffer, bufferConstrained);
+    #ifdef SPOOLES
+      SPOOLESSolver solver(systemMatrix);
+      int info = solver.SolveLinearSystem(buffer, bufferConstrained);
+      char solverString[16] = "SPOOLES";
+    #endif
+
+    #ifdef PARDISO
+      int info = pardisoSolver->FactorMatrix(systemMatrix);
+      if (info == 0)
+        info = pardisoSolver->SolveLinearSystem(buffer, bufferConstrained);
+      char solverString[16] = "PARDISO";
+    #endif
+
+    #ifdef PCG
+      int info = jacobiPreconditionedCGSolver->SolveLinearSystemWithJacobiPreconditioner(buffer, bufferConstrained, 1e-6, 10000);
+      if (info > 0)
+        info = 0;
+      char solverString[16] = "PCG";
+    #endif
 
     if (info != 0)
     {
-      printf("Error: %s sparse solver returned non-zero exit status %d.\n", this->linearSolver->getSolverType().c_str(), (int)info);
+      printf("Error: %s sparse solver returned non-zero exit status %d.\n", solverString, (int)info);
       return 1;
     }
 
     counterSystemSolveTime.StopCounter();
     systemSolveTime = counterSystemSolveTime.GetElapsedTime();
 
-    InsertRows(r, buffer, qdelta, numConstrainedDOFs, constrainedDOFs);
+    ConstrainedDOFs::InsertDOFs(r, buffer, qdelta, numConstrainedDOFs, constrainedDOFs);
 
 /*
     printf("qdelta:\n");
@@ -349,22 +396,25 @@ int ImplicitNewmarkSparse::DoTimestep()
 }
 
 void ImplicitNewmarkSparse::UseStaticSolver(bool useStaticSolver_)
-{
+{ 
   useStaticSolver = useStaticSolver_;
 
-  if (!useStaticSolver)
+  if (!useStaticSolver) 
   {
     memset(qvel, 0, sizeof(double) * r);
     memset(qaccel, 0, sizeof(double) * r);
-    memset(qvel_1, 0, sizeof(double) * r);
+    memset(qvel_1, 0, sizeof(double) * r); 
     memset(qaccel_1, 0, sizeof(double) * r);
     memcpy(q_1, q, sizeof(double) * r);
   }
-}
+} 
 
-SparseMatrix* ImplicitNewmarkSparse::GetSystemMatrix() const
+void ImplicitNewmarkSparse::SetTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset_, int reuseTopology)
 {
-  return this->systemMatrix;
+  if (tangentStiffnessMatrixOffset == NULL)
+    reuseTopology = 0;
+  IntegratorBaseSparse::SetTangentStiffnessMatrixOffset(tangentStiffnessMatrixOffset_, reuseTopology);
+  if (!reuseTopology)
+    tangentStiffnessMatrix->BuildSubMatrixIndices(*tangentStiffnessMatrixOffset, 2);
 }
 
-}
diff --git a/libraries/integratorSparse/implicitNewmarkSparse.h b/libraries/integratorSparse/implicitNewmarkSparse.h
new file mode 100644
index 0000000000000000000000000000000000000000..ead5345755f5cb6542d780a10b1723c58e856ab4
--- /dev/null
+++ b/libraries/integratorSparse/implicitNewmarkSparse.h
@@ -0,0 +1,136 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A class to timestep large sparse dynamics using implicit Newmark.
+  E.g., unreduced nonlinear FEM deformable dynamics.
+
+  See also integratorBase.h .
+
+  This class either uses SPOOLES, PARDISO, or our own Jacobi-preconitioned 
+  CG to solve the large sparse linear systems.
+
+  You can switch between these solvers at compile time,
+  by modifying the file integratorSolverSelection.h (see below)
+  (run-time solver switching would be possible too with more coding).
+*/
+
+#ifndef _IMPLICITNEWMARKSPARSE_H_
+#define _IMPLICITNEWMARKSPARSE_H_
+
+#ifdef __APPLE__
+  #include "TargetConditionals.h"
+#endif
+
+// This code supports three different solvers for sparse linear systems of equations:
+// SPOOLES, PARDISO, Jacobi-preconditioned Conjugate Gradients
+// You must define exactly one of the macros SPOOLES, PARDISO, PCG,
+// by changing the file integratorSolverSelection.h .
+// PCG is available with our code; look for it in the "sparseMatrix" library (CGSolver.h)
+// SPOOLES is available at: http://www.netlib.org/linalg/spooles/spooles.2.2.html
+// For PARDISO, the class was tested with the PARDISO implementation from the Intel Math Kernel Library
+
+#include "integratorSolverSelection.h"
+#include "sparseMatrix.h"
+#include "integratorBaseSparse.h"
+
+#ifdef PARDISO
+  #include "sparseSolvers.h"
+#endif
+#ifdef SPOOLES
+  #include "sparseSolvers.h"
+#endif
+#ifdef PCG
+  #include "CGSolver.h"
+#endif
+
+class ImplicitNewmarkSparse : public IntegratorBaseSparse
+{
+public:
+
+  // constrainedDOFs is an integer array of degrees of freedom that are to be fixed to zero (e.g., to permanently fix a vertex in a deformable simulation)
+  // constrainedDOFs are 0-indexed (separate DOFs for x,y,z), and must be pre-sorted (ascending)
+  // numThreads applies only to the PARDISO solver; if numThreads > 0, the sparse linear solves are multi-threaded; default: 0 (use single-threading)
+  ImplicitNewmarkSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0, int maxIterations = 1, double epsilon = 1E-6, double NewmarkBeta=0.25, double NewmarkGamma=0.5, int numSolverThreads=0); 
+
+  virtual ~ImplicitNewmarkSparse();
+
+  // damping matrix provides damping in addition to mass and stiffness damping (it does not replace it)
+  virtual void SetDampingMatrix(SparseMatrix * dampingMatrix);
+  inline virtual void SetTimestep(double timestep) { this->timestep = timestep; UpdateAlphas(); }
+
+  // sets q, qvel 
+  // automatically computes acceleration assuming zero external force
+  // returns 0 on succes, 1 if solver fails to converge
+  // note: there are also other state setting routines in the base class
+  virtual int SetState(double * q, double * qvel=NULL);
+
+  // see parent class for usage
+  virtual void SetTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset, int reuseTopology=1);
+
+  // performs one step of simulation (returns 0 on sucess, and 1 on failure)
+  virtual int DoTimestep(); 
+
+  inline void SetNewmarkBeta(double NewmarkBeta) { this->NewmarkBeta = NewmarkBeta; UpdateAlphas(); }
+  inline void SetNewmarkGamma(double NewmarkGamma) { this->NewmarkGamma = NewmarkGamma; UpdateAlphas(); }
+
+  // dynamic solver is default (i.e. useStaticSolver=false)
+  virtual void UseStaticSolver(bool useStaticSolver);
+
+protected:
+  SparseMatrix * rayleighDampingMatrix;
+  SparseMatrix * tangentStiffnessMatrix;
+  SparseMatrix * systemMatrix;
+
+  double * bufferConstrained;
+
+  // parameters for implicit Newmark
+  double NewmarkBeta,NewmarkGamma;
+  double alpha1, alpha2, alpha3, alpha4, alpha5, alpha6;
+  double epsilon; 
+  int maxIterations;
+
+  void UpdateAlphas();
+  bool useStaticSolver;
+
+  int numSolverThreads;
+  #ifdef PARDISO
+    PardisoSolver * pardisoSolver;
+  #endif
+
+  #ifdef PCG
+    CGSolver * jacobiPreconditionedCGSolver;
+  #endif
+};
+
+#endif
+
diff --git a/src/libintegratorSparse/integratorBaseSparse.cpp b/libraries/integratorSparse/integratorBaseSparse.cpp
similarity index 66%
rename from src/libintegratorSparse/integratorBaseSparse.cpp
rename to libraries/integratorSparse/integratorBaseSparse.cpp
index cc14b57e0f242cc75cbda3bee15523eecb3796e5..c5ff7e6712cbfa43f24d20f7048d27c38cbf135f 100644
--- a/src/libintegratorSparse/integratorBaseSparse.cpp
+++ b/libraries/integratorSparse/integratorBaseSparse.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC     *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,10 +34,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "integratorBaseSparse.h"
-#include "linearSolver.h"
 
-namespace vega
-{
 IntegratorBaseSparse::IntegratorBaseSparse(int r, double timestep, SparseMatrix * massMatrix_, ForceModel * forceModel_, int numConstrainedDOFs_, int * constrainedDOFs_, double dampingMassCoef, double dampingStiffnessCoef): IntegratorBase(r, timestep, dampingMassCoef, dampingStiffnessCoef), massMatrix(massMatrix_), forceModel(forceModel_), numConstrainedDOFs(numConstrainedDOFs_)
 {
   systemSolveTime = 0.0;
@@ -45,11 +46,16 @@ IntegratorBaseSparse::IntegratorBaseSparse(int r, double timestep, SparseMatrix
   ownDampingMatrix = 1;
   SparseMatrixOutline outline(r);
   dampingMatrix = new SparseMatrix(&outline);
+
+  tangentStiffnessMatrixOffset = NULL;
 }
 
 IntegratorBaseSparse::~IntegratorBaseSparse()
 {
   free(constrainedDOFs);
+  if (ownDampingMatrix)
+    delete(dampingMatrix);
+  delete(tangentStiffnessMatrixOffset);
 }
 
 void IntegratorBaseSparse::SetDampingMatrix(SparseMatrix * dampingMatrix_)
@@ -71,19 +77,25 @@ double IntegratorBaseSparse::GetTotalMass()
   return massMatrix->SumEntries();
 }
 
-const LinearSolver* IntegratorBaseSparse::getLinearSolver() const
+void IntegratorBaseSparse::SetTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset_, int reuseTopology)
 {
-    return linearSolver;
+  if (reuseTopology && (tangentStiffnessMatrixOffset != NULL))
+    *tangentStiffnessMatrixOffset = *tangentStiffnessMatrixOffset_;
+  else
+  {
+    delete(tangentStiffnessMatrixOffset);
+    tangentStiffnessMatrixOffset = new SparseMatrix(*tangentStiffnessMatrixOffset_);
+  }
 }
 
-void IntegratorBaseSparse::setLinearSolver ( LinearSolver* solver )
+void IntegratorBaseSparse::AddTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset_)
 {
-    linearSolver = solver;
+  *tangentStiffnessMatrixOffset += *tangentStiffnessMatrixOffset_;
 }
 
-SparseMatrix* IntegratorBaseSparse::GetSystemMatrix() const
+void IntegratorBaseSparse::ClearTangentStiffnessMatrixOffset()
 {
-  return this->massMatrix;
+  delete(tangentStiffnessMatrixOffset);
+  tangentStiffnessMatrixOffset = NULL;
 }
 
-}
diff --git a/libraries/integratorSparse/integratorBaseSparse.h b/libraries/integratorSparse/integratorBaseSparse.h
new file mode 100644
index 0000000000000000000000000000000000000000..50ab255cc072c7ce9ba7666731fb0406145473d6
--- /dev/null
+++ b/libraries/integratorSparse/integratorBaseSparse.h
@@ -0,0 +1,95 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "integrator" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC     *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A base class to timestep large sparse dynamics.
+  E.g., unreduced nonlinear FEM deformable dynamics.
+
+  See also integratorBase.h .
+*/
+
+#ifndef _INTEGRATORBASESPARSE_H_
+#define _INTEGRATORBASESPARSE_H_
+
+#include "sparseMatrix.h"
+#include "forceModel.h"
+#include "integratorBase.h"
+
+class IntegratorBaseSparse : public IntegratorBase
+{
+public:
+
+  // constrainedDOFs is an integer array of degrees of freedom that are to be fixed to zero (e.g., to permanently fix a vertex in a deformable simulation)
+  // constrainedDOFs are 0-indexed (separate DOFs for x,y,z), and must be pre-sorted (ascending)
+  // damping matrix provides damping in addition to mass and stiffness damping
+  IntegratorBaseSparse(int r, double timestep, SparseMatrix * massMatrix, ForceModel * forceModel, int numConstrainedDOFs=0, int * constrainedDOFs=NULL, double dampingMassCoef=0.0, double dampingStiffnessCoef=0.0);
+
+  virtual ~IntegratorBaseSparse();
+
+  inline virtual void SetForceModel(ForceModel * forceModel) { this->forceModel = forceModel; }
+
+  // damping matrix provides damping in addition to mass and stiffness damping (it does not replace it)
+  virtual void SetDampingMatrix(SparseMatrix * dampingMatrix);
+
+  // add an offset to the tangent stiffness matrix (default: no offset)
+  virtual void SetTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset, int reuseTopology=1); // you must use reuseTopology=0 if the offset has been previously already set, and the topology of the new tangentStiffnessMatrixOffset matrix is different from the old one
+  virtual void AddTangentStiffnessMatrixOffset(SparseMatrix * tangentStiffnessMatrixOffset); // the operand must have same topology as the previously set tangent stiffness matrix
+  virtual void ClearTangentStiffnessMatrixOffset();
+
+  // performs one step of simulation (returns 0 on sucess, and 1 on failure)
+  // failure can occur, for example, if you are using the positive definite solver and the system matrix has negative eigenvalues
+  virtual int DoTimestep() = 0;
+
+  // returns the execution time of the last r x r linear system solve
+  inline virtual double GetSystemSolveTime() { return systemSolveTime; }
+  inline virtual double GetForceAssemblyTime() { return forceAssemblyTime; }
+
+  virtual double GetKineticEnergy();
+  virtual double GetTotalMass();
+
+protected:
+  SparseMatrix * massMatrix; 
+  ForceModel * forceModel;
+  int ownDampingMatrix;
+  SparseMatrix * dampingMatrix;
+  
+  SparseMatrix * tangentStiffnessMatrixOffset;
+
+  int numConstrainedDOFs;
+  int * constrainedDOFs;
+
+  double systemSolveTime;
+  double forceAssemblyTime;
+};
+
+#endif
+
diff --git a/libraries/interpolationCoordinates/barycentricCoordinates.cpp b/libraries/interpolationCoordinates/barycentricCoordinates.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8e386f0708ac7229d2f654e0602d2cc96558e3c2
--- /dev/null
+++ b/libraries/interpolationCoordinates/barycentricCoordinates.cpp
@@ -0,0 +1,298 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "interpolationCoordinates" library , Copyright (C) 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "barycentricCoordinates.h"
+#include "generateInterpolationMatrix.h"
+#ifdef CONSTRUCTOR_WITH_SEED
+  #include "objMeshGraph.h"
+  #include "tetMeshManifold.h"
+#endif
+#include <cassert>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <queue>
+#include <climits>
+using namespace std;
+
+BarycentricCoordinates::BarycentricCoordinates(int numLocations_, int numElementVertices_, const int* indices_, const double* weights_,
+  const int * elementIndices_)
+{
+  numLocations = numLocations_;
+  numElementVertices = numElementVertices_;
+  indices.resize(numElementVertices * numLocations);
+  weights.resize(numElementVertices * numLocations);
+  memcpy(&indices[0], indices_, sizeof(int) * indices.size());
+  memcpy(&weights[0], weights_, sizeof(double) * weights.size());
+  elements.resize(numLocations, -1);
+  if (elementIndices_)
+    elements.assign(elementIndices_, elementIndices_ + numLocations);
+}
+
+BarycentricCoordinates::BarycentricCoordinates(const std::vector<Vec4i> & tetVtxIndices, const std::vector<Vec4d> & tetWeights,
+  const int * elementIndices) : BarycentricCoordinates(min(tetVtxIndices.size(), tetWeights.size()), 4,
+  (const int*)tetVtxIndices.data(), (const double*) tetWeights.data(), elementIndices)
+{
+  assert(tetVtxIndices.size() == tetWeights.size());
+}
+
+#ifdef CONSTRUCTOR_WITH_SEED
+
+BarycentricCoordinates::BarycentricCoordinates(const ObjMesh * objMesh,  const TetMesh * volumetricMesh, int numThreads, int seed)
+{
+  numLocations = objMesh->getNumVertices();
+  numElementVertices = volumetricMesh->getNumElementVertices();
+  indices.resize(numElementVertices * numLocations, 0);
+  weights.resize(numElementVertices * numLocations, 0);
+  elements.resize(numLocations, -1);
+
+  vector<double> locations;
+  exportObjMeshGeometry(objMesh, locations);
+  TetMeshManifold manifold;
+
+  map<UTetKey, int> index;
+  for(int i = 0; i < volumetricMesh->getNumElements(); i++)
+  {
+    assert(manifold.add(volumetricMesh->getElementVertexIndices(i)));
+    UTetKey key(volumetricMesh->getElementVertexIndices(i));
+    index.insert(pair<UTetKey,int>(key, i));
+  }
+  const TetMeshManifold::TetMap & tetMap = manifold.getTetMap();
+
+
+  vector<Vec3d> vs;
+  for(int i = 0; i < volumetricMesh->getNumVertices(); i++)
+    vs.push_back(*volumetricMesh->getVertex(i));
+  VerticesQuery query(vs.size(), vs.data());
+
+  assert((int)seed < numLocations);
+  FastInterpolationWeights interpolation(volumetricMesh);
+  interpolation.generateInterpolationWeights(&locations[3*seed], &indices[numElementVertices * seed], &weights[numElementVertices * seed], -1, &elements[seed]);
+
+  // Creates a graph where the nodes are obj mesh vertices. Two nodes are connected if they are adjacent in the mesh.
+  Graph * graph = ObjMeshGraph::GenerateVertexGraph(objMesh, 0);
+  set<int> visited;
+  queue<int> candidates;
+  candidates.push(seed);
+  visited.insert(seed);
+
+  while(candidates.size() > 0)
+  {
+    int vtx = candidates.front();
+    int element = elements[vtx];
+    candidates.pop();
+    if (element < 0)
+      continue;
+
+    int nn = graph->GetNumNeighbors(vtx);
+    for(int i = 0; i < nn; i++)
+    {
+      int nbr = graph->GetNeighbor(vtx, i);
+      if (visited.insert(nbr).second == false) // this nbr is already visited before
+        continue;
+
+      Vec3d p = objMesh->getPosition(nbr);
+
+      // find elements containing nbr
+      int curEle = element;
+      while (true)
+      {
+        UTetKey key(volumetricMesh->getElementVertexIndices(curEle));
+        TetMeshManifold::TetCIter it = tetMap.find(key);
+        assert(it != tetMap.end());
+        const TetMeshManifold::Tetrahedron * t = it->second;
+        const TetMeshManifold::Tetrahedron * next = NULL;
+
+        int j = 0;
+        for(j = 0; j < 4; j++)
+        {
+          int f[3];
+          t->getOppositeFace(j, f);
+
+          if (query.toPlane(p, f[0], f[1], f[2]) > 0)
+          {
+            //            assert(weight[j] < 0);
+            const TetMeshManifold::Tetrahedron * nbr = t->getNeighbor(j);
+            if (nbr)
+            {
+              next = nbr;
+              break;
+            }
+          }
+        }
+        if (j == 4)  // the point is inside this tet
+          break;
+        else if (next)
+        {
+          int vtx[4];
+          for(int j = 0; j < 4; j++)
+            vtx[j] = next->getVtx(j);
+          UTetKey nbkey(vtx[0], vtx[1], vtx[2], vtx[3]);
+          assert(nbkey < key || key < nbkey);
+          curEle = index[nbkey];
+        }
+        else // the point is outside of a surface face on the tet
+        {
+          curEle = -1;
+          break;
+        }
+      } // end while (true)
+
+      if (curEle >= 0)
+      {
+        candidates.push(nbr);
+        elements[nbr] = curEle;
+        double * weight = &weights[numElementVertices * nbr];
+        int * vtxIndex = &indices[numElementVertices * nbr];
+        volumetricMesh->computeBarycentricWeights(curEle, p, weight);
+        for(int j = 0; j < 4; j++)
+          vtxIndex[j] = volumetricMesh->getVertexIndex(curEle,j);
+      }
+    }
+  }
+
+  for(int i = 0; i < numLocations; i++)
+  {
+    if (elements[i] < 0)
+    {
+      // use fastInterpolationWeights to compute interp data
+      interpolation.generateInterpolationWeights(&locations[3*i], &indices[numElementVertices * i], &weights[numElementVertices * i], -1, &elements[i]);
+    }
+  }
+}
+
+#endif
+
+void BarycentricCoordinates::deform(const double * verticesDisp, double* locationDisp) const
+{
+  VolumetricMesh::interpolate(verticesDisp, locationDisp, numLocations, numElementVertices, &indices[0], &weights[0]);
+}
+
+int BarycentricCoordinates::saveInterpolationWeights(const string & filename) const
+{
+  //saveInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices, int * vertices, double * weights);
+  int ret = VolumetricMesh::saveInterpolationWeights(filename.c_str(), numLocations, numElementVertices, &indices[0], &weights[0]);
+  cout << (ret == 0 ? "Saved" : "Failed to save");
+  cout << " interpolation weights (numLocations: " << numLocations << ", numElementVertices: " << numElementVertices << ") to " << filename << "." << endl;
+  return ret;
+}
+
+SparseMatrix * BarycentricCoordinates::generateInterpolationMatrix() const
+{
+  SparseMatrix * A = NULL;
+  GenerateInterpolationMatrix::generate(numLocations, numElementVertices, &indices[0], &weights[0], &A);
+  return A;
+}
+
+#define READ_ONE_PAIR \
+  do { \
+    int index = 0; \
+    double w = 0; \
+    ss >> index; \
+    ss >> w; \
+    if (ss.fail()) { \
+      cerr << index << " " << w << endl; \
+      cerr << "Error: incorrect interp file format at \"" << buffer << "\" in interp file " << filename << "." << endl; \
+      throw 1; \
+    } \
+    if (index < 0) { \
+      cerr << "Error: invalid index at \"" << buffer << "\" in interp file " << filename << "." << endl; \
+      throw 1; \
+    } \
+    indices.push_back(index); \
+    weights.push_back(w); \
+  } while(0)
+
+BarycentricCoordinates::BarycentricCoordinates(const std::string & filename)
+{
+  ifstream fin(filename.c_str(), ios::binary);
+  if (!fin)
+  {
+    cerr << "Error: cannot open interp file " << filename << "." << endl;
+    throw 1;
+  }
+  fin >> ws;
+
+  int count = 0;
+  string buffer;
+  stringstream ss;
+  numElementVertices = INT_MAX;
+  while(!fin.eof())
+  {
+    getline(fin, buffer);
+    fin >> ws;
+    if (buffer.size() == 0)
+      continue;
+
+//    istringstream ss(buffer);
+    ss.clear();
+    ss.str(buffer);
+    ss.seekg(0);
+    int c = 0;
+    ss >> c;
+//    cout << "At line: " << buffer << endl;
+    if (c != count)
+    {
+      cerr << "Warning: wrong line index at \"" << buffer << "\" in interp file " << filename << "." << endl;
+    }
+    if (numElementVertices == INT_MAX)
+    {
+      // determine #element vertices
+      ss >> ws;
+//      cout << "First line " << endl;
+      numElementVertices = 0;
+      while(!ss.eof())
+      {
+//        cout << "RRR" << endl;
+        READ_ONE_PAIR;
+        numElementVertices++;
+//        cout << "numElementVertices now: " << numElementVertices << endl;
+        ss >> ws;
+      }
+    }
+    else
+    {
+      for(int i = 0; i < numElementVertices; i++)
+      {
+        READ_ONE_PAIR;
+      }
+    }
+    count++;
+  }
+  numLocations = count;
+
+  elements.resize(numLocations, -1); // no information for elements read from file
+
+  cout << "Loaded " << numLocations << " locations from file " << filename << " with numElementVertices = " << numElementVertices << "." << endl;
+  assert((int)indices.size() == numLocations * numElementVertices);
+  assert((int)weights.size() == numLocations * numElementVertices);
+}
diff --git a/libraries/interpolationCoordinates/barycentricCoordinates.h b/libraries/interpolationCoordinates/barycentricCoordinates.h
new file mode 100644
index 0000000000000000000000000000000000000000..802c08c067a82570411dcd47baebdd41c19870f2
--- /dev/null
+++ b/libraries/interpolationCoordinates/barycentricCoordinates.h
@@ -0,0 +1,96 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "interpolationCoordinates" library , Copyright (C) 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef BARYCENTRICCOORDINATES_H
+#define BARYCENTRICCOORDINATES_H
+
+#include "volumetricMesh.h"
+#include "interpolationCoordinates.h"
+#include "sparseMatrix.h"
+#include "vec4d.h"
+#include "vec4i.h"
+#include "tetMesh.h"
+#include "objMesh.h"
+#include <vector>
+#include <string>
+
+// Compute and store barycentric coordinates.
+// Optionally, compute the indices of the elements that the embedded vertices belong to.
+
+class BarycentricCoordinates : public InterpolationCoordinates
+{
+public:
+
+  BarycentricCoordinates() {}
+
+  // Load from file. No element indices are available.
+  BarycentricCoordinates(const std::string & filename);
+
+  // Initialize with only element vertex indices and weights. No element indices are available.
+  BarycentricCoordinates(int numLocations, int numElementVertices, const int * elementVertexIndices, const double * weights,
+    const int * elementIndices = nullptr);
+
+  BarycentricCoordinates(const std::vector<Vec4i> & tetVtxIndices, const std::vector<Vec4d> & tetWeights, const int * elementIndices = nullptr);
+
+  virtual ~BarycentricCoordinates() {}
+
+  virtual void deform(const double * verticesDisp, double * locationDisp) const override;
+
+  int getNumLocations() const { return numLocations; }
+  int getNumElementVertices() const { return numElementVertices; }
+
+  const int * getEmbeddingVertexIndices(int embeddedVtx) const { return &indices[numElementVertices * embeddedVtx]; }
+  const double * getEmbeddingWeights(int embeddedVtx) const { return &weights[numElementVertices * embeddedVtx]; }
+
+  // Get internal data.
+  std::vector<int> & getEmbeddingVertexIndices() { return indices; }
+  std::vector<double> & getEmbeddingWeights() { return weights; }
+  std::vector<int> & getElements() { return elements; }
+
+  // Return embedding element index if it's available; -1 otherwise.
+  int getEmbeddingElement(int embeddedVtx) const { return elements[embeddedVtx]; }
+
+  // Save weights to file.
+  int saveInterpolationWeights(const std::string & filename) const;
+
+  SparseMatrix * generateInterpolationMatrix() const;
+
+protected:
+  int numLocations = 0;
+  int numElementVertices = 0;
+  std::vector<int> indices;
+  std::vector<double> weights;
+  std::vector<int> elements;
+};
+
+#endif /* BARYCENTRICCOORDINATES_H */
+
diff --git a/libraries/interpolationCoordinates/interpolationCoordinates.h b/libraries/interpolationCoordinates/interpolationCoordinates.h
new file mode 100644
index 0000000000000000000000000000000000000000..5edb8455ea4b74924fd1ad9cd7fbeddef324ef8d
--- /dev/null
+++ b/libraries/interpolationCoordinates/interpolationCoordinates.h
@@ -0,0 +1,51 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "interpolationCoordinates" library , Copyright (C) 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef INTERPOLATIONCOORDINATES_H
+#define INTERPOLATIONCOORDINATES_H
+
+class InterpolationCoordinates
+{
+public:
+  virtual ~InterpolationCoordinates() {}
+
+  // deform embedded geometry
+  // embedderDisp: vertex displacements of the embedder mesh
+  // embeddedDisp: vertex displacements of the embedded mesh
+  virtual void deform(const double * embedderDisp, double * embeddedDisp) const = 0;
+
+protected:
+  InterpolationCoordinates() {}
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/MooneyRivlin-Wikipedia.png b/libraries/isotropicHyperelasticFEM/MooneyRivlin-Wikipedia.png
similarity index 100%
rename from src/libisotropicHyperelasticFEM/MooneyRivlin-Wikipedia.png
rename to libraries/isotropicHyperelasticFEM/MooneyRivlin-Wikipedia.png
diff --git a/src/libisotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.cpp b/libraries/isotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.cpp
similarity index 91%
rename from src/libisotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.cpp
rename to libraries/isotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.cpp
index 610a60076971ff61c1d7d30d3f5c4bdd8e9daeca..c3fe90d2b33444dc7cc7cce3b70155818f40a970 100644
--- a/src/libisotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.cpp
+++ b/libraries/isotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,8 +34,6 @@
 #include "MooneyRivlinIsotropicMaterial.h"
 #include "volumetricMeshMooneyRivlinMaterial.h"
 
-namespace vega
-{
 MooneyRivlinIsotropicMaterial::MooneyRivlinIsotropicMaterial(TetMesh * tetMesh, int enableCompressionResistance_, double compressionResistance_) : IsotropicMaterialWithCompressionResistance(enableCompressionResistance_), compressionResistance(compressionResistance_)
 {
   int numElements = tetMesh->getNumElements();
@@ -164,4 +166,3 @@ double MooneyRivlinIsotropicMaterial::GetCompressionResistanceFactor(int element
   return EdivNuFactor[elementIndex];
 }
 
-}
diff --git a/libraries/isotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.h b/libraries/isotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d5e855b4ad8784fbf170d8ff755da6f041d3317
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/MooneyRivlinIsotropicMaterial.h
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _MOONEYRIVLINISOTROPICMATERIAL_H_
+#define _MOONEYRIVLINISOTROPICMATERIAL_H_
+
+#include "isotropicMaterialWithCompressionResistance.h"
+#include "tetMesh.h"
+
+/*
+  The implemented compressible Mooney-Rivlin material has the following energy function:
+
+  energy = 0.5 * mu01 * ((Ic^2 - IIc) / pow(IIIc, 2/3) - 6) +
+                 mu10 * (Ic / pow(IIIc, 1/3) - 3) +
+                 v1 * (sqrt(IIIc) - 1)^2
+
+  This is a standard adaption of the material presented in:
+
+  R. S. Rivlin and D. W. Saunders: 
+  Large elastic deformations of isotropic materials VII. 
+  Experiments on the deformation of rubber. 
+  Philosophical Transactions Royal Society London Series A, 
+  243(865), 1951, pp. 251-288.
+
+  See the Wikipedia page, "Polynomial_(hyperelastic_model)".
+  A snapshot of the Wikipedia page is included in this folder.
+*/
+
+class MooneyRivlinIsotropicMaterial : public IsotropicMaterialWithCompressionResistance
+{
+public:
+  MooneyRivlinIsotropicMaterial(TetMesh * tetMesh, int enableCompressionResistance=0, double compressionResistance=0.0);
+  virtual ~MooneyRivlinIsotropicMaterial();
+
+  virtual double ComputeEnergy(int elementIndex, double * invariants);
+  virtual void ComputeEnergyGradient(int elementIndex, double * invariants, double * gradient); // invariants and gradient are 3-vectors
+  virtual void ComputeEnergyHessian(int elementIndex, double * invariants, double * hessian); // invariants is a 3-vector, hessian is a 3x3 symmetric matrix, unrolled into a 6-vector, in the following order: (11, 12, 13, 22, 23, 33).
+
+protected:
+  double * mu01_;
+  double * mu10_;
+  double * v1_;
+
+  double compressionResistance;
+  double * EdivNuFactor;
+  virtual double GetCompressionResistanceFactor(int elementIndex);
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/StVKIsotropicMaterial.cpp b/libraries/isotropicHyperelasticFEM/StVKIsotropicMaterial.cpp
similarity index 90%
rename from src/libisotropicHyperelasticFEM/StVKIsotropicMaterial.cpp
rename to libraries/isotropicHyperelasticFEM/StVKIsotropicMaterial.cpp
index 33a6e3b4434074229203cfc0300257f0d9edc0e8..4276aaf0e6afa584d8609fb5d7bf04d42aa6ff9d 100644
--- a/src/libisotropicHyperelasticFEM/StVKIsotropicMaterial.cpp
+++ b/libraries/isotropicHyperelasticFEM/StVKIsotropicMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,8 +34,6 @@
 #include "StVKIsotropicMaterial.h"
 #include "volumetricMeshENuMaterial.h"
 
-namespace vega
-{
 StVKIsotropicMaterial::StVKIsotropicMaterial(TetMesh * tetMesh, int enableCompressionResistance_, double compressionResistance_) : IsotropicMaterialWithCompressionResistance(enableCompressionResistance_), compressionResistance(compressionResistance_)
 {
   //printf("Entering StVKIsotropicMaterial::StVKIsotropicMaterial\n");
@@ -160,4 +162,3 @@ double StVKIsotropicMaterial::GetCompressionResistanceFactor(int elementIndex)
   return EdivNuFactor[elementIndex];
 }
 
-}
diff --git a/libraries/isotropicHyperelasticFEM/StVKIsotropicMaterial.h b/libraries/isotropicHyperelasticFEM/StVKIsotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..298d7bf502b2c71412355c7e3f6141166f48a8be
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/StVKIsotropicMaterial.h
@@ -0,0 +1,69 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVKISOTROPICMATERIAL_H_
+#define _STVKISOTROPICMATERIAL_H_
+
+#include "isotropicMaterialWithCompressionResistance.h"
+#include "tetMesh.h"
+
+/*
+   StVK material. Material properties are read from the tet mesh, and can be heterogeneous.
+
+   The implemented St.Venant-Kirchhoff material is described in:
+   BONET J., WOOD R. D.: Nonlinear Continuum Mechanics
+   for Finite Element Analysis, 2nd Ed. Cambridge University
+   Press, 2008, page 158
+*/
+
+class StVKIsotropicMaterial : public IsotropicMaterialWithCompressionResistance
+{
+public:
+  StVKIsotropicMaterial(TetMesh * tetMesh, int enableCompressionResistance=0, double compressionResistance=0.0);
+
+  virtual ~StVKIsotropicMaterial();
+
+  virtual double ComputeEnergy(int elementIndex, double * invariants);
+  virtual void ComputeEnergyGradient(int elementIndex, double * invariants, double * gradient); // invariants and gradient are 3-vectors
+  virtual void ComputeEnergyHessian(int elementIndex, double * invariants, double * hessian); // invariants is a 3-vector, hessian is a 3x3 symmetric matrix, unrolled into a 6-vector, in the following order: (11, 12, 13, 22, 23, 33).
+
+protected:
+  double * lambdaLame;
+  double * muLame;
+
+  double compressionResistance;
+  double * EdivNuFactor;
+  virtual double GetCompressionResistanceFactor(int elementIndex);
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.cpp b/libraries/isotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.cpp
similarity index 88%
rename from src/libisotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.cpp
rename to libraries/isotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.cpp
index 5c474683e143d4bdc638a84015913ff830f561fa..e1bfa0f9ef55ec963c6350cfe3b54c07e5a2957d 100644
--- a/src/libisotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.cpp
+++ b/libraries/isotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include <math.h>
 #include "homogeneousMooneyRivlinIsotropicMaterial.h"
 
-namespace vega
-{
 HomogeneousMooneyRivlinIsotropicMaterial::HomogeneousMooneyRivlinIsotropicMaterial(double mu01_, double mu10_, double v1_, int enableCompressionResistance_, double compressionResistance_) : IsotropicMaterialWithCompressionResistance(enableCompressionResistance_), mu01(mu01_), mu10(mu10_), v1(v1_), compressionResistance(compressionResistance_) 
 {
   // invert the following formulas to compute "pseudo" E and nu that correspond to this material
@@ -114,4 +116,3 @@ double HomogeneousMooneyRivlinIsotropicMaterial::GetCompressionResistanceFactor(
   return EdivNuFactor;
 }
 
-}
diff --git a/libraries/isotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.h b/libraries/isotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..318cfe01dd38e3dd620276e84963747752a50828
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/homogeneousMooneyRivlinIsotropicMaterial.h
@@ -0,0 +1,77 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _HOMOGENEOUSMOONEYRIVLINISOTROPICMATERIAL_H_
+#define _HOMOGENEOUSMOONEYRIVLINISOTROPICMATERIAL_H_
+
+#include "isotropicMaterialWithCompressionResistance.h"
+
+/*
+  The implemented compressible Mooney-Rivlin material has the following energy function:
+
+  energy = 0.5 * mu01 * ((Ic^2 - IIc) / pow(IIIc, 2/3) - 6) +
+                 mu10 * (Ic / pow(IIIc, 1/3) - 3) +
+                 v1 * (sqrt(IIIc) - 1)^2
+
+  This is a standard adaption of the material presented in:
+
+  R. S. Rivlin and D. W. Saunders: 
+  Large elastic deformations of isotropic materials VII. 
+  Experiments on the deformation of rubber. 
+  Philosophical Transactions Royal Society London Series A, 
+  243(865), 1951, pp. 251-288.
+
+  See the Wikipedia page, "Polynomial_(hyperelastic_model)".
+  A snapshot of the Wikipedia page is included in this folder.
+*/
+
+class HomogeneousMooneyRivlinIsotropicMaterial : public IsotropicMaterialWithCompressionResistance
+{
+public:
+  HomogeneousMooneyRivlinIsotropicMaterial(double mu01, double mu10, double v1, int enableCompressionResistance=0, double compressionResistance=0.0);
+  virtual ~HomogeneousMooneyRivlinIsotropicMaterial();
+
+  virtual double ComputeEnergy(int elementIndex, double * invariants);
+  virtual void ComputeEnergyGradient(int elementIndex, double * invariants, double * gradient); // invariants and gradient are 3-vectors
+  virtual void ComputeEnergyHessian(int elementIndex, double * invariants, double * hessian); // invariants is a 3-vector, hessian is a 3x3 symmetric matrix, unrolled into a 6-vector, in the following order: (11, 12, 13, 22, 23, 33).
+
+protected:
+  double mu01, mu10;
+  double v1;
+
+  double compressionResistance;
+  double EdivNuFactor;
+  virtual double GetCompressionResistanceFactor(int elementIndex);
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.cpp b/libraries/isotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.cpp
similarity index 87%
rename from src/libisotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.cpp
rename to libraries/isotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.cpp
index e2a00cd83ab441a10097d18a8976021f1f034854..0b3a308b20b683929349a003cff14d00085b1a3c 100644
--- a/src/libisotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.cpp
+++ b/libraries/isotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include <math.h>
 #include "homogeneousNeoHookeanIsotropicMaterial.h"
 
-namespace vega
-{
 HomogeneousNeoHookeanIsotropicMaterial::HomogeneousNeoHookeanIsotropicMaterial(double E_, double nu_, int enableCompressionResistance_, double compressionResistance_) : IsotropicMaterialWithCompressionResistance(enableCompressionResistance_), compressionResistance(compressionResistance_)
 {
   SetYoungModulusAndPoissonRatio(E_, nu_);
@@ -105,4 +107,3 @@ double HomogeneousNeoHookeanIsotropicMaterial::GetCompressionResistanceFactor(in
   return EdivNuFactor;
 }
 
-}
diff --git a/libraries/isotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.h b/libraries/isotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..01bd1a73c7a4716ad35caedf3faac927ab9eb6f6
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/homogeneousNeoHookeanIsotropicMaterial.h
@@ -0,0 +1,70 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _HOMOGENEOUSNEOHOOKEANISOTROPICMATERIAL_H_
+#define _HOMOGENEOUSNEOHOOKEANISOTROPICMATERIAL_H_
+
+#include "isotropicMaterialWithCompressionResistance.h"
+
+/*
+   Homogeneous neo-Hookean material. Material properties are constant throughout the mesh.
+
+   The implemented neo-Hookean material is described in:
+   BONET J., WOOD R. D.: Nonlinear Continuum Mechanics
+   for Finite Element Analysis, 2nd Ed. Cambridge University
+   Press, 2008, page 162
+*/
+
+class HomogeneousNeoHookeanIsotropicMaterial : public IsotropicMaterialWithCompressionResistance
+{
+public:
+  HomogeneousNeoHookeanIsotropicMaterial(double E, double nu, int enableCompressionResistance=0, double compressionResistance=0.0);
+  virtual ~HomogeneousNeoHookeanIsotropicMaterial();
+
+  void SetYoungModulusAndPoissonRatio(double E, double nu);
+  void SetLameCoefficients(double lambda, double mu);
+
+  virtual double ComputeEnergy(int elementIndex, double * invariants);
+  virtual void ComputeEnergyGradient(int elementIndex, double * invariants, double * gradient); // invariants and gradient are 3-vectors
+  virtual void ComputeEnergyHessian(int elementIndex, double * invariants, double * hessian); // invariants is a 3-vector, hessian is a 3x3 symmetric matrix, unrolled into a 6-vector, in the following order: (11, 12, 13, 22, 23, 33).
+
+protected:
+  double E, nu;
+  double lambdaLame, muLame; // Lam\'e coefficients, not "lame" :)
+
+  double compressionResistance;
+  double EdivNuFactor;
+  virtual double GetCompressionResistanceFactor(int elementIndex);
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.cpp b/libraries/isotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.cpp
similarity index 86%
rename from src/libisotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.cpp
rename to libraries/isotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.cpp
index b6a804ebe5f6bc7df46eacab153da4b530dc9436..7123533328a77c390ee988bbb60c1d6dd4369bd3 100644
--- a/src/libisotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.cpp
+++ b/libraries/isotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include <math.h>
 #include "homogeneousStVKIsotropicMaterial.h"
 
-namespace vega
-{
 HomogeneousStVKIsotropicMaterial::HomogeneousStVKIsotropicMaterial(double E_, double nu_, int enableCompressionResistance_, double compressionResistance_) : IsotropicMaterialWithCompressionResistance(enableCompressionResistance_), compressionResistance(compressionResistance_)
 {
   SetYoungModulusAndPoissonRatio(E_, nu_);
@@ -99,4 +101,3 @@ double HomogeneousStVKIsotropicMaterial::GetCompressionResistanceFactor(int elem
   return EdivNuFactor;
 }
 
-}
diff --git a/libraries/isotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.h b/libraries/isotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e6fc5234c6aec25a4f3bf38a3a7dfdd9f74698c
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/homogeneousStVKIsotropicMaterial.h
@@ -0,0 +1,71 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _HOMOGENEOUSISOTROPICSTVKMATERIAL_H_
+#define _HOMOGENEOUSISOTROPICSTVKMATERIAL_H_
+
+#include "isotropicMaterialWithCompressionResistance.h"
+#include "tetMesh.h"
+
+/*
+   Homogeneous StVK material. Material properties are constant throughout the mesh.
+ 
+   The implemented St.Venant-Kirchhoff material is described in:
+   BONET J., WOOD R. D.: Nonlinear Continuum Mechanics
+   for Finite Element Analysis, 2nd Ed. Cambridge University
+   Press, 2008, page 158
+*/
+
+class HomogeneousStVKIsotropicMaterial : public IsotropicMaterialWithCompressionResistance
+{
+public:
+  HomogeneousStVKIsotropicMaterial(double E, double nu, int enableCompressionResistance=0, double compressionResistance=0.0);
+  virtual ~HomogeneousStVKIsotropicMaterial();
+
+  void SetYoungModulusAndPoissonRatio(double E, double nu); 
+  void SetLameCoefficients(double lambda, double mu); 
+
+  virtual double ComputeEnergy(int elementIndex, double * invariants);
+  virtual void ComputeEnergyGradient(int elementIndex, double * invariants, double * gradient); // invariants and gradient are 3-vectors
+  virtual void ComputeEnergyHessian(int elementIndex, double * invariants, double * hessian); // invariants is a 3-vector, hessian is a 3x3 symmetric matrix, unrolled into a 6-vector, in the following order: (11, 12, 13, 22, 23, 33).
+
+protected:
+  double E, nu;
+  double lambdaLame, muLame; // Lam\'e coefficients, not "lame" :)
+
+  double compressionResistance;
+  double EdivNuFactor;
+  virtual double GetCompressionResistanceFactor(int elementIndex);
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEM.cpp b/libraries/isotropicHyperelasticFEM/isotropicHyperelasticFEM.cpp
similarity index 65%
rename from src/libisotropicHyperelasticFEM/isotropicHyperelasticFEM.cpp
rename to libraries/isotropicHyperelasticFEM/isotropicHyperelasticFEM.cpp
index 96ae503ad1d420f338102c85c16cc3b111d09ebe..02fafb4937aeb400331072dacd1bb3373b1f645e 100644
--- a/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEM.cpp
+++ b/libraries/isotropicHyperelasticFEM/isotropicHyperelasticFEM.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,15 +32,17 @@
 
 #include "isotropicHyperelasticFEM.h"
 #include "matrixIO.h"
+#include "mat3d.h"
+
+#define SVD_singularValue_eps 1e-8
 
-namespace vega
-{
 IsotropicHyperelasticFEM::IsotropicHyperelasticFEM(TetMesh * tetMesh_, IsotropicMaterial * isotropicMaterial_, double inversionThreshold_, bool addGravity_, double g_) :
   tetMesh(tetMesh_),
   isotropicMaterial(isotropicMaterial_),
   inversionThreshold(inversionThreshold_),
   addGravity(addGravity_), 
-  g(g_)
+  g(g_),
+  enforceSPD(false)
 {
   if (tetMesh->getNumElementVertices() != 4)
   {
@@ -64,10 +70,10 @@ IsotropicHyperelasticFEM::IsotropicHyperelasticFEM(TetMesh * tetMesh_, Isotropic
   restVerticesPosition = (double*) malloc (sizeof(double) * 3 * numVertices);
   for (int i=0; i<numVertices; i++)
   {
-    Vec3d * v = tetMesh->getVertex(i);
-    restVerticesPosition[3*i+0] = (*v)[0];
-    restVerticesPosition[3*i+1] = (*v)[1];
-    restVerticesPosition[3*i+2] = (*v)[2];
+    Vec3d v = tetMesh->getVertex(i);
+    restVerticesPosition[3*i+0] = v[0];
+    restVerticesPosition[3*i+1] = v[1];
+    restVerticesPosition[3*i+2] = v[2];
   }
 
   ComputeTetVolumes();
@@ -108,7 +114,7 @@ IsotropicHyperelasticFEM::IsotropicHyperelasticFEM(TetMesh * tetMesh_, Isotropic
   // DS = D_s
   // dDSdU = d D_s / d U
   // set dDSdU here, it's a constant matrix (does not change during the simulation)
-  memset(dDSdU, 0.0, sizeof(double) * 108);
+  memset(dDSdU, 0, sizeof(double) * 108);
   dDSdU[tensor9x12Index(0,0,0,0)] = -1.0;
   dDSdU[tensor9x12Index(1,0,0,1)] = -1.0;
   dDSdU[tensor9x12Index(2,0,0,2)] = -1.0;
@@ -172,22 +178,19 @@ IsotropicHyperelasticFEM::~IsotropicHyperelasticFEM()
 /*
   Compute the elastic strain energy given the current vertex displacements u
 */
-double IsotropicHyperelasticFEM::ComputeEnergy(double * u)
+double IsotropicHyperelasticFEM::ComputeEnergy(const double * u)
 {
-  int computationMode = COMPUTE_ENERGY;
-  double energy;
-  GetEnergyAndForceAndTangentStiffnessMatrixHelper(u, &energy, NULL, NULL, computationMode);
+  double energy = 0.0;
+  GetEnergyAndForceAndTangentStiffnessMatrixHelper(u, &energy, NULL, NULL);
   return energy;
 }
 
 /*
   Compute the internal forces given the current vertex displacements u
 */
-void IsotropicHyperelasticFEM::ComputeForces(double * u, double * internalForces)
+void IsotropicHyperelasticFEM::ComputeForces(const double * u, double * internalForces)
 {
-  //printf("Entering IsotropicHyperelasticFEM::ComputeForces\n"); 
-  int computationMode = IsotropicHyperelasticFEM::COMPUTE_INTERNALFORCES;
-  GetEnergyAndForceAndTangentStiffnessMatrixHelper(u, NULL, internalForces, NULL, computationMode);
+  GetEnergyAndForceAndTangentStiffnessMatrixHelper(u, NULL, internalForces, NULL);
 }
 
 /*
@@ -231,87 +234,128 @@ void IsotropicHyperelasticFEM::GetStiffnessMatrixTopology(SparseMatrix ** tangen
 /*
   Get the tangent stiffness matrix given the current vertex displacement u
 */
-void IsotropicHyperelasticFEM::GetTangentStiffnessMatrix(double * u, SparseMatrix * tangentStiffnessMatrix)
+void IsotropicHyperelasticFEM::GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix)
 {
-  int computationMode = COMPUTE_TANGENTSTIFFNESSMATRIX;
-  GetEnergyAndForceAndTangentStiffnessMatrixHelper(u, NULL, NULL, tangentStiffnessMatrix, computationMode);
+  GetEnergyAndForceAndTangentStiffnessMatrixHelper(u, NULL, NULL, tangentStiffnessMatrix);
 }
 
 /*
   Get both internal forces and stiffness matrix, given the current vertex displacement u.
   Note: this is economic, because we have to compute the deformation gradients and do SVD on them only once.
 */
-void IsotropicHyperelasticFEM::GetForceAndTangentStiffnessMatrix(double * u, double * internalForce, SparseMatrix * tangentStiffnessMatrix)
+void IsotropicHyperelasticFEM::GetForceAndTangentStiffnessMatrix(const double * u, double * internalForce, SparseMatrix * tangentStiffnessMatrix)
 {
-  int computationMode = COMPUTE_INTERNALFORCES | COMPUTE_TANGENTSTIFFNESSMATRIX;
-  GetEnergyAndForceAndTangentStiffnessMatrixHelper(u, NULL, internalForce, tangentStiffnessMatrix, computationMode);
+  GetEnergyAndForceAndTangentStiffnessMatrixHelper(u, NULL, internalForce, tangentStiffnessMatrix);
+}
+
+void IsotropicHyperelasticFEM::ComputeTetVolume(int el)
+{
+  tetVolumes[el] = TetMesh::getTetVolume(
+    tetMesh->getVertex(el, 0), tetMesh->getVertex(el, 1), 
+    tetMesh->getVertex(el, 2), tetMesh->getVertex(el, 3)
+  );
+  //tetVolumes[el] = TetMesh::getTetVolume(&restVerticesPosition[3*tetMesh->getVertexIndex(el, 0)], &restVerticesPosition[3*tetMesh->getVertexIndex(el, 1)], 
+    //                                     &restVerticesPosition[3*tetMesh->getVertexIndex(el, 2)], &restVerticesPosition[3*tetMesh->getVertexIndex(el, 3)]);
 }
 
 void IsotropicHyperelasticFEM::ComputeTetVolumes()
 {
   int numElements = tetMesh->getNumElements();
   for (int el=0; el<numElements; el++)
-    tetVolumes[el] = TetMesh::getTetVolume(tetMesh->getVertex(el, 0), tetMesh->getVertex(el, 1), tetMesh->getVertex(el, 2), tetMesh->getVertex(el, 3));
+    ComputeTetVolume(el);
 }
 
 /*
   Compute the area-weighted vertex normals.
   See p3 section 4 of [Irving 04] for more details.
 */
-void IsotropicHyperelasticFEM::ComputeAreaWeightedVertexNormals()
+void IsotropicHyperelasticFEM::ComputeAreaWeightedVertexNormals(int el)
 {
-  int numElements = tetMesh->getNumElements();
-  for (int el=0; el<numElements; el++)
+  Vec3d va = tetMesh->getVertex(el, 0);
+  Vec3d vb = tetMesh->getVertex(el, 1);
+  Vec3d vc = tetMesh->getVertex(el, 2);
+  Vec3d vd = tetMesh->getVertex(el, 3);
+
+  // compute normals for the four faces: acb, adc, abd, bcd
+  Vec3d acbNormal = cross(vc-va, vb-va); 
+  Vec3d adcNormal = cross(vd-va, vc-va); 
+  Vec3d abdNormal = cross(vb-va, vd-va); 
+  Vec3d bcdNormal = cross(vc-vb, vd-vb); 
+
+  // if the tet vertices abcd form a positive orientation, the normals are now correct
+  // otherwise, we need to flip them
+  double orientation = dot(vd-va, cross(vb-va, vc-va));
+  if (orientation < 0)
   {
-    Vec3d * va = tetMesh->getVertex(el, 0);
-    Vec3d * vb = tetMesh->getVertex(el, 1);
-    Vec3d * vc = tetMesh->getVertex(el, 2);
-    Vec3d * vd = tetMesh->getVertex(el, 3);
-
-    // compute normals for the four faces: acb, adc, abd, bcd
-    Vec3d acbNormal = cross(*vc-*va, *vb-*va); 
-    Vec3d adcNormal = cross(*vd-*va, *vc-*va); 
-    Vec3d abdNormal = cross(*vb-*va, *vd-*va); 
-    Vec3d bcdNormal = cross(*vc-*vb, *vd-*vb); 
-
-    // if the tet vertices abcd form a positive orientation, the normals are now correct
-    // otherwise, we need to flip them
-    double orientation = dot(*vd-*va, cross(*vb-*va, *vc-*va));
-    if (orientation < 0)
-    {
-      acbNormal *= -1.0;
-      adcNormal *= -1.0;
-      abdNormal *= -1.0;
-      bcdNormal *= -1.0;
-    }
+    acbNormal *= -1.0;
+    adcNormal *= -1.0;
+    abdNormal *= -1.0;
+    bcdNormal *= -1.0;
+  }
 
-    // triangle area = 0.5 |u x v|
-    double acbArea = 0.5 * sqrt(dot(acbNormal, acbNormal));
-    double adcArea = 0.5 * sqrt(dot(adcNormal, adcNormal));
-    double abdArea = 0.5 * sqrt(dot(abdNormal, abdNormal));
-    double bcdArea = 0.5 * sqrt(dot(bcdNormal, bcdNormal));
+  // triangle area = 0.5 |u x v|
+  double acbArea = 0.5 * sqrt(dot(acbNormal, acbNormal));
+  double adcArea = 0.5 * sqrt(dot(adcNormal, adcNormal));
+  double abdArea = 0.5 * sqrt(dot(abdNormal, abdNormal));
+  double bcdArea = 0.5 * sqrt(dot(bcdNormal, bcdNormal));
 
-    // normalize
-    acbNormal.normalize();
-    adcNormal.normalize();
-    abdNormal.normalize();
-    bcdNormal.normalize();
+  // normalize
+  acbNormal.normalize();
+  adcNormal.normalize();
+  abdNormal.normalize();
+  bcdNormal.normalize();
 
-    areaWeightedVertexNormals[4*el+0] = (acbArea * acbNormal + adcArea * adcNormal + abdArea * abdNormal) / 3.0;
-    areaWeightedVertexNormals[4*el+1] = (acbArea * acbNormal + abdArea * abdNormal + bcdArea * bcdNormal) / 3.0;
-    areaWeightedVertexNormals[4*el+2] = (acbArea * acbNormal + adcArea * adcNormal + bcdArea * bcdNormal) / 3.0;
-    areaWeightedVertexNormals[4*el+3] = (adcArea * adcNormal + abdArea * abdNormal + bcdArea * bcdNormal) / 3.0;
+  areaWeightedVertexNormals[4*el+0] = (acbArea * acbNormal + adcArea * adcNormal + abdArea * abdNormal) / 3.0;
+  areaWeightedVertexNormals[4*el+1] = (acbArea * acbNormal + abdArea * abdNormal + bcdArea * bcdNormal) / 3.0;
+  areaWeightedVertexNormals[4*el+2] = (acbArea * acbNormal + adcArea * adcNormal + bcdArea * bcdNormal) / 3.0;
+  areaWeightedVertexNormals[4*el+3] = (adcArea * adcNormal + abdArea * abdNormal + bcdArea * bcdNormal) / 3.0;
 
-    /*
-      printf("--- areaWeightedVertexNormals ---\n");
-      printf("a = "); areaWeightedVertexNormals[4*el+0].print();
-      printf("b = "); areaWeightedVertexNormals[4*el+1].print();
-      printf("c = "); areaWeightedVertexNormals[4*el+2].print();
-      printf("d = "); areaWeightedVertexNormals[4*el+3].print();
-    */
+  /*
+    printf("--- areaWeightedVertexNormals ---\n");
+    printf("a = "); areaWeightedVertexNormals[4*el+0].print();
+    printf("b = "); areaWeightedVertexNormals[4*el+1].print();
+    printf("c = "); areaWeightedVertexNormals[4*el+2].print();
+    printf("d = "); areaWeightedVertexNormals[4*el+3].print();
+  */
+}
+
+/*
+  Compute the area-weighted vertex normals.
+  See p3 section 4 of [Irving 04] for more details.
+*/
+void IsotropicHyperelasticFEM::ComputeAreaWeightedVertexNormals()
+{
+  int numElements = tetMesh->getNumElements();
+  for (int el=0; el<numElements; el++)
+  {
+    ComputeAreaWeightedVertexNormals(el);
   }
 }
 
+/*
+  Compute the inverse of the Dm matrices.
+  The Dm is a 3x3 matrix where the columns are the edge vectors of a
+  tet in rest configuration. See p3 section 3 of [Irving 04] for more details.
+ */
+void IsotropicHyperelasticFEM::PrepareDeformGrad(int el)
+{
+  Vec3d va = tetMesh->getVertex(el, 0);
+  Vec3d vb = tetMesh->getVertex(el, 1);
+  Vec3d vc = tetMesh->getVertex(el, 2);
+  Vec3d vd = tetMesh->getVertex(el, 3);
+
+  Vec3d dm1 = vd - va;
+  Vec3d dm2 = vd - vb;
+  Vec3d dm3 = vd - vc;
+
+  Mat3d tmp(dm1[0], dm2[0], dm3[0], dm1[1], dm2[1], dm3[1], dm1[2], dm2[2], dm3[2]);
+  //printf("--- dm ---\n");
+  //tmp.print();
+  dmInverses[el] = inv(tmp);
+  //printf("--- inv(dm) ---\n");
+  //dmInverses[e].print();
+}
+
 /*
   Compute the inverse of the Dm matrices.
   The Dm is a 3x3 matrix where the columns are the edge vectors of a
@@ -322,21 +366,7 @@ void IsotropicHyperelasticFEM::PrepareDeformGrad()
   int numElements = tetMesh->getNumElements();
   for (int el=0; el<numElements; el++)
   {
-    Vec3d * va = tetMesh->getVertex(el, 0);
-    Vec3d * vb = tetMesh->getVertex(el, 1);
-    Vec3d * vc = tetMesh->getVertex(el, 2);
-    Vec3d * vd = tetMesh->getVertex(el, 3);
-
-    Vec3d dm1 = *vd - *va;
-    Vec3d dm2 = *vd - *vb;
-    Vec3d dm3 = *vd - *vc;
-
-    Mat3d tmp(dm1[0], dm2[0], dm3[0], dm1[1], dm2[1], dm3[1], dm1[2], dm2[2], dm3[2]);
-    //printf("--- dm ---\n");
-    //tmp.print();
-    dmInverses[el] = inv(tmp);
-    //printf("--- inv(dm) ---\n");
-    //dmInverses[e].print();
+    PrepareDeformGrad(el);
   }
 }
 
@@ -344,15 +374,15 @@ void IsotropicHyperelasticFEM::PrepareDeformGrad()
   This function computes the energy, internal forces, and/or tangent stiffness matrix, as requested by the computationMode.
   It is declared virtual and is overloaded in the multicore (MT) derived class.
 */
-int IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelper(double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix, int computationMode)
+int IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelper(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
 {
-  GetEnergyAndForceAndTangentStiffnessMatrixHelperPrologue(u, energy, internalForces, tangentStiffnessMatrix, computationMode); // resets the energy, internal forces and/or tangent stiffness matrix to zero
-  int code = GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse(0, tetMesh->getNumElements(), u, energy, internalForces, tangentStiffnessMatrix, computationMode);
+  GetEnergyAndForceAndTangentStiffnessMatrixHelperPrologue(u, energy, internalForces, tangentStiffnessMatrix); // resets the energy, internal forces and/or tangent stiffness matrix to zero
+  int code = GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse(0, tetMesh->getNumElements(), u, energy, internalForces, tangentStiffnessMatrix);
   return code;
 }
 
 // initializes the energy, internal forces, and/or stiffness matrix
-void IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperPrologue(double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix, int computationMode)
+void IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperPrologue(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
 {
   // compute the current deformed positions
   int numVertices = tetMesh->getNumVertices();
@@ -360,10 +390,10 @@ void IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperP
   for (int i=0; i<numVertices3; i++)
     currentVerticesPosition[i] = restVerticesPosition[i] + u[i];
 
-  if (computationMode & COMPUTE_ENERGY)
+  if (energy)
     *energy = 0.0;
 
-  if (computationMode & COMPUTE_INTERNALFORCES)
+  if (internalForces)
   {
     // reset internal forces
     if (addGravity)
@@ -371,24 +401,153 @@ void IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperP
       for (int i=0; i<numVertices; i++)
       {
         internalForces[3*i+0] = 0.0;
-        internalForces[3*i+1] = -g;
+        internalForces[3*i+1] = g; // gravity acts in negative-y direction; internal forces are opposite of external forces
         internalForces[3*i+2] = 0.0;
       }
     }
     else
     {
       // zero out the forces
-      memset(internalForces, 0.0, sizeof(double) * numVertices3);
+      memset(internalForces, 0, sizeof(double) * numVertices3);
     }
   }
 
-  if (computationMode & COMPUTE_TANGENTSTIFFNESSMATRIX)
+  if (tangentStiffnessMatrix)
   {
     // reset stiffness matrix
     tangentStiffnessMatrix->ResetToZero();
   }
 }
 
+void IsotropicHyperelasticFEM::GetElementLocalEnergyAndForceAndMatrix(int el, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix)
+{
+  // since ComputeElementLocalData assumes current positions are updated, we should update current positions here
+  for(int i = 0; i < 4; i++)
+  {
+    int index = 3 * tetMesh->getVertexIndex(el, i);
+    Vec3d pos = Vec3d(&restVerticesPosition[index]) + Vec3d(&u[index]);
+    pos.convertToArray(&currentVerticesPosition[index]);
+  }
+  ComputeElementLocalData(el, u, energy, internalForces, tangentStiffnessMatrix);
+}
+
+int IsotropicHyperelasticFEM::ComputeElementLocalData(int el, const double * u, double * energy, double internalForces[12], double tangentStiffnessMatrix[144])
+{
+  int exitCode = 0;
+  //  Compute the deformation gradient F.
+  //  F = Ds * inv(Dm), where Ds is a 3x3 matrix where
+  //  the columns are edge vectors of a tet in the current deformation,
+  //  and Dm is a 3x3 matrix where the columns are edge vectors of a tet in
+  //  the rest configuration. See p3 section 3 of [Irving 04] for more details.
+  int vaIndex = 3 * tetMesh->getVertexIndex(el, 0);
+  int vbIndex = 3 * tetMesh->getVertexIndex(el, 1);
+  int vcIndex = 3 * tetMesh->getVertexIndex(el, 2);
+  int vdIndex = 3 * tetMesh->getVertexIndex(el, 3);
+
+  Vec3d va(currentVerticesPosition[vaIndex], currentVerticesPosition[vaIndex+1], currentVerticesPosition[vaIndex+2]);
+  Vec3d vb(currentVerticesPosition[vbIndex], currentVerticesPosition[vbIndex+1], currentVerticesPosition[vbIndex+2]);
+  Vec3d vc(currentVerticesPosition[vcIndex], currentVerticesPosition[vcIndex+1], currentVerticesPosition[vcIndex+2]);
+  Vec3d vd(currentVerticesPosition[vdIndex], currentVerticesPosition[vdIndex+1], currentVerticesPosition[vdIndex+2]);
+
+  Vec3d ds1 = vd - va;
+  Vec3d ds2 = vd - vb;
+  Vec3d ds3 = vd - vc;
+  
+  Mat3d tmp(ds1[0], ds2[0], ds3[0], ds1[1], ds2[1], ds3[1], ds1[2], ds2[2], ds3[2]);
+  Fs[el] = tmp * dmInverses[el];
+
+  /*
+    The deformation gradient has now been computed and is available in Fs[el]
+  */
+
+  // perform modified SVD on the deformation gradient
+  Mat3d & F = Fs[el];
+  Mat3d & U = Us[el];
+  Mat3d & V = Vs[el];
+  Vec3d & Fhat = Fhats[el];
+  int modifiedSVD = 1;
+  if (SVD(F, U, Fhat, V, SVD_singularValue_eps, modifiedSVD) != 0)
+  {
+    printf("error in diagonalization, el=%d\n", el);
+    exitCode = 1;
+  }
+
+  /*
+    SVD for the deformation gradient has now been computed.
+    It is available in Us[el], Fhats[el], Vs[el].
+  */
+
+  // clamp fHat if below the principal stretch threshold
+  double fHat[3];
+  int clamped = 0;
+  for(int i = 0; i < 3; i++)
+  {
+    if(Fhats[el][i] < inversionThreshold)
+    {
+      //dropBelowThreshold = true;
+      Fhats[el][i] = inversionThreshold;
+      clamped |= (1 << i);
+    }
+  }
+  fHat[0] = Fhats[el][0];
+  fHat[1] = Fhats[el][1];
+  fHat[2] = Fhats[el][2];
+  clamped = 0; // disable clamping
+
+  // query the user-provided isotropic material to compute the strain energy
+  if (energy)
+    *energy = tetVolumes[el] * ComputeEnergyFromStretches(el, fHat);
+
+  if (internalForces)
+  {
+    /*
+      --- Now compute the internal forces ---
+  
+      The first Piola-Kirchhoff stress P is calculated by equation 1 
+      in p3 section 5 of [Irving 04]. Once we have P, we can compute
+      the nodal forces G=PBm as described in section 4 of [Irving 04]
+    */
+
+    double pHat[3];
+    ComputeDiagonalPFromStretches(el, fHat, pHat); // calls the isotropic material to compute the diagonal P tensor, given the principal stretches in fHat
+    Vec3d pHatv(pHat);
+
+    // This is the 1st equation in p3 section 5 of [Irving 04]
+    // P = Us[el] * diag(pHat) * trans(Vs[el])
+    Mat3d P = Us[el];
+    P.multiplyDiagRight(pHatv);
+    P = P * trans(Vs[el]);
+
+    
+    //  we compute the nodal forces by G=PBm as described in 
+    //  section 4 of [Irving 04]
+    // multiply by 4 because each tet has 4 vertices
+    Vec3d forceUpdateA = P * areaWeightedVertexNormals[4 * el + 0];
+    Vec3d forceUpdateB = P * areaWeightedVertexNormals[4 * el + 1];
+    Vec3d forceUpdateC = P * areaWeightedVertexNormals[4 * el + 2];
+    Vec3d forceUpdateD = P * areaWeightedVertexNormals[4 * el + 3];
+    forceUpdateA.convertToArray(&internalForces[0]);
+    forceUpdateB.convertToArray(&internalForces[3]);
+    forceUpdateC.convertToArray(&internalForces[6]);
+    forceUpdateD.convertToArray(&internalForces[9]);
+  }
+
+  if (tangentStiffnessMatrix)
+  {
+    /*
+      --- Now compute the tangent stiffness matrix ---
+
+      This implementation is based on section 6 & 7 of [Teran 05].
+      We go through each tet in the mesh and compute the element
+      stiffness matrix K, and then we put the entries of K into
+      the correct position of the final stiffness matrix (i.e.,
+      the stiffness matrix for the entire mesh)
+     */
+    ComputeTetK(el, tangentStiffnessMatrix, clamped);
+  }
+
+  return exitCode;
+}
 /*
   This is the workhorse of the IFEM class. It computes strain energy,
   internal forces, and/or the tangent stiffness matrix for a subset of the elements, startEl <= el < endEl
@@ -399,156 +558,53 @@ void IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperP
   and the stiffness matrix computation is based on 
   section 6 & 7 of [Teran 05].
 */
-int IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse(int startEl, int endEl, double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix, int computationMode)
+int IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse(int startEl, int endEl, const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
 {
   //printf("Entering IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse\n"); 
   //printf("inversionThreshold=%G\n", inversionThreshold);
 
   int numElementVertices = tetMesh->getNumElementVertices();
-  double energyResult = 0.0;
   //bool dropBelowThreshold = false; // becomes true when a principal stretch falls below the threshold; only used for printing out informative comments
   
   // traverse the elements and assemble strain energy, internal forces and tangent stiffness matrix
   int exitCode = 0;
+  double fElement[12];
+  double KElement[144];
+  double eleEnergy = 0.0;
   for (int el=startEl; el<endEl; el++)
   {
-    /*
-      Compute the deformation gradient F.
-      F = Ds * inv(Dm), where Ds is a 3x3 matrix where
-      the columns are edge vectors of a tet in the current deformation,
-      and Dm is a 3x3 matrix where the columns are edge vectors of a tet in
-      the rest configuration. See p3 section 3 of [Irving 04] for more details.
-     */
-    int vaIndex = 3 * tetMesh->getVertexIndex(el, 0);
-    int vbIndex = 3 * tetMesh->getVertexIndex(el, 1);
-    int vcIndex = 3 * tetMesh->getVertexIndex(el, 2);
-    int vdIndex = 3 * tetMesh->getVertexIndex(el, 3);
-
-    Vec3d va(currentVerticesPosition[vaIndex], currentVerticesPosition[vaIndex+1], currentVerticesPosition[vaIndex+2]);
-    Vec3d vb(currentVerticesPosition[vbIndex], currentVerticesPosition[vbIndex+1], currentVerticesPosition[vbIndex+2]);
-    Vec3d vc(currentVerticesPosition[vcIndex], currentVerticesPosition[vcIndex+1], currentVerticesPosition[vcIndex+2]);
-    Vec3d vd(currentVerticesPosition[vdIndex], currentVerticesPosition[vdIndex+1], currentVerticesPosition[vdIndex+2]);
+    exitCode |= ComputeElementLocalData(el, u, (energy ? &eleEnergy : NULL), (internalForces ? fElement : NULL), (tangentStiffnessMatrix ? KElement : NULL));
 
-    Vec3d ds1 = vd - va;
-    Vec3d ds2 = vd - vb;
-    Vec3d ds3 = vd - vc;
-    
-    Mat3d tmp(ds1[0], ds2[0], ds3[0], ds1[1], ds2[1], ds3[1], ds1[2], ds2[2], ds3[2]);
-    Fs[el] = tmp * dmInverses[el];
-    //printf("F =\n");
-    //Fs[el].print();
+    if (energy)
+      *energy += eleEnergy;
 
-    /*
-      The deformation gradient has now been computed and is available in Fs[el]
-    */
-
-    // perform modified SVD on the deformation gradient
-    Mat3d & F = Fs[el];
-    Mat3d & U = Us[el];
-    Mat3d & V = Vs[el];
-    Vec3d & Fhat = Fhats[el];
-    if (ModifiedSVD(F, U, Fhat, V) != 0)
+    if (internalForces)
     {
-      printf("error in diagonalization, el=%d\n", el);
-      exitCode = 1;
-    }
-
-    /*
-      SVD for the deformation gradient has now been computed.
-      It is available in Us[el], Fhats[el], Vs[el].
-    */
-
-    // clamp fHat if below the principal stretch threshold
-    double fHat[3];
-    int clamped = 0;
-    for(int i = 0; i < 3; i++)
-    {
-      if(Fhats[el][i] < inversionThreshold)
-      {
-        //dropBelowThreshold = true;
-        Fhats[el][i] = inversionThreshold;
-        clamped |= (1 << i);
-      }
-    }
-    fHat[0] = Fhats[el][0];
-    fHat[1] = Fhats[el][1];
-    fHat[2] = Fhats[el][2];
-    clamped = 0; // disable clamping
-
-    // query the user-provided isotropic material to compute the strain energy
-    if (computationMode & COMPUTE_ENERGY)
-      energyResult += tetVolumes[el] * ComputeEnergyFromStretches(el, fHat);
-
-    if (computationMode & COMPUTE_INTERNALFORCES)
-    {
-      /*
-        --- Now compute the internal forces ---
-    
-        The first Piola-Kirchhoff stress P is calculated by equation 1 
-        in p3 section 5 of [Irving 04]. Once we have P, we can compute
-        the nodal forces G=PBm as described in section 4 of [Irving 04]
-      */
-
-      double pHat[3];
-      ComputeDiagonalPFromStretches(el, fHat, pHat); // calls the isotropic material to compute the diagonal P tensor, given the principal stretches in fHat
-      Vec3d pHatv(pHat);
-  
-      // This is the 1st equation in p3 section 5 of [Irving 04]
-      // P = Us[el] * diag(pHat) * trans(Vs[el])
-      Mat3d P = Us[el];
-      P.multiplyDiagRight(pHatv);
-      P = P * trans(Vs[el]);
-
-      //printf("--- P ---\n");
-      //P.print();
-
-      /*
-        we compute the nodal forces by G=PBm as described in 
-        section 4 of [Irving 04]
-      */
-      // multiply by 4 because each tet has 4 vertices
-      Vec3d forceUpdateA = P * areaWeightedVertexNormals[4 * el + 0];
-      Vec3d forceUpdateB = P * areaWeightedVertexNormals[4 * el + 1];
-      Vec3d forceUpdateC = P * areaWeightedVertexNormals[4 * el + 2];
-      Vec3d forceUpdateD = P * areaWeightedVertexNormals[4 * el + 3];
       // multiply by 3 because each force (at vertex) has 3 components
       int vIndexA = 3 * tetMesh->getVertexIndex(el,0);
       int vIndexB = 3 * tetMesh->getVertexIndex(el,1);
       int vIndexC = 3 * tetMesh->getVertexIndex(el,2);
       int vIndexD = 3 * tetMesh->getVertexIndex(el,3);
 
-      internalForces[vIndexA+0] += forceUpdateA[0];
-      internalForces[vIndexA+1] += forceUpdateA[1];
-      internalForces[vIndexA+2] += forceUpdateA[2];
+      internalForces[vIndexA+0] += fElement[0];
+      internalForces[vIndexA+1] += fElement[1];
+      internalForces[vIndexA+2] += fElement[2];
       
-      internalForces[vIndexB+0] += forceUpdateB[0];
-      internalForces[vIndexB+1] += forceUpdateB[1];
-      internalForces[vIndexB+2] += forceUpdateB[2];
+      internalForces[vIndexB+0] += fElement[3];
+      internalForces[vIndexB+1] += fElement[4];
+      internalForces[vIndexB+2] += fElement[5];
       
-      internalForces[vIndexC+0] += forceUpdateC[0];
-      internalForces[vIndexC+1] += forceUpdateC[1];
-      internalForces[vIndexC+2] += forceUpdateC[2];
+      internalForces[vIndexC+0] += fElement[6];
+      internalForces[vIndexC+1] += fElement[7];
+      internalForces[vIndexC+2] += fElement[8];
       
-      internalForces[vIndexD+0] += forceUpdateD[0];
-      internalForces[vIndexD+1] += forceUpdateD[1];
-      internalForces[vIndexD+2] += forceUpdateD[2];
+      internalForces[vIndexD+0] += fElement[9];
+      internalForces[vIndexD+1] += fElement[10];
+      internalForces[vIndexD+2] += fElement[11];
     }
 
-    if (computationMode & COMPUTE_TANGENTSTIFFNESSMATRIX)
+    if (tangentStiffnessMatrix)
     {
-      /*
-        --- Now compute the tangent stiffness matrix ---
-
-        This implementation is based on section 6 & 7 of [Teran 05].
-        We go through each tet in the mesh and compute the element
-        stiffness matrix K, and then we put the entries of K into
-        the correct position of the final stiffness matrix (i.e.,
-        the stiffness matrix for the entire mesh)
-       */
-
-      double K[144];
-      ComputeTetK(el, K, clamped);
-
       // write matrices in place
       for(int vtxIndexA=0; vtxIndexA<4; vtxIndexA++)
         for(int vtxIndexB=0; vtxIndexB<4; vtxIndexB++)
@@ -563,20 +619,12 @@ int IsotropicHyperelasticFEM::GetEnergyAndForceAndTangentStiffnessMatrixHelperWo
             {
               int row = 3 * vtxA + i;
               int columnIndex = 3 * columnIndexCompressed + j;
-              double * value = &K[ELT(12, 3*vtxIndexA+i, 3*vtxIndexB+j)];
-              
+              double * value = &KElement[ELT(12, 3*vtxIndexA+i, 3*vtxIndexB+j)];
               tangentStiffnessMatrix->AddEntry(row, columnIndex, *value);
             }
         }
     }
   }
-
-  //if (dropBelowThreshold)
-  //  printf("Principal stretch dropped below %f\n", inversionThreshold);
-
-  if (computationMode & COMPUTE_ENERGY)
-    *energy = energyResult;
-
   return exitCode;
 }
 
@@ -629,7 +677,7 @@ void IsotropicHyperelasticFEM::Compute_dGdF(Vec3d * b0, Vec3d * b1, Vec3d * b2,
                                             double dPdF[81], double dGdF[81])
 {
   //Both G and F are 3x3 matrices, so dGdF has 81 entries
-    memset(dGdF, 0.0, sizeof(double) * 81);
+    memset(dGdF, 0, sizeof(double) * 81);
 
   /*
            | ga_x gb_x gc_x |   | 0 1 2 |
@@ -957,8 +1005,16 @@ void IsotropicHyperelasticFEM::Compute_dPdF(int el, double dPdF[81], int clamped
   x3113 = beta13;
   x3223 = beta23;
 
+  if (enforceSPD)
+  {
+    FixPositiveIndefiniteness(x1111, x2211, x3311, x2222, x3322, x3333);
+    FixPositiveIndefiniteness(x2121, x2112);
+    FixPositiveIndefiniteness(x3131, x3113);
+    FixPositiveIndefiniteness(x3232, x3223);
+  }
+
   double dPdF_atFhat[81];
-  memset(dPdF_atFhat, 0.0, sizeof(double) * 81);
+  memset(dPdF_atFhat, 0, sizeof(double) * 81);
   dPdF_atFhat[tensor9x9Index(0,0,0,0)] = x1111;
   dPdF_atFhat[tensor9x9Index(0,0,1,1)] = x2211;
   dPdF_atFhat[tensor9x9Index(0,0,2,2)] = x3311;
@@ -1013,8 +1069,8 @@ void IsotropicHyperelasticFEM::Compute_dPdF(int el, double dPdF[81], int clamped
   */
 
   double eiejVector[9];
-  memset(eiejVector, 0.0, sizeof(double) * 9);
-  memset(dPdF, 0.0, sizeof(double) * 81);
+  memset(eiejVector, 0, sizeof(double) * 9);
+  memset(dPdF, 0, sizeof(double) * 81);
   for (int column=0; column<9; column++)
   {
     eiejVector[column] = 1.0;
@@ -1080,6 +1136,62 @@ int IsotropicHyperelasticFEM::tensor9x9Index(int i, int j, int m, int n)
   return (9 * rowIndex_in9x9Matrix + columnIndex_in9x9Matrix);
 }
 
+void IsotropicHyperelasticFEM::FixPositiveIndefiniteness(double & B11, double & B12)
+{
+  Vec2d eigenValues(B11 - B12, B11 + B12);
+
+  bool hasNegativeEigenValues = false;
+  for(int i = 0; i < 2; i++)
+  {
+    if (eigenValues[i] < 0)
+      hasNegativeEigenValues = true;
+  }
+
+  if (hasNegativeEigenValues)
+  {
+    if (eigenValues[0] < 0)
+      eigenValues[0] = 0;
+
+    if (eigenValues[1] < 0)
+      eigenValues[1] = 0;
+
+    B11 = 0.5 * ( eigenValues[0] + eigenValues[1]);
+    B12 = 0.5 * (-eigenValues[0] + eigenValues[1]);
+  }
+}
+
+void IsotropicHyperelasticFEM::FixPositiveIndefiniteness(double & A11, double & A12, double & A13, double & A22, double & A23, double & A33)
+{
+  Mat3d mat(A11, A12, A13, A12, A22, A23, A13, A23, A33);
+  Vec3d eigenValues;
+  Vec3d eigenVectors[3];
+  eigen_sym(mat, eigenValues, eigenVectors);
+  bool hasNegativeEigenValues = false;
+  for(int i = 0; i < 3; i++)
+  {
+    if (eigenValues[i] < 0)
+    {
+      hasNegativeEigenValues = true;
+      eigenValues[i] = 0;
+    }
+  }
+
+  if (hasNegativeEigenValues)
+  {
+    Mat3d V(eigenVectors[0][0], eigenVectors[1][0], eigenVectors[2][0],
+            eigenVectors[0][1], eigenVectors[1][1], eigenVectors[2][1],
+            eigenVectors[0][2], eigenVectors[1][2], eigenVectors[2][2]);
+
+    Mat3d newMat = (V.multiplyDiagRight(eigenValues)) * trans(V);
+    A11 = newMat[0][0];
+    A22 = newMat[1][1];
+    A33 = newMat[2][2];
+    A12 = newMat[0][1];
+    A13 = newMat[0][2];
+    A23 = newMat[1][2];
+  }
+}
+
 /*
   Compute damping forces based on the vertex velocities.
   See p6 section 6.2 in [Irving 04] for details.
@@ -1139,243 +1251,5 @@ void IsotropicHyperelasticFEM::ComputeDampingForces(double dampingPsi, double da
   }
 }
 
-/*
-  Given a deformation gradient F, decompose it using SVD so
-  that F = U F^hat V^T where U and V are rotational matrices
-  and F^hat is a diagonal matrix.
-
-  The SVD we are using is a special one such that it gives the 
-  following properties:
-
-  1) The determinant of both U and V are 1 (i.e., they are 
-     truly rotation, not reflection). This is required for
-     the method to work.
-
-     Note that standard SVD may generate U (or V) with 
-     determinant equals to -1 (i.e., reflection).
-
-  2) The diagonal values (a.k.a. singular values, or principal 
-     stretches) of F^hat are in descending order and the values 
-     can be negative.
-
-     Note that the singular values resulting from standard SVD 
-     are non-negative.
-
-  This implementation follows section 5 of [Irving 04].
-  It computes F^T F, and computes its eigenvectors, F^T F = V Fhat^2 V^T . 
-  Fhat is then recovered using sqrt from Fhat^2.
-  To recover U, compute U = F * V * diag(Fhat^{-1}).
-  Care must be taken when singular values of Fhat are small (handled in the code below).
-*/
-
-#define modifiedSVD_singularValue_eps 1e-8
-
-int IsotropicHyperelasticFEM::ModifiedSVD(Mat3d & F, Mat3d & U, Vec3d & Fhat, Mat3d & V)
-{
-  // The code handles the following special situations:
-
-  //---------------------------------------------------------
-  // 1. det(V) == -1
-  //    - multiply the first column of V by -1
-  //---------------------------------------------------------
-  // 2. An entry of Fhat is near zero
-  //---------------------------------------------------------
-  // 3. Tet is inverted.
-  //    - check if det(U) == -1
-  //    - If yes, then negate the minimal element of Fhat
-  //      and the corresponding column of U
-  //---------------------------------------------------------
-
-  // form F^T F and do eigendecomposition
-  Mat3d normalEq = trans(F) * F;
-  Vec3d eigenValues;
-  Vec3d eigenVectors[3];
-
-  eigen_sym(normalEq, eigenValues, eigenVectors);
-
-  V.set(eigenVectors[0][0], eigenVectors[1][0], eigenVectors[2][0],
-    eigenVectors[0][1], eigenVectors[1][1], eigenVectors[2][1],
-    eigenVectors[0][2], eigenVectors[1][2], eigenVectors[2][2]);
-  /*
-    printf("--- original V ---\n");
-    V.print();
-    printf("--- eigenValues ---\n");
-    printf("%G %G %G\n", eigenValues[0], eigenValues[1], eigenValues[2]);
-  */
-
-  // Handle situation:
-  // 1. det(V) == -1
-  //    - multiply the first column of V by -1
-  if (det(V) < 0.0)
-  {
-    // convert V into a rotation (multiply column 1 by -1)
-    V[0][0] *= -1.0;
-    V[1][0] *= -1.0;
-    V[2][0] *= -1.0;
-  }
-
-  Fhat[0] = (eigenValues[0] > 0.0) ? sqrt(eigenValues[0]) : 0.0;
-  Fhat[1] = (eigenValues[1] > 0.0) ? sqrt(eigenValues[1]) : 0.0;
-  Fhat[2] = (eigenValues[2] > 0.0) ? sqrt(eigenValues[2]) : 0.0;
-
-  //printf("--- Fhat ---\n");
-  //printf("%G %G %G\n", Fhat[0][0], Fhat[1][1], Fhat[2][2]);
-
-  // compute inverse of singular values
-  // also check if singular values are close to zero
-  Vec3d FhatInverse;
-  FhatInverse[0] = (Fhat[0] > modifiedSVD_singularValue_eps) ? (1.0 / Fhat[0]) : 0.0;
-  FhatInverse[1] = (Fhat[1] > modifiedSVD_singularValue_eps) ? (1.0 / Fhat[1]) : 0.0;
-  FhatInverse[2] = (Fhat[2] > modifiedSVD_singularValue_eps) ? (1.0 / Fhat[2]) : 0.0;
-  
-  // compute U using the formula:
-  // U = F * V * diag(FhatInverse)
-  U = F * V;
-  U.multiplyDiagRight(FhatInverse);
-
-  // In theory, U is now orthonormal, U^T U = U U^T = I .. it may be a rotation or a reflection, depending on F.
-  // But in practice, if singular values are small or zero, it may not be orthonormal, so we need to fix it.
-  // Handle situation:
-  // 2. An entry of Fhat is near zero
-  // ---------------------------------------------------------
-
-  /*
-    printf("--- FhatInverse ---\n");
-    FhatInverse.print();
-    printf(" --- U ---\n");
-    U.print();
-  */
-  
-  if ((Fhat[0] < modifiedSVD_singularValue_eps) && (Fhat[1] < modifiedSVD_singularValue_eps) && (Fhat[2] < modifiedSVD_singularValue_eps))
-  {
-    // extreme case, all singular values are small, material has collapsed almost to a point
-    // see [Irving 04], p. 4
-    U.set(1.0, 0.0, 0.0,
-          0.0, 1.0, 0.0,
-          0.0, 0.0, 1.0);
-  }
-  else 
-  {
-    // handle the case where two singular values are small, but the third one is not
-    // handle it by computing two (arbitrary) vectors orthogonal to the eigenvector for the large singular value
-    int done = 0;
-    for(int dim=0; dim<3; dim++)
-    {
-      int dimA = dim;
-      int dimB = (dim + 1) % 3;
-      int dimC = (dim + 2) % 3;
-      if ((Fhat[dimB] < modifiedSVD_singularValue_eps) && (Fhat[dimC] < modifiedSVD_singularValue_eps))
-      {
-        // only the column dimA can be trusted, columns dimB and dimC correspond to tiny singular values
-        Vec3d tmpVec1(U[0][dimA], U[1][dimA], U[2][dimA]); // column dimA
-        Vec3d tmpVec2;
-        FindOrthonormalVector(tmpVec1, tmpVec2);
-        Vec3d tmpVec3 = norm(cross(tmpVec1, tmpVec2));
-        U[0][dimB] = tmpVec2[0];
-        U[1][dimB] = tmpVec2[1];
-        U[2][dimB] = tmpVec2[2];
-        U[0][dimC] = tmpVec3[0];
-        U[1][dimC] = tmpVec3[1];
-        U[2][dimC] = tmpVec3[2];
-        if (det(U) < 0.0)
-        {
-          U[0][dimB] *= -1.0;
-          U[1][dimB] *= -1.0;
-          U[2][dimB] *= -1.0;
-        }
-        done = 1;
-        break; // out of for
-      }
-    }
-
-    // handle the case where one singular value is small, but the other two are not
-    // handle it by computing the cross product of the two eigenvectors for the two large singular values
-    if (!done) 
-    {
-      for(int dim=0; dim<3; dim++)
-      {
-        int dimA = dim;
-        int dimB = (dim + 1) % 3;
-        int dimC = (dim + 2) % 3;
-
-        if (Fhat[dimA] < modifiedSVD_singularValue_eps)
-        {
-          // columns dimB and dimC are both good, but column dimA corresponds to a tiny singular value
-          Vec3d tmpVec1(U[0][dimB], U[1][dimB], U[2][dimB]); // column dimB
-          Vec3d tmpVec2(U[0][dimC], U[1][dimC], U[2][dimC]); // column dimC
-          Vec3d tmpVec3 = norm(cross(tmpVec1, tmpVec2));
-          U[0][dimA] = tmpVec3[0];
-          U[1][dimA] = tmpVec3[1];
-          U[2][dimA] = tmpVec3[2];
-          if (det(U) < 0.0)
-          {
-            U[0][dimA] *= -1.0;
-            U[1][dimA] *= -1.0;
-            U[2][dimA] *= -1.0;
-          }
-          done = 1;
-          break; // out of for
-        }
-      }
-    }
-
-    if (!done)
-    {
-      // Handle situation:
-      // 3. Tet is inverted.
-      //    - check if det(U) == -1
-      //    - If yes, then negate the minimal element of Fhat
-      //      and the corresponding column of U
-
-      double detU = det(U);
-      if (detU < 0.0)
-      {
-        // tet is inverted
-        // find the smallest singular value (they are all non-negative)
-        int smallestSingularValueIndex = 0;
-        for(int dim=1; dim<3; dim++)
-          if (Fhat[dim] < Fhat[smallestSingularValueIndex])
-            smallestSingularValueIndex = dim;
-
-        // negate the smallest singular value
-        Fhat[smallestSingularValueIndex] *= -1.0;
-        U[0][smallestSingularValueIndex] *= -1.0;
-        U[1][smallestSingularValueIndex] *= -1.0;
-        U[2][smallestSingularValueIndex] *= -1.0;
-      }
-    }
-  }
-
-  /*
-    printf("U = \n");
-    U.print();
-    printf("Fhat = \n");
-    Fhat.print();
-    printf("V = \n");
-    V.print();
-  */
-
-  return 0;
-}
-
 #undef modifiedSVD_singularValue_eps
 
-/*
-  Given an input vector v, find a unit vector that is orthogonal to it 
-*/
-void IsotropicHyperelasticFEM::FindOrthonormalVector(Vec3d & v, Vec3d & result)
-{
-  // find smallest abs component of v
-  int smallestIndex = 0;
-  for(int dim=1; dim<3; dim++)
-    if (fabs(v[dim]) < fabs(v[smallestIndex]))
-      smallestIndex = dim;
-
-  Vec3d axis(0.0, 0.0, 0.0);
-  axis[smallestIndex] = 1.0;
-
-  // this cross-product will be non-zero (as long as v is not zero)
-  result = norm(cross(v, axis));
-}
-
-}
diff --git a/libraries/isotropicHyperelasticFEM/isotropicHyperelasticFEM.h b/libraries/isotropicHyperelasticFEM/isotropicHyperelasticFEM.h
new file mode 100644
index 0000000000000000000000000000000000000000..17afa6ab5f6bcc4b82bebef1e73170924deddb49
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/isotropicHyperelasticFEM.h
@@ -0,0 +1,244 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _ISOTROPICHYPERELASTICFEM_H_
+#define _ISOTROPICHYPERELASTICFEM_H_
+
+#include <float.h>
+#include "tetMesh.h"
+#include "sparseMatrix.h"
+#include "isotropicMaterial.h"
+
+/*
+  Implementation of hyperelastic isotropic nonlinear FEM elasticity, using
+  linear tetrahedral elements. 
+
+  This code can compute strain energy, internal forces and tangent stiffness matric
+es (derivative of internal forces). It can also handle and restore from element inversion.
+
+  The tangent stiffness matrix is computed using the block-diagonal dP/dF tensor, as described in [Teran 2005].
+
+  Citations:
+
+  IRVING G., TERAN J., FEDKIW R.: Invertible Finite
+  Elements for Robust Simulation of Large Deformation. In Proc.  of the Symp. on Comp. Animation 2004 (2004), pp. 131–140.
+
+  TERAN J., SIFAKIS E., IRVING G., FEDKIW R.: Robust
+  Quasistatic Finite Elements and Flesh Simulation. In 2005
+  ACM SIGGRAPH / Eurographics Symp. on Computer Animation
+  (July 2005), pp. 181–190.
+
+*/
+
+class IsotropicHyperelasticFEM
+{
+public:
+  // Before creating this class, you must first create the tet mesh, and create an instance of the "IsotropicMaterial" material (e.g., NeoHookeanMaterial).
+  // 
+  // The inversionThreshold controls when the inversion prevention mechanism activates:
+  // If the principal stretches (the "lambdas") are smaller than the inversionThreshold, they will be clamped to that, which will generally prevent element inversion. For example, a typical inversionThreshold value would be 0.1. By default, inversion handling is disabled (inversionThreshold=-infinity). Values of 1.0 or higher should not be used.
+  //
+  // The material properties are determined as follows:
+  // If the "isotropicMaterial" is of the "Homogeneous" kind, then the material properties are homogeneous and are specified by "isotropicMaterial"; material properties in the "tetMesh" are ignored (only geometry is used)
+  // If the "isotropicMaterial" is not of the "Homogeneous" kind, then the material properties are such as specified in the "tetMesh" and "isotropicMaterial" class (and may be non-homogeneous)
+  IsotropicHyperelasticFEM(TetMesh * tetMesh, IsotropicMaterial * isotropicMaterial, double inversionThreshold=-DBL_MAX, bool addGravity=false, double g=9.81);
+  virtual ~IsotropicHyperelasticFEM();
+
+  double ComputeEnergy(const double * u); // get the nonlinear elastic strain energy
+
+  // get the nonlinear internal forces
+  // both vertex displacements "u" and internal forces refer to the vertices of the simulation mesh
+  // they must be (pre-allocated) vectors of length 3 * numVertices
+  // the internal forces are returned with the sign corresponding to f_int(x) on the left side of the equation M * x'' + f_int(x) = f_ext
+  // i.e., the computed internal forces are negatives of the actual physical internal forces acting on the material
+  void ComputeForces(const double * u, double * internalForces);
+
+  // allocate memory for the non-zero entries of the stiffness matrix
+  void GetStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix);
+  // get the nonlinear stiffness matrix given the vertex displacement vector u
+  void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix);
+  // get both nonlinear internal forces and nonlinear stiffness matrix
+  void GetForceAndTangentStiffnessMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+
+  // compute damping forces based on the velocity of the vertices,
+  // see p6 section 6.2 of [Irving 04]
+  void ComputeDampingForces(double dampingPsi, double dampingAlpha, double * u, double * uvel, double * dampingForces);
+
+  // enables or disables the gravity (note: you can also set this in the constructor; use this routine to turn the gravity on/off during the simulation)
+  void SetGravity(bool addGravity) { this->addGravity = addGravity; } // if addGravity is enabled, ComputeForces will subtract the gravity force from the internal forces (note: subtraction, not addition, is used because the internal forces are returned with the sign as described in the f_int(x) comment above)
+
+  inline TetMesh * GetTetMesh() { return tetMesh; }
+
+  void SetMaterial(IsotropicMaterial * isotropicMaterial_) { isotropicMaterial = isotropicMaterial_; }
+
+  // enforces the tangent stiffness matrix to be symmetric positive-definite (which is good for stability)
+  void EnforceSPD(bool enforceSPD) { this->enforceSPD = enforceSPD; }
+
+  // === Advanced functions below; you normally do not need to use them: ===
+  // Computes strain energy, internal forces, and/or tangent stiffness matrix, 
+  // It returns 0 on success, and non-zero on failure.
+  virtual int GetEnergyAndForceAndTangentStiffnessMatrixHelper(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+  // Initialization for "GetEnergyAndForceAndTangentStiffnessMatrixPrologue" (must always be called before calling "GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse")
+  void GetEnergyAndForceAndTangentStiffnessMatrixHelperPrologue(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+  // The workhorse (main computational routine); processes mesh elements startEl <= el < endEl 
+  // (assembles partial strain energy, internal forces, and/or tangent stiffness matrix. They can be nullptr.
+  // It returns 0 on success, and non-zero on failure.)
+  int GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse(int startEl, int endEl, const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+  // the same as above, except that it processes a single mesh element el ans store the date in dense format.
+  void GetElementLocalEnergyAndForceAndMatrix(int el, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix);
+
+protected:
+  void ComputeTetVolume(int el);
+  void ComputeAreaWeightedVertexNormals(int el);
+  void PrepareDeformGrad(int el);
+  // compute local energy, internalForces, tangentStiffnessMatrix assuming currentVerticesPosition is updated
+  int ComputeElementLocalData(int el, const double * u, double * energy, double internalForces[12], double tangentStiffnessMatrix[144]);
+  TetMesh * tetMesh; // the tet mesh
+  IsotropicMaterial * isotropicMaterial; // the material 
+
+  // acceleration indices
+  int ** row_;
+  int ** column_;
+
+  double * restVerticesPosition;    // length equals to the #vertices in the mesh times 3
+  double * currentVerticesPosition; // it equals restVerticesPosition + u
+
+  // If a principal stretch (i.e., the F^hat in Irving 2004 paper) is smaller than 
+  // inversionThreshold, it will be clamped to that.  This is important to ensure invertibility. 
+  // For example, a typical value for invertible StVK would be 0.5.
+  double inversionThreshold;
+
+  bool addGravity;
+  double g;
+
+  bool enforceSPD;
+
+  // this is the b=(A1N1 + A2N2 + A3N3) in the paper,
+  // see p.3 section 4
+  Vec3d * areaWeightedVertexNormals;
+  // this is an array of inv(Dm), Dm is the matrix
+  // of the edge vectors of a tet. See p3 section 3 of [Irving 04]
+  // length of this array equals to the number of tet in the mesh
+  Mat3d * dmInverses;
+  // an array of deformation gradient F
+  Mat3d * Fs;
+  // an array of F^hat (i.e., the principal stretches)
+  Vec3d * Fhats;
+  // an array of the V rotation matrices
+  Mat3d * Vs;
+  // an array of the U rotation matrices
+  Mat3d * Us;
+
+  // tet volumes; necessary to compute the elastic strain energy
+  double * tetVolumes;
+  void ComputeTetVolumes();
+
+  // the area weighted vertex normals in the rest configuration
+  void ComputeAreaWeightedVertexNormals();
+  // compute inv(Dm)
+  void PrepareDeformGrad(); // called once in the constructor
+
+  // Compute strain energy based on the user-specified material
+  virtual double ComputeEnergyFromStretches(int elementIndex, double * lambda);
+  // Compute the diagonalized first Piola-Kirchhoff stress P^hat
+  virtual void ComputeDiagonalPFromStretches(int elementIndex, double * lambda, double * PDiag);
+  // Compute the element stiffness matrix
+  virtual void ComputeTetK(int el, double K[144], int clamped);
+  // Compute the derivative of the first Piola Kirchhoff stress P with respect to
+  // the deformation gradient F. Since P and F both have 9 entries, dPdF has 81 entries
+  virtual void Compute_dPdF(int el, double dPdF[81], int clamped);
+  // Compute the derivative of the deformation gradient F with respect 
+  // to the displacement vector u
+  void Compute_dFdU();
+  // The G is a 3x3 matrix where the columns are the nodal forces 
+  // (see p3 section 4 of [Irving 04]). So dGdF is the derivative of G (i.e., nodal forces)
+  // with respect to the deformation gradient F
+  void Compute_dGdF(Vec3d * b0, Vec3d * b1, Vec3d * b2, double dPdF[], double dGdF[]);
+
+  // {i,j,m} goes from 0 to 2 inclusively,
+  // and {n} goes from 0 to 3 inclusively.
+  // converts 3x3x3x4 tensor indices to 9x12 matrix indices
+  int tensor9x12Index(int i, int j, int m, int n);
+
+  // dFdUs is an array of dFdU (i.e., derivative of the deformation gradient with
+  // respect to the displacement vector u), and dFdU is stored as a array of doubles.
+  double * dFdUs; // array of length 9x12 x numElements
+  // Ds is the matrix which the columns are the edge vector of a tet (see p3 
+  // section 3 of [Irving 04]). dDSdU is a 9x12 matrix which stores the derivative
+  // of the Ds matrix with respect to the displacement vector u. Because Ds has 9 entries
+  // and the u vector (of a single tet) has 12 entries (i.e., 4 vertices * 3 dof), so I 
+  // re-arrange dDSdU to be a 9x12 matrix
+  double dDSdU[108]; //in 9x12 matrix format
+
+  void dP_From_dF(Mat3d & dF, Mat3d & dP);
+  //this gammaValue function is used by dP_dF
+  inline double gammaValue(int i, int j, double sigma[3], double invariants[3], double gradient[3], double hessian[6]);
+
+  // {i,j,m,n} goes from 0 to 2 inclusively
+  // converts 3x3x3x3 tensor indices to 9x9 row-major matrix indices
+  int tensor9x9Index(int i, int j, int m, int n);
+
+  /*
+    rowMajorMatrixToTeran is a mapping from a row-major 3x3 matrix to a 9-vector in Teran's order
+    i.e., 
+             | m00 m01 m02 |   | 0 1 2 |
+    from M = | m10 m11 m12 | = | 3 4 5 |
+             | m20 m21 m22 |   | 6 7 8 |
+
+    to V = [m00 m11 m22 m01 m10 m02 m20 m12 m21] = [0 4 8 1 3 2 6 5 7]
+  */
+  int rowMajorMatrixToTeran[9];
+
+  /*
+    "teranToRowMajorMatrix" is a mapping from a 9-vector (in Teran's order) to row-major 3x3 matrix
+
+    from V = [m00 m11 m22 m01 m10 m02 m20 m12 m21]
+
+           | m00 m01 m02 |   | 0 1 2 |
+    to M = | m10 m11 m12 | = | 3 4 5 |
+           | m20 m21 m22 |   | 6 7 8 |
+  */
+  int teranToRowMajorMatrix[9];
+
+  // enforce SPD:
+  // on the "A" matrix in Teran's paper, Section 8
+  // Aij is the entry in row i and column j of 3x3 matrix A (which is symmetric)
+  void FixPositiveIndefiniteness(double & A11, double & A12, double & A13,
+                                 double & A22, double & A23, double & A33);
+
+  // on the "B" matrix in Teran's paper, Section 8
+  // Bij is the entry in row i and column j of 2x2 matrix B (which is symmetric)
+  void FixPositiveIndefiniteness(double & B11, double & B12);
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/isotropicMaterial.cpp b/libraries/isotropicHyperelasticFEM/isotropicMaterial.cpp
similarity index 74%
rename from src/libisotropicHyperelasticFEM/isotropicMaterial.cpp
rename to libraries/isotropicHyperelasticFEM/isotropicMaterial.cpp
index 580986fa4bce9ef799f66c015f61457098d56e3a..96bd059e56de93683a7ad974e0c5ab464df49d2b 100644
--- a/src/libisotropicHyperelasticFEM/isotropicMaterial.cpp
+++ b/libraries/isotropicHyperelasticFEM/isotropicMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,10 +32,7 @@
 
 #include "isotropicMaterial.h"
 
-namespace vega
-{
 IsotropicMaterial::IsotropicMaterial() {}
 
 IsotropicMaterial::~IsotropicMaterial() {}
 
-}
diff --git a/libraries/isotropicHyperelasticFEM/isotropicMaterial.h b/libraries/isotropicHyperelasticFEM/isotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4f04fc2790e288ac7b69b66c5dd5f7f05d4db38
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/isotropicMaterial.h
@@ -0,0 +1,50 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _ISOTROPICMATERIAL_H_
+#define _ISOTROPICMATERIAL_H_
+
+class IsotropicMaterial
+{
+public:
+  IsotropicMaterial();
+  virtual ~IsotropicMaterial();
+
+  virtual double ComputeEnergy(int elementIndex, double * invariants)=0;
+  virtual void ComputeEnergyGradient(int elementIndex, double * invariants, double * gradient)=0; // invariants and gradient are 3-vectors
+  virtual void ComputeEnergyHessian(int elementIndex, double * invariants, double * hessian)=0; // invariants is a 3-vector, hessian is a 3x3 symmetric matrix, unrolled into a 6-vector, in the following order: (11, 12, 13, 22, 23, 33).
+
+protected:
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.cpp b/libraries/isotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.cpp
similarity index 79%
rename from src/libisotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.cpp
rename to libraries/isotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.cpp
index 74c8b7614c0fb3d54453174a0002994141883a1f..5393ac2487f9629eb8cb1e2d295a97c7c9ccfc7b 100644
--- a/src/libisotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.cpp
+++ b/libraries/isotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include <math.h>
 #include "isotropicMaterialWithCompressionResistance.h"
 
-namespace vega
-{
 IsotropicMaterialWithCompressionResistance::IsotropicMaterialWithCompressionResistance(int enableCompressionResistance_) : IsotropicMaterial(), enableCompressionResistance(enableCompressionResistance_)
 {
 }
@@ -46,9 +48,8 @@ void IsotropicMaterialWithCompressionResistance::AddCompressionResistanceEnergy(
 
     if (J < 1)
     {
-      double fac = (J - 1.0) * (J - 1.0) * (J - 1.0) / 216.0;
       double compressionResistanceFactor = GetCompressionResistanceFactor(elementIndex);
-      *energy += -compressionResistanceFactor * fac / 12.0;
+      *energy += -compressionResistanceFactor * (J - 1.0) * (J - 1.0) * (J - 1.0) / 2592.0;
     }
   }
 }
@@ -62,9 +63,8 @@ void IsotropicMaterialWithCompressionResistance::AddCompressionResistanceGradien
 
     if (J < 1)
     {
-      double fac = (J - 1.0) * (J - 1.0) / 36.0;
       double compressionResistanceFactor = GetCompressionResistanceFactor(elementIndex);
-      gradient[2] += -compressionResistanceFactor * fac / (8.0 * J);
+      gradient[2] += -compressionResistanceFactor * (J - 1.0) * (J - 1.0) / (1728.0 * J);
     }
   }
 }
@@ -79,14 +79,13 @@ void IsotropicMaterialWithCompressionResistance::AddCompressionResistanceHessian
     if (J < 1.0)
     {
       double compressionResistanceFactor = GetCompressionResistanceFactor(elementIndex);
-      hessian[5] += compressionResistanceFactor * (1.0 - J) * (1.0 + 11.0 * J) / (576.0 * J * J * J);
+      hessian[5] += compressionResistanceFactor * (1.0 - J) * (1.0 + J) / (3456.0 * J * J * J);
     }
   }
 }
 
 double IsotropicMaterialWithCompressionResistance::GetCompressionResistanceFactor(int elementIndex)
 {
-  return 1.0;
+  return 1.0; // generic
 }
 
-}
diff --git a/libraries/isotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.h b/libraries/isotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.h
new file mode 100644
index 0000000000000000000000000000000000000000..e8f0235e55e9f3b82f3b026ea31449d30ba545d2
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/isotropicMaterialWithCompressionResistance.h
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _ISOTROPICMATERIALWITHCOMPRESSIONRESISTANCE_H_
+#define _ISOTROPICMATERIALWITHCOMPRESSIONRESISTANCE_H_
+
+#include "isotropicMaterial.h"
+
+class IsotropicMaterialWithCompressionResistance : public IsotropicMaterial
+{
+public:
+  IsotropicMaterialWithCompressionResistance(int enableCompressionResistance=0);
+  virtual ~IsotropicMaterialWithCompressionResistance();
+
+  virtual double ComputeEnergy(int elementIndex, double * invariants)=0;
+  virtual void ComputeEnergyGradient(int elementIndex, double * invariants, double * gradient)=0; // invariants and gradient are 3-vectors
+  virtual void ComputeEnergyHessian(int elementIndex, double * invariants, double * hessian)=0; // invariants is a 3-vector, hessian is a 3x3 symmetric matrix, unrolled into a 6-vector, in the following order: (11, 12, 13, 22, 23, 33).
+
+protected:
+  int enableCompressionResistance;
+
+  virtual double GetCompressionResistanceFactor(int elementIndex);
+  void AddCompressionResistanceEnergy(int elementIndex, double * invariants, double * energy);
+  void AddCompressionResistanceGradient(int elementIndex, double * invariants, double * gradient);
+  void AddCompressionResistanceHessian(int elementIndex, double * invariants, double * hessian); 
+};
+
+#endif
+
diff --git a/src/libisotropicHyperelasticFEM/neoHookeanIsotropicMaterial.cpp b/libraries/isotropicHyperelasticFEM/neoHookeanIsotropicMaterial.cpp
similarity index 89%
rename from src/libisotropicHyperelasticFEM/neoHookeanIsotropicMaterial.cpp
rename to libraries/isotropicHyperelasticFEM/neoHookeanIsotropicMaterial.cpp
index bb80620274476d0459600a7077440a0a3014aabd..ac2025c732b6b9620db7616bad0c73e35e44ce8a 100644
--- a/src/libisotropicHyperelasticFEM/neoHookeanIsotropicMaterial.cpp
+++ b/libraries/isotropicHyperelasticFEM/neoHookeanIsotropicMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,8 +34,6 @@
 #include "neoHookeanIsotropicMaterial.h"
 #include "volumetricMeshENuMaterial.h"
 
-namespace vega
-{
 NeoHookeanIsotropicMaterial::NeoHookeanIsotropicMaterial(TetMesh * tetMesh, int enableCompressionResistance_, double compressionResistance_) : IsotropicMaterialWithCompressionResistance(enableCompressionResistance_), compressionResistance(compressionResistance_)
 {
   int numElements = tetMesh->getNumElements();
@@ -124,4 +126,3 @@ double NeoHookeanIsotropicMaterial::GetCompressionResistanceFactor(int elementIn
   return EdivNuFactor[elementIndex];
 }
 
-}
diff --git a/libraries/isotropicHyperelasticFEM/neoHookeanIsotropicMaterial.h b/libraries/isotropicHyperelasticFEM/neoHookeanIsotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..204973e4c6b2146be7dc91a47c0ebd2d4c387838
--- /dev/null
+++ b/libraries/isotropicHyperelasticFEM/neoHookeanIsotropicMaterial.h
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isotropic hyperelastic FEM" library , Copyright (C) 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Fun Shing Sin                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _NEOHOOKEANISOTROPICMATERIAL_H_
+#define _NEOHOOKEANISOTROPICMATERIAL_H_
+
+#include "tetMesh.h"
+#include "isotropicMaterialWithCompressionResistance.h"
+
+/*
+   Neo-Hookean material. Material properties are read from the tet mesh, and can be heterogeneous.
+
+   The implemented neo-Hookean material is described in:
+   BONET J., WOOD R. D.: Nonlinear Continuum Mechanics
+   for Finite Element Analysis, 2nd Ed. Cambridge University
+   Press, 2008, page 162
+*/
+
+class NeoHookeanIsotropicMaterial : public IsotropicMaterialWithCompressionResistance
+{
+public:
+  NeoHookeanIsotropicMaterial(TetMesh * tetMesh, int enableCompressionResistance=0, double compressionResistance=0.0);
+  virtual ~NeoHookeanIsotropicMaterial();
+
+  virtual double ComputeEnergy(int elementIndex, double * invariants);
+  virtual void ComputeEnergyGradient(int elementIndex, double * invariants, double * gradient); // invariants and gradient are 3-vectors
+  virtual void ComputeEnergyHessian(int elementIndex, double * invariants, double * hessian); // invariants is a 3-vector, hessian is a 3x3 symmetric matrix, unrolled into a 6-vector, in the following order: (11, 12, 13, 22, 23, 33).
+
+protected:
+  double * lambdaLame; 
+  double * muLame; 
+
+  double compressionResistance;
+  double * EdivNuFactor;
+  virtual double GetCompressionResistanceFactor(int elementIndex);
+};
+
+#endif
+
diff --git a/libraries/laplacianMatrix/laplacianMatrix.cpp b/libraries/laplacianMatrix/laplacianMatrix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3a08921519aeba4038e9d60fc08eaf7eff74ae27
--- /dev/null
+++ b/libraries/laplacianMatrix/laplacianMatrix.cpp
@@ -0,0 +1,237 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "laplacianMatrix" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <vector>
+#include <set>
+using namespace std;
+#include "laplacianMatrix.h"
+#include "LagrangeMultiplierSolver.h"
+#include "generateGradientMatrix.h"
+
+// standard Laplace matrix on the tets
+// dimension is T x T, T is #tets
+// In each row i, the diagonal entry is the #tets neighboring to tet i. And the entries corresponding to the neighboring tet indices are set to -1.
+SparseMatrix * LaplacianMatrix::ComputeTetLaplacianMatrix(const TetMesh * tetMesh, int biLaplace)
+{
+  int numElements = tetMesh->getNumElements();
+  int numElementVertices = tetMesh->getNumElementVertices();
+  int numVertices = tetMesh->getNumVertices();
+
+  SparseMatrixOutline * LaplacianMatrixOutline = new SparseMatrixOutline(numElements);
+
+  // build elements that neighbor each vertex
+  vector<set<int> > vertexNeighbors(numVertices);
+
+  for(int el = 0; el < numElements; el++)
+  {
+    for(int vtxIdx=0; vtxIdx<numElementVertices; vtxIdx++)
+    {
+      int vertexIndex = tetMesh->getVertexIndex(el, vtxIdx);
+      vertexNeighbors[vertexIndex].insert(el);
+    }
+  }
+
+  // build elements that neighbor each element, and assemble L
+  for(int el=0; el<numElements; el++)
+  {
+    set<int> elementNeighbors;
+    for(int vtxIdx=0; vtxIdx<numElementVertices; vtxIdx++)
+    {
+      int vertexIndex = tetMesh->getVertexIndex(el, vtxIdx);
+      elementNeighbors.insert(vertexNeighbors[vertexIndex].begin(), vertexNeighbors[vertexIndex].end());
+    }
+
+    double counter = 0.0;
+    for (set<int>::iterator it=elementNeighbors.begin(); it!=elementNeighbors.end(); it++)
+    {
+      if ((*it) != el)
+      {
+        LaplacianMatrixOutline->AddEntry(el, (*it), -1.0);
+        counter++;
+      }
+    }
+
+    LaplacianMatrixOutline->AddEntry(el, el, counter);
+  }
+
+  SparseMatrix * L;
+  
+  if (!biLaplace)
+  {
+    // compute standard Laplace
+    L = new SparseMatrix(LaplacianMatrixOutline);
+  }
+  else 
+  {
+    // compute biLaplace
+    SparseMatrix * LaplacianTetMatrix = new SparseMatrix(LaplacianMatrixOutline);
+    SparseMatrix * Identity = SparseMatrix::CreateIdentityMatrix(numElements);
+    L = new SparseMatrix(Identity->ConjugateMatrix(*LaplacianTetMatrix, 0, numElements));
+    delete(LaplacianTetMatrix);
+    delete(Identity);
+  }
+
+  delete(LaplacianMatrixOutline);
+
+  return L;
+}
+
+SparseMatrix * LaplacianMatrix::ComputeTetVolumeWeightedLaplacianMatrix(const TetMesh * tetMesh)
+{
+  int numElements = tetMesh->getNumElements();
+  int numElementVertices = tetMesh->getNumElementVertices();
+  int numVertices = tetMesh->getNumVertices();
+
+  SparseMatrixOutline * LaplacianMatrixOutline = new SparseMatrixOutline(numElements);
+
+  // build elements that neighbor each vertex
+  vector<set<int> > vertexNeighbors(numVertices);
+
+  for(int el = 0; el < numElements; el++)
+  {
+    for(int vtxIdx=0; vtxIdx<numElementVertices; vtxIdx++)
+    {
+      int vertexIndex = tetMesh->getVertexIndex(el, vtxIdx);
+      vertexNeighbors[vertexIndex].insert(el);
+    }
+  }
+
+  double * vols = (double*) malloc (sizeof(double) * numElements);
+  for(int el=0; el<numElements; el++)
+    vols[el] = tetMesh->getElementVolume(el);
+
+  // build elements that neighbor each element, and assemble L
+  for(int el=0; el<numElements; el++)
+  {
+    set<int> elementNeighbors;
+    for(int vtxIdx=0; vtxIdx<numElementVertices; vtxIdx++)
+    {
+      int vertexIndex = tetMesh->getVertexIndex(el, vtxIdx);
+      elementNeighbors.insert(vertexNeighbors[vertexIndex].begin(), vertexNeighbors[vertexIndex].end());
+    }
+ 
+    double denom = 0.0;
+    for (set<int>::iterator it=elementNeighbors.begin(); it!=elementNeighbors.end(); it++)
+    {
+      if ((*it) != el)
+      {
+        LaplacianMatrixOutline->AddEntry(el, (*it), -(vols[el] + vols[*it]));
+        denom += (vols[el] + vols[*it]);
+      }
+    }
+
+    LaplacianMatrixOutline->AddEntry(el, el, denom);
+  }
+
+  SparseMatrix * L = new SparseMatrix(LaplacianMatrixOutline);
+    
+  free(vols);
+  delete(LaplacianMatrixOutline);
+
+  return L;
+}
+
+
+SparseMatrix * LaplacianMatrix::ComputeTetFEMLaplacianMatrix(const TetMesh * tetMesh)
+{
+  int numVertices = tetMesh->getNumVertices();
+  int numElements = tetMesh->getNumElements();
+ 
+  double * sqrtvol = (double*) malloc (sizeof(double) * numElements);
+  for(int el=0; el<numElements; el++)
+    sqrtvol[el] = sqrt(tetMesh->getElementVolume(el));
+ 
+  SparseMatrix * LaplacianVertexMatrix;
+  GenerateGradientMatrix::GenerateForScalarField(tetMesh, &LaplacianVertexMatrix, sqrtvol);
+
+  SparseMatrixOutline * smoothMatrixOutline = new SparseMatrixOutline(numElements);
+
+  for(int i=0; i<numVertices; i++)
+  {
+    vector<int> elementVertices;
+    elementVertices.push_back(i);
+
+    vector<int> elementNeighbors;
+    tetMesh->getElementsTouchingVertices(elementVertices, elementNeighbors);
+
+    double volT = 0;
+    for(unsigned int el = 0; el < elementNeighbors.size(); el++)
+      volT += sqrtvol[elementNeighbors[el]] * sqrtvol[elementNeighbors[el]];
+
+    for(unsigned int el = 0; el < elementNeighbors.size(); el++)
+      smoothMatrixOutline->AddEntry(i, elementNeighbors[el], sqrtvol[elementNeighbors[el]] * sqrtvol[elementNeighbors[el]] / volT);
+  }
+
+  SparseMatrix * smoothMatrix = new SparseMatrix(smoothMatrixOutline);
+  SparseMatrix * L = new SparseMatrix(LaplacianVertexMatrix->ConjugateMatrix(*smoothMatrix, 0, numElements));
+
+  delete(LaplacianVertexMatrix);
+  delete(smoothMatrixOutline);
+  delete(smoothMatrix);
+  free(sqrtvol);
+
+  return L;
+}
+
+void LaplacianMatrix::ExtrapolateScalarField(const SparseMatrix * L, int numSelectedTets, const int * selectedTets, const double * Sbar, double * x, int numThreads)
+{
+  int numElements = L->GetNumRows();
+
+  if (numSelectedTets == 0)
+  {
+    printf("Warning: no selected tets in \"ExtrapolateScalarField\". Output x is not modified.\n");
+    return;
+  }
+
+  double * xExtended = (double*) malloc (sizeof(double) * (numElements + numSelectedTets));
+  double * rhs = (double*) malloc (sizeof(double)  * (numSelectedTets + numElements));
+  memset(rhs, 0, sizeof(double) * numElements);
+
+  SparseMatrixOutline * JOutline = new SparseMatrixOutline(numSelectedTets);
+  for(int i=0; i<numSelectedTets; i++)
+  {
+    JOutline->AddEntry(i, selectedTets[i], 1.0);
+    rhs[numElements + i] = Sbar[i];
+  }
+  SparseMatrix * J = new SparseMatrix(JOutline);
+  LagrangeMultiplierSolver * solver = new LagrangeMultiplierSolver(L, J, NULL, 0, NULL, numThreads);
+  solver->SolveLinearSystem(xExtended, rhs);
+
+  memcpy(x, xExtended, sizeof(double) * numElements);
+
+  delete(solver);
+  delete(J);
+  delete(JOutline);
+  free(rhs);
+  free(xExtended);
+}
+
diff --git a/libraries/laplacianMatrix/laplacianMatrix.h b/libraries/laplacianMatrix/laplacianMatrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e79696ba8dd62356e9b8336a8b801b32486cb30
--- /dev/null
+++ b/libraries/laplacianMatrix/laplacianMatrix.h
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "laplacianMatrix" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+   Computes the Laplacian matrix of a 3D tetrahedral mesh. The
+   computed matrices are element-based (as opposed to vertex-based) : 
+   they transform scalar quantities defined on the elements. 
+*/
+
+#ifndef _LAPLACIANMATRIX_H_
+#define _LAPLACIANMATRIX_H_
+
+#include "sparseMatrix.h"
+#include "tetMesh.h"
+
+class LaplacianMatrix
+{
+public:
+
+  // computes the ``umbrella'' discrete Laplacian matrix L on the tets
+  // dimension is T x T, where T is the number of tets
+  // In each row i, the diagonal entry is the #tets neighboring to tet i. 
+  // And the entries corresponding to the neighboring tet indices are set to -1.
+  // if biLaplace=1, the routine computes the bilaplace matrix, i.e., L^T L
+  static SparseMatrix * ComputeTetLaplacianMatrix(const TetMesh * tetMesh, int biLaplace=0);
+
+  // volume-weighted Laplacian on tets
+  static SparseMatrix * ComputeTetVolumeWeightedLaplacianMatrix(const TetMesh * tetMesh);
+
+  // "FEM-style" Laplacian on tets, obtained by conjugating the vertex-based Laplacian with 
+  // a volume-weighted matrix converting tet quantities to vertex quantities
+  static SparseMatrix * ComputeTetFEMLaplacianMatrix(const TetMesh * tetMesh);
+
+  // Extrapolates a scalar field given at a few elements, to the entire mesh, in a smooth way
+  // using the Laplace operator. Mathematically:
+  //
+  // minimize 1/2 <L * x, x>
+  // subject to S x = Sbar
+  //
+  // input: L, S, Sbar
+  // L is an arbitrary Laplacian matrix (computed, say, using the routines above)
+  // S is the selection matrix. It is given by the array "selectedTets", of length "numSelectedTets".
+  // Sbar are the prescribed values at the selected tets. 
+  // numThreads is the number of employed threads. Value of 0 means single-threaded computation (equivalent to value of 1).
+  // output: x (x is a vector of length n, where L is n x n); input value of x is ignored
+  static void ExtrapolateScalarField(const SparseMatrix * L, int numSelectedTets, const int * selectedTets, const double * Sbar, double * x, int numThreads = 0);
+};
+
+#endif
+
diff --git a/libraries/libiglInterface/iglRemeshSelfIntersection.cpp b/libraries/libiglInterface/iglRemeshSelfIntersection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2cfa60d8607cba3592cb18e93fcf581a7ec0c814
--- /dev/null
+++ b/libraries/libiglInterface/iglRemeshSelfIntersection.cpp
@@ -0,0 +1,180 @@
+// This file is modified from libigl, a simple c++ geometry processing library.
+// libigl is under the Mozilla Public License v. 2.0.
+// According to the license restriction, we release this code including the changes
+// we made to libigl code under the same license.
+//
+// Copyright of our changes: (C) 2018 USC
+// Code authors of our changes: Yijing Li, Jernej Barbic
+//
+// This Source Code Form is subject to the terms of the Mozilla Public License
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can
+// obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "iglRemeshSelfIntersection.h"
+
+#include <Eigen/Core>
+#include "igl/copyleft/cgal/RemeshSelfIntersectionsParam.h"
+#include "igl/copyleft/cgal/remesh_self_intersections.h"
+#include "igl/remove_unreferenced.h"
+#include "igl/unique_edge_map.h"
+#include "igl/extract_manifold_patches.h"
+#include "igl/copyleft/cgal/extract_cells.h"
+#include "igl/copyleft/cgal/propagate_winding_numbers.h"
+#include "igl/copyleft/cgal/mesh_boolean.h"
+#include "igl/copyleft/cgal/assign_scalar.h"
+
+#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
+
+#include <iostream>
+#include <algorithm>
+
+namespace iglInterface
+{
+
+SelfCutMeshData remeshSelfIntersection(TriMeshRef mesh, bool stitch,
+    bool computeWindingNumbers, bool computeDoublePrecisionPos, bool computePatchAndCells)
+{
+  using namespace Eigen;
+  using namespace std;
+  SelfCutMeshData ret;
+
+  Matrix<double, Eigen::Dynamic, 3, Eigen::RowMajor>::MapType VV((double*)mesh.positions(), mesh.numVertices(), 3);
+  Matrix<int, Eigen::Dynamic, 3, Eigen::RowMajor>::MapType FF((int*)mesh.triangles(), mesh.numTriangles(), 3);
+
+  typedef double Scalar;
+  typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
+  typedef Kernel::FT ExactScalar;
+  typedef Eigen::Matrix<Scalar,Eigen::Dynamic,3> MatrixX3S;
+  typedef Eigen::Matrix<int,Eigen::Dynamic,1> VectorXJ;
+  typedef Eigen::Matrix<
+      ExactScalar,
+      Eigen::Dynamic,
+      Eigen::Dynamic,
+      Eigen::RowMajor> MatrixXES;
+  MatrixXES V;
+  Matrix<int, Eigen::Dynamic, 3, Eigen::RowMajor> F;
+  VectorXJ  CJ;
+  Matrix<int, Eigen::Dynamic, 2, Eigen::RowMajor> IF;
+
+  MatrixXES Vr; // unstitched vertices
+  Matrix<int, Eigen::Dynamic, 3, Eigen::RowMajor>  Fr; // unstitched faces
+  Eigen::VectorXi I; // vtxStitchIDs
+  {
+
+    igl::copyleft::cgal::RemeshSelfIntersectionsParam params;
+    params.stitch_all = stitch;
+    //    params.stitch_all = false;
+
+    igl::copyleft::cgal::remesh_self_intersections(
+        VV, FF, params, Vr, Fr, IF, CJ, I);
+    for(int i = 0; i < Fr.rows(); i++)
+    {
+      assert(Fr(i,0) != Fr(i, 1) && Fr(i, 0) != Fr(i,2) && Fr(i,1) != Fr(i,2));
+    }
+    assert(I.size() == Vr.rows());
+    if (IF.size() > 0) assert(IF.cols() == 2);
+    assert(CJ.size() == Fr.rows());
+//    cout << "After remesh, " << endl;
+//    cout << "#v " << Vr.rows() << " #f: " << Fr.rows() << endl;
+
+    // Merge coinciding vertices into non-manifold vertices.
+    Matrix<int, Eigen::Dynamic, 3, Eigen::RowMajor>  Ftmp = Fr;
+    std::for_each(Ftmp.data(), Ftmp.data()+Ftmp.size(),
+        [&I](typename Matrix<int, Eigen::Dynamic, 3, Eigen::RowMajor>::Scalar& a) { a=I[a]; });
+    // Remove unreferenced vertices.
+    Eigen::VectorXi UIM;
+    igl::remove_unreferenced(Vr, Ftmp, V, F, UIM);
+//    cout << "After remove, " << endl;
+//    cout << "#v " << V.rows() << " #f: " << F.rows() << endl;
+//    cout << "#UIM size " << UIM.size() << endl;
+    // assert(UIM.size() == Fr.rows());
+    assert(F.rows() == Fr.rows());
+
+    // Eigen::MatrixX3i Ftmp;
+    // for(int i = 0; i < Fr.rows(); i++)
+    //   Ftmp.row(UIM(i)) = Fr.row(i);
+  }
+
+  // Compute edges of (F) --> (E,uE,EMAP,uE2E)
+  Eigen::MatrixXi E, uE;
+  Eigen::VectorXi EMAP;
+  std::vector<std::vector<size_t> > uE2E;
+  Eigen::VectorXi P;
+  size_t num_patches = 0, num_cells = 0;
+  Eigen::MatrixXi per_patch_cells;
+
+  if (computePatchAndCells)
+  {
+    igl::unique_edge_map(F, E, uE, EMAP, uE2E);
+    // Compute patches (F,EMAP,uE2E) --> (P)
+    num_patches = igl::extract_manifold_patches(F, EMAP, uE2E, P);
+    // Compute cells (V,F,P,E,uE,EMAP) -> (per_patch_cells)
+    num_cells = igl::copyleft::cgal::extract_cells(V, F, P, E, uE, uE2E, EMAP, per_patch_cells);
+  }
+
+
+  bool valid = true;
+  const size_t num_faces = F.rows();
+  Eigen::MatrixXi W;
+  Eigen::VectorXi labels = Eigen::VectorXi::Constant(num_faces, 0);
+  if (num_faces > 0 && computeWindingNumbers)
+  {
+    valid = valid &
+        igl::copyleft::cgal::propagate_winding_numbers(
+            V, F, uE, uE2E, num_patches, P, num_cells, per_patch_cells, labels, W);
+    assert((size_t)W.rows() == num_faces);
+  } else
+  {
+    W.resize(0, 2);
+  }
+
+  // If W doesn't have enough columns, pad with zeros
+  if (W.cols() <= 2)
+  {
+    const int old_ncols = W.cols();
+    W.conservativeResize(num_faces,2);
+    W.rightCols(2-old_ncols).setConstant(0);
+  }
+  assert((size_t)W.cols() == 2);
+
+
+  // begin exporting data
+
+  vector<Vec3d> Vd(Vr.size(), Vec3d(0.0));
+  ret.cutPosExact.resize(Vr.rows());
+  for(int i = 0; i < Vr.rows(); i++)
+  {
+    for(int j = 0; j < 3; j++)
+    {
+      if (computeDoublePrecisionPos)
+        igl::copyleft::cgal::assign_scalar(Vr(i,j), Vd[i][j]); // assign CGAL exact numbers to double
+      ret.cutPosExact[i][j] = assignCGALToER(Vr(i,j));
+    }
+  }
+
+  ret.cutMesh = TriMeshGeo(Vr.rows(), (double*)Vd.data(), Fr.rows(), Fr.data());
+
+  ret.interTriPairs.resize(IF.rows(), pair<int,int>(0,0));
+  for(int i = 0; i < IF.rows(); i++)
+  {
+    ret.interTriPairs[i] = { IF(i,0), IF(i,1) };
+  }
+  ret.oldTriIDs.assign(CJ.data(), CJ.data() + CJ.size());
+  ret.triPatchIDs.assign(P.data(), P.data() + P.size());
+  ret.vtxStitchIDs.assign(I.data(), I.data() + I.size());
+  ret.cellIDsAtPatch.resize(per_patch_cells.rows(), pair<int,int>(0,0));
+  for(int i = 0; i < per_patch_cells.rows(); i++)
+  {
+    ret.cellIDsAtPatch[i] = { per_patch_cells(i, 0), per_patch_cells(i, 1) };
+  }
+
+  ret.windAroundTri.resize(W.rows(), pair<int,int>(0,0));
+  for(int i = 0; i < W.rows(); i++)
+  {
+    ret.windAroundTri[i] = { W(i, 0), W(i, 1) };
+  }
+
+  return ret;
+}
+
+}
diff --git a/libraries/libiglInterface/iglRemeshSelfIntersection.h b/libraries/libiglInterface/iglRemeshSelfIntersection.h
new file mode 100644
index 0000000000000000000000000000000000000000..eecc0230b68087251bbacdd4c5e0230ef65962ce
--- /dev/null
+++ b/libraries/libiglInterface/iglRemeshSelfIntersection.h
@@ -0,0 +1,64 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "libiglInterface" library , Copyright (C) 2018 USC                    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef IGLREMESHSELFINTERSECTION_H
+#define IGLREMESHSELFINTERSECTION_H
+
+#include "triMeshGeo.h"
+#include "vec3ER.h"
+namespace iglInterface
+{
+
+struct SelfCutMeshData
+{
+  TriMeshGeo cutMesh; // the new vtx at triangle intersections stitch the intersected triangles together
+  std::vector<Vec3ER> cutPosExact; // exact cut positions
+  std::vector<std::pair<int, int>> interTriPairs; // Indices of the uncut mesh
+  std::vector<int> oldTriIDs;
+  std::vector<int> triPatchIDs; // triID -> patchID it belongs to
+  std::vector<int> vtxStitchIDs; // vtxID -> new ID if vtx on the same positions merge
+
+  // cellID around each patch
+  // patchID -> <cellID on front side of patch, cellID on back side of patch>
+  // by default, cellID = 0 is the infinity cell (outer space of the mesh)
+  std::vector<std::pair<int, int>> cellIDsAtPatch;
+  // winding numbers around each triangle
+  // triID -> <#winding on front side of triID, #winding on back side of triID>
+  std::vector<std::pair<int, int>> windAroundTri;
+};
+
+SelfCutMeshData remeshSelfIntersection(TriMeshRef mesh, bool stitch = true, bool computeWindingNumbers = true,
+    bool computeDoublePrecisionPos = true, bool computePatchAndCells = true);
+
+}
+
+#endif
diff --git a/libraries/lighting/cameraLighting.cpp b/libraries/lighting/cameraLighting.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5a9041c2f0050a3dd804fcb21ce8115cad3a11f2
--- /dev/null
+++ b/libraries/lighting/cameraLighting.cpp
@@ -0,0 +1,147 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "lighting" library , Copyright (C) 2018 USC                           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "cameraLighting.h"
+#include "configFile.h"
+#include "openGL-headers.h"
+#include "camera.h"
+#include <iostream>
+using namespace std;
+
+void CameraLighting::initialize()
+{
+  if (lightEnabled)
+    glEnable(GL_LIGHT0);
+  else
+    glDisable(GL_LIGHT0);
+
+  glDisable(GL_LIGHT1);
+  glDisable(GL_LIGHT2);
+  glDisable(GL_LIGHT3);
+  glDisable(GL_LIGHT4);
+  glDisable(GL_LIGHT5);
+  glDisable(GL_LIGHT6);
+  glDisable(GL_LIGHT7);
+
+  if (glGetError() != GL_NO_ERROR)
+    printf("Warning: error in the OpenGL state in the cameraLighting constructor.\n");
+
+  GLfloat aGa[] = { ambientIntensity, ambientIntensity, ambientIntensity, 1.0 };
+  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, aGa);
+  glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, localViewer);
+  glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, twoSidedLighting);
+}
+
+CameraLighting::CameraLighting()
+{
+  initialize();
+}
+
+CameraLighting::CameraLighting(const char * configFilename)
+{
+  if (glGetError() != GL_NO_ERROR)
+    printf("Warning: error in the OpenGL state at the beginning of camera lighting constructor.\n");
+
+  ConfigFile configFile;
+  configFile.addOptionOptional("globalAmbientIntensity", &ambientIntensity, ambientIntensity);
+  configFile.addOptionOptional("localViewer", &localViewer, localViewer);
+  configFile.addOptionOptional("twoSidedLighting", &twoSidedLighting, twoSidedLighting);
+  configFile.addOptionOptional("enableAmbientTerm", &enableAmbientTerm, enableAmbientTerm);
+  configFile.addOptionOptional("enableDiffuseTerm", &enableDiffuseTerm, enableDiffuseTerm);
+  configFile.addOptionOptional("enableSpecularTerm", &enableSpecularTerm, enableSpecularTerm);
+  configFile.addOptionOptional("lightEnabled", &lightEnabled, lightEnabled);
+  configFile.addOptionOptional("lightIntensity", &lightIntensity, lightIntensity);
+
+  if (configFile.parseOptions(configFilename) != 0)
+    throw 1;
+
+  initialize();
+}
+
+void CameraLighting::LightScene(const SphericalCamera * camera)
+{
+  double cx[3], cy[3], cz[3];
+  camera->Get3DAxes(cx, cy, cz);
+  for(int i = 0; i < 3; i++)
+    lightPosition[i] = cz[i];
+  lightPosition[3] = 0.0;
+
+  glLightfv(GL_LIGHT0, GL_POSITION, &lightPosition[0]);
+
+  if (enableAmbientTerm)
+    lKa[0] = lKa[1] = lKa[2] = lightIntensity;
+  else
+    lKa[0] = lKa[1] = lKa[2] = 0;
+
+  if (enableAmbientTerm)
+    lKd[0] = lKd[1] = lKd[2] = lightIntensity;
+  else
+    lKd[0] = lKd[1] = lKd[2] = 0;
+
+  if (enableAmbientTerm)
+    lKs[0] = lKs[1] = lKs[2] = lightIntensity;
+  else
+    lKs[0] = lKs[1] = lKs[2] = 0;
+
+  lKa[3]= lKd[3] = lKs[3] = 1.0;
+
+  glLightfv(GL_LIGHT0, GL_AMBIENT, &lKa[0]);
+  glLightfv(GL_LIGHT0, GL_DIFFUSE, &lKd[0]);
+  glLightfv(GL_LIGHT0, GL_SPECULAR, &lKs[0]);
+
+//  cout << "light scene" << endl;
+//  cout << lightPosition[0] << " " << lightPosition[1] << " " << lightPosition[2] << endl;
+//  cout << "lka " << lKa[0] << " " << lKd[0] << " " << lKs[0] << " " << lightEnabled << endl;
+
+  if (lightEnabled)
+    glEnable(GL_LIGHT0);
+  else
+    glDisable(GL_LIGHT0);
+
+  glEnable(GL_LIGHTING);
+}
+
+int CameraLighting::SaveConfig(const char * filename)
+{
+  ConfigFile configFile;
+  configFile.addOptionOptional("globalAmbientIntensity", &ambientIntensity, ambientIntensity);
+  configFile.addOptionOptional("localViewer", &localViewer, localViewer);
+  configFile.addOptionOptional("twoSidedLighting", &twoSidedLighting, twoSidedLighting);
+  configFile.addOptionOptional("enableAmbientTerm", &enableAmbientTerm, enableAmbientTerm);
+  configFile.addOptionOptional("enableDiffuseTerm", &enableDiffuseTerm, enableDiffuseTerm);
+  configFile.addOptionOptional("enableSpecularTerm", &enableSpecularTerm, enableSpecularTerm);
+  configFile.addOptionOptional("lightEnabled", &lightEnabled, lightEnabled);
+  configFile.addOptionOptional("lightIntensity", &lightIntensity, lightIntensity);
+
+  return configFile.saveOptions(filename);
+}
+
diff --git a/libraries/lighting/cameraLighting.h b/libraries/lighting/cameraLighting.h
new file mode 100644
index 0000000000000000000000000000000000000000..019b1c08da7dd0f408745f5efc5220c0a03b8748
--- /dev/null
+++ b/libraries/lighting/cameraLighting.h
@@ -0,0 +1,120 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "lighting" library , Copyright (C) 2018 USC                           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef CAMERALIGHTING_H
+#define CAMERALIGHTING_H
+
+#include <stdlib.h>
+#include <string.h>
+
+class SphericalCamera;
+
+class CameraLighting
+{
+public:
+  CameraLighting();
+  CameraLighting(const char * configFilename);
+
+  // call this inside your OpenGL display routine, after setting up the modelview and projection matrices
+  void LightScene(const SphericalCamera * camera);
+
+  // pos is a 4-float array representing generalized coord. of the light source
+  inline void GetLightPosition(float pos[4]) const;
+  inline void GetLightAmbientTerm(float Ka[4]) const;
+  inline void GetLightDiffuseTerm(float Kd[4]) const;
+  inline void GetLightSpecularTerm(float Ks[4]) const;
+  inline float GetLightIntensity() const;
+  inline float GetAmbientIntensity() const;
+  inline bool IsLightEnabled() const;
+  inline bool IsAmbientEnabled() const { return enableAmbientTerm; }
+  inline bool IsDiffuseEnabled() const { return enableDiffuseTerm; }
+  inline bool IsSpecularEnabled() const { return enableSpecularTerm; }
+
+  inline void SetLightEnabled(bool e) { lightEnabled = e; }
+  inline void SetLightIntensity(float intensity) { lightIntensity = intensity; }
+  inline void SetAmbientEnabled(bool e) { enableAmbientTerm = e; }
+  inline void SetDiffuseEnabled(bool e) { enableDiffuseTerm = e; }
+  inline void SetSpecularEnabled(bool e) { enableSpecularTerm = e; }
+
+  // toggle light, return whether the toggled light is enabled
+  inline bool ToggleLight() { lightEnabled = !lightEnabled; return lightEnabled; }
+
+  // save lighting config to file
+  int SaveConfig(const char * filename);
+
+protected:
+  void initialize();
+
+  float ambientIntensity = 0.0f;
+  bool localViewer = true, twoSidedLighting = false;
+  bool lightEnabled = true;
+  float lightPosition[4] = {0.0};
+  float lKa[4] = {0.0}, lKd[4] = {0.0}, lKs[4] = {0.0};
+  float lightIntensity = 1.0;
+  bool enableAmbientTerm = true, enableDiffuseTerm = true, enableSpecularTerm = true;
+};
+
+inline float CameraLighting::GetLightIntensity() const
+{
+  return lightIntensity;
+}
+
+inline void CameraLighting::GetLightPosition(float pos[4]) const
+{
+  memcpy(pos, &(lightPosition[0]), 4 * sizeof(float));
+}
+
+inline void CameraLighting::GetLightAmbientTerm(float Ka[4]) const
+{
+  memcpy(Ka, &(lKa[0]), 4* sizeof(float));
+}
+
+inline void CameraLighting::GetLightDiffuseTerm(float Kd[4]) const
+{
+  memcpy(Kd, &(lKd[0]), 4* sizeof(float));
+}
+
+inline void CameraLighting::GetLightSpecularTerm(float Ks[4]) const
+{
+  memcpy(Ks, &(lKs[0]), 4* sizeof(float));
+}
+
+inline float CameraLighting::GetAmbientIntensity() const
+{
+  return ambientIntensity;
+}
+
+inline bool CameraLighting::IsLightEnabled() const
+{
+  return lightEnabled;
+}
+#endif
diff --git a/libraries/lighting/lighting.cpp b/libraries/lighting/lighting.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f5f9ce2f25b1c8edc91fb663dee0f04e82bfddae
--- /dev/null
+++ b/libraries/lighting/lighting.cpp
@@ -0,0 +1,355 @@
+/*
+  * Copyright (c) 2007, Carnegie Mellon University
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions are met:
+  *     * Redistributions of source code must retain the above copyright
+  *       notice, this list of conditions and the following disclaimer.
+  *     * Redistributions in binary form must reproduce the above copyright
+  *       notice, this list of conditions and the following disclaimer in the
+  *       documentation and/or other materials provided with the distribution.
+  *     * Neither the name of Carnegie Mellon University, nor the
+  *       names of its contributors may be used to endorse or promote products
+  *       derived from this software without specific prior written permission.
+  *
+  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  * DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <float.h>
+#include <cassert>
+#include "configFile.h"
+#include "lighting.h"
+#include "openGL-headers.h"
+
+
+void Lighting::initialize()
+{
+  ambientIntensity = 0.0f;
+  localViewer = true;
+  twoSidedLighting = false;
+  enableAmbientTerm = true;
+  enableDiffuseTerm = true;
+  enableSpecularTerm = true;
+  memset(lightEnabled, 0, sizeof(lightEnabled));
+  memset(lightPosition, 0, sizeof(lightPosition));
+  for(int light = 0; light < 8; light++)
+  {
+    lightPosition[4*light+3] = 1.0f; //that's the direction component as in (x,y,z,d)
+    lightIntensity[light] = 1.0f;
+    lKa[light*4+0] = lKa[light*4+1] = lKa[light*4+2] = lightIntensity[light];
+    lKd[light*4+0] = lKd[light*4+1] = lKd[light*4+2] = lightIntensity[light];
+    lKs[light*4+0] = lKs[light*4+1] = lKs[light*4+2] = lightIntensity[light];
+    lKa[light*4+3] = lKd[light*4+3] = lKs[light*4+3] = 1.0;
+  }  
+}
+
+Lighting::Lighting()
+{
+  initialize();
+}
+
+void Lighting::buildConfig(ConfigFile & configFile)
+{
+  configFile.addOptionOptional("globalAmbientIntensity", &ambientIntensity, ambientIntensity);
+  configFile.addOptionOptional("localViewer", &localViewer, localViewer);
+  configFile.addOptionOptional("twoSidedLighting", &twoSidedLighting, twoSidedLighting);
+  configFile.addOptionOptional("enableAmbientTerm", &enableAmbientTerm, enableAmbientTerm);
+  configFile.addOptionOptional("enableDiffuseTerm", &enableDiffuseTerm, enableDiffuseTerm);
+  configFile.addOptionOptional("enableSpecularTerm", &enableSpecularTerm, enableSpecularTerm);
+
+  for(int light=0; light<8; light++)
+  {
+    char lightCh = '0' + light;
+    char optionName[128];
+
+    sprintf(optionName, "lightEnabled_%c", lightCh);
+    configFile.addOptionOptional(optionName, &lightEnabled[light], lightEnabled[light]);
+
+    sprintf(optionName, "position_%c_X", lightCh);
+    configFile.addOptionOptional(optionName, &lightPosition[4*light+0], lightPosition[4*light+0]);
+    sprintf(optionName, "position_%c_Y", lightCh);
+    configFile.addOptionOptional(optionName, &lightPosition[4*light+1], lightPosition[4*light+1]);
+    sprintf(optionName, "position_%c_Z", lightCh);
+    configFile.addOptionOptional(optionName, &lightPosition[4*light+2], lightPosition[4*light+2]);
+    sprintf(optionName, "position_%c_DIR", lightCh);
+    configFile.addOptionOptional(optionName, &lightPosition[4*light+3], lightPosition[4*light+3]);
+
+    sprintf(optionName, "lightIntensity_%c", lightCh);
+    configFile.addOptionOptional(optionName, &lightIntensity[light], lightIntensity[light]);
+  }
+}
+
+Lighting::Lighting(const char * configFilename)
+{
+  initialize();
+
+  if (glGetError() != GL_NO_ERROR)
+    printf("Warning: error in the OpenGL state at the beginning of lighting constructor.\n");
+
+  ConfigFile configFile;
+
+  buildConfig(configFile);
+
+  if (configFile.parseOptions(configFilename) != 0)
+    throw 1;
+
+
+  //configFile.printOptions();
+
+  #define TURNONLIGHT(i)\
+   if (lightEnabled[i])\
+     glEnable(GL_LIGHT##i);\
+   else\
+     glDisable(GL_LIGHT##i);\
+
+  TURNONLIGHT(0);
+  TURNONLIGHT(1);
+  TURNONLIGHT(2);
+  TURNONLIGHT(3);
+  TURNONLIGHT(4);
+  TURNONLIGHT(5);
+  TURNONLIGHT(6);
+  TURNONLIGHT(7);
+
+  if (glGetError() != GL_NO_ERROR)
+    printf("Warning: error in the OpenGL state in the Lighting constructor.\n");
+
+  for(int light=0; light<8; light++)
+  {
+    if (enableAmbientTerm) 
+    {
+      lKa[light*4+0] = lKa[light*4+1] = lKa[light*4+2] = lightIntensity[light];
+    }
+    else
+    {
+      lKa[light*4+0] = lKa[light*4+1] = lKa[light*4+2] = 0.0f;
+    }
+
+    if (enableDiffuseTerm)
+    {
+      lKd[light*4+0] = lKd[light*4+1] = lKd[light*4+2] = lightIntensity[light];
+    }
+    else
+    {
+      lKd[light*4+0] = lKd[light*4+1] = lKd[light*4+2] = 0.0f;
+    }
+
+    if (enableSpecularTerm)
+    {
+      lKs[light*4+0] = lKs[light*4+1] = lKs[light*4+2] = lightIntensity[light];
+    }
+    else
+    {
+      lKs[light*4+0] = lKs[light*4+1] = lKs[light*4+2] = 0.0f;
+    }
+
+    // set alpha to 1.0
+    lKa[light*4+3] = lKd[light*4+3] = lKs[light*4+3] = 1.0;
+  }
+
+  GLfloat aGa[] = { ambientIntensity, ambientIntensity, ambientIntensity, 1.0 };
+  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, aGa);
+  glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, localViewer);
+  glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, twoSidedLighting);
+}
+
+int Lighting::SaveConfig(const char * filename)
+{
+  ConfigFile configFile;
+
+  buildConfig(configFile);
+
+  return configFile.saveOptions(filename);
+}
+
+void Lighting::SetLightBox(const double lightBoxMin[3], const double lightBoxMax[3])
+{
+  float boxMin[3], boxMax[3];
+  for(int i = 0; i < 3; i++)
+  {
+    boxMin[i] = lightBoxMin[i];
+    boxMax[i] = lightBoxMax[i];
+  }
+  SetLightBox(boxMin, boxMax);
+}
+
+void Lighting::SetLightBox(const float lightBoxMin[3], const float lightBoxMax[3])
+{
+  GLfloat lP0[4] = { lightBoxMin[0], lightBoxMin[1], lightBoxMin[2], 1.0 };
+  GLfloat lP1[4] = { lightBoxMax[0], lightBoxMin[1], lightBoxMin[2], 1.0 };
+  GLfloat lP2[4] = { lightBoxMin[0], lightBoxMax[1], lightBoxMin[2], 1.0 };
+  GLfloat lP3[4] = { lightBoxMax[0], lightBoxMax[1], lightBoxMin[2], 1.0 };
+  GLfloat lP4[4] = { lightBoxMin[0], lightBoxMin[1], lightBoxMax[2], 1.0 };
+  GLfloat lP5[4] = { lightBoxMax[0], lightBoxMin[1], lightBoxMax[2], 1.0 };
+  GLfloat lP6[4] = { lightBoxMin[0], lightBoxMax[1], lightBoxMax[2], 1.0 };
+  GLfloat lP7[4] = { lightBoxMax[0], lightBoxMax[1], lightBoxMax[2], 1.0 };
+  GLfloat * lightPos[8] = { lP0, lP1, lP2, lP3, lP4, lP5, lP6, lP7 };
+  for(int i = 0; i < 8; i++)
+    memcpy(lightPosition + 4*i, lightPos[i], sizeof(float) * 4);
+}
+
+void Lighting::SetAllLightsEnabled(bool e)
+{
+  for(int i = 0; i < 8; i++)
+  {
+    lightEnabled[i] = e;
+  }
+  TURNONLIGHT(0);
+  TURNONLIGHT(1);
+  TURNONLIGHT(2);
+  TURNONLIGHT(3);
+  TURNONLIGHT(4);
+  TURNONLIGHT(5);
+  TURNONLIGHT(6);
+  TURNONLIGHT(7);
+}
+
+void Lighting::SetAllLightsIntensity(float intensity)
+{
+  for(int i = 0; i < 8; i++)
+    SetLightIntensity(i, intensity);
+}
+
+void Lighting::LightScene() const
+{
+  #define LIGHTSETUP(i)\
+  if (lightEnabled[i])\
+  {\
+    glLightfv(GL_LIGHT##i, GL_POSITION, &lightPosition[4*i]);\
+    glLightfv(GL_LIGHT##i, GL_AMBIENT, &lKa[4*i]);\
+    glLightfv(GL_LIGHT##i, GL_DIFFUSE, &lKd[4*i]);\
+    glLightfv(GL_LIGHT##i, GL_SPECULAR, &lKs[4*i]);\
+  }
+
+  LIGHTSETUP(0);
+  LIGHTSETUP(1);
+  LIGHTSETUP(2);
+  LIGHTSETUP(3);
+  LIGHTSETUP(4);
+  LIGHTSETUP(5);
+  LIGHTSETUP(6);
+  LIGHTSETUP(7);
+
+  glEnable(GL_LIGHTING);
+}
+
+void Lighting::SetLightEnabled(int lightID, bool e)
+{
+  if (lightEnabled[lightID] != e)
+  {
+    lightEnabled[lightID] = e;
+    switch(lightID)
+    {
+      case 0: TURNONLIGHT(0); break;
+      case 1: TURNONLIGHT(1); break;
+      case 2: TURNONLIGHT(2); break;
+      case 3: TURNONLIGHT(3); break;
+      case 4: TURNONLIGHT(4); break;
+      case 5: TURNONLIGHT(5); break;
+      case 6: TURNONLIGHT(6); break;
+      case 7: TURNONLIGHT(7); break;
+    }
+  }
+}
+
+void Lighting::SetLightPosition(int lightID, const float pos[4])
+{
+  memcpy(lightPosition + 4*lightID, pos, 4 * sizeof(float));
+}
+
+void Lighting::SetLightIntensity(int lightID, float intensity)
+{
+  lightIntensity[lightID] = intensity;
+  if (enableAmbientTerm)
+    lKa[lightID*4+0] = lKa[lightID*4+1] = lKa[lightID*4+2] = intensity;
+
+  if (enableDiffuseTerm)
+    lKd[lightID*4+0] = lKd[lightID*4+1] = lKd[lightID*4+2] = intensity;
+
+  if (enableSpecularTerm)
+    lKs[lightID*4+0] = lKs[lightID*4+1] = lKs[lightID*4+2] = intensity;
+
+  //lKa[lightID*4+3] = lKd[lightID*4+3] = lKs[lightID*4+3] = 1.0;
+}
+
+void Lighting::SetAmbientEnabled(bool e)
+{
+  if (enableAmbientTerm != e)
+  {
+    enableAmbientTerm = e;
+    for(int light = 0; light < 8; light++)
+    {
+      if (enableAmbientTerm) 
+        lKa[light*4+0] = lKa[light*4+1] = lKa[light*4+2] = lightIntensity[light];
+      else
+        lKa[light*4+0] = lKa[light*4+1] = lKa[light*4+2] = 0.0f;
+    }
+  }
+}
+
+void Lighting::SetDiffuseEnabled(bool e)
+{
+  if (enableDiffuseTerm != e)
+  {
+    enableDiffuseTerm = e;
+    for(int light = 0; light < 8; light++)
+    {
+      if (enableDiffuseTerm)
+        lKd[light*4+0] = lKd[light*4+1] = lKd[light*4+2] = lightIntensity[light];
+      else
+        lKd[light*4+0] = lKd[light*4+1] = lKd[light*4+2] = 0.0f;
+    }
+  }
+}
+
+void Lighting::SetSpecularEnabled(bool e)
+{
+  if (enableSpecularTerm != e)
+  {
+    enableSpecularTerm = e;
+    for(int light = 0; light < 8; light++)
+    {
+      if (enableSpecularTerm)
+        lKs[light*4+0] = lKs[light*4+1] = lKs[light*4+2] = lightIntensity[light];
+      else
+        lKs[light*4+0] = lKs[light*4+1] = lKs[light*4+2] = 0.0f;
+    }
+  }
+}
+
+bool Lighting::ToggleLight(int lightID) 
+{
+  assert(lightID >= 0 && lightID < 8);
+  SetLightEnabled(lightID, !lightEnabled[lightID]);
+  return lightEnabled[lightID];
+}
+
+bool Lighting::HasLightEnabled() const
+{
+  for(int i = 0; i < 8; i++)
+    if(lightEnabled[i] == true)
+      return true;
+  return false;
+}
+
+bool Lighting::HasLightDisabled() const
+{
+  for(int i = 0; i < 8; i++)
+    if(lightEnabled[i] == false)
+      return true;
+  return false; 
+}
diff --git a/src/liblighting/lighting.h b/libraries/lighting/lighting.h
similarity index 57%
rename from src/liblighting/lighting.h
rename to libraries/lighting/lighting.h
index 505b7fabe58a2060d23e7145af8c415d56e5aec7..1a743dd4330347a83ab03ba5cbfeadb8df27b11a 100644
--- a/src/liblighting/lighting.h
+++ b/libraries/lighting/lighting.h
@@ -72,80 +72,108 @@
 #include <stdlib.h>
 #include <string.h>
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
-#include "openGL-headers.h"
-#include "macros.h"
+class ConfigFile;
 
-namespace vega
-{
 class Lighting
 {
 public:
 
+  Lighting();
   // read OpenGL lighting parameters from a configuration file
-  Lighting(char * configFilename);
+  Lighting(const char * configFilename);
+  virtual ~Lighting() {}
 
   // call this inside your OpenGL display routine, after setting up the modelview and projection matrices
-  void LightScene(); 
-
-  inline void GetLightPosition(int lightID, float * pos);
-  inline void GetLightAmbientTerm(int lightID, float * Ka);
-  inline void GetLightDiffuseTerm(int lightID, float * Kd);
-  inline void GetLightSpecularTerm(int lightID, float * Ks);
-  inline float GetLightIntensity(int lightID);
-  inline float GetAmbientIntensity();
-  inline bool IsLightEnabled(int lightID);
-  inline bool IsAmbientEnabled() { return enableAmbientTerm; }
-  inline bool IsDiffuseEnabled() { return enableDiffuseTerm; }
-  inline bool IsSpecularEnabled() { return enableSpecularTerm; }
+  void LightScene() const; 
+
+  inline void GetLightPosition(int lightID, float pos[4]) const;
+  inline void GetLightAmbientTerm(int lightID, float Ka[4]) const;
+  inline void GetLightDiffuseTerm(int lightID, float Kd[4]) const;
+  inline void GetLightSpecularTerm(int lightID, float Ks[4]) const;
+  inline float GetLightIntensity(int lightID) const;
+  inline float GetAmbientIntensity() const;
+  inline bool IsLightEnabled(int lightID) const;
+  inline bool IsAmbientEnabled() const { return enableAmbientTerm; }
+  inline bool IsDiffuseEnabled() const { return enableDiffuseTerm; }
+  inline bool IsSpecularEnabled() const { return enableSpecularTerm; }
+
+  // pos is a 4-float array representing generalized coord. of the light source
+  void SetLightPosition(int lightID, const float pos[4]);
+  void SetLightEnabled(int lightID, bool e);
+  void SetLightIntensity(int lightID, float intensity);
+  void SetAmbientEnabled(bool e);
+  void SetDiffuseEnabled(bool e);
+  void SetSpecularEnabled(bool e);
+
+  // set a light box. Move all lights to the vertices of the box
+  // l0: (bmin[0], bmin[1], bmin[2])
+  // l1: (bmax[0], bmin[1], bmin[2])
+  // l2: (bmin[0], bmax[1], bmin[2])
+  // l3: (bmax[0], bmax[1], bmin[2])
+  // l4: (bmin[0], bmin[1], bmax[2])
+  // l5: (bmax[0], bmin[1], bmax[2])
+  // l6: (bmin[0], bmax[1], bmax[2])
+  // l7: (bmax[0], bmax[1], bmax[2])
+  void SetLightBox(const double boxMin[3], const double boxMax[3]);
+  void SetLightBox(const float boxMin[3], const float boxMax[3]);
+  bool HasLightEnabled() const;  // return whether at least one light is enabled
+  bool HasLightDisabled() const; // return wheter at least one light is disabled
+  void SetAllLightsEnabled(bool e);
+  void SetAllLightsIntensity(float intensity);
+
+  // toggle light[lightID], return whether the toggled light is enabled
+  bool ToggleLight(int lightID);
+
+  // save lighting config to file
+  int SaveConfig(const char * filename);
 
 protected:
-  float ambientIntensity;
-  bool localViewer, twoSidedLighting;
+  void buildConfig(ConfigFile & );
+  void initialize();
+
+  float ambientIntensity = 0.0f;
+  bool localViewer = true, twoSidedLighting = false;
   bool lightEnabled[8];
   float lightPosition[32];
   float lKa[32], lKd[32], lKs[32];
   float lightIntensity[8];
-  bool enableAmbientTerm, enableDiffuseTerm, enableSpecularTerm;
+  bool enableAmbientTerm = true, enableDiffuseTerm = true, enableSpecularTerm = true;
 };
 
-inline float Lighting::GetLightIntensity(int lightID)
+inline float Lighting::GetLightIntensity(int lightID) const
 {
   return lightIntensity[lightID];
 }
 
-inline void Lighting::GetLightPosition(int lightID, float * pos)
+inline void Lighting::GetLightPosition(int lightID, float pos[4]) const
 {
-  memcpy(pos, &(lightPosition[4*lightID]), 4* sizeof(float));
+  memcpy(pos, &(lightPosition[4*lightID]), 4 * sizeof(float));
 }
 
-inline void Lighting::GetLightAmbientTerm(int lightID, float * Ka)
+inline void Lighting::GetLightAmbientTerm(int lightID, float Ka[4]) const
 {
   memcpy(Ka, &(lKa[4*lightID]), 4* sizeof(float));
 }
 
-inline void Lighting::GetLightDiffuseTerm(int lightID, float * Kd)
+inline void Lighting::GetLightDiffuseTerm(int lightID, float Kd[4]) const
 {
   memcpy(Kd, &(lKd[4*lightID]), 4* sizeof(float));
 }
 
-inline void Lighting::GetLightSpecularTerm(int lightID, float * Ks)
+inline void Lighting::GetLightSpecularTerm(int lightID, float Ks[4]) const
 {
   memcpy(Ks, &(lKs[4*lightID]), 4* sizeof(float));
 }
 
-inline float Lighting::GetAmbientIntensity()
+inline float Lighting::GetAmbientIntensity() const
 {
   return ambientIntensity;
 }
 
-inline bool Lighting::IsLightEnabled(int lightID)
+inline bool Lighting::IsLightEnabled(int lightID) const
 {
   return lightEnabled[lightID];
 }
-}
+
 #endif
 
diff --git a/libraries/listIO/listIO.cpp b/libraries/listIO/listIO.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6613ad263eda62bef88a9185216cda5417b89897
--- /dev/null
+++ b/libraries/listIO/listIO.cpp
@@ -0,0 +1,412 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "listIO" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "listIO.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <algorithm>
+using namespace std;
+
+// removes all whitespace characters from string s
+void ListIO::stripBlanks(char * s)
+{
+  char * w = s;
+  while (*w != '\0')
+  {
+    while (*w != '\0' && !isgraph(*w)) // erase blank
+    {
+      char * u = w;
+      while (*u != '\0') // shift everything left one char
+      {
+        *u = *(u+1);
+        u++;
+      }
+    }
+    w++;
+  }
+}
+
+int compareListIO(const void * a, const void * b)
+{
+  return ( *(int*)a - *(int*)b );
+}
+
+
+int ListIO::load(const char * filename, std::vector<int> & listEntries, int offset, bool sort)
+{
+  ifstream fin(filename,ios::binary);
+  if (!fin)
+  {
+    printf("Error: could not open file %s.\n",filename);
+    return 1;
+  }
+  
+  listEntries.clear();
+
+  int lastNum = -1;
+  bool hasLastNum = false, consecutive = false;
+  fin >> ws;
+  string line;
+  istringstream ss;
+  while(!fin.eof())
+  {
+    line.clear();
+    getline(fin, line);
+    fin >> ws;
+    if (line.size() == 0)
+      continue;
+    if (line[0] == '#')
+      continue; // it's a comment
+
+    ss.str(line);
+    ss.seekg(0);
+    while(!ss.eof())
+    {
+      int k = 0;
+      ss.clear();
+      ss >> k;
+      if (!ss.fail())
+      {
+        if (consecutive) // we have loaded "a : b"
+        {
+          for(int i = lastNum+1; i < k; i++) 
+            listEntries.push_back(i - offset);
+          consecutive = false;
+        }
+        listEntries.push_back(k - offset);
+        lastNum = k;
+        hasLastNum = true;
+      } 
+      else
+      {
+        ss.clear(); //clear fail bit
+        char separateSymbol = 0;
+        ss >> separateSymbol;
+        if (separateSymbol == ':' && hasLastNum)
+          consecutive = true;
+        else if (separateSymbol == ',') // including the case separateSymbol == ','
+          consecutive = false;
+        else // unknown symbols, do nothing
+          consecutive = false; 
+      }
+      ss >> ws;
+    } // end while (!ss.eof)
+  } // end while (!fin.eof)
+  fin.close();
+
+  // sort the list entries
+  if (sort)
+    std::sort(listEntries.begin(), listEntries.end());
+  return 0;
+}
+
+int ListIO::save(const char * filename, const std::vector<int> & listEntries, int offset)
+{
+  return save(filename, listEntries.size(), listEntries.data(), offset);
+}
+
+int ListIO::load(const char * filename, std::set<int> & listEntries, int offset)
+{
+  vector<int> v;
+  int ret = load(filename, v, offset, false);
+  if (ret != 0)
+    return ret;
+  listEntries.clear();
+  listEntries.insert(v.begin(), v.end());
+  return 0;
+}
+
+int ListIO::save(const char * filename, const std::set<int> & listEntries, int offset)
+{
+  vector<int> v(listEntries.begin(), listEntries.end());
+  return save(filename, v, offset);
+}
+  
+int ListIO::load(const char * filename, int * numListEntries, int ** listEntries, int offset, bool sort)
+{
+#ifndef OLD_LOAD
+  *numListEntries = 0;
+  *listEntries = NULL;
+
+  vector<int> nums;
+  int ret = load(filename, nums, offset, sort);
+  if (ret != 0)
+    return ret;
+
+  *numListEntries = nums.size();
+  *listEntries = (int*) malloc (sizeof(int) * nums.size());
+  memcpy(*listEntries, &(nums[0]), sizeof(int) * nums.size());
+
+  #ifdef DEBUG_LOADLIST
+    cout << "List is: " << endl;
+    for(int i = 0; i < nums.size(); i++)
+    {
+      cout << (*listEntries)[i] << " ";
+    }
+    cout << endl;
+  #endif
+
+  return 0;
+
+#else
+  // comma-separated text file of fixed vertices
+  FILE * fin;
+  fin = fopen(filename,"r");
+  if (!fin)
+  {
+    printf("Error: could not open file %s.\n",filename);
+    return 1;
+  }
+
+  *numListEntries = 0;
+
+  char s[4096];
+  while (fgets(s,4096,fin) != NULL)
+  { 
+    stripBlanks(s);
+
+    char * pch;
+    pch = strtok (s,",");
+    while ((pch != NULL) && (isdigit(*pch)))
+    {
+      (*numListEntries)++;
+      pch = strtok (NULL, ",");
+    } 
+  }
+
+  *listEntries = (int*) malloc (sizeof(int) * (*numListEntries));
+
+  rewind(fin);
+
+  (*numListEntries) = 0;
+
+  while (fgets(s,4096,fin) != NULL)
+  {
+    stripBlanks(s);
+    char * pch;
+    pch = strtok (s,",");
+    while ((pch != NULL) && (isdigit(*pch)))
+    {
+      (*listEntries)[*numListEntries] = atoi(pch) - offset;
+      (*numListEntries)++;
+      pch = strtok (NULL, ",");
+    }
+  }
+
+  // sort the list entries
+  if (sort)
+    qsort ((*listEntries), *numListEntries, sizeof(int), compareListIO);
+
+  fclose(fin);
+
+  return 0;
+#endif
+}
+
+int listIOComparator(const void * a, const void * b)
+{
+  if (*(int*)a < *(int*)b)
+    return -1;
+
+  if (*(int*)a == *(int*)b)
+    return 0;
+
+  return 1;
+}
+
+void ListIO::sort(int numListEntries, int * listEntries)
+{
+  qsort(listEntries,numListEntries,sizeof(int),listIOComparator);
+}
+
+int ListIO::save(const char * filename, int numListEntries, const int * listEntries, int offset)
+{
+  // comma-separated text file of fixed vertices
+  FILE * fout;
+  fout = fopen(filename, "w");
+  if (!fout)
+  {
+    printf("Error: could not open list file %s.\n",filename);
+    return 1;
+  }
+
+  for(int nv=0; nv < numListEntries; nv++)
+  {
+    fprintf(fout, "%d,", listEntries[nv] + offset);
+    if (nv % 8 == 7)
+      fprintf(fout,"\n");
+  }
+  fprintf(fout,"\n");
+
+  fclose(fout);
+
+  return 0;
+}
+
+void ListIO::print(int numListEntries, int * listEntries)
+{
+  for(int nv=0; nv < numListEntries; nv++)
+  {
+    printf("%d,", listEntries[nv]);
+    if (nv % 8 == 7)
+      printf("\n");
+  }
+  printf("\n"); fflush(NULL);
+}
+
+int ListIO::loadBinary(const char * filename, int * numListEntries, int ** listEntries, int offset)
+{
+  FILE * fin;
+  fin = fopen(filename, "rb");
+  if (!fin)
+  {
+    printf("Error: could not open list file %s.\n",filename);
+    return 1;
+  }
+
+  int code = loadBinary(fin, numListEntries, listEntries, offset);
+  fclose(fin);
+  return code;
+}
+
+int ListIO::loadBinary(FILE * fin, int * numListEntries, int ** listEntries, int offset)
+{
+  int item = fread(numListEntries, sizeof(int), 1, fin);
+  if (item != 1)
+  {
+    printf("Error: could not read the number of list entries.\n");
+    return 1;
+  }
+  *listEntries = (int*) malloc (sizeof(int) * (*numListEntries));
+
+  for(int nv=0; nv < *numListEntries; nv++)
+  {
+    item = fread(&(*listEntries)[nv], sizeof(int), 1, fin);
+    if (item != 1)
+    {
+      printf("Error: could not read the list entry %d.\n", nv);
+      return 1;
+    }
+  }
+
+  for(int nv=0; nv < *numListEntries; nv++)
+    (*listEntries)[nv] -= offset;
+
+  return 0;
+}
+
+int ListIO::saveBinary(const char * filename, int numListEntries, int * listEntries, int offset)
+{
+  FILE * fout;
+  fout = fopen(filename, "wb");
+  if (!fout)
+  {
+    printf("Error: could not open list file %s.\n",filename);
+    return 1;
+  }
+
+  int code = saveBinary(fout, numListEntries, listEntries, offset);
+  fclose(fout);
+  return code;
+}
+
+int ListIO::saveBinary(FILE * fout, int numListEntries, int * listEntries, int offset)
+{
+  fwrite(&numListEntries, sizeof(int), 1, fout);
+
+  for(int nv=0; nv < numListEntries; nv++)
+  {
+    int value = listEntries[nv] + offset;
+    fwrite(&value, sizeof(int), 1, fout);
+  }
+
+  return 0;
+}
+
+// loads/saves multiple lists to one binary file
+int ListIO::loadBinaryMulti(const char * filename, int * numLists, int ** numListEntries, int *** listEntries, int offset)
+{
+  FILE * fin;
+  fin = fopen(filename, "rb");
+  if (!fin)
+  {
+    printf("Error: could not open list file %s.\n",filename);
+    return 1;
+  }
+
+  int item = fread(numLists, sizeof(int), 1, fin);
+  if (item != 1)
+  {
+    printf("Error: could not read the number of list.\n");
+    return 1;
+  }
+
+  *numListEntries = (int*) malloc (sizeof(int) * *numLists);
+  *listEntries = (int**) malloc (sizeof(int*) * *numLists);
+
+  int code = 0;
+  for(int i=0; i<*numLists; i++)
+    code = code || loadBinary(fin, &((*numListEntries)[i]), &((*listEntries)[i]), offset);
+
+  fclose(fin);
+
+  return code;
+}
+
+int ListIO::saveBinaryMulti(const char * filename, int numLists, int * numListEntries, int ** listEntries, int offset)
+{
+  FILE * fout;
+  fout = fopen(filename, "wb");
+  if (!fout)
+  {
+    printf("Error: could not open list file %s.\n",filename);
+    return 1;
+  }
+
+  fwrite(&numLists, sizeof(int), 1, fout);
+
+  int code = 0;
+  for(int i=0; i<numLists; i++)
+    code = code || saveBinary(fout, numListEntries[i], listEntries[i], offset);
+
+  fclose(fout);
+
+  return code;
+}
+
diff --git a/libraries/listIO/listIO.h b/libraries/listIO/listIO.h
new file mode 100644
index 0000000000000000000000000000000000000000..974d9c8381b04e7cb6f5ddb9acb14ce0fdbc367a
--- /dev/null
+++ b/libraries/listIO/listIO.h
@@ -0,0 +1,78 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "listIO" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _LISTIO_H_
+#define _LISTIO_H_
+
+/*
+  A class to load an integer list from a text file into memory. 
+*/
+
+#include <stdio.h>
+#include <vector>
+#include <set>
+
+class ListIO
+{
+public:
+
+  // returns 0 on success and non-zero otherwise
+  // load: sort the loaded entries if sortAfterLoad is true
+  // numbers are separated by ' ' or ','. Support syntax "1:5" to load {1,2,3,4,5}
+  // comments start with '#'
+  static int load(const char * filename, int * numListEntries, int ** listEntries, int offset=0, bool sortAfterLoad = true);
+  static int save(const char * filename, int numListEntries, const int * listEntries, int offset=0);
+
+  static int load(const char * filename, std::vector<int> & listEntries, int offset=0, bool sortAfterLoad = true);
+  static int save(const char * filename, const std::vector<int> & listEntries, int offset=0);
+
+  static int load(const char * filename, std::set<int> & listEntries, int offset=0);
+  static int save(const char * filename, const std::set<int> & listEntries, int offset=0);
+
+  static int loadBinary(const char * filename, int * numListEntries, int ** listEntries, int offset=0);
+  static int loadBinary(FILE * fin, int * numListEntries, int ** listEntries, int offset=0);
+  static int saveBinary(const char * filename, int numListEntries, int * listEntries, int offset=0);
+  static int saveBinary(FILE * fout, int numListEntries, int * listEntries, int offset=0);
+
+  // loads/saves multiple lists to one binary file
+  static int loadBinaryMulti(const char * filename, int * numLists, int ** numListEntries, int *** listEntries, int offset=0);
+  static int saveBinaryMulti(const char * filename, int numLists, int * numListEntries, int ** listEntries, int offset=0);
+
+  static void sort(int numListEntries, int * listEntries);
+  static void print(int numListEntries, int * listEntries);
+  static void stripBlanks(char * s);
+
+protected:
+};
+
+#endif
+
diff --git a/src/libmassSpringSystem/massSpringSystem.cpp b/libraries/massSpringSystem/massSpringSystem.cpp
similarity index 85%
rename from src/libmassSpringSystem/massSpringSystem.cpp
rename to libraries/massSpringSystem/massSpringSystem.cpp
index 93ff8d74a6d0c19052fd87a882b5e35589f06ea1..51808968e22e0c03f04fe22f6d8fcf23b603ef85 100644
--- a/src/libmassSpringSystem/massSpringSystem.cpp
+++ b/libraries/massSpringSystem/massSpringSystem.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,8 +41,6 @@
 #include "massSpringSystem.h"
 using namespace std;
 
-namespace vega
-{
 MassSpringSystem::MassSpringSystem(int numParticles_, double * masses_, double * restPositions_, int numEdges_, int * edges_, int * edgeGroups_, int numMaterialGroups_, double * groupStiffness_, double * groupDamping_, int addGravity_) : addGravity(addGravity_), g(9.81)
 {
   GenerateMassSpringSystem(numParticles_, masses_, restPositions_, numEdges_, edges_, edgeGroups_, numMaterialGroups_, groupStiffness_, groupDamping_);
@@ -263,6 +265,7 @@ MassSpringSystem::~MassSpringSystem()
   free(masses);
   free(restPositions);
   free(restLengths);
+  free(edges);
   free(inverseIndices);
   free(edgeGroups);
   free(groupStiffness);
@@ -500,7 +503,34 @@ double MassSpringSystem::GetTriangleSurfaceArea(double * p0, double * p1, double
   return 0.5 * sqrt(crossp[0]*crossp[0] + crossp[1]*crossp[1] + crossp[2]*crossp[2]);
 }
 
-void MassSpringSystem::ComputeForce(double * u, double * f, bool addForce)
+double MassSpringSystem::ComputeEnergy(const double * u)
+{
+  double energy = 0.0;
+  AddEnergy(u, &energy, 0, numEdges);
+  return energy;
+}
+
+void MassSpringSystem::AddEnergy(const double * u, double * energy, int startEdge, int endEdge)
+{
+  for(int i=startEdge; i<endEdge; i++)
+  {
+    int group = edgeGroups[i];
+    int particleA = edges[2*i+0];
+    int particleB = edges[2*i+1];
+
+    double z[3]; // vector from A to B
+    z[0] = restPositions[3*particleB+0] + u[3*particleB+0] - restPositions[3*particleA+0] - u[3*particleA+0];    
+    z[1] = restPositions[3*particleB+1] + u[3*particleB+1] - restPositions[3*particleA+1] - u[3*particleA+1]; 
+    z[2] = restPositions[3*particleB+2] + u[3*particleB+2] - restPositions[3*particleA+2] - u[3*particleA+2];    
+
+    double len = sqrt(z[0]*z[0] + z[1]*z[1] + z[2]*z[2]);
+    double deltaLen = (len - restLengths[i]);
+
+    *energy += 0.5 * groupStiffness[group] * deltaLen * deltaLen;
+  }
+}
+
+void MassSpringSystem::ComputeForce(const double * u, double * f, bool addForce)
 {
   if (!addForce)
     memset(f, 0, sizeof(double) * 3 * numParticles);
@@ -511,7 +541,7 @@ void MassSpringSystem::ComputeForce(double * u, double * f, bool addForce)
     ComputeGravity(f, true);
 }
 
-void MassSpringSystem::AddForce(double * u, double * f, int startEdge, int endEdge)
+void MassSpringSystem::AddForce(const double * u, double * f, int startEdge, int endEdge)
 {
   for(int i=startEdge; i<endEdge; i++)
   {
@@ -581,7 +611,7 @@ void MassSpringSystem::GetStiffnessMatrixTopology(SparseMatrix ** stiffnessMatri
   *stiffnessMatrixTopology = new SparseMatrix(&KOutline);
 }
 
-void MassSpringSystem::ComputeStiffnessMatrix(double * u, SparseMatrix * K, bool addMatrix)
+void MassSpringSystem::ComputeStiffnessMatrix(const double * u, SparseMatrix * K, bool addMatrix)
 {
   if (!addMatrix)
     K->ResetToZero();
@@ -589,7 +619,7 @@ void MassSpringSystem::ComputeStiffnessMatrix(double * u, SparseMatrix * K, bool
   AddStiffnessMatrix(u, K, 0, numEdges);
 }
 
-void MassSpringSystem::AddStiffnessMatrix(double * u, SparseMatrix * K, int startEdge, int endEdge)
+void MassSpringSystem::AddStiffnessMatrix(const double * u, SparseMatrix * K, int startEdge, int endEdge)
 {
   for(int i=startEdge; i<endEdge; i++)
   {
@@ -632,7 +662,84 @@ void MassSpringSystem::AddStiffnessMatrix(double * u, SparseMatrix * K, int star
   }
 }
 
-void MassSpringSystem::ComputeDampingForce(double * uvel, double * f, bool addForce)
+void MassSpringSystem::ComputeEdgeInfo(int eid, const double * u, double * energy, double f[6], double K[36])
+{
+  int group = edgeGroups[eid];
+  int particleA = edges[2*eid+0];
+  int particleB = edges[2*eid+1];
+
+  double z[3]; // vector from A to B
+  z[0] = restPositions[3*particleB+0] + u[3*particleB+0] - restPositions[3*particleA+0] - u[3*particleA+0];
+  z[1] = restPositions[3*particleB+1] + u[3*particleB+1] - restPositions[3*particleA+1] - u[3*particleA+1];
+  z[2] = restPositions[3*particleB+2] + u[3*particleB+2] - restPositions[3*particleA+2] - u[3*particleA+2];
+
+  double len = sqrt(z[0]*z[0] + z[1]*z[1] + z[2]*z[2]);
+
+  if (energy)
+  {
+    double deltaLen = (len - restLengths[eid]);
+    *energy = 0.5 * groupStiffness[group] * deltaLen * deltaLen;
+  }
+
+  if (f)
+  {
+    double force[3]; // force on particle A
+    force[0] = groupStiffness[group] * (len - restLengths[eid]) * z[0] / len;
+    force[1] = groupStiffness[group] * (len - restLengths[eid]) * z[1] / len;
+    force[2] = groupStiffness[group] * (len - restLengths[eid]) * z[2] / len;
+
+    f[0] = -force[0];
+    f[1] = -force[1];
+    f[2] = -force[2];
+
+    f[3] = force[0];
+    f[4] = force[1];
+    f[5] = force[2];
+  }
+
+  if (K) 
+  {
+    double dFdz[9];
+    double invLen = 1.0 / len;
+
+    z[0] *= invLen;
+    z[1] *= invLen;
+    z[2] *= invLen;
+
+    memset(dFdz, 0, sizeof(double) * 9);
+    dFdz[0] = 1.0 - restLengths[eid] * invLen;
+    dFdz[4] = 1.0 - restLengths[eid] * invLen;
+    dFdz[8] = 1.0 - restLengths[eid] * invLen;
+
+    for(int j=0; j<3; j++)
+      for(int k=0; k<3; k++)
+        dFdz[3*k+j] += restLengths[eid] * z[j] * z[k] * invLen;
+
+    for(int j=0; j<9; j++)
+      dFdz[j] *= groupStiffness[group];
+
+    // write matrices in place
+    for(int j=0; j<3; j++)
+    {
+      for(int k=0; k<3; k++)
+      {
+        int row = j, col = k;
+        K[col * 6 + row] = dFdz[3 * k + j];
+
+        row = j, col = k + 3;
+        K[col * 6 + row] = -dFdz[3 * k + j];
+
+        row = j + 3, col = k;
+        K[col * 6 + row] = -dFdz[3 * k + j];
+
+        row = j + 3, col = k + 3;
+        K[col * 6 + row] = dFdz[3 * k + j];
+      }
+    }
+  }
+}
+
+void MassSpringSystem::ComputeDampingForce(const double * uvel, double * f, bool addForce)
 {
   if (!addForce)
     memset(f, 0, sizeof(double) * 3 * numParticles);
@@ -640,7 +747,7 @@ void MassSpringSystem::ComputeDampingForce(double * uvel, double * f, bool addFo
   AddDampingForce(uvel, f, 0, numEdges);
 }
 
-void MassSpringSystem::AddDampingForce(double * uvel, double * f, int startEdge, int endEdge)
+void MassSpringSystem::AddDampingForce(const double * uvel, double * f, int startEdge, int endEdge)
 {
   for(int i=startEdge; i<endEdge; i++)
   {
@@ -668,14 +775,14 @@ void MassSpringSystem::AddDampingForce(double * uvel, double * f, int startEdge,
   }
 }
 
-void MassSpringSystem::ComputeStiffnessMatrixCorrection(double * u, double * du, SparseMatrix * dK, bool addMatrix)
+void MassSpringSystem::ComputeStiffnessMatrixCorrection(const double * u, double * du, SparseMatrix * dK, bool addMatrix)
 {
   if (!addMatrix)
     dK->ResetToZero();
   AddHessianApproximation(u, du, dK, 0, numEdges);
 }
 
-void MassSpringSystem::AddHessianApproximation(double * u, double * du, SparseMatrix * dK, int startEdge, int endEdge)
+void MassSpringSystem::AddHessianApproximation(const double * u, double * du, SparseMatrix * dK, int startEdge, int endEdge)
 {
   for(int i=startEdge; i<endEdge; i++)
   {
@@ -772,7 +879,7 @@ double MassSpringSystem::GetCubeVolume(double a[3], double b[3], double c[3], do
   return (g[0] - a[0]) * (g[1] - a[1]) * (g[2] - a[2]);
 }
 
-void MassSpringSystem::CreateObjMesh(char * filename, double * u)
+void MassSpringSystem::CreateObjMesh(const char * filename, double * u)
 {
   FILE * fout = fopen(filename, "w");
 
@@ -797,4 +904,3 @@ void MassSpringSystem::CreateObjMesh(char * filename, double * u)
   fclose(fout);
 }
 
-}
diff --git a/src/libmassSpringSystem/massSpringSystem.h b/libraries/massSpringSystem/massSpringSystem.h
similarity index 77%
rename from src/libmassSpringSystem/massSpringSystem.h
rename to libraries/massSpringSystem/massSpringSystem.h
index d37868a6aa97cb804f86aa71a021b5a7c686c38e..5b1c847ad9b96515e6e8c8751ea868828308bb0f 100644
--- a/src/libmassSpringSystem/massSpringSystem.h
+++ b/libraries/massSpringSystem/massSpringSystem.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -45,8 +49,6 @@
 
 #include "sparseMatrix.h"
 
-namespace vega
-{
 enum MassSpringSystemElementType {TET, CUBE};
 
 class MassSpringSystem
@@ -90,21 +92,24 @@ public:
   // creates the mass matrix (diagonal); each diagonal entry is expanded into a diagonal submatrix of size 'expanded' (typically, for 3D simulations, expanded should be 3)
   void GenerateMassMatrix(SparseMatrix ** M, int expanded=3);
 
+  // compute the elastic energy, under deformation u
+  virtual double ComputeEnergy(const double * u);
+
   // compute the internal elastic force, under deformation u
   // important: the force f has the same sign as in the other deformable models in Vega, i.e., 
   //   it appears on the left side in equation M u'' + D u' + f = f_ext.
   //   If you want f to be interpreted as an external mass-spring force acting
   //   on the particles, you must flip the sign of f.
   //   Same comment applies to damping forces, stiffness matrices and their Hessian corrections.
-  virtual void ComputeForce(double * u, double * f, bool addForce=false); // if addForce, f will be not be reset to zero prior to adding the forces
+  virtual void ComputeForce(const double * u, double * f, bool addForce=false); // if addForce, f will be not be reset to zero prior to adding the forces
 
   // compute the damping force (it damps any relative velocities along each edge)
-  virtual void ComputeDampingForce(double * uvel, double * f, bool addForce=false); 
+  virtual void ComputeDampingForce(const double * uvel, double * f, bool addForce=false); 
   // compute the tangent stiffness matrix
   void GetStiffnessMatrixTopology(SparseMatrix ** stiffnessMatrixTopology); // call once to establish the location of sparse entries of the stiffness matrix
-  virtual void ComputeStiffnessMatrix(double * u, SparseMatrix * K, bool addMatrix=false);
+  virtual void ComputeStiffnessMatrix(const double * u, SparseMatrix * K, bool addMatrix=false);
   // computes an approximation to dK, using the Hessian of internal forces, assuming the deformations change from u to u + du
-  virtual void ComputeStiffnessMatrixCorrection(double * u, double * du, SparseMatrix * dK, bool addMatrix=false);
+  virtual void ComputeStiffnessMatrixCorrection(const double * u, double * du, SparseMatrix * dK, bool addMatrix=false);
 
   // computes the gravitational force (result goes into f)
   void ComputeGravity(double * f, bool addForce=false);
@@ -112,14 +117,22 @@ public:
   // creates a 3D triangle mesh, where each mass-spring system edge is one (degenerate) triangle
   // useful to visualize the mass-spring system
   // u is the deformation
-  void CreateObjMesh(char * filename, double * u = NULL); // if NULL, assumes zero deformation 
+  void CreateObjMesh(const char * filename, double * u = NULL); // if NULL, assumes zero deformation 
 
   // == advanced routines below ===
-
-  void AddForce(double * u, double * f, int startEdge, int endEdge); 
-  void AddStiffnessMatrix(double * u, SparseMatrix * K, int startEdge, int endEdge);
-  void AddDampingForce(double * uvel, double * f, int startEdge, int endEdge); 
-  void AddHessianApproximation(double * u, double * du, SparseMatrix * dK, int startEdge, int endEdge);
+  // compute the energy, internal forces and stiffness matrix of a single spring.
+  // u is the input displacement with dimention (# vtx x 3)
+  // E is a pointer to a double
+  // f is a pointer to a array of 6 doubles (2x3)
+  // K is a pointer to a array of 36 doubles ((2x3) x (2x3)). The matrix is store in column major.
+  // E, f, K can be nullptr. If it is, the function will not compute it.
+  void ComputeEdgeInfo(int edgeId, const double * u, double * energy, double f[6], double K[36]);
+
+  void AddEnergy(const double * u, double * energy, int startEdge, int endEdge); 
+  void AddForce(const double * u, double * f, int startEdge, int endEdge); 
+  void AddStiffnessMatrix(const double * u, SparseMatrix * K, int startEdge, int endEdge);
+  void AddDampingForce(const double * uvel, double * f, int startEdge, int endEdge); 
+  void AddHessianApproximation(const double * u, double * du, SparseMatrix * dK, int startEdge, int endEdge);
 
 protected:
 
@@ -149,6 +162,6 @@ protected:
   int addGravity;
   double g;
 };
-}
+
 #endif
 
diff --git a/src/libmassSpringSystem/massSpringSystemFromCubicMesh.cpp b/libraries/massSpringSystem/massSpringSystemFromCubicMesh.cpp
similarity index 80%
rename from src/libmassSpringSystem/massSpringSystemFromCubicMesh.cpp
rename to libraries/massSpringSystem/massSpringSystemFromCubicMesh.cpp
index 53bb21b42cbd72b3f2da21aae54d03d86bb428be..d2684ec62d26b3fe502d27b12f6d8b29f740ba81 100644
--- a/src/libmassSpringSystem/massSpringSystemFromCubicMesh.cpp
+++ b/libraries/massSpringSystem/massSpringSystemFromCubicMesh.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,8 +34,6 @@
 #include "cubicMesh.h"
 #include "massSpringSystemFromCubicMesh.h"
 
-namespace vega
-{
 int MassSpringSystemFromCubicMesh::GenerateMassSpringSystem(CubicMesh * cubicMesh, MassSpringSystem ** massSpringSystem, double density, double tensileStiffness, double damping, int addGravity)
 {
   int numParticles;
@@ -55,5 +57,4 @@ int MassSpringSystemFromCubicMesh::GenerateMassSpringSystem(CubicMesh * cubicMes
 
   return 0;
 }
-}
 
diff --git a/src/libmassSpringSystem/massSpringSystemFromCubicMesh.h b/libraries/massSpringSystem/massSpringSystemFromCubicMesh.h
similarity index 79%
rename from src/libmassSpringSystem/massSpringSystemFromCubicMesh.h
rename to libraries/massSpringSystem/massSpringSystemFromCubicMesh.h
index a5c1dc04e289bc954c72014324998d3b925248a9..f4d9c314e1211b38bcfa9ea0f5e852863d55db31 100644
--- a/src/libmassSpringSystem/massSpringSystemFromCubicMesh.h
+++ b/libraries/massSpringSystem/massSpringSystemFromCubicMesh.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 
 #include "massSpringSystem.h"
 
-namespace vega
-{
 /*
   This class can generate a mass-spring system from the given cube mesh: 
   each cube mesh vertex becomes a mass point, and each pair of vertices
@@ -49,6 +51,6 @@ public:
 
   static int GenerateMassSpringSystem(CubicMesh * CubicMesh, MassSpringSystem ** massSpringSystem, double density, double tensileStiffness, double damping, int addGravity=0);
 };
-}
+
 #endif
 
diff --git a/src/libmassSpringSystem/massSpringSystemFromCubicMeshConfigFile.cpp b/libraries/massSpringSystem/massSpringSystemFromCubicMeshConfigFile.cpp
similarity index 80%
rename from src/libmassSpringSystem/massSpringSystemFromCubicMeshConfigFile.cpp
rename to libraries/massSpringSystem/massSpringSystemFromCubicMeshConfigFile.cpp
index d70da717e14cb6e67454fe02907c7e97960ab8e5..5008466596fa82e7ee32eedb76e0533f50e249ef 100644
--- a/src/libmassSpringSystem/massSpringSystemFromCubicMeshConfigFile.cpp
+++ b/libraries/massSpringSystem/massSpringSystemFromCubicMeshConfigFile.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,9 +36,7 @@
 #include "massSpringSystemFromCubicMesh.h"
 #include "massSpringSystemFromCubicMeshConfigFile.h"
 
-namespace vega
-{
-char * MassSpringSystemFromCubicMeshConfigFile::DuplicateString(char * s)
+char * MassSpringSystemFromCubicMeshConfigFile::DuplicateString(const char * s)
 {
   // strdup sometimes didn't work well, so we used this
   char * p = (char*) malloc (sizeof(char) * (strlen(s) + 1));
@@ -42,7 +44,7 @@ char * MassSpringSystemFromCubicMeshConfigFile::DuplicateString(char * s)
   return p;
 }
 
-int MassSpringSystemFromCubicMeshConfigFile::GenerateMassSpringSystem(char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemCubicMeshConfiguration * massSpringSystemCubicMeshConfiguration)
+int MassSpringSystemFromCubicMeshConfigFile::GenerateMassSpringSystem(const char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemCubicMeshConfiguration * massSpringSystemCubicMeshConfiguration)
 {
   char cubicMeshFilename[4096];
   char surfaceMeshFilename[4096];
@@ -81,9 +83,7 @@ int MassSpringSystemFromCubicMeshConfigFile::GenerateMassSpringSystem(char * con
   };
   printf("sq mesh loaded.\n");
 
-  MassSpringSystemFromCubicMesh massSpringSystemFromCubicMesh;
-
-  int code = massSpringSystemFromCubicMesh.GenerateMassSpringSystem(cubeMesh, massSpringSystem, density, tensileStiffness, damping, addGravity);
+  int code = MassSpringSystemFromCubicMesh::GenerateMassSpringSystem(cubeMesh, massSpringSystem, density, tensileStiffness, damping, addGravity);
 
   delete(cubeMesh);
 
@@ -100,4 +100,3 @@ int MassSpringSystemFromCubicMeshConfigFile::GenerateMassSpringSystem(char * con
   return code;
 }
 
-}
diff --git a/src/libmassSpringSystem/massSpringSystemFromCubicMeshConfigFile.h b/libraries/massSpringSystem/massSpringSystemFromCubicMeshConfigFile.h
similarity index 75%
rename from src/libmassSpringSystem/massSpringSystemFromCubicMeshConfigFile.h
rename to libraries/massSpringSystem/massSpringSystemFromCubicMeshConfigFile.h
index 628a5fa6e738fe6a1064d5cbb10f44b7a21f160a..288eebdb1e41ad88d9ef01c0c4b8c22ab83c1bd6 100644
--- a/src/libmassSpringSystem/massSpringSystemFromCubicMeshConfigFile.h
+++ b/libraries/massSpringSystem/massSpringSystemFromCubicMeshConfigFile.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic, Daniel Schroeder                          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 
 #include "massSpringSystem.h"
 
-namespace vega
-{
 class MassSpringSystemCubicMeshConfiguration
 {
 public:
@@ -54,11 +56,11 @@ public:
 
   // generates a mass spring system from the given configuration file
   // if massSpringSystemConfiguration is not NULL, it also returns the mass spring parameters read from the file (massSpringSystemConfiguration is output parameter only)
-  int GenerateMassSpringSystem(char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemCubicMeshConfiguration * massSpringSystemCubicMeshConfiguration = NULL);
+  int GenerateMassSpringSystem(const char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemCubicMeshConfiguration * massSpringSystemCubicMeshConfiguration = NULL);
 
 protected:
-  char * DuplicateString(char*);
+  char * DuplicateString(const char*);
 };
-}
+
 #endif
 
diff --git a/src/libmassSpringSystem/massSpringSystemFromObjMesh.cpp b/libraries/massSpringSystem/massSpringSystemFromObjMesh.cpp
similarity index 81%
rename from src/libmassSpringSystem/massSpringSystemFromObjMesh.cpp
rename to libraries/massSpringSystem/massSpringSystemFromObjMesh.cpp
index 2280b0f2fa9c24b7dbcaeaac2ac1a0a710daa3b7..37428bc07a58e00b9bf597fc32ad97f4773de03f 100644
--- a/src/libmassSpringSystem/massSpringSystemFromObjMesh.cpp
+++ b/libraries/massSpringSystem/massSpringSystemFromObjMesh.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,8 +34,6 @@
 #include "objMesh.h"
 #include "massSpringSystemFromObjMesh.h"
 
-namespace vega
-{
 int MassSpringSystemFromObjMesh::GenerateMassSpringSystem(ObjMesh * quadMesh, MassSpringSystem ** massSpringSystem, double surfaceDensity, double tensileStiffness, double shearStiffness, double bendStiffness, double damping, int addGravity)
 {
   if (!quadMesh->isQuadrilateralMesh())
@@ -56,4 +58,3 @@ int MassSpringSystemFromObjMesh::GenerateMassSpringSystem(ObjMesh * quadMesh, Ma
 
   return 0;
 }
-}
diff --git a/src/libmassSpringSystem/massSpringSystemFromObjMesh.h b/libraries/massSpringSystem/massSpringSystemFromObjMesh.h
similarity index 78%
rename from src/libmassSpringSystem/massSpringSystemFromObjMesh.h
rename to libraries/massSpringSystem/massSpringSystemFromObjMesh.h
index 2bd02f0337bfe708a5645d1f9d754901b34564b1..81ad453bc8040fdbfbaca20aa4bc95ce3a21f00f 100644
--- a/src/libmassSpringSystem/massSpringSystemFromObjMesh.h
+++ b/libraries/massSpringSystem/massSpringSystemFromObjMesh.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic, Daniel Schroeder                          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,9 +35,6 @@
 #define _MASSSPRINGSYSTEMFROMOBJMESH_H_
 
 #include "massSpringSystem.h"
-
-namespace vega
-{
 class ObjMesh;
 
 class MassSpringSystemFromObjMesh
@@ -43,6 +44,6 @@ public:
   // generates a mass-spring system from the given quad mesh (builds tensile, shear, and bending springs)
   int GenerateMassSpringSystem(ObjMesh * quadMesh, MassSpringSystem ** massSpringSystem, double surfaceDensity, double tensileStiffness, double shearStiffness, double bendStiffness, double damping, int addGravity=0);
 };
-}
+
 #endif
 
diff --git a/src/libmassSpringSystem/massSpringSystemFromObjMeshConfigFile.cpp b/libraries/massSpringSystem/massSpringSystemFromObjMeshConfigFile.cpp
similarity index 83%
rename from src/libmassSpringSystem/massSpringSystemFromObjMeshConfigFile.cpp
rename to libraries/massSpringSystem/massSpringSystemFromObjMeshConfigFile.cpp
index 4ed8faa1dde41ac31a264f2453cee4c6727b84b7..f462278798dc34790c30759607f9a243286b539b 100644
--- a/src/libmassSpringSystem/massSpringSystemFromObjMeshConfigFile.cpp
+++ b/libraries/massSpringSystem/massSpringSystemFromObjMeshConfigFile.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,9 +37,7 @@
 #include "massSpringSystemFromObjMeshConfigFile.h"
 #include <string.h>
 
-namespace vega
-{
-char * MassSpringSystemFromObjMeshConfigFile::DuplicateString(char * s)
+char * MassSpringSystemFromObjMeshConfigFile::DuplicateString(const char * s)
 {
   // strdup sometimes causes problems, so we use this
   char * p = (char*) malloc (sizeof(char) * (strlen(s) + 1));
@@ -43,7 +45,7 @@ char * MassSpringSystemFromObjMeshConfigFile::DuplicateString(char * s)
   return p;
 }
 
-int MassSpringSystemFromObjMeshConfigFile::GenerateMassSpringSystem(char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemObjMeshConfiguration * massSpringSystemObjConfiguration)
+int MassSpringSystemFromObjMeshConfigFile::GenerateMassSpringSystem(const char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemObjMeshConfiguration * massSpringSystemObjConfiguration)
 {
   char massSpringMeshFilename[4096];
   double surfaceDensity, tensileStiffness, shearStiffness, bendStiffness, damping;
@@ -100,4 +102,3 @@ int MassSpringSystemFromObjMeshConfigFile::GenerateMassSpringSystem(char * confi
   return code;
 }
 
-}
diff --git a/src/libmassSpringSystem/massSpringSystemFromObjMeshConfigFile.h b/libraries/massSpringSystem/massSpringSystemFromObjMeshConfigFile.h
similarity index 75%
rename from src/libmassSpringSystem/massSpringSystemFromObjMeshConfigFile.h
rename to libraries/massSpringSystem/massSpringSystemFromObjMeshConfigFile.h
index cbf6efb765fcaa4d4b40f2ac7287dd5dda67c6e1..8aad27114f8119eb7556097b8745d7db41024da6 100644
--- a/src/libmassSpringSystem/massSpringSystemFromObjMeshConfigFile.h
+++ b/libraries/massSpringSystem/massSpringSystemFromObjMeshConfigFile.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic, Daniel Schroeder                          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 
 #include "massSpringSystem.h"
 
-namespace vega
-{
 class MassSpringSystemObjMeshConfiguration
 {
 public:
@@ -53,11 +55,11 @@ public:
 
   // generates a mass spring system from the given configuration file
   // if massSpringSystemConfiguration is not NULL, it also returns the mass spring parameters read from the file (massSpringSystemConfiguration is output parameter only)
-  int GenerateMassSpringSystem(char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemObjMeshConfiguration * massSpringSystemObjConfiguration = NULL);
+  int GenerateMassSpringSystem(const char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemObjMeshConfiguration * massSpringSystemObjConfiguration = NULL);
 
 protected:
-  char * DuplicateString(char *);
+  char * DuplicateString(const char *);
 };
-}
+
 #endif
 
diff --git a/src/libmassSpringSystem/massSpringSystemFromTetMesh.cpp b/libraries/massSpringSystem/massSpringSystemFromTetMesh.cpp
similarity index 81%
rename from src/libmassSpringSystem/massSpringSystemFromTetMesh.cpp
rename to libraries/massSpringSystem/massSpringSystemFromTetMesh.cpp
index bb49c18b900262daa8a78cee23f0ed7b5940e06f..c3e67152646692aa6fb79dcfbc7a2f55aeaae090 100644
--- a/src/libmassSpringSystem/massSpringSystemFromTetMesh.cpp
+++ b/libraries/massSpringSystem/massSpringSystemFromTetMesh.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,8 +34,6 @@
 #include "tetMesh.h"
 #include "massSpringSystemFromTetMesh.h"
 
-namespace vega
-{
 int MassSpringSystemFromTetMesh::GenerateMassSpringSystem(TetMesh * tetMesh, MassSpringSystem ** massSpringSystem, double density, double tensileStiffness, double damping, int addGravity)
 {
   int numParticles;
@@ -56,5 +58,4 @@ int MassSpringSystemFromTetMesh::GenerateMassSpringSystem(TetMesh * tetMesh, Mas
 
   return 0;
 }
-}
 
diff --git a/src/libmassSpringSystem/massSpringSystemFromTetMesh.h b/libraries/massSpringSystem/massSpringSystemFromTetMesh.h
similarity index 78%
rename from src/libmassSpringSystem/massSpringSystemFromTetMesh.h
rename to libraries/massSpringSystem/massSpringSystemFromTetMesh.h
index b1f227191050bf084cf9b24eac5a9a19deedfc3d..357061502a84e2479772bd1d04e884b55cc78c4c 100644
--- a/src/libmassSpringSystem/massSpringSystemFromTetMesh.h
+++ b/libraries/massSpringSystem/massSpringSystemFromTetMesh.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic, Daniel Schroeder                          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -38,8 +42,6 @@
   See also massSpringSystem.h
 */
 
-namespace vega
-{
 class TetMesh;
 
 class MassSpringSystemFromTetMesh
@@ -48,6 +50,6 @@ public:
 
   static int GenerateMassSpringSystem(TetMesh * tetMesh, MassSpringSystem ** massSpringSystem, double density, double tensileStiffness, double damping, int addGravity=0);
 };
-}
+
 #endif
 
diff --git a/src/libmassSpringSystem/massSpringSystemFromTetMeshConfigFile.cpp b/libraries/massSpringSystem/massSpringSystemFromTetMeshConfigFile.cpp
similarity index 79%
rename from src/libmassSpringSystem/massSpringSystemFromTetMeshConfigFile.cpp
rename to libraries/massSpringSystem/massSpringSystemFromTetMeshConfigFile.cpp
index 1a6a8c56f3e8d33327000cc4984dda6d5c99ea9a..7d910fac5033893077ef3fb4768753c7e87162f4 100644
--- a/src/libmassSpringSystem/massSpringSystemFromTetMeshConfigFile.cpp
+++ b/libraries/massSpringSystem/massSpringSystemFromTetMeshConfigFile.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,9 +36,7 @@
 #include "massSpringSystemFromTetMesh.h"
 #include "massSpringSystemFromTetMeshConfigFile.h"
 
-namespace vega
-{
-char * MassSpringSystemFromTetMeshConfigFile::DuplicateString(char * s)
+char * MassSpringSystemFromTetMeshConfigFile::DuplicateString(const char * s)
 {
   // strdup sometimes didn't work well, so we used this
   char * p = (char*) malloc (sizeof(char) * (strlen(s) + 1));
@@ -42,7 +44,7 @@ char * MassSpringSystemFromTetMeshConfigFile::DuplicateString(char * s)
   return p;
 }
 
-int MassSpringSystemFromTetMeshConfigFile::GenerateMassSpringSystem(char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemTetMeshConfiguration * massSpringSystemTetMeshConfiguration)
+int MassSpringSystemFromTetMeshConfigFile::GenerateMassSpringSystem(const char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemTetMeshConfiguration * massSpringSystemTetMeshConfiguration)
 {
   char tetMeshFilename[4096];
   char surfaceMeshFilename[4096];
@@ -81,9 +83,7 @@ int MassSpringSystemFromTetMeshConfigFile::GenerateMassSpringSystem(char * confi
   };
   printf("Tet mesh loaded.\n");
 
-  MassSpringSystemFromTetMesh massSpringSystemFromTetMesh;
-
-  int code = massSpringSystemFromTetMesh.GenerateMassSpringSystem(tetMesh, massSpringSystem, density, tensileStiffness, damping, addGravity);
+  int code = MassSpringSystemFromTetMesh::GenerateMassSpringSystem(tetMesh, massSpringSystem, density, tensileStiffness, damping, addGravity);
 
   delete(tetMesh);
 
@@ -100,4 +100,3 @@ int MassSpringSystemFromTetMeshConfigFile::GenerateMassSpringSystem(char * confi
   return code;
 }
 
-}
diff --git a/src/libmassSpringSystem/massSpringSystemFromTetMeshConfigFile.h b/libraries/massSpringSystem/massSpringSystemFromTetMeshConfigFile.h
similarity index 75%
rename from src/libmassSpringSystem/massSpringSystemFromTetMeshConfigFile.h
rename to libraries/massSpringSystem/massSpringSystemFromTetMeshConfigFile.h
index b1349d0e74064da677dc6c4b168a1eb3c9735d2f..e34a646f899a92e810bc9d1f269c49a35e6a0de3 100644
--- a/src/libmassSpringSystem/massSpringSystemFromTetMeshConfigFile.h
+++ b/libraries/massSpringSystem/massSpringSystemFromTetMeshConfigFile.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic, Daniel Schroeder                          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,8 +36,6 @@
 
 #include "massSpringSystem.h"
 
-namespace vega
-{
 class MassSpringSystemTetMeshConfiguration
 {
 public:
@@ -54,11 +56,11 @@ public:
 
   // generates a mass spring system from the given configuration file
   // if massSpringSystemConfiguration is not NULL, it also returns the mass spring parameters read from the file (massSpringSystemConfiguration is output parameter only)
-  int GenerateMassSpringSystem(char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemTetMeshConfiguration * massSpringSystemTetMeshConfiguration = NULL);
+  int GenerateMassSpringSystem(const char * configFilename, MassSpringSystem ** massSpringSystem, MassSpringSystemTetMeshConfiguration * massSpringSystemTetMeshConfiguration = NULL);
 
 protected:
-  char * DuplicateString(char*);
+  char * DuplicateString(const char*);
 };
-}
+
 #endif
 
diff --git a/src/libmassSpringSystem/renderSprings.cpp b/libraries/massSpringSystem/renderSprings.cpp
similarity index 82%
rename from src/libmassSpringSystem/renderSprings.cpp
rename to libraries/massSpringSystem/renderSprings.cpp
index 53973f6904393120ff925ec1444b33268aba24c5..c609de9cf9bcb3ebd35fa3c7389a8cc5eeeea8b0 100644
--- a/src/libmassSpringSystem/renderSprings.cpp
+++ b/libraries/massSpringSystem/renderSprings.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -27,9 +31,6 @@
  *                                                                       *
  *************************************************************************/
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
 #include "openGL-headers.h"
 #include "renderSprings.h"
 
@@ -50,10 +51,10 @@ void RenderSprings::Render(MassSpringSystem * massSpringSystem, double * u)
     double posA[3] = { massSpringSystem->restPositions[3*vtxA+0] + u[3*vtxA+0], massSpringSystem->restPositions[3*vtxA+1] + u[3*vtxA+1], massSpringSystem->restPositions[3*vtxA+2] + u[3*vtxA+2] };
     double posB[3] = { massSpringSystem->restPositions[3*vtxB+0] + u[3*vtxB+0], massSpringSystem->restPositions[3*vtxB+1] + u[3*vtxB+1], massSpringSystem->restPositions[3*vtxB+2] + u[3*vtxB+2] };
 
-    glColor3f(color[0], color[1], color[2]);
+    glColor3d(color[0], color[1], color[2]);
     glBegin(GL_LINES);
-      glVertex3f(posA[0], posA[1], posA[2]);
-      glVertex3f(posB[0], posB[1], posB[2]);
+      glVertex3d(posA[0], posA[1], posA[2]);
+      glVertex3d(posB[0], posB[1], posB[2]);
     glEnd();
   }
 }
diff --git a/src/libmassSpringSystem/renderSprings.h b/libraries/massSpringSystem/renderSprings.h
similarity index 77%
rename from src/libmassSpringSystem/renderSprings.h
rename to libraries/massSpringSystem/renderSprings.h
index b57ae97a036a774f6e7179db4b3fe1740942d9ce..be5ee8de6bf72ff77a22bc4ac6b84b692bcdc612 100644
--- a/src/libmassSpringSystem/renderSprings.h
+++ b/libraries/massSpringSystem/renderSprings.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
+ *                                           2018 USC                    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic, Daniel Schroeder                          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrix/example.cpp b/libraries/matrix/example.cpp
similarity index 65%
rename from src/libmatrix/example.cpp
rename to libraries/matrix/example.cpp
index 99f0eb2c96c972173f7666fed1167b8717c3b547..a9774cab4c08ce7735e763ebf3668c8d88a5545c 100644
--- a/src/libmatrix/example.cpp
+++ b/libraries/matrix/example.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrix/expokit_xgpadm.h b/libraries/matrix/expokit_xgpadm.h
similarity index 68%
rename from src/libmatrix/expokit_xgpadm.h
rename to libraries/matrix/expokit_xgpadm.h
index 518f6e4ed6c826d71fa7053b3a2b2c68f0415c6f..c0f22db69a2f737fb927e26a48198b19d71473a5 100644
--- a/src/libmatrix/expokit_xgpadm.h
+++ b/libraries/matrix/expokit_xgpadm.h
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrix/matrix.cpp b/libraries/matrix/matrix.cpp
similarity index 66%
rename from src/libmatrix/matrix.cpp
rename to libraries/matrix/matrix.cpp
index 3665c7f7894aa2f0b0f72f92fce48251ae623460..147716db9dee0b3bbfb9f8c6ec82d8ba2f3af6b5 100644
--- a/src/libmatrix/matrix.cpp
+++ b/libraries/matrix/matrix.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -25,9 +34,12 @@
 #include "matrix.h"
 #include "matrixIO.h"
 #include "matrixExp.h"
+#include <cassert>
+#include <algorithm>
+using namespace std;
 
 template<class real>
-Matrix<real>::Matrix(char * filename)
+Matrix<real>::Matrix(const char * filename)
 {
   if (ReadMatrixFromDisk(filename, &m, &n, &data) != 0)
   {
@@ -236,10 +248,28 @@ Matrix<real> & Matrix<real>::operator*= (const real alpha)
 {
   int mn = m * n;
   for(int i=0; i<mn; i++)
-    data[i] *= alpha;   
+    data[i] *= alpha;
   return (*this);
 }
 
+template<class real>
+bool Matrix<real>::operator == (const Matrix<real> & mtx2) const
+{
+  if ((n != mtx2.Getm()) || (n != mtx2.Getn()))
+    return false;
+  int mn = m * n;
+  for(int i = 0; i < mn; i++)
+    if (data[i] != mtx2.data[i])
+      return false;
+  return true;
+}
+
+template<class real>
+bool Matrix<real>::operator != (const Matrix<real> & mtx2) const
+{
+  return !(*this == mtx2);
+}
+
 template<class real>
 real Matrix<real>::MaxAbsEntry() const
 {
@@ -253,6 +283,13 @@ real Matrix<real>::MaxAbsEntry() const
   return maxAbsEntry;
 }
 
+template<class real>
+real Matrix<real>::GetFrobeniusNorm() const
+{
+  return VectorNorm(m*n, data);
+}
+
+
 template<class real>
 Matrix<real> & Matrix<real>::InPlaceTranspose()
 {
@@ -332,7 +369,7 @@ int Matrix<real>::EigenDecomposition(Matrix<real> & EigenVectors, Matrix<real> &
 }
 
 template<class real>
-int Matrix<real>::LUSolve(const Matrix<real> & x, const Matrix<real> & rhs)
+int Matrix<real>::LUSolve(Matrix<real> & x, const Matrix<real> & rhs)
 {
   if ((Getm() != Getn()) || (Getm() != x.Getm()) || (x.Getm() != rhs.Getm()) || (x.Getn() != rhs.Getn()) )
   {    
@@ -380,7 +417,7 @@ void Matrix<real>::MExpv(real t, const Matrix<real> & v, Matrix<real> & w)
 
 
 template<class real>
-int Matrix<real>::Save(char * filename) const
+int Matrix<real>::Save(const char * filename) const
 {
   if (WriteMatrixToDisk(filename, m, n, data) != 0)
   {
@@ -390,12 +427,31 @@ int Matrix<real>::Save(char * filename) const
   return 0;
 }
 
+template<class real>
+int Matrix<real>::Load(const char * filename)
+{
+  if (freeDataInDestructor)
+    free(data);
+  data = NULL;
+  m = n = 0;
+  freeDataInDestructor = true;
+  if (ReadMatrixFromDisk(filename, &m, &n, &data) != 0)
+  {
+    printf("Error loading matrix from %s.\n", filename);
+    m = n = 0;
+    data = NULL;
+    return 1;
+  }
+  return 0;
+}
+
+
 template<class real>
 void Matrix<real>::SetSubmatrix(int I, int J, const Matrix<real> & submatrix)
 {
   int subm = submatrix.Getm();
   int subn = submatrix.Getn();
-  real * subdata = submatrix.GetData();
+  const real * subdata = submatrix.GetData();
 
   if ((I < 0) || (J < 0) || (I + subm > m) || (J + subn > n))
   {
@@ -408,6 +464,24 @@ void Matrix<real>::SetSubmatrix(int I, int J, const Matrix<real> & submatrix)
       data[ELT(m,I+i,J+j)] = subdata[ELT(subm,i,j)];
 }
 
+template<class real>
+void Matrix<real>::GetSubmatrix(int I, int J, Matrix<real> & submatrix) const
+{
+  int subm = submatrix.Getm();
+  int subn = submatrix.Getn();
+  real * subdata = submatrix.GetData();
+
+  if ((I < 0) || (J < 0) || (I + subm > m) || (J + subn > n))
+  {
+    printf("Error: matrix index out of bounds.\n");
+    throw 21;
+  }
+
+  for(int j=0; j<subn; j++)
+    for(int i=0; i<subm; i++)
+      subdata[ELT(subm,i,j)] = data[ELT(m,I+i,J+j)];
+}
+
 template<class real>
 void Matrix<real>::RemoveColumns(int columnStart, int columnEnd)
 {
@@ -416,7 +490,7 @@ void Matrix<real>::RemoveColumns(int columnStart, int columnEnd)
   for(int column=columnEnd; column<n; column++)
   {
     // write column to column-stride
-    memcpy(&data[ELT(m, 0, column-stride)], &data[ELT(m, 0, column)], sizeof(double) * m);
+    memcpy(&data[ELT(m, 0, column-stride)], &data[ELT(m, 0, column)], sizeof(real) * m);
   }
 
   // free the space
@@ -428,22 +502,64 @@ void Matrix<real>::RemoveColumns(int columnStart, int columnEnd)
 template<class real>
 void Matrix<real>::RemoveRows(int rowStart, int rowEnd)
 {
-  InPlaceTransposeMatrix(m, n, data);
-  int mBuf = m;
-  m = n;
-  n = mBuf;
-  RemoveColumns(rowStart, rowEnd);
-  InPlaceTransposeMatrix(m, n, data);
-  mBuf = m;
-  m = n;
-  n = mBuf;
+  assert(0 <= rowStart && rowStart <= rowEnd && rowEnd <= m);
+  int mNew = m - (rowEnd - rowStart);
+  // for i-th column, move consecutive data block from entry (rowEnd, i) to (rowStart, i+1) to the 
+  // target location in the resultant matrix
+  // to avoid potential memory block overlap, we use memmove instead of memcpy; the latter is not safe
+  for(int i = 0, num = n-1; i < num; i++)
+    memmove(data + i*mNew + rowStart, data + i*m + rowEnd, sizeof(real) * mNew);
+
+  // the last column is a special case because the consecutive data block is from (rowEnd, n-1) to (m-1, n-1)
+  if (n >= 1)
+    memmove(data + (n-1)*mNew + rowStart, data + (n-1)*m + rowEnd, sizeof(real) * (m - rowEnd));
+  data = (real*) realloc (data, sizeof(real) * mNew * n);
+  m = mNew;
 }
 
 template<class real>
-void Matrix<real>::RemoveRowsColumns(int columnStart, int columnEnd)
+void Matrix<real>::RemoveRowsColumns(int start, int end)
 {
-  RemoveColumns(columnStart, columnEnd);
-  RemoveRows(columnStart, columnEnd);
+  assert(0 <= start && start <= end);
+  assert(end <= m && end <= n);
+
+  int stride = end - start;
+  int mNew = m - stride;
+  int nNew = n - stride;
+
+  // similar method as in RemoveRows()
+  // for i-th column, move consecutive data block from entry (rowEnd, i) to (rowStart, i+1) to the 
+  // target location in the resultant matrix, until the (start-1) column
+  for(int i = 0, num = start-1; i < num; i++)
+    memmove(data + i*mNew + start, data + i*m + end, sizeof(real) * mNew);
+
+  // the (start-1) column is a special case because the consecutive data block is from (rowEnd, start-1)
+  // to (m-1, start-1)
+  if (start >= 1)
+    memmove(data + (start-1)*mNew + start, data + (start-1)*m + end, sizeof(real) * (m - end));
+
+  // we skip columns from start to end-1
+  // if we have columns after end
+  if (end < n)
+  {
+    // move data from (end, 0) to (end, start-1) to the tagert location
+    memmove(data + start*mNew, data + end*m, sizeof(real) * start);
+
+    // fot the i-th column after the end column, move the consecutive data block as well
+    for(int i = start, num = nNew-1; i < num; i++)
+      memmove(data + i*mNew + start, data + (i+stride)*m + end, sizeof(real) * mNew);
+  
+    // process the special case at the last column
+    if (nNew >= 1 && n >= 1)
+      memmove(data + (nNew-1)*mNew+start, data + (n-1)*m + end, sizeof(real) * (m - end));
+  }
+
+  data = (real*) realloc (data, sizeof(real) * mNew * nNew);
+  m = mNew;
+  n = nNew;
+
+//  RemoveColumns(start, end);
+//  RemoveRows(start, end);
 }
 
 template<class real>
@@ -463,22 +579,16 @@ void Matrix<real>::AppendColumns(const Matrix<real> & columns)
 template<class real>
 void Matrix<real>::AppendRows(const Matrix<real> & rows)
 {
-  if (rows.Getn() != n)
+  if (rows.n != n)
   {
     printf("Error: mismatch in number of columns in AppendRows.\n");
     throw 42;
   }
 
-  InPlaceTransposeMatrix(m, n, data);
-  int mBuf = m;
-  m = n;
-  n = mBuf;
-  Matrix<real> rowsT = Transpose(rows);
-  AppendColumns(rowsT);
-  InPlaceTransposeMatrix(m, n, data);
-  mBuf = m;
-  m = n;
-  n = mBuf;
+  int oldm = m;
+  Resize(0,0, m + rows.m, n);
+  for(int i = 0; i < n; i++)
+    memcpy(data + i*m + oldm, rows.data + i*rows.m, sizeof(real) * rows.m);
 }
 
 template<class real>
@@ -517,81 +627,55 @@ void Matrix<real>::AppendRowsColumns(const Matrix<real> & bottomLeftBlock, const
   AppendRows(blockMatrix);
 }
 
-// === float ===
-template Matrix<float>::Matrix(char * filename);
-template Matrix<float>::Matrix(int m, int n, const float * data,
-                     bool makeInternalDataCopy, bool freeDataInDestructor);
-template Matrix<float>::Matrix(int m, int n, bool freeDataInDestructor);
-template Matrix<float>::Matrix(int m, int n, float constEntry, bool freeDataInDestructor);
-template Matrix<float>::Matrix(int m, float diagonal, bool freeDataInDestructor);
-template Matrix<float>::Matrix(int m, const float * diagonal, bool freeDataInDestructor);
-template Matrix<float>::Matrix (int m, const Matrix<float> & vec, bool freeDataInDestructor);
-template Matrix<float>::Matrix(const Matrix<float> & mtx2);
-template Matrix<float>::~Matrix();
-template const Matrix<float> Matrix<float>::operator+ (const Matrix<float> & mtx2) const;
-template const Matrix<float> Matrix<float>::operator- (const Matrix<float> & mtx2) const;
-template const Matrix<float> Matrix<float>::operator* (const Matrix<float> & mtx2) const;
-template const Matrix<float> Matrix<float>::MultiplyT(const Matrix<float> & mtx2) const;
-template Matrix<float> & Matrix<float>::operator= (const Matrix<float> & mtx2);
-template Matrix<float> & Matrix<float>::operator+= (const Matrix<float> & mtx2);
-template Matrix<float> & Matrix<float>::operator-= (const Matrix<float> & mtx2);
-template Matrix<float> & Matrix<float>::operator*= (const Matrix<float> & mtx2);
-template Matrix<float> & Matrix<float>::operator*= (const float alpha);
-template float Matrix<float>::MaxAbsEntry() const;
-template Matrix<float> & Matrix<float>::InPlaceTranspose();
-template void Matrix<float>::SymmetricEigenDecomposition(Matrix<float> & Q, Matrix<float> & Lambda);
-template void Matrix<float>::SVD(Matrix<float> & U, Matrix<float> & Sigma, Matrix<float> & VT);
-template int Matrix<float>::EigenDecomposition(Matrix<float> & EigenVectors, Matrix<float> & LambdaRe, Matrix<float> & LambdaIm);
-template int Matrix<float>::LUSolve(const Matrix<float> & x, const Matrix<float> & rhs);
-#ifdef USE_EXPOKIT
-  template void Matrix<float>::MExpv(float t, const Matrix<float> & v, Matrix<float> & w);
-#endif
-template void Matrix<float>::Print(int numDigits) const; 
-template int Matrix<float>::Save(char * filename) const;
-template void Matrix<float>::SetSubmatrix(int I, int J, const Matrix<float> & submatrix);
-template void Matrix<float>::RemoveColumns(int columnStart, int columnEnd);
-template void Matrix<float>::RemoveRows(int rowStart, int rowEnd);
-template void Matrix<float>::RemoveRowsColumns(int columnStart, int columnEnd);
-template void Matrix<float>::AppendRows(const Matrix<float> & rows);
-template void Matrix<float>::AppendColumns(const Matrix<float> & columns);
-template void Matrix<float>::AppendRowsColumns(const Matrix<float> & bottomLeftBlock, const Matrix<float> & topRightBlock, const Matrix<float> & bottomRightBlock);
-
-// === double ===
-template Matrix<double>::Matrix(char * filename);
-template Matrix<double>::Matrix(int m, int n, const double * data,
-                     bool makeInternalDataCopy, bool freeDataInDestructor);
-template Matrix<double>::Matrix(int m, int n, bool freeDataInDestructor);
-template Matrix<double>::Matrix(int m, int n, double constEntry, bool freeDataInDestructor);
-template Matrix<double>::Matrix(int m, double diagonal, bool freeDataInDestructor);
-template Matrix<double>::Matrix(int m, const double * diagonal, bool freeDataInDestructor);
-template Matrix<double>::Matrix (int m, const Matrix<double> & vec, bool freeDataInDestructor);
-template Matrix<double>::Matrix(const Matrix<double> & mtx2);
-template Matrix<double>::~Matrix();
-template const Matrix<double> Matrix<double>::operator+ (const Matrix<double> & mtx2) const;
-template const Matrix<double> Matrix<double>::operator- (const Matrix<double> & mtx2) const;
-template const Matrix<double> Matrix<double>::operator* (const Matrix<double> & mtx2) const;
-template Matrix<double> & Matrix<double>::operator*= (const double alpha);
-template const Matrix<double> Matrix<double>::MultiplyT(const Matrix<double> & mtx2) const;
-template Matrix<double> & Matrix<double>::operator= (const Matrix<double> & mtx2);
-template Matrix<double> & Matrix<double>::operator+= (const Matrix<double> & mtx2);
-template Matrix<double> & Matrix<double>::operator-= (const Matrix<double> & mtx2);
-template Matrix<double> & Matrix<double>::operator*= (const Matrix<double> & mtx2);
-template double Matrix<double>::MaxAbsEntry() const;
-template Matrix<double> & Matrix<double>::InPlaceTranspose();
-template void Matrix<double>::SymmetricEigenDecomposition(Matrix<double> & Q, Matrix<double> & Lambda);
-template void Matrix<double>::SVD(Matrix<double> & U, Matrix<double> & Sigma, Matrix<double> & VT);
-template int Matrix<double>::EigenDecomposition(Matrix<double> & EigenVectors, Matrix<double> & LambdaRe, Matrix<double> & LambdaIm);
-template int Matrix<double>::LUSolve(const Matrix<double> & x, const Matrix<double> & rhs);
-#ifdef USE_EXPOKIT
-  template void Matrix<double>::MExpv(double t, const Matrix<double> & v, Matrix<double> & w);
-#endif
-template void Matrix<double>::Print(int numDigits) const;
-template int Matrix<double>::Save(char * filename) const;
-template void Matrix<double>::SetSubmatrix(int I, int J, const Matrix<double> & submatrix);
-template void Matrix<double>::RemoveColumns(int columnStart, int columnEnd);
-template void Matrix<double>::RemoveRows(int rowStart, int rowEnd);
-template void Matrix<double>::RemoveRowsColumns(int columnStart, int columnEnd);
-template void Matrix<double>::AppendRows(const Matrix<double> & rows);
-template void Matrix<double>::AppendColumns(const Matrix<double> & columns);
-template void Matrix<double>::AppendRowsColumns(const Matrix<double> & bottomLeftBlock, const Matrix<double> & topRightBlock, const Matrix<double> & bottomRightBlock);
+template<class real>
+void Matrix<real>::AppendRowsColumns(int numAddedRows, int numAddedCols)
+{
+  assert(numAddedRows >= 0 && numAddedCols >= 0);
+  int newRows = m + numAddedRows;
+  int newCols = n + numAddedCols;
+
+  real * newdata = (real*) calloc(newRows * newCols, sizeof(real));
+
+  for(int col = 0; col < n; col++)
+    memcpy(&newdata[newRows*col], &data[m*col], sizeof(real) * m);
+
+  free(data);
+  data = newdata;
+  m = newRows;
+  n = newCols;
+}
+
+template<class real>
+void Matrix<real>::Resize(int newRows, int newCols)
+{
+  assert(newRows >= 0 && newCols >= 0);
+
+  real * newdata = (real*) calloc(newRows * newCols, sizeof(real));
+  free(data);
+  data = newdata;
+  m = newRows;
+  n = newCols;
+}
+
+template<class real>
+void Matrix<real>::Resize(int i, int j, int newRows, int newCols)
+{
+  assert(newRows >= 0 && newCols >= 0);
+
+  real * newdata = (real*) calloc(newRows * newCols, sizeof(real));
+  int colStartToCopy = max(j, 0);
+  int numColsToCopy = min(j+newCols, n) - colStartToCopy;
+  int rowStartToCopy = max(i, 0);
+  int numRowsToCopy = min(i+newRows, m) - rowStartToCopy;
+
+  for(int col = 0; col < numColsToCopy; col++)
+    memcpy(&newdata[newRows*col], &data[m*col + colStartToCopy + rowStartToCopy], sizeof(real) * numRowsToCopy);
+
+  free(data);
+  data = newdata;
+  m = newRows;
+  n = newCols;
+}
 
+template class Matrix<float>;
+template class Matrix<double>;
diff --git a/src/libmatrix/matrix.h b/libraries/matrix/matrix.h
similarity index 56%
rename from src/libmatrix/matrix.h
rename to libraries/matrix/matrix.h
index 8136a59312d2ca7715b7b04be15a40a706b048e4..f05791fd5663bca9304198dedd8b8c44743ba339 100644
--- a/src/libmatrix/matrix.h
+++ b/libraries/matrix/matrix.h
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -65,7 +74,7 @@ class Matrix
 {
 public:
   // load matrix from a file (see matrixIO.h for the binary file format)
-  Matrix(char * filename); 
+  Matrix(const char * filename); 
 
   // create a m x n matrix; "data" must an array of length m*n, giving the matrix entries in column-major order
   // if "makeInternalDataCopy" is true (default), the class will internally make a copy of the data; otherwise, it will only direct its internal pointer to the user-provided array data
@@ -76,7 +85,7 @@ public:
   Matrix (int m, int n, bool freeDataInDestructor = true);
   // create a m x n matrix of constant entries
   Matrix (int m, int n, real constEntry, bool freeDataInDestructor = true);
-  // create a m x m diagonal matrix with all the diagonal entries equalling "diagonal"
+  // create a m x m diagonal matrix with all the diagonal entries equaling "diagonal"
   Matrix (int m, real diagonal, bool freeDataInDestructor = true);
   // create a m x m diagonal matrix with the diagonal entries specified by the array diagonal (of length m)
   Matrix (int m, const real * diagonal, bool freeDataInDestructor = true);
@@ -87,7 +96,12 @@ public:
 
   inline int Getm() const { return m; }
   inline int Getn() const { return n; }
-  inline real * GetData() const { return data; }
+  inline real * GetData() { return data; }
+  inline const real * GetData() const { return data; }
+  inline const real & At(int row, int column) const { return data[ELT(m,row,column)]; }
+  inline real & At(int row, int column) { return data[ELT(m,row,column)]; }
+  inline const real * GetColumn(int col) const { return data + col * m; }
+  inline real * GetColumn(int col) { return data + col * m; }
 
   const Matrix operator+ (const Matrix & mtx2) const;
   const Matrix operator- (const Matrix & mtx2) const;
@@ -97,8 +111,10 @@ public:
   Matrix & operator-= (const Matrix & mtx2);
   Matrix & operator*= (const Matrix & mtx2);
   Matrix & operator*= (const real alpha);
-  inline const real & operator() (int row, int columns) const;
-  inline real & operator() (int row, int columns);
+  inline const real & operator() (int row, int column) const { return this->At(row, column); }
+  inline real & operator() (int row, int column) { return this->At(row, column); }
+  bool operator == (const Matrix & mtx2) const;
+  bool operator != (const Matrix & mtx2) const;
 
   // output = trans(this) * mtx2
   const Matrix MultiplyT (const Matrix & mtx2) const;
@@ -108,6 +124,10 @@ public:
 
   // sets the submatrix, defined by its upper-left corner at (i,j) and the dimensions of "submatrix", to entries from the matrix "submatrix"
   void SetSubmatrix(int i, int j, const Matrix<real> & submatrix);
+  // gets the submatrix, defined by its upper-left corner at (i,j) and the dimensions of "submatrix", to entries from the matrix "submatrix"
+  void GetSubmatrix(int i, int j, Matrix<real> & submatrix) const;
+
+  // === matrix resize routines ===
 
   void RemoveRows(int rowStart, int rowEnd);
   void RemoveColumns(int columnStart, int columnEnd);
@@ -115,11 +135,21 @@ public:
   void AppendRows(const Matrix<real> & rows);
   void AppendColumns(const Matrix<real> & columns);
   void AppendRowsColumns(const Matrix<real> & bottomLeftBlock, const Matrix<real> & topRightBlock, const Matrix<real> & bottomRightBlock);
+  void AppendRowsColumns(int numAddedRows, int numAddedCols);
+
+  // resize the matrix and destroy the stored data
+  void Resize(int newRows, int newCols);
+  // resize the matrix and retain the data in the matrix defined by its upper-left corner at (rowIdx, colIdx) = (i,j)
+  // and dimension of size (newRows, newCols). Fill in zero if necessary
+  void Resize(int i, int j, int newRows, int newCols);
+
 
   real MaxAbsEntry() const;
+  real GetFrobeniusNorm() const;  // matrix Frobenius norm: sqrt of the sum of squares of its elements
 
   void Print(int numDigits=-1) const; // if numDigits=-1: use %G flag
-  int Save(char * filename) const; // returns 0 on success, 1 on failure
+  int Save(const char * filename) const; // returns 0 on success, 1 on failure
+  int Load(const char * filename); // returns 0 on success, 1 on failure
 
   // factor <this matrix> = Q * Lambda * Q^T
   // matrix must be symmetric (no checks performed)
@@ -141,7 +171,7 @@ public:
 
   // solves linear system (via LU):
   // (this matrix) * x = rhs
-  int LUSolve(const Matrix<real> & x, const Matrix<real> & rhs);
+  int LUSolve(Matrix<real> & x, const Matrix<real> & rhs);
 
   // scalar-matrix multiplication
   // example: A = 0.37 * B;
@@ -151,72 +181,6 @@ public:
     return Matrix<real>(mtx2.Getm(),mtx2.Getn(),output,false);
   }
 
-  // compute the Frobenius inner product of two matrices (sum of products of all pairwise entries)
-  // example: double product = FrobeniusInnerProduct(A, B);
-  inline friend real FrobeniusInnerProduct(const Matrix<real> & mtx1, const Matrix<real> & mtx2)
-  {
-    if ((mtx1.Getm() != mtx2.Getm()) || (mtx1.Getn() != mtx2.Getn()))
-    {    
-      printf("Matrix size mismatch in Matrix::FrobeniusInnerProduct.\n");
-      throw 10;
-    }
-    real product = 0.0;
-    int mn = mtx1.Getm() * mtx1.Getn();
-    for(int i=0; i<mn; i++)
-      product += mtx1.data[i] * mtx2.data[i];
-    return product;
-  }
-  
-  // transpose the matrix
-  // example: A = Transpose(B);
-  inline friend const Matrix<real> Transpose(const Matrix<real> & mtx2)
-  {
-    real * buffer = (real*) malloc (sizeof(real) * mtx2.Getm() * mtx2.Getn());
-    memcpy(buffer, mtx2.GetData(), sizeof(real) * mtx2.Getm() * mtx2.Getn());
-    InPlaceTransposeMatrix(mtx2.Getm(), mtx2.Getn(), buffer);
-    return Matrix<real>(mtx2.Getn(), mtx2.Getm(), buffer, false);
-  }
-
-  // compute matrix inverse
-  // example: A = Inverse(B);
-  inline friend const Matrix<real> Inverse(const Matrix<real> & mtx2)
-  {
-    if (mtx2.Getm() != mtx2.Getn())
-    {    
-      printf("Matrix size mismatch in Matrix::Inverse . mtx2.m = %d, mtx2.n = %d\n",      mtx2.Getm(), mtx2.Getn());
-      throw 11;
-    }
-    real * buffer = InverseMatrix(mtx2.Getm(), mtx2.GetData());
-    return Matrix<real>(mtx2.Getm(), mtx2.Getm(), buffer, false);
-  }
-
-  // compute matrix pseudoinverse
-  // example: A = PseudoInverse(B);
-  inline friend const Matrix<real> PseudoInverse(const Matrix<real> & mtx2, real singularValueThreshold, int * rank)
-  {
-    real * buffer = PseudoInverseMatrix(mtx2.Getm(), mtx2.Getn(),
-      mtx2.GetData(), singularValueThreshold, rank);
-    return Matrix<real>(mtx2.Getn(), mtx2.Getm(), buffer, false);
-  }
-
-  // solve the least square system: mtx * output = rhs
-  // rcond is the condition number beyond which matrix singular values are considered singular (see also LAPACK)
-  // rank is a pointer to a single integer, giving the non-singular rank of the matrix, based on rcond
-  // example:  A * X = B
-  // int rank;
-  // X = LeastSquareSolve(A, B, 1e-12, &rank);
-  inline friend const Matrix<real> LeastSquareSolve(const Matrix<real> & mtx, const Matrix<real> & rhs, real rcond, int * rank)
-  {
-    if (mtx.Getm() != rhs.Getm())
-    {    
-      printf("Matrix size mismatch in Matrix::LeastSquareSolve . mtx.m = %d, rhs.m = %d\n", mtx.Getm(), rhs.Getm());
-      throw 12;
-    }
-    real * buffer = MatrixLeastSquareSolve(mtx.Getm(), mtx.Getn(), rhs.Getn(), 
-      mtx.GetData(), rhs.GetData(), rcond, rank);
-    return Matrix<real>(mtx.Getn(), rhs.Getn(), buffer, false);
-  }  
-
   // === matrix exponential routines ===
 
   // In order to use these routines, you must enable the USE_EXPOKIT definition in the header of matrix.h (disabled by default to avoid linker problems when expokit is not used), and link your code against the expokit library:
@@ -232,43 +196,126 @@ public:
   // note: this routine uses the same algorithm as MExp below: it explicitly constructs exp((this matrix)*t), and then multiplies by v
   void MExpv(real t, const Matrix<real> & v, Matrix<real> & w);
 
-  // computes A = exp(t*mtx), where A and mtx are square matrices, and t is a scalar
-  // code is a pointer to an integer where the xgpasm expokit routine exit code will be stored (you can pass NULL to discard this code); normal termination is code 0
-  inline friend const Matrix<real> MExp(real t, const Matrix<real> & mtx, int * code = NULL)
-  {
-    #ifdef USE_EXPOKIT
-      if (mtx.Getm() != mtx.Getn())
-      {    
-        printf("Matrix size mismatch in Matrix::MExp . mtx.m = %d, mtx.n = %d\n", mtx.Getm(), mtx.Getn());
-        throw 13;
-      }
-
-      real * buffer = (real*) malloc (sizeof(real) * mtx.Getm() * mtx.Getm());
-      int codei = MatrixExp(mtx.Getm(), mtx.GetData(), t, buffer);
-      if (code != NULL)
-        *code = codei;
-      return Matrix<real>(mtx.Getm(), mtx.Getm(), buffer, false);
-    #else
-      printf("Error: MExp is not enabled.\n");
-    #endif
-  }
-
 protected:
   int m, n;
   real * data;
   bool freeDataInDestructor;
 };
 
+// compute the Frobenius inner product of two matrices (sum of products of all pairwise entries)
+// example: double product = FrobeniusInnerProduct(A, B);
+template<class real>
+real FrobeniusInnerProduct(const Matrix<real> & mtx1, const Matrix<real> & mtx2);
+
+// transpose the matrix
+// example: A = Transpose(B);
+template<class real>
+inline const Matrix<real> Transpose(const Matrix<real> & mtx2);
+
+// compute matrix inverse
+// example: A = Inverse(B);
+template<class real>
+inline const Matrix<real> Inverse(const Matrix<real> & mtx2);
+
+// compute matrix pseudoinverse
+// example: A = PseudoInverse(B);
+template<class real>
+inline const Matrix<real> PseudoInverse(const Matrix<real> & mtx2, real singularValueThreshold, int * rank);
+
+// solve the least square system: mtx * output = rhs
+// rcond is the condition number beyond which matrix singular values are considered singular (see also LAPACK)
+// rank is a pointer to a single integer, giving the non-singular rank of the matrix, based on rcond
+// example:  A * X = B
+// int rank;
+// X = LeastSquareSolve(A, B, 1e-12, &rank);
+template<class real>
+inline const Matrix<real> LeastSquareSolve(const Matrix<real> & mtx, const Matrix<real> & rhs, real rcond, int * rank);
+
+
+// computes A = exp(t*mtx), where A and mtx are square matrices, and t is a scalar
+// code is a pointer to an integer where the xgpasm expokit routine exit code will be stored (you can pass NULL to discard this code); normal termination is code 0
+template<class real>
+inline const Matrix<real> MExp(real t, const Matrix<real> & mtx, int * code = NULL);
+
+// === below is the inlined implementation ===
+
+template<class real>
+inline real FrobeniusInnerProduct(const Matrix<real> & mtx1, const Matrix<real> & mtx2)
+{
+  if ((mtx1.Getm() != mtx2.Getm()) || (mtx1.Getn() != mtx2.Getn()))
+  {    
+    printf("Matrix size mismatch in Matrix::FrobeniusInnerProduct.\n");
+    throw 10;
+  }
+  real product = 0.0;
+  int mn = mtx1.Getm() * mtx1.Getn();
+  for(int i=0; i<mn; i++)
+    product += mtx1.GetData()[i] * mtx2.GetData()[i];
+  return product;
+}
+
 template<class real>
-inline const real & Matrix<real>::operator() (int row, int column) const
+inline const Matrix<real> Transpose(const Matrix<real> & mtx2)
 {
-  return data[ELT(m,row,column)];
+  Matrix<real> newMat(mtx2.Getn(), mtx2.Getm());
+  for(int col = 0; col < mtx2.Getn(); col++)
+    for(int row = 0; row < mtx2.Getm(); row++)
+      newMat(col, row) = mtx2(row, col);
+  return newMat;
 }
 
 template<class real>
-inline real & Matrix<real>::operator() (int row, int column) 
+inline const Matrix<real> Inverse(const Matrix<real> & mtx2)
 {
-  return data[ELT(m,row,column)];
+  if (mtx2.Getm() != mtx2.Getn())
+  {    
+    printf("Matrix size mismatch in Matrix::Inverse . mtx2.m = %d, mtx2.n = %d\n",      mtx2.Getm(), mtx2.Getn());
+    throw 11;
+  }
+  real * buffer = InverseMatrix(mtx2.Getm(), mtx2.GetData());
+  return Matrix<real>(mtx2.Getm(), mtx2.Getm(), buffer, false);
+}
+
+template<class real>
+inline const Matrix<real> PseudoInverse(const Matrix<real> & mtx2, real singularValueThreshold, int * rank)
+{
+  real * buffer = PseudoInverseMatrix(mtx2.Getm(), mtx2.Getn(),
+    mtx2.GetData(), singularValueThreshold, rank);
+  return Matrix<real>(mtx2.Getn(), mtx2.Getm(), buffer, false);
+}
+
+template<class real>
+inline const Matrix<real> LeastSquareSolve(const Matrix<real> & mtx, const Matrix<real> & rhs, real rcond, int * rank)
+{
+  if (mtx.Getm() != rhs.Getm())
+  {    
+    printf("Matrix size mismatch in Matrix::LeastSquareSolve . mtx.m = %d, rhs.m = %d\n", mtx.Getm(), rhs.Getm());
+    throw 12;
+  }
+  real * buffer = MatrixLeastSquareSolve(mtx.Getm(), mtx.Getn(), rhs.Getn(), 
+    mtx.GetData(), rhs.GetData(), rcond, rank);
+  return Matrix<real>(mtx.Getn(), rhs.Getn(), buffer, false);
+}  
+
+template<class real>
+inline const Matrix<real> MExp(real t, const Matrix<real> & mtx, int * code)
+{
+  #ifdef USE_EXPOKIT
+    if (mtx.Getm() != mtx.Getn())
+    {    
+      printf("Matrix size mismatch in Matrix::MExp . mtx.m = %d, mtx.n = %d\n", mtx.Getm(), mtx.Getn());
+      throw 13;
+    }
+
+    real * buffer = (real*) malloc (sizeof(real) * mtx.Getm() * mtx.Getm());
+    int codei = MatrixExp(mtx.Getm(), mtx.GetData(), t, buffer);
+    if (code != NULL)
+      *code = codei;
+    return Matrix<real>(mtx.Getm(), mtx.Getm(), buffer, false);
+  #else
+    printf("Error: MExp is not enabled.\n");
+    return Matrix<real>();
+  #endif
 }
 
 #endif
diff --git a/libraries/matrix/matrixBLAS.cpp b/libraries/matrix/matrixBLAS.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2297211af9ba43b9730804659f585e27e84e1e5f
--- /dev/null
+++ b/libraries/matrix/matrixBLAS.cpp
@@ -0,0 +1,112 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "matrixMacros.h"
+#include "matrixBLAS.h"
+
+#define BLAS
+
+#ifdef BLAS
+  #include "matrixBLASOptimized.cpp"
+#else
+  #include "matrixBLASVanilla.cpp"
+#endif
+
+#include "vegalong.h"
+
+// transposes the matrix (without making a separate copy)
+template <class real>
+void InPlaceTransposeMatrix(int m, int n, real * mtx)
+{ 
+  real buffer;
+  #define SWAP_ELT(i,j)\
+    buffer = mtx[i];\
+    mtx[i] = mtx[j];\
+    mtx[j] = buffer;
+
+  vegalong M = m;
+  vegalong N = n;
+  vegalong MN = M*N;
+  
+  for(vegalong i=0; i< MN; i++)
+  {
+    vegalong current = i;
+    do
+    {
+      // evaluate permutation on 'current'
+      vegalong k = current / N;
+      vegalong l = current % N;
+  
+      current = M * l + k;
+    } 
+    while(current < i);
+
+    if (current>i)
+    {
+      SWAP_ELT(i,current);
+      //printf("Swap: %d %d .\n",i,current);
+    }
+  }
+}
+
+template float * MultiplyMatrices<float>(int m, int p, int n, const float * mtx1, const float * mtx2, float * output);
+template double * MultiplyMatrices<double>(int m, int p, int n, const double * mtx1, const double * mtx2, double * output);
+
+template void MultiplyMatricesAdd<float>(int m, int p, int n, const float * mtx1, const float * mtx2, float * output);
+template void MultiplyMatricesAdd<double>(int m, int p, int n, const double * mtx1, const double * mtx2, double * output);
+
+template void MultiplyMatricesSub<float>(int m, int p, int n, const float * mtx1, const float * mtx2, float * output);
+template void MultiplyMatricesSub<double>(int m, int p, int n, const double * mtx1, const double * mtx2, double * output);
+
+template float * MultiplyMatricesT<float>(int m, int p, int n, const float * mtx1, const float * mtx2, float * output);
+template double * MultiplyMatricesT<double>(int m, int p, int n, const double * mtx1, const double * mtx2, double * output);
+
+template void MultiplyMatricesTAdd<float>(int m, int p, int n, const float * mtx1, const float * mtx2, float * output);
+template void MultiplyMatricesTAdd<double>(int m, int p, int n, const double * mtx1, const double * mtx2, double * output);
+
+template void MultiplyMatricesTSub<float>(int m, int p, int n, const float * mtx1, const float * mtx2, float * output);
+template void MultiplyMatricesTSub<double>(int m, int p, int n, const double * mtx1, const double * mtx2, double * output);
+
+template float * SumMatrices<float>(int m, int n, const float * mtx1, const float * mtx2, float * output);
+template double * SumMatrices<double>(int m, int n, const double * mtx1, const double * mtx2, double * output);
+
+template float * SubtractMatrices<float>(int m, int n, const float * mtx1, const float * mtx2, float * output);
+template double * SubtractMatrices<double>(int m, int n, const double * mtx1, const double * mtx2, double * output);
+
+template float * ScalarMultiplyMatrix<float>(int m, int n, float alpha, const float * mtx, float * output);
+template double * ScalarMultiplyMatrix<double>(int m, int n, double alpha, const double * mtx, double * output);
+
+template void InPlaceTransposeMatrix<double>(int m, int n, double * U);
+template void InPlaceTransposeMatrix<float>(int m, int n, float * U);
+
+template double VectorNorm<double>(int m, const double * vec);
+template float VectorNorm<float>(int m, const float * vec);
diff --git a/libraries/matrix/matrixBLAS.h b/libraries/matrix/matrixBLAS.h
new file mode 100644
index 0000000000000000000000000000000000000000..a07c33351ccf4affd8c5c4c28c7ed8498ccd43ec
--- /dev/null
+++ b/libraries/matrix/matrixBLAS.h
@@ -0,0 +1,85 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Wrappers to matrix BLAS routines.
+  See also matrix.h.
+*/
+
+#ifndef _MATRIX_BLAS_H_
+#define _MATRIX_BLAS_H_
+
+#include <stdlib.h>
+
+template<class real>
+real * SumMatrices(int m, int n, const real * mtx1, const real * mtx2, real * output = NULL);
+
+template<class real>
+real * SubtractMatrices(int m, int n, const real * mtx1, const real * mtx2, real * output = NULL);
+
+// output = mtx1 * mtx2
+// mtx1 is m x p
+// mtx2 is p x n
+template<class real>
+real * MultiplyMatrices(int m, int p, int n, const real * mtx1, const real * mtx2, real * output = NULL);
+// output += mtx1 * mtx2
+template<class real>
+void MultiplyMatricesAdd(int m, int p, int n, const real * mtx1, const real * mtx2, real * output);
+// output -= mtx1 * mtx2
+template<class real>
+void MultiplyMatricesSub(int m, int p, int n, const real * mtx1, const real * mtx2, real * output);
+
+// output = trans(mtx1) * mtx2
+// trans(mtx1) is m x p
+// mtx2 is p x n
+template<class real>
+real * MultiplyMatricesT(int m, int p, int n, const real * mtx1, const real * mtx2, real * output = NULL);
+// output += trans(mtx1) * mtx2
+template<class real>
+void MultiplyMatricesTAdd(int m, int p, int n, const real * mtx1, const real * mtx2, real * output);
+// output -= trans(mtx1) * mtx2
+template<class real>
+void MultiplyMatricesTSub(int m, int p, int n, const real * mtx1, const real * mtx2, real * output);
+
+template<class real>
+real * ScalarMultiplyMatrix(int m, int n, real alpha, const real * mtx, real * output = NULL);
+
+// transposes the matrix (without making a separate copy)
+template <class real>
+void InPlaceTransposeMatrix(int m, int n, real * mtx);
+
+// computes Euclidean norm of a vector
+template <class real>
+real VectorNorm(int m, const real * vec);
+
+#endif
+
diff --git a/src/libmatrix/matrixBLASOptimized.cpp b/libraries/matrix/matrixBLASOptimized.cpp
similarity index 70%
rename from src/libmatrix/matrixBLASOptimized.cpp
rename to libraries/matrix/matrixBLASOptimized.cpp
index c846f10183bacf2e01d85983feedf623a3649c17..b1788901f4f66075a817da9893f32cb5194a0851 100644
--- a/src/libmatrix/matrixBLASOptimized.cpp
+++ b/libraries/matrix/matrixBLASOptimized.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -25,12 +34,6 @@
 #include "matrixBLAS.h"
 #include "lapack-headers.h"
 
-#ifdef __APPLE__
-  #define INTEGER long int
-#else
-  #define INTEGER int
-#endif
-
 //void cblas_scopy(const int N, const float *X, const int incX, float *Y, const int incY); 
 template<bool C>
 class _xcopy {};
@@ -39,7 +42,7 @@ template<>
 class _xcopy<true> 
 {
 public:
-    inline static void f(const int N, float * X, const int incX, float * Y, const int incY)
+    inline static void f(const int N, const float * X, const int incX, float * Y, const int incY)
     {
       cblas_scopy(N,X,incX,Y,incY);
     }
@@ -49,7 +52,7 @@ template<>
 class _xcopy<false> 
 {
 public:
-    inline static void f(const int N, double * X, const int incX, double * Y, const int incY)
+    inline static void f(const int N, const double * X, const int incX, double * Y, const int incY)
     {
       cblas_dcopy(N,X,incX,Y,incY);
     }
@@ -152,7 +155,7 @@ public:
 };
 
 template<class real>
-real * SumMatrices(int m, int n, real * mtx1, real * mtx2, real * output)
+real * SumMatrices(int m, int n, const real * mtx1, const real * mtx2, real * output)
 {
   real * target = output;
   if (target == NULL)
@@ -169,7 +172,7 @@ real * SumMatrices(int m, int n, real * mtx1, real * mtx2, real * output)
 }
 
 template<class real>
-real * SubtractMatrices(int m, int n, real * mtx1, real * mtx2, real * output)
+real * SubtractMatrices(int m, int n, const real * mtx1, const real * mtx2, real * output)
 {
   real * target = output;
   if (target == NULL)
@@ -186,7 +189,7 @@ real * SubtractMatrices(int m, int n, real * mtx1, real * mtx2, real * output)
 }
 
 template<class real>
-real * MultiplyMatrices(int m, int p, int n, real * mtx1, real * mtx2, real * output)
+real * MultiplyMatrices(int m, int p, int n, const real * mtx1, const real * mtx2, real * output)
 {
   real * target = output;
   if (target == NULL)
@@ -208,7 +211,19 @@ real * MultiplyMatrices(int m, int p, int n, real * mtx1, real * mtx2, real * ou
 }
 
 template<class real>
-real * MultiplyMatricesT(int m, int p, int n, real * mtx1, real * mtx2, real * output)
+void MultiplyMatricesAdd(int m, int p, int n, const real * mtx1, const real * mtx2, real * output)
+{
+  _xgemm<sizeof(real)==sizeof(float)>::f(CblasColMajor, CblasNoTrans, CblasNoTrans, m, n, p, 1.0, mtx1, m, mtx2, p, 1.0, output, m);
+}
+
+template<class real>
+void MultiplyMatricesSub(int m, int p, int n, const real * mtx1, const real * mtx2, real * output)
+{
+  _xgemm<sizeof(real)==sizeof(float)>::f(CblasColMajor, CblasNoTrans, CblasNoTrans, m, n, p, -1.0, mtx1, m, mtx2, p, 1.0, output, m);
+}
+
+template<class real>
+real * MultiplyMatricesT(int m, int p, int n, const real * mtx1, const real * mtx2, real * output)
 {
   real * target = output;
   if (target == NULL)
@@ -230,7 +245,19 @@ real * MultiplyMatricesT(int m, int p, int n, real * mtx1, real * mtx2, real * o
 }
 
 template<class real>
-real * ScalarMultiplyMatrix(int m, int n, real alpha, real * mtx, real * output)
+void MultiplyMatricesTAdd(int m, int p, int n, const real * mtx1, const real * mtx2, real * output)
+{
+  _xgemm<sizeof(real)==sizeof(float)>::f(CblasColMajor, CblasTrans, CblasNoTrans, m, n, p, 1.0, mtx1, p, mtx2, p, 1.0, output, m);
+}
+
+template<class real>
+void MultiplyMatricesTSub(int m, int p, int n, const real * mtx1, const real * mtx2, real * output)
+{
+  _xgemm<sizeof(real)==sizeof(float)>::f(CblasColMajor, CblasTrans, CblasNoTrans, m, n, p, -1.0, mtx1, p, mtx2, p, 1.0, output, m);
+}
+
+template<class real>
+real * ScalarMultiplyMatrix(int m, int n, real alpha, const real * mtx, real * output)
 {
   real * target = output;
   if (target == NULL)
@@ -249,7 +276,7 @@ real * ScalarMultiplyMatrix(int m, int n, real alpha, real * mtx, real * output)
 
 // computes Euclidean norm of a vector
 template <class real>
-real VectorNorm(int m, real * vec)
+real VectorNorm(int m, const real * vec)
 {
   return _xnrm2<sizeof(real)==sizeof(float)>::f(m, vec, 1);
 }
diff --git a/src/libmatrix/matrixBLASVanilla.cpp b/libraries/matrix/matrixBLASVanilla.cpp
similarity index 77%
rename from src/libmatrix/matrixBLASVanilla.cpp
rename to libraries/matrix/matrixBLASVanilla.cpp
index 32ff9d1f654c555fe94ebfb61979ad31d5b0a929..fdb5a7baf46cb7da72ec6dba99f053a519d3f2cb 100644
--- a/src/libmatrix/matrixBLASVanilla.cpp
+++ b/libraries/matrix/matrixBLASVanilla.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrix/matrixExp.cpp b/libraries/matrix/matrixExp.cpp
similarity index 83%
rename from src/libmatrix/matrixExp.cpp
rename to libraries/matrix/matrixExp.cpp
index a9348c175de3a2252d273dbc84255fcce55b4011..51d415e8f5db580ae867f7e0d14f1a0e2b8cce6a 100644
--- a/src/libmatrix/matrixExp.cpp
+++ b/libraries/matrix/matrixExp.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrix/matrixExp.h b/libraries/matrix/matrixExp.h
similarity index 66%
rename from src/libmatrix/matrixExp.h
rename to libraries/matrix/matrixExp.h
index 7b061f2594093d90606d96f7ceb493c84353da6a..323c30b2e3a856283249990bcd61bd2ace136c9b 100644
--- a/src/libmatrix/matrixExp.h
+++ b/libraries/matrix/matrixExp.h
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrix/matrixLAPACK.cpp b/libraries/matrix/matrixLAPACK.cpp
similarity index 88%
rename from src/libmatrix/matrixLAPACK.cpp
rename to libraries/matrix/matrixLAPACK.cpp
index 64cf8f0a111d3f1dfcbcf5ee440ab0cdfb961186..e7f6a7382b0b813c4d19ac5647f221c5abdca541 100644
--- a/src/libmatrix/matrixLAPACK.cpp
+++ b/libraries/matrix/matrixLAPACK.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -51,24 +60,24 @@
   #define SGPADM sgpadm_
   #define INTEGER __CLPK_integer
 #else
-//   #define DGETRF dgetrf
-//   #define DGETRI dgetri
-//   #define SGETRF sgetrf
-//   #define SGETRI sgetri
-//   #define DGESVD dgesvd
-//   #define SGESVD sgesvd
-//   #define DGELSY dgelsy
-//   #define SGELSY sgelsy
-//   #define DGESV dgesv
-//   #define SGESV sgesv
-//   #define DSYEV dsyev
-//   #define SSYEV ssyev
-//   #define DSYGV dsygv
-//   #define SSYGV ssygv
-//   #define SGEEV sgeev
-//   #define DGEEV dgeev
-//   #define DGPADM dgpadm_
-//   #define SGPADM sgpadm_
+  #define DGETRF dgetrf
+  #define DGETRI dgetri
+  #define SGETRF sgetrf
+  #define SGETRI sgetri
+  #define DGESVD dgesvd
+  #define SGESVD sgesvd
+  #define DGELSY dgelsy
+  #define SGELSY sgelsy
+  #define DGESV dgesv
+  #define SGESV sgesv
+  #define DSYEV dsyev
+  #define SSYEV ssyev
+  #define DSYGV dsygv
+  #define SSYGV ssygv
+  #define SGEEV sgeev
+  #define DGEEV dgeev
+  #define DGPADM dgpadm_
+  #define SGPADM sgpadm_
   #define INTEGER int
 #endif
 
@@ -136,7 +145,7 @@ public:
 };
 
 template<class real>
-real * InverseMatrix(int m, real * mtx, real * output)
+real * InverseMatrix(int m, const real * mtx, real * output)
 {
   real * target = output;
   if (target == NULL)
@@ -190,7 +199,7 @@ public:
 };
 
 template<class real>
-real * PseudoInverseMatrix(int m, int n, real * mtx, real singularValueThreshold, int * rank_, real * output)
+real * PseudoInverseMatrix(int m, int n, const real * mtx, real singularValueThreshold, int * rank_, real * output)
 {
   real * A = (real*) malloc (sizeof(real) * m * n);
   memcpy(A, mtx, sizeof(real) * m * n);
@@ -370,7 +379,7 @@ public:
 };
 
 template<class real>
-real * MatrixLeastSquareSolve(int m, int n, int nRhs, real * mtx, real * b, real rcond, int * rank_, real * output)
+real * MatrixLeastSquareSolve(int m, int n, int nRhs, const real * mtx, const real * b, real rcond, int * rank_, real * output)
 {
   real * A = (real*) malloc (sizeof(real) * m * n);
   if (!A)
@@ -495,7 +504,7 @@ public:
 };
 
 template<class real>
-void MatrixLUSolve(int n, int nRhs, real * mtx, real * x, real * b)
+void MatrixLUSolve(int n, int nRhs, const real * mtx, real * x, const real * b)
 {
   real * A = (real*) malloc (sizeof(real) * n * n);
   if (!A)
@@ -807,12 +816,12 @@ void MatrixSVD(int m, int n, real * mtx, real * U, real * Sigma, real * VT)
   }
 }
 
-template float * InverseMatrix(int m, float * mtx, float * output);
-template double * InverseMatrix(int m, double * mtx, double * output);
-template float * PseudoInverseMatrix(int m, int n, float * mtx, float singularValueThreshold, int * rank, float * output);
-template double * PseudoInverseMatrix(int m, int n, double * mtx, double singularValueThreshold, int * rank, double * output);
-template float * MatrixLeastSquareSolve(int m, int n, int nRhs, float * mtx, float * b, float rcond, int * rank, float * output);
-template double * MatrixLeastSquareSolve(int m, int n, int nRhs, double * mtx, double * b, double rcond, int * rank, double * output);
+template float * InverseMatrix(int m, const float * mtx, float * output);
+template double * InverseMatrix(int m, const double * mtx, double * output);
+template float * PseudoInverseMatrix(int m, int n, const float * mtx, float singularValueThreshold, int * rank, float * output);
+template double * PseudoInverseMatrix(int m, int n, const double * mtx, double singularValueThreshold, int * rank, double * output);
+template float * MatrixLeastSquareSolve(int m, int n, int nRhs, const float * mtx, const float * b, float rcond, int * rank, float * output);
+template double * MatrixLeastSquareSolve(int m, int n, int nRhs, const double * mtx, const double * b, double rcond, int * rank, double * output);
 template void SymmetricMatrixEigenDecomposition(int m, float * mtx, float * Q, float * Lambda);
 template void SymmetricMatrixEigenDecomposition(int m, double * mtx, double * Q, double * Lambda);
 template void SymmetricMatrixGeneralEigenDecomposition(int m, float * mtx, float * mtx2, float * Q, float * Lambda);
@@ -821,6 +830,6 @@ template int MatrixEigenDecomposition(int m, float * mtx, float * EigenVectors,
 template int MatrixEigenDecomposition(int m, double * mtx, double * EigenVectors, double * LambdaRe, double * LambdaIm);
 template void MatrixSVD(int m, int n, float * mtx, float * U, float * Sigma, float * VT);
 template void MatrixSVD(int m, int n, double * mtx, double * U, double * Sigma, double * VT);
-template void MatrixLUSolve(int n, int nRhs, float * mtx, float * x, float * b);
-template void MatrixLUSolve(int n, int nRhs, double * mtx, double * x, double * b);
+template void MatrixLUSolve(int n, int nRhs, const float * mtx, float * x, const float * b);
+template void MatrixLUSolve(int n, int nRhs, const double * mtx, double * x, const double * b);
 
diff --git a/src/libmatrix/matrixLAPACK.h b/libraries/matrix/matrixLAPACK.h
similarity index 59%
rename from src/libmatrix/matrixLAPACK.h
rename to libraries/matrix/matrixLAPACK.h
index 6de1588cb4a41f5ccc76fbaa42d83d254812cd8c..ebdf2856eebbb106a618eca6b1b61a4d21afe410 100644
--- a/src/libmatrix/matrixLAPACK.h
+++ b/libraries/matrix/matrixLAPACK.h
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,22 +41,23 @@
 #include <stdlib.h>
 
 template<class real>
-real * InverseMatrix(int m, real * mtx, real * output = NULL);
+real * InverseMatrix(int m, const real * mtx, real * output = NULL);
 
 template<class real>
-real * PseudoInverseMatrix(int m, int n, real * mtx, real singularValueThreshold = 1e-6, int * rank = NULL, real * output = NULL);
+real * PseudoInverseMatrix(int m, int n, const real * mtx, real singularValueThreshold = 1e-6, int * rank = NULL, real * output = NULL);
 
 template<class real>
-real * MatrixLeastSquareSolve(int m, int n, int nRhs, real * mtx, real * b, real rcond, int * rank = NULL, real * output = NULL);
+real * MatrixLeastSquareSolve(int m, int n, int nRhs, const real * mtx, const real * b, real rcond, int * rank = NULL, real * output = NULL);
 
 template<class real>
-void MatrixLUSolve(int n, int nRhs, real * mtx, real * x, real * b);
+void MatrixLUSolve(int n, int nRhs, const real * mtx, real * x, const real * b);
 
 // mtx * x = lambda * x
 template<class real>
 void SymmetricMatrixEigenDecomposition(int m, real * mtx, real * Q, real * Lambda);
 
 // mtx * x = lambda * mtx2 * x
+// Warning: mtx2 will be modified after this function call.
 template<class real>
 void SymmetricMatrixGeneralEigenDecomposition(int m, real * mtx, real * mtx2, real * Q, real * Lambda);
 
diff --git a/src/libmatrix/matrixPCA.cpp b/libraries/matrix/matrixPCA.cpp
similarity index 86%
rename from src/libmatrix/matrixPCA.cpp
rename to libraries/matrix/matrixPCA.cpp
index efe85021c2c532eb62de25ca9e2dc2d85dae540f..93326c0e031613c32f5108cd3d66077813ef073a 100644
--- a/src/libmatrix/matrixPCA.cpp
+++ b/libraries/matrix/matrixPCA.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -161,7 +170,7 @@ int MatrixPCA(ThresholdingSpecification * thresholdingSpecification,
     #define DGESVD dgesvd_
     #define INTEGER __CLPK_integer
   #else
-//     #define DGESVD dgesvd
+    #define DGESVD dgesvd
     #define INTEGER int
   #endif
 
diff --git a/src/libmatrix/matrixPCA.h b/libraries/matrix/matrixPCA.h
similarity index 71%
rename from src/libmatrix/matrixPCA.h
rename to libraries/matrix/matrixPCA.h
index e55dd5bc3a4105b27d9b8ece238a3240a3514913..f873801b8696868b5c09d01d6e4d241b6fd12cf7 100644
--- a/src/libmatrix/matrixPCA.h
+++ b/libraries/matrix/matrixPCA.h
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrix/matrixProjection.cpp b/libraries/matrix/matrixProjection.cpp
similarity index 86%
rename from src/libmatrix/matrixProjection.cpp
rename to libraries/matrix/matrixProjection.cpp
index ece5107ea8466bfacb46971a6793b08f2376b83d..dcc024382c4bc7573eeeace539fd26dda2e64228 100644
--- a/src/libmatrix/matrixProjection.cpp
+++ b/libraries/matrix/matrixProjection.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrix/matrixProjection.h b/libraries/matrix/matrixProjection.h
similarity index 74%
rename from src/libmatrix/matrixProjection.h
rename to libraries/matrix/matrixProjection.h
index 9d1368899ec242e8cd049150ed52546dbf2567df..c7561b739ad84f0ec032b6846d3796de65895ae9 100644
--- a/src/libmatrix/matrixProjection.h
+++ b/libraries/matrix/matrixProjection.h
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmatrixIO/matrixIO.cpp b/libraries/matrixIO/matrixIO.cpp
similarity index 75%
rename from src/libmatrixIO/matrixIO.cpp
rename to libraries/matrixIO/matrixIO.cpp
index 919d147fb6c6528088782b3b4dc876e054c8a0b7..cf73c562a986a150ba89b666a98c228a69bb255f 100644
--- a/src/libmatrixIO/matrixIO.cpp
+++ b/libraries/matrixIO/matrixIO.cpp
@@ -1,14 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
+ * "matrixIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,13 +37,10 @@
 #include "matrixMacros.h"
 #include "matrixIO.h"
 
-
-namespace vega
-{
 // writes out the m x n matrix onto the stream, in binary format
 // LAPACK column-major order
 template <class real>
-int WriteMatrixToStream(FILE * file, int m, int n, real * matrix)
+int WriteMatrixToStream(FILE * file, int m, int n, const real * matrix)
 {
   if ((int)(fwrite(matrix,sizeof(real),m*n,file)) < m*n)
     return 1;
@@ -55,10 +61,9 @@ int WriteMatrixHeaderToStream(FILE * file, int m, int n)
 
 
 template <class real>
-int WriteMatrixToDisk(const char * filename, int m, int n, real * matrix)
+int WriteMatrixToDisk(const char * filename, int m, int n, const real * matrix)
 {
-  FILE * file;
-  file = fopen(filename, "wb");
+  FILE * file = fopen(filename, "wb");
   if (!file)
   {
     printf ("Can't open output file: %s.\n", filename);
@@ -83,14 +88,18 @@ int WriteMatrixToDisk(const char * filename, int m, int n, real * matrix)
 }
 
 template <class real>
-int AppendMatrixToDisk(const char * filename, int mAppendix, int nAppendix, real * matrixAppendix)
+int AppendMatrixToDisk(const char * filename, int mAppendix, int nAppendix, const real * matrixAppendix)
 {
-  int m, n;
-  real * matrix;
-  int code = ReadMatrixFromDisk(filename, &m, &n, &matrix);
-  if (code != 0)
+  int m = 0, n = 0;
+  FILE * file = fopen(filename, "rb+"); // read/update bindary mode
+  if (!file) // if no such file, create one
   {
-    printf ("Can't open output file: %s.\n", filename);
+    return WriteMatrixToDisk(filename, mAppendix, nAppendix, matrixAppendix);
+  }
+
+  if (ReadMatrixSizeFromDisk(filename, &m, &n) != 0) 
+  {
+    printf ("Error reading matrix header from disk file: %s.\n", filename);
     return 1;
   }
 
@@ -100,18 +109,33 @@ int AppendMatrixToDisk(const char * filename, int mAppendix, int nAppendix, real
     return 1;
   }
 
-  matrix = (real*) realloc (matrix, sizeof(real) * mAppendix * (n + nAppendix));
-  memcpy(&matrix[mAppendix * n], matrixAppendix, sizeof(real) * mAppendix * nAppendix);
+  if (fseek(file, 0, SEEK_SET) != 0) // rewind file
+  {
+    printf ("Error setting position indicator on file %s.\n", filename);
+    return 1;
+  }
 
-  code = WriteMatrixToDisk(filename, mAppendix, n + nAppendix, matrix);
-  if (code != 0)
+  int nNew = n + nAppendix;
+  if (WriteMatrixHeaderToStream(file, m, nNew) != 0)
   {
-    printf ("Error writing the matrix to disk file: %s.\n", filename);
+    printf ("Error writing the matrix header to disk file: %s.\n", filename);
     return 1;
   }
 
-  free(matrix);
+  
+  if (fseek(file, sizeof(real) * m * n, SEEK_CUR) != 0) // skip existing stored data
+  {
+    printf ("Error setting position indicator on file %s.\n", filename);
+    return 1;
+  }
 
+  if (WriteMatrixToStream(file,m,nAppendix,matrixAppendix) != 0)
+  {
+    printf ("Error appending the matrix to disk file: %s.\n", filename);
+    return 1;
+  }
+
+  fclose(file);
   return 0;
 }
 
@@ -132,8 +156,7 @@ int ReadMatrixFromStream(FILE * file, int M, int N, real * matrix)
 template <class real>
 int ReadMatrixFromDisk(const char * filename, int * m, int * n, real ** matrix)
 {
-  FILE * file;
-  file = fopen(filename ,"rb");
+  FILE * file = fopen(filename ,"rb");
   if (!file)
   {
     printf ("Can't open input matrix file: %s.\n", filename);
@@ -151,6 +174,38 @@ int ReadMatrixFromDisk(const char * filename, int * m, int * n, real ** matrix)
   *matrix = (real *) malloc (sizeof(real)*(*m)*(*n));
 
   if (ReadMatrixFromStream(file,*m,*n,*matrix) != 0)
+  {
+    printf ("Error reading matrix data from disk file: %s.\n", filename);
+    free(*matrix);
+    *matrix = NULL;
+    return 1;
+  }
+
+  fclose(file);
+
+  return 0;
+}
+
+template <class real>
+int ReadMatrixFromDisk(const char * filename, int * m, int * n, std::vector<real> & matrix)
+{
+  FILE * file = fopen(filename ,"rb");
+  if (!file)
+  {
+    printf ("Can't open input matrix file: %s.\n", filename);
+    return 1;
+  }
+
+  if (ReadMatrixSizeFromStream(file,m,n) != 0)
+  {
+    printf ("Error reading matrix header from disk file: %s.\n", filename);
+    return 1;
+  }
+
+  //int size = (*m) * (*n) * sizeof(real) + 2 * sizeof(int);
+  matrix.resize((*m) * (*n));
+
+  if (ReadMatrixFromStream(file, *m, *n, matrix.data()) != 0)
   {
     printf ("Error reading matrix data from disk file: %s.\n", filename);
     return 1;
@@ -184,7 +239,7 @@ int ReadMatrixFromDiskListOfFiles(const char * fileList, int * m, int * n, real
     while (matrixFilename[k] != '\0')
       k++;
 
-    if (matrixFilename[k-1] == '\n')
+    if ((k > 0) && (matrixFilename[k-1] == '\n'))
       matrixFilename[k-1] = '\0';
      
     if (k <= 1) // blank line, ignore it
@@ -243,7 +298,7 @@ int ReadMatrixFromDiskListOfFiles(const char * fileList, int * m, int * n, real
     {
       k++;
     }
-    if (matrixFilename[k-1] == '\n')
+    if ((k > 0) && (matrixFilename[k-1] == '\n'))
       matrixFilename[k-1] = '\0';
      
     if (k <= 1) // blank line, ignore it
@@ -259,6 +314,9 @@ int ReadMatrixFromDiskListOfFiles(const char * fileList, int * m, int * n, real
     if (ReadMatrixFromDisk(matrixFilename, &M, &N, &currentMatrix) != 0)
     {
       printf("Couldn't load matrix from file %s.\n",matrixFilename);
+      free(currentMatrix);
+      free(*matrix);
+      (*matrix) = NULL;
       return 1;
     }
 
@@ -279,7 +337,7 @@ int ReadMatrixFromDiskListOfFiles(const char * fileList, int * m, int * n, real
 }
 
 template <class real>
-int WriteMatrixToDisk_(const char * filename, int m, int n, real * matrix)
+int WriteMatrixToDisk_(const char * filename, int m, int n, const real * matrix)
 {
   if (WriteMatrixToDisk(filename, m, n, matrix) != 0)
     exit(1);
@@ -288,7 +346,7 @@ int WriteMatrixToDisk_(const char * filename, int m, int n, real * matrix)
 }
 
 template <class real>
-int AppendMatrixToDisk_(const char * filename, int mAppendix, int nAppendix, real * matrixAppendix)
+int AppendMatrixToDisk_(const char * filename, int mAppendix, int nAppendix, const real * matrixAppendix)
 {
   if (AppendMatrixToDisk(filename, mAppendix, nAppendix, matrixAppendix) != 0)
     exit(1);
@@ -305,10 +363,18 @@ int ReadMatrixFromDisk_(const char * filename, int * m, int * n, real ** matrix)
   return 0;
 }
 
+template <class real>
+int ReadMatrixFromDisk_(const char * filename, int * m, int * n, std::vector<real> & matrix)
+{
+  if (ReadMatrixFromDisk(filename, m, n, matrix) != 0)
+    exit(1);
+
+  return 0;
+}
+
 int ReadMatrixSizeFromDisk(const char * filename, int * m, int * n)
 {
-  FILE * file;
-  file = fopen(filename, "rb");
+  FILE * file = fopen(filename, "rb");
   if (!file)
   {
     printf ("Can't open input matrix file: %s.\n", filename);
@@ -343,7 +409,7 @@ int ReadMatrixSizeFromStream(FILE * file, int * m, int * n)
   return 0;
 }
 
-int OpenFile_(const char * filename, FILE ** fin, char * mode)
+int OpenFile_(const char * filename, FILE ** fin, const char * mode)
 {
   *fin = fopen(filename, mode);
   if (!(*fin))
@@ -399,7 +465,7 @@ void PrintMatrixInMathematicaFormat(int n, int r, real * U)
     {
       printf("%.15f",U[ELT(n,i,j)]);
       if (j != r-1)
-	printf(", ");
+      printf(", ");
     }
     printf("}");
 
@@ -461,8 +527,7 @@ template <class real>
 int ReadMatrixFromDiskTextFile(const char * filename, int * m, int * n, real ** matrix)
 {
   // open input
-  FILE * file;
-  file = fopen(filename, "r");
+  FILE * file = fopen(filename, "r");
   if (!file)
   {
     printf("Error: couldn't open input file %s.\n", filename);
@@ -522,6 +587,7 @@ int ReadMatrixFromDiskTextFile(const char * filename, int * m, int * n, real **
       printf("Error: the number of data rows in file %s is greater than the declared dimension %d x %d .\n", filename, *m, *n);
       free(buf);
       free(*matrix);
+      *matrix = NULL;
       return 1;
     }
 
@@ -561,11 +627,12 @@ int ReadMatrixFromDiskTextFile(const char * filename, int * m, int * n, real **
       if (token == NULL)
       {
         printf("Token is NULL. Error parsing line %d.\n",i+1);
+        free(buf);
+        free(*matrix);
+        *matrix = NULL;
         return  1 ;
       }
 
-      
-
       if (sscanf(token,sscanfFormat,&data) < 1)
       {
         printf("Cannot parse data from current token. Error parsing line %d.\n",i);
@@ -590,6 +657,7 @@ int ReadMatrixFromDiskTextFile(const char * filename, int * m, int * n, real **
   {
     printf("Error: the number of data rows in file %s is smaller than the declared dimension %d x %d .\n", filename, *m, *n);
     free(*matrix);
+    *matrix = NULL;
     return 1;
   }
 
@@ -597,7 +665,7 @@ int ReadMatrixFromDiskTextFile(const char * filename, int * m, int * n, real **
 }
 
 template <class real>
-int WriteMatrixToDiskTextFile(const char * filename, int m, int n, real * matrix)
+int WriteMatrixToDiskTextFile(const char * filename, int m, int n, const real * matrix)
 {
   // open file
   FILE * fout;
@@ -631,8 +699,7 @@ int WriteMatrixToDiskTextFile(const char * filename, int m, int n, real * matrix
 template <class real>
 int ReadModesFromDisk(const char * filename, int * N, int * R, real ** frequencies, real ** modes)
 {
-  FILE * file;
-  file = fopen(filename, "r");
+  FILE * file = fopen(filename, "r");
 
   if (!file)
   {
@@ -691,6 +758,10 @@ int ReadModesFromDisk(const char * filename, int * N, int * R, real ** frequenci
     if (!fgets(s,1024,file)) // node header
     {
       printf ("ReadModesFromDisk: failed to read mode header for mode %d.\n",i+1);
+      free(*modes);
+      free(*frequencies);
+      *modes = NULL;
+      *frequencies = NULL;
       return 1;
     }
 
@@ -702,6 +773,10 @@ int ReadModesFromDisk(const char * filename, int * N, int * R, real ** frequenci
 	      &((*modes)[ELT(3*n,3*j+2,i)])) < 4)
       {
         printf ("ReadModesFromDisk: failed to read mode %d data at node %d.\n",i+1,j+1);
+        free(*modes);
+        free(*frequencies);
+        *modes = NULL;
+        *frequencies = NULL;
         return 1;
       }
     }
@@ -714,8 +789,7 @@ int ReadModesFromDisk(const char * filename, int * N, int * R, real ** frequenci
 
 int ReadModeInfoFromDisk(const char * filename, int * N, int * R)
 {
-  FILE * file;
-  file = fopen(filename, "r");
+  FILE * file = fopen(filename, "r");
 
   if (!file)
   {
@@ -742,8 +816,7 @@ int ReadModeInfoFromDisk(const char * filename, int * N, int * R)
 template <class real>
 int WriteModesToDisk(const char * filename, int n, int r, real * frequencies, real * modes)
 {
-  FILE * file;
-  file = fopen(filename, "w");
+  FILE * file = fopen(filename, "w");
 
   if (!file)
     return 1;
@@ -784,41 +857,55 @@ int WriteModesToDisk(const char * filename, int n, int r, real * frequencies, re
 template void PrintMatrixInMathematicaFormat<double>(int n, int r, double * U);
 template void PrintMatrixInMathematicaFormat<float>(int n, int r, float * U);
 
-template int ReadModesFromDisk<double>(const char * filename, int * n, int * r, double ** frequencies, double ** modes);
-template int ReadModesFromDisk<float>(const char * filename, int * n, int * r, float ** frequencies, float ** modes);
-
-template int WriteModesToDisk<double>(const char * filename, int n, int r, double * frequencies, double * modes);
-template int WriteModesToDisk<float>(const char * filename, int n, int r, float * frequencies, float * modes);
+template int WriteMatrixToStream<double>(FILE * file, int m, int n, const double * matrix);
+template int WriteMatrixToStream<float>(FILE * file, int m, int n, const float * matrix);
+template int WriteMatrixToStream<int>(FILE * file, int m, int n, const int * matrix);
 
-template int WriteMatrixToStream<double>(FILE * file, int m, int n, double * matrix);
-template int WriteMatrixToStream<float>(FILE * file, int m, int n, float * matrix);
+template int WriteMatrixToDisk<double>(const char * filename, int m, int n, const double * matrix);
+template int WriteMatrixToDisk<float>(const char * filename, int m, int n, const float * matrix);
+template int WriteMatrixToDisk<int>(const char * filename, int m, int n, const int * matrix);
 
-template int WriteMatrixToDisk<double>(const char * filename, int m, int n, double * matrix);
-template int WriteMatrixToDisk<float>(const char * filename, int m, int n, float * matrix);
+template int WriteMatrixToDisk_<double>(const char * filename, int m, int n, const double * matrix);
+template int WriteMatrixToDisk_<float>(const char * filename, int m, int n, const float * matrix);
+template int WriteMatrixToDisk_<int>(const char * filename, int m, int n, const int * matrix);
 
-template int WriteMatrixToDisk_<double>(const char * filename, int m, int n, double * matrix);
-template int WriteMatrixToDisk_<float>(const char * filename, int m, int n, float * matrix);
+template int AppendMatrixToDisk<double>(const char * filename, int m, int n, const double * matrix);
+template int AppendMatrixToDisk<float>(const char * filename, int m, int n, const float * matrix);
+template int AppendMatrixToDisk<int>(const char * filename, int m, int n, const int * matrix);
 
-template int AppendMatrixToDisk_<double>(const char * filename, int m, int n, double * matrix);
-template int AppendMatrixToDisk_<float>(const char * filename, int m, int n, float * matrix);
+template int AppendMatrixToDisk_<double>(const char * filename, int m, int n, const double * matrix);
+template int AppendMatrixToDisk_<float>(const char * filename, int m, int n, const float * matrix);
+template int AppendMatrixToDisk_<int>(const char * filename, int m, int n, const int * matrix);
 
 template int ReadMatrixFromDiskListOfFiles<double>(const char * fileList, int * m, int * n, double ** matrix);
 template int ReadMatrixFromDiskListOfFiles<float>(const char * fileList, int * m, int * n, float ** matrix);
+template int ReadMatrixFromDiskListOfFiles<int>(const char * fileList, int * m, int * n, int ** matrix);
 
 template int ReadMatrixFromStream<double>(FILE * file, int m, int n, double * matrix);
 template int ReadMatrixFromStream<float>(FILE * file, int m, int n, float * matrix);
+template int ReadMatrixFromStream<int>(FILE * file, int m, int n, int * matrix);
 
 template int ReadMatrixFromDisk<double>(const char * filename, int * m, int * n, double ** matrix);
 template int ReadMatrixFromDisk<float>(const char * filename, int * m, int * n, float ** matrix);
+template int ReadMatrixFromDisk<int>(const char * filename, int * m, int * n, int ** matrix);
+
+template int ReadMatrixFromDisk<double>(const char * filename, int * m, int * n, std::vector<double> & matrix);
+template int ReadMatrixFromDisk<float>(const char * filename, int * m, int * n, std::vector<float> & matrix);
+template int ReadMatrixFromDisk<int>(const char * filename, int * m, int * n, std::vector<int> & matrix);
 
 template int ReadMatrixFromDisk_<double>(const char * filename, int * m, int * n, double ** matrix);
 template int ReadMatrixFromDisk_<float>(const char * filename, int * m, int * n, float ** matrix);
+template int ReadMatrixFromDisk_<int>(const char * filename, int * m, int * n, int ** matrix);
+
+template int ReadMatrixFromDisk_<double>(const char * filename, int * m, int * n, std::vector<double> & matrix);
+template int ReadMatrixFromDisk_<float>(const char * filename, int * m, int * n, std::vector<float> & matrix);
+template int ReadMatrixFromDisk_<int>(const char * filename, int * m, int * n, std::vector<int> & matrix);
 
 template int ReadMatrixFromDiskTextFile<double>(const char * filename, int * m, int * n, double ** matrix);
 template int ReadMatrixFromDiskTextFile<float>(const char * filename, int * m, int * n, float ** matrix);
 
-template int WriteMatrixToDiskTextFile<double>(const char * filename, int m, int n, double * matrix);
-template int WriteMatrixToDiskTextFile<float>(const char * filename, int m, int n, float * matrix);
+template int WriteMatrixToDiskTextFile<double>(const char * filename, int m, int n, const double * matrix);
+template int WriteMatrixToDiskTextFile<float>(const char * filename, int m, int n, const float * matrix);
 
 template int ReadBinary(FILE * fin, bool * data);
 template int ReadBinary(FILE * fin, int * data);
@@ -840,4 +927,9 @@ template void ReadBinaryBuffer_(FILE * fin, int size, int * data);
 template void ReadBinaryBuffer_(FILE * fin, int size, float * data);
 template void ReadBinaryBuffer_(FILE * fin, int size, double * data);
 
-}
+
+template int ReadModesFromDisk<double>(const char * filename, int * n, int * r, double ** frequencies, double ** modes);
+template int ReadModesFromDisk<float>(const char * filename, int * n, int * r, float ** frequencies, float ** modes);
+
+template int WriteModesToDisk<double>(const char * filename, int n, int r, double * frequencies, double * modes);
+template int WriteModesToDisk<float>(const char * filename, int n, int r, float * frequencies, float * modes);
diff --git a/libraries/matrixIO/matrixIO.h b/libraries/matrixIO/matrixIO.h
new file mode 100644
index 0000000000000000000000000000000000000000..be67be71b9302aeabcc78428e76467fcd6f59506
--- /dev/null
+++ b/libraries/matrixIO/matrixIO.h
@@ -0,0 +1,181 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "matrixIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  These routines allow you to read/write a dense matrix from/to a file,
+  using a special simple binary format. LAPACK-style column-major
+  format is used. The code is templated for "float" and "double" datatypes.
+
+  See also matrix.h.
+  
+  Binary file format, for a m x n matrix, is:
+  <m> <n> <data>
+  where: m is an integer (of type "int")
+         n is an integer (of type "int")
+         data is a sequence of m*n real numbers (of type either "float" or "double")
+
+  All quantities are stored in binary format.
+  Data is stored in column-major format (as in LAPACK): 
+  first column first, then second column, and so on.
+  Total file size is 2 * sizeof(int) + m * n * sizeof(real),
+  where real is either "float" or "double".
+
+  Underscored routines will perform error checking and cause the program to abort 
+  in case of error when accessing the file.
+*/
+
+#ifndef _MATRIXIO_H_
+#define _MATRIXIO_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+#include "matrixMacros.h"
+
+// === matrix input/output routines ===
+
+// function ending with "_" will abort if fails to read/write
+template <class real>
+int ReadMatrixFromDisk(const char * filename, int * m, int * n, real ** matrix);
+template <class real>
+int ReadMatrixFromDisk(const char * filename, int * m, int * n, std::vector<real> & matrix);
+template <class real>
+int ReadMatrixFromDisk(const char * filename, int & m, int & n, std::vector<real> & matrix) { return ReadMatrixFromDisk(filename, &m, &n, matrix); }
+
+template <class real>
+int ReadMatrixFromDisk_(const char * filename, int * m, int * n, real ** matrix);
+template <class real>
+int ReadMatrixFromDisk_(const char * filename, int * m, int * n, std::vector<real> & matrix);
+template <class real>
+int ReadMatrixFromDisk_(const char * filename, int & m, int & n, std::vector<real> & matrix) { return ReadMatrixFromDisk_(filename, &m, &n, matrix); }
+
+template <class real>
+int ReadVectorFromDisk(const char * filename, int & m, std::vector<real> & vec);
+template <class real>
+int ReadVectorFromDisk(const char * filename, std::vector<real> & vec) { int n = 0; return ReadVectorFromDisk(filename, n, vec); }
+
+int ReadMatrixSizeFromDisk(const char * filename, int * m, int * n);
+void ReadMatrixSizeFromDisk_(const char * filename, int * m, int * n);
+void ChangeMatrixHeader(const char * filename, int numRows, int numColumns); // overwrites any previous header; keeps data intact
+
+template <class real>
+int WriteMatrixToDisk(const char * filename, int m, int n, const real * matrix);
+template <class real>
+int WriteMatrixToDisk_(const char * filename, int m, int n, const real * matrix);
+
+template <class real>
+int WriteVectorToDisk(const char * filename, int m, const real * vector) { return WriteMatrixToDisk(filename, m, 1, vector); }
+template <class real>
+int WriteVectorToDisk(const char * filename, const std::vector<real> & vec) { return WriteVectorToDisk(filename, vec.size(), vec.data()); }
+
+// assert m is the same as the matrix already stored in filename
+template <class real>
+int AppendMatrixToDisk(const char * filename, int m, int n, const real * matrix);
+template <class real>
+int AppendMatrixToDisk_(const char * filename, int m, int n, const real * matrix);
+
+template <class real>
+int ReadMatrixFromDiskTextFile(const char * filename, int * m, int * n, real ** matrix);
+
+template <class real>
+int WriteMatrixToDiskTextFile(const char * filename, int m, int n, const real * matrix);
+
+// "fileList" is a filename of a text file that contains the matrices to be loaded (concatenated column-wise) into the matrix, one text entry per line of "fileList"
+template <class real>
+int ReadMatrixFromDiskListOfFiles(const char * fileList, int * m, int * n, real ** matrix);
+
+// prints the matrix to standard output in Mathematica format
+template <class real>
+void PrintMatrixInMathematicaFormat(int n, int r, real * U);
+
+// === assert and abort ===
+
+// if (a!=b) exit program (and print the integer "assertionIdentifier" to stdout)
+int Assert_(int a, int b, int assertionIdentifier);
+int Assert_(bool cond, int assertionIdentifier);
+
+// exit the program, by first printing the "reason" text message
+void Abort_(const char * reason);
+void Abort_(const char * reason, int exitCode);
+
+// === stream routines (they operate on a file that is already open, in binary mode) ===
+
+int OpenFile_(const char * filename, FILE ** fin, const char * mode);
+
+template <class real>
+int WriteMatrixToStream(FILE * file, int m, int n, const real * matrix);
+
+int WriteMatrixHeaderToStream(FILE * file, int m, int n);
+
+template <class real>
+int ReadMatrixFromStream(FILE * file, int m, int n, real * matrix);
+
+int ReadMatrixSizeFromStream(FILE * file, int * m, int * n);
+
+// === helper routines ===
+
+template<class T>
+int ReadBinary(FILE * fin, T * data);
+template<class T>
+void ReadBinary_(FILE * fin, T * data);
+
+template<class T>
+int ReadBinaryBuffer(FILE * fin, int size, T * data);
+template<class T>
+void ReadBinaryBuffer_(FILE * fin, int size, T * data);
+
+// === specific research routines for linear modal analysis (rarely used) ===
+
+template <class real>
+int ReadModesFromDisk(const char * filename, int * n, int * r, real ** frequencies, real ** modes);
+
+int ReadModeInfoFromDisk(const char * filename, int * n, int * r);
+
+template <class real>
+int WriteModesToDisk(const char * filename, int n, int r, real * frequencies, real * modes);
+
+// =================================================================
+// IMPLEMENTATION
+// =================================================================
+
+template <class real>
+int ReadVectorFromDisk(const char * filename, int & m, std::vector<real> & vec)
+{
+  int n = 0;
+  int ret = ReadMatrixFromDisk(filename, m, n, vec);
+  if (ret != 0) return ret;
+  if (n != 1) return 2;
+  return ret;
+}
+
+#endif
+
diff --git a/libraries/matrixIO/matrixMacros.h b/libraries/matrixIO/matrixMacros.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd28180c25cc4974104c4e7c1e724db2fb5ad8d3
--- /dev/null
+++ b/libraries/matrixIO/matrixMacros.h
@@ -0,0 +1,56 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "matrixIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Commonly used macros in the matrix library.
+*/
+
+#ifndef _MATRIXMACROS_H_
+#define _MATRIXMACROS_H_
+
+/*
+   Returns the array offset index of element located in row i and column j (both 0-indexed). The matrix has "numRows" rows, and is stored in LAPACK-style column-major order.
+*/
+#ifndef ELT
+  #define ELT(numRows,i,j) (((long)j)*((long)numRows)+((long)i))
+#endif
+
+#ifndef MIN
+  #define MIN(x,y) ((x)<(y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+  #define MAX(x,y) ((x)>(y) ? (x) : (y))
+#endif
+
+
+#endif
diff --git a/libraries/matrixIO/multiMatrixIO.cpp b/libraries/matrixIO/multiMatrixIO.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a17b4af135933755b12785c40e754a6aa2b0451d
--- /dev/null
+++ b/libraries/matrixIO/multiMatrixIO.cpp
@@ -0,0 +1,159 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "matrixIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Load/save multiple binary matrices from a single file.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "multiMatrixIO.h"
+
+int MultiMatrixIO::Save(const char * filename, int numMatrices, int * m, int * n, double ** matrices)
+{
+  FILE * fout = fopen(filename, "wb");
+  if (fout == nullptr)
+  {
+    printf("Error in MultiMatrixIO::Save: cannot open %s for writing.\n", filename);
+    return 1;
+  }
+
+  // write the number of matrices
+  fwrite(&numMatrices, sizeof(int), 1, fout);
+
+  // write the dimensions of the matrices
+  for(int i=0; i<numMatrices; i++)
+  {
+    fwrite(&m[i], sizeof(int), 1, fout);
+    fwrite(&n[i], sizeof(int), 1, fout);
+  }
+
+  // write the matrices
+  for(int i=0; i<numMatrices; i++)
+  {
+    fwrite(matrices[i], sizeof(double), m[i] * n[i], fout);
+  }
+
+  fclose(fout);
+
+  return 0;
+}
+
+int MultiMatrixIO::Load(const char * filename, int * numMatrices, int ** m, int ** n, double *** matrices)
+{
+  *m = nullptr;
+  *n = nullptr;
+  *matrices = nullptr;
+
+  FILE * fin = fopen(filename, "rb");
+  if (fin == nullptr)
+  {
+    printf("Error in MultiMatrixIO::Load: cannot open %s for reading.\n", filename);
+    return 1;
+  }
+
+  // read number of matrices
+  unsigned int items = fread(numMatrices, sizeof(int), 1, fin);
+  if (items != 1)
+  {
+    printf("Error in MultiMatrixIO::Load: mismatch in the number of matrices.\n");
+    fclose(fin);
+    return 1;
+  }
+
+  *m = (int*) malloc (sizeof(int) * (*numMatrices));
+  *n = (int*) malloc (sizeof(int) * (*numMatrices));
+
+  // read the matrix dimensions 
+  for(int i=0; i<*numMatrices; i++)
+  {
+    items = fread(&((*m)[i]), sizeof(int), 1, fin);
+    if (items != 1)
+    {
+      printf("Error in MultiMatrixIO::Load: mismatch in the number of matrices.\n");
+      free(*m);
+      *m = nullptr;
+      free(*n);
+      *n = nullptr;
+      fclose(fin);
+      return 1;
+    }
+
+    items = fread(&((*n)[i]), sizeof(int), 1, fin);
+    if (items != 1)
+    {
+      printf("Error in MultiMatrixIO::Load: mismatch in the number of matrices.\n");
+      free(*m);
+      *m = nullptr;
+      free(*n);
+      *n = nullptr;
+      fclose(fin);
+      return 1;
+    }
+  }
+
+  // allocate memory for matrices
+  (*matrices) = (double **) malloc (sizeof(double*) * *numMatrices);
+  for(int i=0; i<*numMatrices; i++)
+  {
+    int numEntries = (*m)[i] * (*n)[i];
+    if (numEntries > 0)
+      (*matrices)[i] = (double *) malloc(sizeof(double) * numEntries);
+    else
+      (*matrices)[i] = nullptr;
+  }
+
+  // read the each matrices
+  for(int i=0; i<*numMatrices; i++)
+  {
+    unsigned int matrixSize = (*m)[i] * (*n)[i];
+    items = fread((*matrices)[i], sizeof(double), matrixSize, fin);
+    if (items != matrixSize)
+    {
+      printf("Error in MultiMatrixIO::Load: invalid number of bytes.\n");
+      free(*m);
+      *m = nullptr;
+      free(*n);
+      *n = nullptr;
+      for(int i = 0; i < *numMatrices; i++) { free((*matrices)[i]); }
+      free(*matrices);
+      *matrices = nullptr;
+      fclose(fin);
+      return 1;
+    }
+  }
+  fclose(fin);
+
+  return 0;
+}
+
diff --git a/libraries/matrixIO/multiMatrixIO.h b/libraries/matrixIO/multiMatrixIO.h
new file mode 100644
index 0000000000000000000000000000000000000000..5a3f20e5efa72e0978bcd2b0bc16c8a5d0d2f14f
--- /dev/null
+++ b/libraries/matrixIO/multiMatrixIO.h
@@ -0,0 +1,57 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "matrixIO" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Load/save multiple binary matrices from a single file.
+*/
+
+#ifndef _MULTIMATRIXIO_H_
+#define _MULTIMATRIXIO_H_
+
+class MultiMatrixIO
+{
+public:
+  // input: filename
+  // output: numMatrices, m, n, matrices
+  // returns 0 on success, non-zero otherwise
+  static int Load(const char * filename, int * numMatrices, int ** m, int ** n, double *** matrices);
+
+  // input: filename, numMatrices, m, n, matrices
+  // output: none
+  // returns 0 on success, non-zero otherwise
+  static int Save(const char * filename, int numMatrices, int * m, int * n, double ** matrices);
+
+protected:
+};
+
+#endif
+
diff --git a/src/libobjMesh/boundingBox.cpp b/libraries/mesh/boundingBox.cpp
similarity index 50%
rename from src/libobjMesh/boundingBox.cpp
rename to libraries/mesh/boundingBox.cpp
index 90b7f8f7b4a366ea61cf8d7e139ef94b8feac219..732127b8cffa324f28b6f4e74dbddcc8dc9b84f7 100644
--- a/src/libobjMesh/boundingBox.cpp
+++ b/libraries/mesh/boundingBox.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,81 +33,79 @@
 //  Bounding Box
 //  Author: Jernej Barbic, CMU
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 #include "openGL-headers.h"
 #include <float.h>
 #include <vector>
 #include <iostream>
+#include <cassert>
 using namespace std;
 
 #include "boundingBox.h"
 #include "triangle.h"
+#include "valueIndex.h"
 
-namespace vega
+BoundingBox::BoundingBox(int numVertices, const Vec3d * vertices)
 {
-template<class Triangle> 
-BoundingBox::BoundingBox(vector<Triangle> & tripool)
+  bmin_ = Vec3d(+DBL_MAX, +DBL_MAX, +DBL_MAX);
+  bmax_ = Vec3d(-DBL_MAX, -DBL_MAX, -DBL_MAX);
+  for(int i = 0; i < numVertices; i++)
+  {
+    updateOnePoint(vertices[i]);
+  }
+  updateData();
+}
+
+BoundingBox::BoundingBox(const vector<BoundingBox> & bbs)
 {
   // set bmin_, bmax_
   bmin_ = Vec3d(+DBL_MAX, +DBL_MAX, +DBL_MAX);
   bmax_ = Vec3d(-DBL_MAX, -DBL_MAX, -DBL_MAX);
-  for(unsigned int i=0; i < tripool.size(); i++) // over all vertices
+  for(size_t i = 0; i < bbs.size(); i++)
   {
-    Vec3d p = tripool[i].first();
-    if (p[0] < bmin_[0])
-      bmin_[0] = p[0];
-    if (p[0] > bmax_[0])
-      bmax_[0] = p[0];
-
-    if (p[1] < bmin_[1])
-      bmin_[1] = p[1];
-    if (p[1] > bmax_[1])
-      bmax_[1] = p[1];
+    for(int j = 0; j < 3; j++)
+    {
+      if (bbs[i].bmin()[j] < bmin_[j])
+        bmin_[j] = bbs[i].bmin()[j];
+      if (bbs[i].bmax()[j] > bmax_[j])
+        bmax_[j] = bbs[i].bmax()[j];
+    }
+  }
+  updateData();
+}
 
-    if (p[2] < bmin_[2])
-      bmin_[2] = p[2];
-    if (p[2] > bmax_[2])
-      bmax_[2] = p[2];
 
+template<class Triangle>
+void BoundingBox::buildFromTriangles(const vector<Triangle> & tripool)
+{
+  // set bmin_, bmax_
+  bmin_ = Vec3d(+DBL_MAX, +DBL_MAX, +DBL_MAX);
+  bmax_ = Vec3d(-DBL_MAX, -DBL_MAX, -DBL_MAX);
+  for(unsigned int i=0; i < tripool.size(); i++) // over all vertices
+  {
+    Vec3d p = tripool[i].first();
+    updateOnePoint(p);
     p = tripool[i].second();
-
-    if (p[0] < bmin_[0])
-      bmin_[0] = p[0];
-    if (p[0] > bmax_[0])
-      bmax_[0] = p[0];
-
-    if (p[1] < bmin_[1])
-      bmin_[1] = p[1];
-    if (p[1] > bmax_[1])
-      bmax_[1] = p[1];
-
-    if (p[2] < bmin_[2])
-      bmin_[2] = p[2];
-    if (p[2] > bmax_[2])
-      bmax_[2] = p[2];
-
+    updateOnePoint(p);
     p = tripool[i].third();
+    updateOnePoint(p);
+  }
 
-    if (p[0] < bmin_[0])
-      bmin_[0] = p[0];
-    if (p[0] > bmax_[0])
-      bmax_[0] = p[0];
+  updateData();
+}
 
-    if (p[1] < bmin_[1])
-      bmin_[1] = p[1];
-    if (p[1] > bmax_[1])
-      bmax_[1] = p[1];
+BoundingBox::BoundingBox(const std::vector<TriangleBasic> & bbs)
+{
+  buildFromTriangles(bbs);
+}
 
-    if (p[2] < bmin_[2])
-      bmin_[2] = p[2];
-    if (p[2] > bmax_[2])
-      bmax_[2] = p[2];
-  }
+BoundingBox::BoundingBox(const std::vector<TriangleWithCollisionInfo> & bbs)
+{
+  buildFromTriangles(bbs);
+}
 
-  updateData();
+BoundingBox::BoundingBox(const std::vector<TriangleWithCollisionInfoAndPseudoNormals> & bbs)
+{
+  buildFromTriangles(bbs);
 }
 
 void BoundingBox::regularize()
@@ -125,19 +127,52 @@ void BoundingBox::regularize()
   updateData();
 }
 
+void BoundingBox::updateOnePoint(const Vec3d & p)
+{
+  if (p[0] < bmin_[0])
+    bmin_[0] = p[0];
+  if (p[0] > bmax_[0])
+    bmax_[0] = p[0];
+
+  if (p[1] < bmin_[1])
+    bmin_[1] = p[1];
+  if (p[1] > bmax_[1])
+    bmax_[1] = p[1];
+
+  if (p[2] < bmin_[2])
+    bmin_[2] = p[2];
+  if (p[2] > bmax_[2])
+    bmax_[2] = p[2];
+}
+
 void BoundingBox::updateData()
 {
   center_ = 0.5 * (bmin_ + bmax_);
   halfSides_ = 0.5 * (bmax_ - bmin_);
 }
 
-void BoundingBox::verifyBox()
+bool BoundingBox::verifyBox() const
 {
   if (!((bmin_[0] <= bmax_[0]) && (bmin_[1] <= bmax_[1]) && (bmin_[2] <= bmax_[2])))
+  {
     printf("Error: inconsistent bounding box.\n");
+    return false;
+  }
+  return true;
 }
 
-void BoundingBox::render()
+bool BoundingBox::checkInside(const Vec3d & p) const
+{
+  return bmin_[0] <= p[0] && p[0] <= bmax_[0] && bmin_[1] <= p[1] && p[1] <= bmax_[1] && bmin_[2] <= p[2] && p[2] <= bmax_[2];
+}
+
+bool BoundingBox::checkInside(const BoundingBox & b) const
+{
+  return bmin_[0] <= b.bmin_[0] && bmin_[1] <= b.bmin_[1] && bmin_[2] <= b.bmin_[2] &&
+    bmax_[0] >= b.bmax_[0] && bmax_[1] >= b.bmax_[1] && bmax_[2] >= b.bmax_[2];
+}
+
+void BoundingBox::render() const
 {
   // render the bounding box
   Vec3d p0(bmin_[0],bmin_[1],bmin_[2]);
@@ -192,7 +227,13 @@ void BoundingBox::expand(double expansionFactor)
   updateData();
 }
 
-bool BoundingBox::lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd, Vec3d * intersection)
+void BoundingBox::expand(const Vec3d & p)
+{
+  updateOnePoint(p);
+  updateData();
+}
+
+bool BoundingBox::lineSegmentIntersection(const Vec3d & segmentStart, const Vec3d & segmentEnd, Vec3d * intersection) const
 {
   /*
     Jernej Barbic, CMU, 2005
@@ -219,25 +260,25 @@ bool BoundingBox::lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd,
   /* Find candidate planes; this loop can be avoided if
   rays cast all from the eye(assume perpsective view) */
   for (i=0; i<NUMDIM; i++)
-    if(segmentStart[i] < bmin_[i]) 
+    if(segmentStart[i] < bmin_[i])
     {
       quadrant[i] = LEFT;
       candidatePlane[i] = bmin_[i];
       inside = false;
     }
-    else if (segmentStart[i] > bmax_[i]) 
+    else if (segmentStart[i] > bmax_[i])
     {
       quadrant[i] = RIGHT;
       candidatePlane[i] = bmax_[i];
       inside = false;
     }
-    else   
+    else
     {
       quadrant[i] = MIDDLE;
     }
-                                                                                                                                                             
+
   /* Ray origin inside bounding box */
-  if(inside)      
+  if(inside)
   {
     *intersection = segmentStart;
     return (true);
@@ -257,20 +298,20 @@ bool BoundingBox::lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd,
       whichPlane = i;
 
   /* Check final candidate actually inside box */
-  if (maxT[whichPlane] < 0.0) 
+  if (maxT[whichPlane] < 0.0)
     return (false);
-  if (maxT[whichPlane] > 1.0) 
+  if (maxT[whichPlane] > 1.0)
     return (false); // remove this for ray
 
   for (i = 0; i < NUMDIM; i++)
   {
-    if (whichPlane != i) 
+    if (whichPlane != i)
     {
       (*intersection)[i] = segmentStart[i] + maxT[whichPlane] *dir[i];
       if ((*intersection)[i] < bmin_[i] || (*intersection)[i] > bmax_[i])
         return (false);
-    } 
-    else 
+    }
+    else
     {
       (*intersection)[i] = candidatePlane[i];
     }
@@ -284,25 +325,109 @@ bool BoundingBox::lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd,
   #undef MIDDLE
 }
 
-double BoundingBox::distanceToPoint(Vec3d point)
+bool BoundingBox::intersect(const BoundingBox & bb) const
+{
+  return
+    (bmin_[0] <= bb.bmax_[0]) && (bmax_[0] >= bb.bmin_[0]) &&
+    (bmin_[1] <= bb.bmax_[1]) && (bmax_[1] >= bb.bmin_[1]) &&
+    (bmin_[2] <= bb.bmax_[2]) && (bmax_[2] >= bb.bmin_[2]);
+}
+
+double BoundingBox::distanceToPoint2(const Vec3d & point) const
 {
   double distance = 0.0;
   for(int dim=0; dim<3; dim++)
   {
     if (point[dim] < bmin_[dim])
       distance += (bmin_[dim] - point[dim]) * (bmin_[dim] - point[dim]);
-    if (point[dim] > bmax_[dim])
+    else if (point[dim] > bmax_[dim])
       distance += (point[dim] - bmax_[dim]) * (point[dim] - bmax_[dim]);
   }
-  return sqrt(distance);
+  return distance;
 }
 
-void BoundingBox::print()
+double BoundingBox::furthestDistanceToPoint2(const Vec3d & point) const
 {
-  cout << bmin_ << " " << bmax_ << endl;
+  double d2 = 0.0;
+  for(int i=0; i<3; i++)
+  {
+    double dist;
+    if (point[i] < center_[i])
+      dist = bmax_[i] - point[i];
+    else
+      dist = point[i] - bmin_[i];
+    d2 += dist * dist;
+  }
+
+  return d2;
 }
 
-template BoundingBox::BoundingBox(vector<TriangleBasic> & tripool);
-template BoundingBox::BoundingBox(vector<TriangleWithCollisionInfo> & tripool);
-template BoundingBox::BoundingBox(vector<TriangleWithCollisionInfoAndPseudoNormals> & tripool);
+std::pair<int, double> BoundingBox::longestSide() const
+{
+  MaxValueIndex vi;
+  for(int i = 0; i < 3; i++)
+    vi.update(bmax_[i] - bmin_[i], i);
+  return make_pair(vi.index, vi.value);
 }
+
+void BoundingBox::print() const
+{
+  cout << (*this) << endl;
+}
+
+
+void BoundingBox::createChildBoundingBoxes(BoundingBox childCubeBoxes[8]) const
+{
+  const auto & center = center_;
+  const auto & bmin = bmin_;
+  const auto & bmax = bmax_;
+
+  //         ^y (top)   (z: far)
+  //         |
+  //         |
+  //(left)-------->x (right)
+  //        /
+  //       /
+  //      z      (y:bottom)
+  //     (z:near, z protruding the screen)
+  // left bottom far
+  childCubeBoxes[0].setbminmax(Vec3d(bmin[0], bmin[1], bmin[2]), Vec3d(center[0], center[1], center[2]));
+
+  // right bottom far
+  childCubeBoxes[1].setbminmax(Vec3d(center[0], bmin[1], bmin[2]), Vec3d(bmax[0], center[1], center[2]));
+
+  // left top far
+  childCubeBoxes[2].setbminmax(Vec3d(bmin[0], center[1], bmin[2]), Vec3d(center[0], bmax[1], center[2]));
+
+  // right top far
+  childCubeBoxes[3].setbminmax(Vec3d(center[0], center[1], bmin[2]), Vec3d(bmax[0], bmax[1], center[2]));
+
+  // left bottom near
+  childCubeBoxes[4].setbminmax(Vec3d(bmin[0], bmin[1], center[2]), Vec3d(center[0], center[1], bmax[2]));
+
+  // right bottom near
+  childCubeBoxes[5].setbminmax(Vec3d(center[0], bmin[1], center[2]), Vec3d(bmax[0], center[1], bmax[2]));
+
+  // left top near
+  childCubeBoxes[6].setbminmax(Vec3d(bmin[0], center[1], center[2]), Vec3d(center[0], bmax[1], bmax[2]));
+
+  // right top near
+  childCubeBoxes[7].setbminmax(Vec3d(center[0], center[1], center[2]), Vec3d(bmax[0], bmax[1], bmax[2]));
+}
+
+BoundingBox BoundingBox::getIntersection(const BoundingBox & bb) const
+{
+  Vec3d newBmin = bmin_, newBmax = bmax_;
+  for(int i = 0; i < 3; i++)
+  {
+    if (bmin_[i] < bb.bmin_[i]) newBmin[i] = bb.bmin_[i];
+    if (bmax_[i] > bb.bmax_[i]) newBmax[i] = bb.bmax_[i];
+  }
+  return BoundingBox(newBmin, newBmax);
+}
+
+template void BoundingBox::buildFromTriangles(const vector<TriangleBasic> & tripool);
+template void BoundingBox::buildFromTriangles(const vector<TriangleWithCollisionInfo> & tripool);
+template void BoundingBox::buildFromTriangles(const vector<TriangleWithCollisionInfoAndPseudoNormals> & tripool);
+
+
diff --git a/libraries/mesh/boundingBox.h b/libraries/mesh/boundingBox.h
new file mode 100644
index 0000000000000000000000000000000000000000..528a75ed02b8cba277edabbd99948f687ae81472
--- /dev/null
+++ b/libraries/mesh/boundingBox.h
@@ -0,0 +1,172 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef __BOUNDINGBOX_H__
+#define __BOUNDINGBOX_H__
+
+//  Bounding Box
+//  Author: Jernej Barbic, CMU
+
+#include <vector>
+#include "minivector.h"
+#include <cfloat>
+#include <cmath>
+#include <iostream>
+
+class TriangleBasic;
+class TriangleWithCollisionInfo;
+class TriangleWithCollisionInfoAndPseudoNormals;
+
+class BoundingBox
+{
+public:
+  BoundingBox() : bmin_(0.0), bmax_(1.0), center_(0.5), halfSides_(0.5) {}
+  BoundingBox(Vec3d bmin_g, Vec3d bmax_g): bmin_(bmin_g), bmax_(bmax_g) { updateData();}
+
+  BoundingBox(int numVertices, const Vec3d * vertices);
+  template <class Vec3dContainer> explicit BoundingBox(const Vec3dContainer & vertices);
+  template <class IntContainer> explicit BoundingBox(const Vec3d * allVertices, const IntContainer & vertexIDs);
+  template <class IntContainer> explicit BoundingBox(const Vec3d * allVertices, const Vec3i * allTriangles, const IntContainer & triIDs);
+
+  explicit BoundingBox(const std::vector<BoundingBox> & bbs);
+  explicit BoundingBox(const std::vector<TriangleBasic> & bbs);
+  explicit BoundingBox(const std::vector<TriangleWithCollisionInfo> & bbs);
+  explicit BoundingBox(const std::vector<TriangleWithCollisionInfoAndPseudoNormals> & bbs);
+
+  // accessors
+  const Vec3d & bmin() const { return bmin_;}
+  const Vec3d & bmax() const { return bmax_;}
+
+  const Vec3d & center() const { return center_;}
+  const Vec3d & halfSides() const { return halfSides_;}
+
+  double diameter() const { return 2.0 * len(halfSides_); }
+  double volume() const { return halfSides_[0] * halfSides_[1] * halfSides_[2] * 8; }
+
+  // mutators
+  void setbmin(const Vec3d & bmin_g) { bmin_=bmin_g; updateData();}
+  void setbmin(double x, double y, double z) { bmin_=Vec3d(x,y,z); updateData();}
+  void setbmax(const Vec3d & bmax_g) { bmax_=bmax_g; updateData();}
+  void setbmax(double x, double y, double z) { bmax_=Vec3d(x,y,z); updateData();}
+  void setbminmax(const Vec3d & bmin, const Vec3d & bmax) { bmin_=bmin; bmax_=bmax; updateData();}
+
+  void render() const;
+
+  double distanceToPoint(const Vec3d & point) const { return sqrt(distanceToPoint2(point)); }
+  double furthestDistanceToPoint(const Vec3d & point) const { return sqrt(furthestDistanceToPoint2(point)); }
+
+  // get squared distance
+  // computing squared distance is much faster than computing distance because the later requires sqrt()
+  double distanceToPoint2(const Vec3d & point) const;
+  double furthestDistanceToPoint2(const Vec3d & point) const;
+
+  // return true if point is inside or touching the bounding box
+  bool checkInside(const Vec3d & point) const;
+  // return true if bb is completely inside or touching from inside the bounding box
+  bool checkInside(const BoundingBox & bb) const;
+
+  // sanity check bmin <= bmax
+  bool verifyBox() const;
+
+  // expands from the center
+  // factor of 1.0 indicates no expansion
+  void expand(double expansionFactor);
+  // expand the bounding box to include this point
+  void expand(const Vec3d & point);
+  void regularize(); // converts the box into one with all sides equal
+
+  bool lineSegmentIntersection(const Vec3d & segmentStart, const Vec3d & segmentEnd, Vec3d * intersection) const;
+  bool intersect(const BoundingBox & bb) const;
+
+  // return the index of the longest side (bmax[i] - bmin[i]) and its value
+  std::pair<int, double> longestSide() const;
+
+  friend inline std::ostream & operator << (std::ostream & o, const BoundingBox & bb) { return o << "[ " << bb.bmin_ << " " << bb.bmax_ << " ]"; }
+  void print() const; // cout << (*this) << endl
+
+  // The ordering of children is as follows:
+  // child ID -> < b2_b1_b0 > (binary notation), each bi is 0 or 1
+  // bi = 0: in dimension i, this child is on the negative side
+  // bi - 1: in dimension i, this child is on the positive side
+  void createChildBoundingBoxes(BoundingBox children[8]) const;
+
+  // get intersection of two bounding boxes
+  // will return an invalid bounding box if this and bb does not intersect
+  BoundingBox getIntersection(const BoundingBox & bb) const;
+
+protected:
+  template<class Triangle> void buildFromTriangles(const std::vector<Triangle> & tripool);
+
+  void updateOnePoint(const Vec3d & p);
+  void updateData(); // updates center and half-sides
+  Vec3d bmin_,bmax_;
+  Vec3d center_, halfSides_;
+};
+
+template <class Vec3dContainer>
+BoundingBox::BoundingBox(const Vec3dContainer & vertices)
+{
+  // set bmin_, bmax_
+  bmin_ = Vec3d(+DBL_MAX, +DBL_MAX, +DBL_MAX);
+  bmax_ = Vec3d(-DBL_MAX, -DBL_MAX, -DBL_MAX);
+  for(const Vec3d & p : vertices)
+  {
+    updateOnePoint(p);
+  }
+  updateData();
+}
+
+template <class IntContainer>
+BoundingBox::BoundingBox(const Vec3d * allVertices, const IntContainer & vertexIDs)
+{
+  bmin_ = Vec3d(+DBL_MAX, +DBL_MAX, +DBL_MAX);
+  bmax_ = Vec3d(-DBL_MAX, -DBL_MAX, -DBL_MAX);
+  for(int ID : vertexIDs)
+  {
+    updateOnePoint(allVertices[ID]);
+  }
+  updateData();
+}
+
+template <class IntContainer>
+BoundingBox::BoundingBox(const Vec3d * allVertices, const Vec3i * allTriangles, const IntContainer & triIDs)
+{
+  bmin_ = Vec3d(+DBL_MAX, +DBL_MAX, +DBL_MAX);
+  bmax_ = Vec3d(-DBL_MAX, -DBL_MAX, -DBL_MAX);
+  for(int triID : triIDs)
+    for(int vtxID : allTriangles[triID])
+    {
+      updateOnePoint(allVertices[vtxID]);
+    }
+  updateData();
+}
+#endif
diff --git a/libraries/mesh/createTriMesh.cpp b/libraries/mesh/createTriMesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a8338ac8340820d015c5864d58a6fdc50d41489d
--- /dev/null
+++ b/libraries/mesh/createTriMesh.cpp
@@ -0,0 +1,151 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "createTriMesh.h"
+#include "triMeshGeo.h"
+#include "triKey.h"
+#include "basicAlgorithms.h"
+#include <iostream>
+#include <cstring>
+#include <cassert>
+#include <fstream>
+#include <cmath>
+#include <functional>
+#include <map>
+using namespace std;
+
+TriMeshGeo createBoxMesh(const Vec3d & bmin, const Vec3d & bmax)
+{
+// vtx order in box:
+//
+//     3 - - - 2
+//    /|      /|
+//   7 - - - 6 |       y
+//   | |     | |       |
+//   | 0 - - | 1       |_ _ _x
+//   |/      |/       /
+//   4 - - - 5       z
+  vector<Vec3d> positions = { bmin, { bmax[0], bmin[1], bmin[2] }, { bmax[0], bmax[1], bmin[2] }, { bmin[0], bmax[1], bmin[2] },
+                            { bmin[0], bmin[1], bmax[2] }, { bmax[0], bmin[1], bmax[2] }, bmax, { bmin[0], bmax[1], bmax[2] } };
+  vector<Vec3i> triangles = { {0,3,2}, {0,2,1}, {4,5,6}, {4,6,7}, {0,1,5}, {0,5,4},
+                              {3,7,6}, {3,6,2}, {1,2,6}, {1,6,5}, {0,4,7}, {0,7,3} };
+  return TriMeshGeo(move(positions), move(triangles));
+}
+
+TriMeshGeo createCubeMesh(const Vec3d & center, double size)
+{
+  Vec3d side(size/2.0);
+  return createBoxMesh(center - side, center + side);
+}
+
+TriMeshGeo createTetSurfaceMesh(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2, const Vec3d & v3)
+{
+  vector<Vec3d> positions = { v0, v1, v2, v3 };
+  vector<Vec3i> triangles = { { 1, 2, 3 }, { 0, 3, 2 }, { 0, 1, 3 }, { 0, 2, 1 } };
+  return TriMeshGeo(move(positions), move(triangles));
+}
+
+TriMeshGeo createSingleTriangleMesh(const TriMeshRef mesh, int triID)
+{
+  vector<Vec3d> positions = { mesh.pos(triID, 0), mesh.pos(triID, 1), mesh.pos(triID, 2) };
+  vector<Vec3i> triangles = { { 0, 1, 2 } };
+  return TriMeshGeo(move(positions), move(triangles));
+}
+
+TriMeshGeo createSingleTriangleMesh(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2)
+{
+  vector<Vec3d> positions = { v0, v1, v2 };
+  vector<Vec3i> triangles = { { 0, 1, 2 } };
+  return TriMeshGeo(move(positions), move(triangles));
+}
+
+// create a cylinder without cap, centered at origin and axis at y-direction
+TriMeshGeo createCylinderWallMesh(double radius, double height, int subdivisionAxis, int subdivisionHeight)
+{
+  assert(subdivisionAxis >= 1);
+  assert(subdivisionHeight >= 1);
+
+  vector<Vec3d> positions(subdivisionAxis * (subdivisionHeight+1));
+  vector<Vec3i> triangles;
+  Vec3d x(1,0,0), nz(0,0,-1);
+  double da = 2 * M_PI / subdivisionAxis;
+  double dh = height / subdivisionHeight;
+  for(int i = 0; i < subdivisionHeight+1; i++)
+  {
+    Vec3d base(0, -height/2.0 + dh*i, 0);
+    for(int j = 0; j < subdivisionAxis; j++)
+    {
+      Vec3d p = base + radius * Vec3d(cos(j*da), 0, -sin(j*da));
+      positions[i*subdivisionAxis + j] = p;
+    }
+  }
+
+  for(int i = 0; i < subdivisionHeight; i++)
+  {
+    for(int j = 0; j < subdivisionAxis; j++)
+    {
+      int nj = (j+1 < subdivisionAxis ? j+1 : 0);
+      int a = i*subdivisionAxis+j;
+      int b = i*subdivisionAxis+nj;
+      int c = (i+1)*subdivisionAxis+nj;
+      int d = (i+1)*subdivisionAxis+j;
+      triangles.emplace_back(a, b, c);
+      triangles.emplace_back(a, c, d);
+    }
+  }
+
+  return TriMeshGeo(move(positions), move(triangles));
+}
+
+// create a cylinder mesh centered at origin and axis at y-direction
+TriMeshGeo createCylinderMesh(double radius, double height, int subdivisionAxis, int subdivisionHeight)
+{
+  TriMeshGeo mesh = createCylinderWallMesh(radius, height, subdivisionAxis, subdivisionHeight);
+  int lowerCenterID = mesh.numVertices();
+  int upperCenterID = mesh.numVertices() + 1;
+  mesh.addPos(Vec3d(0, -height/2.0, 0));
+  mesh.addPos(Vec3d(0, height/2.0, 0));
+  for(int i = 0; i < subdivisionAxis; i++)
+  {
+    int ni = (i+1)%subdivisionAxis;
+    mesh.addTri(Vec3i(ni, i, lowerCenterID));
+  }
+
+  int offsetToUpper = subdivisionAxis * subdivisionHeight;
+  for(int i = 0; i < subdivisionAxis; i++)
+  {
+    int ni = (i+1)%subdivisionAxis;
+    mesh.addTri(Vec3i(offsetToUpper+i, offsetToUpper+ni, upperCenterID));
+  }
+
+  return mesh;
+}
diff --git a/libraries/mesh/createTriMesh.h b/libraries/mesh/createTriMesh.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0cfc5eb5efd9ba08bde8cb1b74d7ae24da1a0a3
--- /dev/null
+++ b/libraries/mesh/createTriMesh.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef CREATETRIMESH_H
+#define CREATETRIMESH_H
+
+#include "triMeshGeo.h"
+
+// =========================================================
+//              Create simple TriMeshes
+// =========================================================
+
+// create an AABB mesh
+TriMeshGeo createBoxMesh(const Vec3d & bmin, const Vec3d & bmax);
+TriMeshGeo createCubeMesh(const Vec3d & center, double size);
+
+// the ordering of v0-v3 affects the normals of the output mesh
+// if v0-v3 have positive tet orientation, the faces in the output mesh have outward normals
+TriMeshGeo createTetSurfaceMesh(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2, const Vec3d & v3);
+
+// create a mesh consisting of only one triangle
+TriMeshGeo createSingleTriangleMesh(const TriMeshRef mesh, int triID);
+TriMeshGeo createSingleTriangleMesh(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2);
+
+// create a cylinder without cap, centered at origin and axis at y-direction
+// subdivisionAxis: #cut on the circle
+// subdivisionHeight: #subdivision on the height direction
+TriMeshGeo createCylinderWallMesh(double radius, double height, int subdivisionAxis, int subdivisionHeight);
+
+// create a cylinder mesh centered at origin and axis at y-direction
+TriMeshGeo createCylinderMesh(double radius, double height, int subdivisionAxis, int subdivisionHeight);
+
+#endif
+
diff --git a/libraries/mesh/edgeKey.h b/libraries/mesh/edgeKey.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4047de2505c069e61627d1609aeb82e8824bb4b
--- /dev/null
+++ b/libraries/mesh/edgeKey.h
@@ -0,0 +1,164 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef EDGEKEY_H
+#define EDGEKEY_H
+
+#include <ostream>
+#include <functional>
+#include <utility>
+
+// unoriented edge
+struct UEdgeKey
+{
+  inline UEdgeKey(int v0, int v1);
+  inline UEdgeKey(const std::pair<int,int> & p) : UEdgeKey(p.first, p.second) {}
+  inline UEdgeKey();//creates an invalid key with v = {-1,-1}
+  const int & operator[] (int index) const { return v[index]; }
+  inline bool operator < (const UEdgeKey &) const;
+  inline bool operator == (const UEdgeKey & o) const { return v[0] == o.v[0] && v[1] == o.v[1]; }
+  inline bool operator != (const UEdgeKey & o) const { return v[0] != o.v[0] || v[1] != o.v[1]; }
+
+  inline bool shareIndex(const UEdgeKey & o) const { return v[0] == o.v[0] || v[0] == o.v[1] || v[1] == o.v[0] || v[1] == o.v[1]; }
+protected:
+  int v[2];
+};
+
+inline std::ostream & operator << (std::ostream & s, const UEdgeKey & v);
+
+// oriented edge
+struct OEdgeKey
+{
+  inline OEdgeKey(int v0, int v1);
+  inline OEdgeKey(const std::pair<int,int> & p) : OEdgeKey(p.first, p.second) {}
+  inline OEdgeKey();//creates an invalid key with v = {-1,-1}
+  const int & operator[] (int index) const { return v[index]; }
+  inline bool operator < (const OEdgeKey &) const;
+  inline bool operator == (const OEdgeKey & o) const { return v[0] == o.v[0] && v[1] == o.v[1]; }
+  inline bool operator != (const OEdgeKey & o) const { return v[0] != o.v[0] || v[1] != o.v[1]; }
+  inline void reverse() { std::swap(v[0], v[1]); } // reverse the orientation of this edge
+  inline OEdgeKey getReversedEdgeKey() const { return OEdgeKey(v[1], v[0]); } // return reversed edge key
+
+protected:
+  int v[2];
+};
+
+inline std::ostream & operator << (std::ostream & s, const OEdgeKey & v);
+
+namespace std
+{
+  template <>
+  struct hash<UEdgeKey>
+  {
+    size_t operator()(const UEdgeKey & k) const
+    {
+      static_assert(sizeof(int) * 2 == sizeof(uint64_t), "uint64_t is not twice the same size as int");
+      uint64_t v = (((uint64_t)k[0]) + ((uint64_t)(k[1]) << (sizeof(int)*8)));
+      return std::hash<uint64_t>()(v);
+    }
+  };
+
+  template <>
+  struct hash<OEdgeKey>
+  {
+    size_t operator()(const OEdgeKey & k) const
+    {
+      static_assert(sizeof(int) * 2 == sizeof(uint64_t), "uint64_t is not twice the same size as int");
+      uint64_t v = (((uint64_t)k[0]) + ((uint64_t)(k[1]) << (sizeof(int)*8)));
+      return std::hash<uint64_t>()(v);
+    }
+  };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//                             IMPLEMENTATION                                //
+///////////////////////////////////////////////////////////////////////////////
+
+
+inline UEdgeKey::UEdgeKey(int v0, int v1)
+{
+  if (v0 < v1)
+  {
+    v[0] = v0; v[1] = v1;
+  }
+  else
+  {
+    v[0] = v1; v[1] = v0;
+  }
+}
+
+inline UEdgeKey::UEdgeKey()
+{
+  v[0] = v[1] = -1;
+}
+
+inline bool UEdgeKey::operator < (const UEdgeKey & other) const
+{
+  if (v[0] < other.v[0])
+    return true;
+  if (v[0] > other.v[0])
+    return false;
+  return v[1] < other.v[1];
+}
+
+inline OEdgeKey::OEdgeKey(int v0, int v1)
+{
+  v[0] = v0;
+  v[1] = v1;
+}
+
+inline OEdgeKey::OEdgeKey()
+{
+  v[0] = v[1] = -1;
+}
+
+inline bool OEdgeKey::operator < (const OEdgeKey & other) const
+{
+  if (v[0] < other.v[0])
+    return true;
+  if (v[0] > other.v[0])
+    return false;
+  return v[1] < other.v[1];
+}
+
+inline std::ostream & operator << (std::ostream & s, const UEdgeKey & v)
+{
+  return s << '(' << v[0] << ' ' << v[1] << ')';
+}
+
+inline std::ostream & operator << (std::ostream & s, const OEdgeKey & v)
+{
+  return s << '(' << v[0] << ' ' << v[1] << ')';
+}
+
+
+#endif
diff --git a/libraries/mesh/exactOctree.cpp b/libraries/mesh/exactOctree.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..685926715e87186ecd54ac2410cbb01a972bed0f
--- /dev/null
+++ b/libraries/mesh/exactOctree.cpp
@@ -0,0 +1,1078 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <iostream>
+#include "openGL-headers.h"
+#include "predicates.h"
+#include "triple.h"
+#include <vector>
+#include <stack>
+#include <cstring>
+#include <cassert>
+#include <algorithm>
+#include <numeric>
+#include <queue>
+#include <fstream>
+#include "basicAlgorithms.h"
+#include "exactOctree.h"
+#include "geometryQuery.h"
+#include "containerHelper.h"
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+
+using namespace std;
+
+/////////////////////////////////////////////////////////////
+//                   ExactOctreeBase
+/////////////////////////////////////////////////////////////
+
+
+void ExactOctreeBase::rangeQuery(BBFilter toBB, ElementProcess toEle) const
+{
+  assert(nodes.empty() == false);
+  stack<int> nodeStack;
+  nodeStack.push(0);
+  while(!nodeStack.empty())
+  {
+    int nodeID = nodeStack.top();
+    nodeStack.pop();
+    const auto & node = nodes[nodeID];
+    if (toBB(node.bb) == false) continue;
+
+    const auto & indices = node.indices;
+    if(indices.size() > 0)
+    { // leaf node
+      for(int eleID : indices) { toEle(eleID); }
+    }
+    else
+    {
+      for(int childID : node.childrenIDs)
+        if(childID >= 0) { nodeStack.push(childID); }
+    }
+  }
+}
+
+void ExactOctreeBase::nearestQuery(DistanceToBB toBB, DistanceToElement toElement,
+    double & minDistance, double distanceHi) const
+{
+  assert(nodes.empty() == false);
+  // distanceHi stores the current closest distances
+  // traverse the hierarchy
+  minDistance = distanceHi;
+
+  // each entry in the stack is for one node: <higher/lower bound of distanceToNode, node ID>
+  stack<tuple<double, double, int>> nodeStack;
+  auto nodeBound = toBB(nodes[0].bb); // get the lower and higher bound of this BV node
+  nodeStack.push(make_tuple(nodeBound.first, nodeBound.second, 0));
+
+  while(nodeStack.size() > 0)
+  {
+    const auto & top = nodeStack.top();
+    int nodeID = get<2>(top);
+    double nodeLowerBound = get<0>(top), nodeHigherBound = get<1>(top);
+    nodeStack.pop();
+    const Node & node = nodes[nodeID];
+    if (nodeLowerBound <= minDistance) // if this BV node's distance lower bound is smaller than distanceHi
+    {
+      // this node needs to be inspected further
+      if (nodeHigherBound < minDistance)
+        minDistance = nodeHigherBound;
+
+      if (node.isLeaf())
+      {
+        // leaf node, traverse all children
+        for(int eleID : node.indices)
+        {
+          double dist = toElement(eleID, minDistance);
+
+          if (dist <= minDistance)
+            minDistance = dist;
+        }
+      }
+      else
+      {
+        // non-leaf node
+        // sort the children nodes according to their distance
+        vector<tuple<double, double, int> > childrenSortBuffer; // <lowerBound, higherBound, nodeID>
+        for(int childID : node.childrenIDs)
+        {
+          if (childID < 0) continue;
+          auto distBound = toBB(nodes[childID].bb);
+          childrenSortBuffer.emplace_back(distBound.first, distBound.second, childID);
+        }
+
+        sort(childrenSortBuffer.begin(), childrenSortBuffer.end());
+        for(auto rit = childrenSortBuffer.rbegin(); rit != childrenSortBuffer.rend(); rit++)
+        {
+          nodeStack.push(*rit);
+        }
+      }
+    }
+  }
+}
+
+void ExactOctreeBase::buildFromRoot(Subdivide divideNode, int maxDepth, int maxNumElementsPerNode)
+{
+  depth = 0;
+
+  stack<int> nodeStack;
+  nodeStack.push(0);
+  while(nodeStack.empty() == false)
+  {
+    int nodeID = nodeStack.top();
+    nodeStack.pop();
+
+    int nodeDepth = nodes[nodeID].depth;
+    if (nodeDepth > depth)
+      depth = nodeDepth;
+
+
+    int numElements = (int)nodes[nodeID].indices.size();
+
+    // if there are fewer elements than the threshold value, or if max depth has been reached,
+    // this node becomes a leaf which stores the element indices
+    // max depth checking is necessary, otherwise the box might get split forever
+    if ((numElements <= maxNumElementsPerNode) || (nodeDepth >= maxDepth))
+      continue;
+
+    vector<int> indices = move(nodes[nodeID].indices);
+    assert(nodes[nodeID].indices.empty()); // non-leaf nodes should not store vertex indices
+
+    std::vector<int> childIDs[8];
+    BoundingBox subBBs[8];
+
+    // assert(nodes[nodeID].bb.verifyBox());
+    divideNode(indices, nodes[nodeID].bb, childIDs, subBBs);
+
+    for(int i = 0; i < 8; i++)
+    {
+      if (childIDs[i].size() > 0)
+      {
+        int childrenID = int(nodes.size());
+        nodes[nodeID].childrenIDs[i] = childrenID;
+        nodes.emplace_back(nodeDepth+1, move(childIDs[i]));
+        auto & childNode = nodes.back();
+        childNode.bb = subBBs[i];
+        // assert(subBBs[i].verifyBox());
+        nodeStack.push(childrenID);
+      }
+    }
+  }
+}
+
+void ExactOctreeBase::boundingBoxPartition(const std::vector<int> & elementList, const BoundingBox & bb,
+    std::vector<int> childIDs[8], BoundingBox subBBs[8], const std::vector<BoundingBox> & elementBBs,
+    BBFromElementList createBBFromElements, ElementBBIntersect intersectElementBB)
+{
+  Vec3d center = bb.center();
+
+  BoundingBox spatialChildrenBBs[8];
+  // partition node.bb into 8 children spatially
+  bb.createChildBoundingBoxes(spatialChildrenBBs);
+
+  // for each child, generate a list of vertices that intersect the child bounding box
+  for(int eleID : elementList)
+  {
+    const auto & triBB = elementBBs[eleID];
+
+    unsigned char lowerChildrenBBCode[3], higherChildrenBBCode[3]; // stores [0, 1]
+    // lowerChildrenBBCCode stores which side bmin of triBB is in the childrenBBs on all dimenstions
+    for(int dim = 0; dim < 3; dim++)
+    {
+      lowerChildrenBBCode[dim] = (triBB.bmin()[dim] > center[dim]);
+      higherChildrenBBCode[dim] = (triBB.bmax()[dim] > center[dim]);
+    }
+
+    for(unsigned char ii = lowerChildrenBBCode[0]; ii <= higherChildrenBBCode[0]; ii++)
+      for(unsigned char jj = lowerChildrenBBCode[1]; jj <= higherChildrenBBCode[1]; jj++)
+        for(unsigned char kk = lowerChildrenBBCode[2]; kk <= higherChildrenBBCode[2]; kk++)
+        {
+          unsigned char code = ii + (jj << 1) + (kk << 2);
+          // cout << spatialChildrenBBs[code] << endl;
+          if (!intersectElementBB || intersectElementBB(eleID, spatialChildrenBBs[code]))
+            childIDs[code].push_back(eleID);
+//          assert(spatialChildrenBBs[code].intersect(triBB));
+        }
+  }
+
+  for(int i = 0; i < 8; i++)
+  {
+    if (childIDs[i].size() > 0)
+    {
+      BoundingBox bbFromElements = createBBFromElements(childIDs[i]);
+
+      // we can use spatialChildrenBBs[i] as childNode.bb
+      // but it might be larger than what triangles occupy
+      // we can shrink this bb by doing intersection with allTriVtxBB
+      subBBs[i] = spatialChildrenBBs[i].getIntersection(bbFromElements);
+//      assert(subBBs[i].verifyBox());
+    }
+  }
+}
+
+
+/////////////////////////////////////////////////////////////
+//                   ExactVertexOctree
+/////////////////////////////////////////////////////////////
+
+void  ExactVertexOctree::clear()
+{
+  vertices.clear();
+  nodes.clear();
+}
+
+void ExactVertexOctree::build(const std::vector<Vec3d> & verticesList, int maxDepth, int maxNumVerticesPerNode)
+{
+  nodes.clear();
+  vertices = verticesList;
+  vector<int> rootIndices(vertices.size());
+  iota(rootIndices.begin(), rootIndices.end(), 0);
+
+  nodes.emplace_back(0, move(rootIndices));
+  nodes[0].bb = BoundingBox(verticesList);
+
+  auto subdivide = [&](const vector<int> & elementList, const BoundingBox & bb,
+      vector<int> childIDs[8], BoundingBox subBBs[8]) -> void
+  {
+    Vec3d center = bb.center();
+    int numVertices = elementList.size();
+
+    // for each child, generate a list of vertices that intersect the child bounding box
+    for(int i = 0; i < numVertices; i++)
+    {
+      const Vec3d & v = verticesList[elementList[i]];
+      unsigned char code = 0;
+      code += (v[0] > center[0]);
+      code += ((v[1] > center[1]) << 1);
+      code += ((v[2] > center[2]) << 2);
+      assert(code < 8);
+      childIDs[code].push_back(elementList[i]);
+    }
+
+    for(int i = 0; i < 8; i++)
+    {
+      if (childIDs[i].size() > 0)
+      {
+        subBBs[i] = BoundingBox(verticesList.data(), childIDs[i]);
+      }
+    }
+  };
+  buildFromRoot(subdivide, maxDepth, maxNumVerticesPerNode);
+}
+
+void ExactVertexOctree::rangeQuery(const SimpleSphere & simpleSphere, std::vector<int> & vertexIDList) const
+{
+  auto toBB = [&](const BoundingBox & bb) -> bool
+  {
+    return simpleSphere.doesBoundingBoxIntersect(bb);
+  };
+
+  auto toEle = [&](int idx) -> bool
+  {
+    return simpleSphere.inside(vertices[idx]);
+  };
+
+  ExactOctreeBase::rangeQuery(toBB, toEle, vertexIDList);
+}
+
+void ExactVertexOctree::rangeQuery(const HalfSpace & halfSpace, std::vector<int> & vertexIDList) const
+{
+  auto toBB = [&](const BoundingBox & bb) -> bool
+  {
+    return halfSpace.intersect(bb);
+  };
+
+  auto toEle = [&](int idx) -> bool
+  {
+    return halfSpace.outside(vertices[idx]) <= 0;
+  };
+
+  ExactOctreeBase::rangeQuery(toBB, toEle, vertexIDList);
+}
+
+/////////////////////////////////////////////////////////////
+//                   ExactTriMeshOctree
+/////////////////////////////////////////////////////////////
+
+void ExactTriMeshOctree::clear()
+{
+  nodes.clear();
+  triBBs.clear();
+}
+
+void ExactTriMeshOctree::build(const TriMeshRef triMesh, int maxDepth, int maxNumTrianglesPerNode)
+{
+  nodes.clear();
+  numVertices = triMesh.numVertices();
+  numTriangles = triMesh.numTriangles();
+  assert(numTriangles > 0);
+  triBBs = triMesh.getTriangleBoundingBoxes();
+  vector<int> rootIndices(triMesh.numTriangles());
+  iota(rootIndices.begin(), rootIndices.end(), 0);
+  BoundingBox rootBB(triMesh.positions(), triMesh.triangles(), rootIndices);
+  nodes.emplace_back(0, move(rootIndices));
+  // We don't build a bounding box out of all triMesh's positions because some vertices might not
+  // be used by triangles
+  nodes[0].bb = rootBB;
+
+  // cout << streamRange(rootIndices) << endl;
+  // cout << nodes[0].bb << endl;
+  // assert(nodes[0].bb.verifyBox());
+
+  auto bbFromTris = [&](const vector<int> & triList) -> BoundingBox
+  {
+    // get the bounding box of all triangles inside this childNode
+    auto bb = BoundingBox(triMesh.positions(), triMesh.triangles(), triList);
+    // assert(bb.verifyBox());
+    return bb;
+  };
+  auto triBBIntersect = [&](int triID, const BoundingBox & bb) -> bool
+  {
+    return intersectTriAABB(triMesh.pos(triID, 0), triMesh.pos(triID, 1), triMesh.pos(triID, 2), bb.bmin(), bb.bmax());
+  };
+
+  auto subdivide = [&](const vector<int> & elementList, const BoundingBox & bb,
+        vector<int> childIDs[8], BoundingBox subBBs[8]) -> void
+  {
+    // assert(bb.verifyBox());
+    boundingBoxPartition(elementList, bb, childIDs, subBBs, triBBs, bbFromTris, triBBIntersect);
+  };
+
+  buildFromRoot(subdivide, maxDepth, maxNumTrianglesPerNode);
+}
+
+void ExactTriMeshOctree::selfIntersectionExact(const TriMeshRef triMesh, std::vector<std::pair<int, int>> & triangleIDList) const
+{
+  // cout << "selfIntersectionExact" << endl;
+  assert(numVertices == triMesh.numVertices());
+  assert(numTriangles == triMesh.numTriangles());
+  assert(nodes.empty() == false);
+
+  unordered_set<UEdgeKey> candidateSet;
+
+  // BVTT is such that it always compares BVs at the same level,
+  //   or at immediately adjacent levels, with recursing first into the first tree node
+
+  // build a stack that stores <BVNodeA, BVNodeB, childIndex>
+  // we will compare intersection between BVNodeA and BVNodeB
+  // we make sure BVNodeA <= BVNodeB to avoid redundant comparison
+
+  auto traverseTree = [&]()
+  {
+    stack<triple<int, int, int> > traversalStack;
+    traversalStack.push(triple<int,int,int>(0, 0, 0));
+    while (true)
+    {
+      triple<int,int,int> stackEntry = traversalStack.top();
+
+      //printf("Top off stack: %d vs %d. Child index: %x . Stack size: %d\n", stackEntry.first, stackEntry.second, stackEntry.third, (int)traversalStack.size());
+
+      int nodeAID = stackEntry.first;
+      int nodeBID = stackEntry.second;
+
+  //    cout << "stack top: " << nodeAID << " " << nodeBID << " " << stackEntry.third << endl;
+
+      assert(nodeAID >= 0 && nodeAID < (int)nodes.size());
+      assert(nodeBID >= 0 && nodeBID < (int)nodes.size());
+      const auto * nodeA = &nodes[nodeAID];
+      const auto * nodeB = &nodes[nodeBID];
+
+      bool neighboringNodes = (nodeAID == nodeBID);
+
+      //printf("Neighboring nodes: %s\n", neighboringNodes ? "TRUE" : "FALSE");
+
+      bool potentialCollision = true;
+
+      // for non-neighboring nodes, we can rule out collisions by checking the BVs
+      // for neighboring nodes, this test would be useless as BVs always intersect
+      if (!neighboringNodes)
+      {
+        potentialCollision = nodeA->bb.intersect(nodeB->bb);
+      }
+
+      // potentialCollision flag is now set (in either case; neighboring nodes or not)
+
+      //printf("BVs %d %d potential collision is subtree: %s\n", nodeA, nodeB, potentialCollision ? "TRUE" : "FALSE");
+
+      // are the nodes on deepest level
+      // negative sign by convention indicates leaf
+      bool deepestLevelA = nodeA->isLeaf();
+      bool deepestLevelB = nodeB->isLeaf();
+
+      bool BVVT_leaf = deepestLevelA && deepestLevelB; // if both nodes are leaves
+
+      //printf("BVVT leaf: %s\n", BVVT_leaf ? "TRUE" : "FALSE");
+
+      if (BVVT_leaf && potentialCollision)
+      {
+        //printf("Checking all triangle pairs...\n");
+        // check all triangle pairs; note: nodeAp and nodeBp could be the same
+        auto processTriPair = [&](int triIDA, int triIDB)
+        {
+          if (triMesh.tri(triIDA).intersect(triMesh.tri(triIDB))) return; // if two triangles are neighbors
+          UEdgeKey ue(triIDA, triIDB);
+          if (usetFind(candidateSet, ue)) return;
+          // do tri vs tri intersection
+          if (triBBs[triIDA].intersect(triBBs[triIDB]) == false) return;
+
+          candidateSet.insert(ue);
+        };
+
+        if (nodeA == nodeB)
+        {
+          const auto & indices = nodeA->indices;
+          for(size_t i = 0; i < indices.size(); i++)
+            for(size_t j = i+1; j < indices.size(); j++)
+            {
+              processTriPair(indices[i], indices[j]);
+            }
+        }
+        else
+        {
+          for(int triIDA : nodeA->indices)
+            for(int triIDB : nodeB->indices)
+            {
+              if (triIDA == triIDB) continue; // same triangle, continue
+              processTriPair(triIDA, triIDB);
+            }
+        }
+      }
+
+      int childIndex = stackEntry.third;
+
+      // for intra-node tests, we store (i,j) indices of current child pair in upper and lower parts of childIndex;
+      // we only traverse children with i <= j; this avoids checking pairs of nodes twice
+      short int * childIndexB = (short int*)&childIndex;
+      short int * childIndexA = childIndexB + 1;
+      bool recurseOnFirstNode = true;
+      int parentChildIndex;
+
+      // go deeper from <nodeA, nodeB> to visit one more pair
+      // if nodeA is nodeB, then the next would be their children pair at location childIndexA and childIndexB
+      // else, either nodeA or nodeB will go deeper to its child at location childIndex
+      auto drillDown = [&]()
+      {
+        if (nodeAID == nodeBID)
+        {
+          nodeAID = nodeA->childrenIDs[*childIndexA];
+          nodeBID = nodeB->childrenIDs[*childIndexB];
+        }
+        else
+        {
+          if (recurseOnFirstNode)
+            nodeAID = nodeA->childrenIDs[childIndex];
+          else
+            nodeBID = nodeB->childrenIDs[childIndex];
+        }
+        assert(nodeAID >= 0 && nodeAID < (int)nodes.size());
+        assert(nodeBID >= 0 && nodeBID < (int)nodes.size());
+        traversalStack.push(triple<int,int,int>(nodeAID, nodeBID, childIndex));
+      };
+
+      auto goToNextChild = [&]()
+      {
+        if (nodeA == nodeB) (*childIndexB)++;
+        else childIndex++;
+      };
+
+      // modify childIndex to get the next available child index
+      // return true if we can find one
+      auto goToValidChild = [&]()
+      {
+        bool hasChild = true;
+        if (nodeA == nodeB)
+        {
+          // find an available childIndexA
+          for(; *childIndexA < 8 && nodeA->childrenIDs[*childIndexA] < 0; (*childIndexB) = ++(*childIndexA)) {}
+
+          // find an available childIndexB
+          for(; *childIndexB < 8 && nodeB->childrenIDs[*childIndexB] < 0; (*childIndexB)++) {}
+
+          if (*childIndexB >= 8)
+          {
+            // find next available childIndexA
+            for((*childIndexA)++; *childIndexA < 8 && nodeA->childrenIDs[*childIndexA] < 0; (*childIndexA)++) {}
+            (*childIndexB) = (*childIndexA);
+            hasChild = (*childIndexA < 8);
+          }
+          else
+          {
+            assert(nodeA->childrenIDs[*childIndexA] >= 0);
+          }
+        }
+        else
+        {
+          recurseOnFirstNode = (nodeB->isLeaf() || ((nodeA->depth <= nodeB->depth) && (nodeA->isLeaf() == false) ));
+          const auto recurseNode = (recurseOnFirstNode ? nodeA : nodeB);
+
+          for(; childIndex < 8 && recurseNode->childrenIDs[childIndex] < 0; childIndex++) {}
+
+          hasChild = (childIndex < 8);
+        }
+        return hasChild;
+      };
+
+      if (!potentialCollision || BVVT_leaf)
+      {
+        // we are traversing the octree
+        // Since there's no collision or it's two-leaf case where collision has been checked
+        // we stop this search direction and pop from the stack
+
+        parentChildIndex = childIndex;
+        bool hasChild = true;
+        do
+        {
+          childIndex = parentChildIndex;
+
+          traversalStack.pop();
+          if (traversalStack.empty())
+          {
+            return;
+          }
+          triple<int, int, int> parentEntry = traversalStack.top();
+          parentChildIndex = parentEntry.third;
+          nodeAID = parentEntry.first;
+          nodeBID = parentEntry.second;
+          nodeA = &nodes[nodeAID];
+          nodeB = &nodes[nodeBID];
+          goToNextChild();
+          hasChild = goToValidChild();
+        }
+        while (hasChild == false);
+        drillDown();
+      }
+      else
+      {
+        // Here, nodeA/B cannot be both leaves
+        // recursively go down one level
+        childIndex = 0;
+        goToValidChild();
+        drillDown();
+      }
+    }
+  };
+  traverseTree();
+
+  // cout << "Finish finding candidate pairs: " << candidatePairs.size() << endl;
+
+  //  sortAndDeduplicate(candidatePairs);
+  vector<UEdgeKey> candidatePairs(candidateSet.begin(), candidateSet.end()); // store all those candidate triangle pairs for parallel evaluations
+  vector<char> intersected(candidatePairs.size(), 0);
+
+#ifdef USE_TBB
+  tbb::parallel_for(tbb::blocked_range<int>(0, candidatePairs.size()), [&](const tbb::blocked_range<int> & rng)
+  {
+    for (int i = rng.begin(); i != rng.end(); ++i)
+    {
+#else
+    for(size_t i = 0; i < candidatePairs.size(); ++i)
+    {
+#endif
+      int triIDA = candidatePairs[i][0], triIDB = candidatePairs[i][1];
+      if (intersectTriTri(triMesh.pos(triIDA, 0), triMesh.pos(triIDA, 1), triMesh.pos(triIDA,2),
+          triMesh.pos(triIDB, 0), triMesh.pos(triIDB, 1), triMesh.pos(triIDB,2)))
+      {
+        intersected[i] = 1;
+      }
+    }
+#ifdef USE_TBB
+  }, tbb::auto_partitioner()); //end for locations
+#endif
+
+  for(size_t i = 0; i < candidatePairs.size(); i++)
+  {
+    if (intersected[i])
+    {
+      triangleIDList.push_back(make_pair(candidatePairs[i][0], candidatePairs[i][1]));
+    }
+  }
+
+  // cout << "selfIntersectionExact Done" << endl;
+}
+
+void ExactTriMeshOctree::intersectionExact(const TriMeshRef triMesh, const TriMeshRef & otherMesh,
+      std::vector<pair<int,int>> & triangleIDList)
+{
+  assert(numVertices == triMesh.numVertices());
+  assert(numTriangles == triMesh.numTriangles());
+  assert(nodes.empty() == false);
+
+  vector<pair<int,int>> allPairList;
+
+#ifdef USE_TBB
+  struct ThreadLocalData
+  {
+    vector<int> IDlist;
+    vector<pair<int,int>> pairList;
+  };
+  tbb::enumerable_thread_specific<ThreadLocalData> threadLocalData;
+  tbb::parallel_for(tbb::blocked_range<int>(0, otherMesh.numTriangles()), [&](const tbb::blocked_range<int> & rng)
+  {
+//  for(int oID = 0; oID < otherMesh.numTriangles(); oID++)
+    auto & local = threadLocalData.local();
+    auto & IDlist = local.IDlist;
+    auto & pairList = local.pairList;
+    for(int oID = rng.begin(); oID != rng.end(); oID++)
+#else
+    vector<int> IDlist;
+    auto & pairList = allPairList;
+    for(int oID = 0; oID < otherMesh.numTriangles(); oID++)
+#endif
+    {
+      IDlist.clear();
+      triangleIntersectionExact(triMesh, otherMesh.pos(oID,0), otherMesh.pos(oID,1), otherMesh.pos(oID,2), IDlist);
+      sortAndDeduplicate(IDlist);
+      for(int triID : IDlist)
+        pairList.emplace_back(triID, oID);
+    }
+#ifdef USE_TBB
+  });
+  for(const auto & local : threadLocalData)
+    vectorInsertRangeBack(allPairList, local.pairList);
+#endif
+
+  sortAndDeduplicate(allPairList);
+  vectorInsertRangeBack(triangleIDList, allPairList);
+}
+
+void ExactTriMeshOctree::intersectionExact(const TriMeshRef triMesh, const ExactTriMeshOctree & otherOctree, const TriMeshRef & otherMesh,
+    std::vector<std::pair<int,int>> & triangleIDList)
+{
+  assert(numVertices == triMesh.numVertices());
+  assert(numTriangles == triMesh.numTriangles());
+  assert(nodes.empty() == false);
+  assert(otherOctree.numVertices == otherMesh.numVertices());
+  assert(otherOctree.numTriangles == otherMesh.numTriangles());
+  assert(otherOctree.nodes.empty() == false);
+
+//  ofstream fout("bbs.txt");
+  vector<pair<int,int>> candidatePairs;
+
+  // BVTT is such that it always compares spheres at the same level,
+  //   or at immediately adjacent levels, with recursing first into the first tree node
+
+  stack<triple<int, int, int> > traversalStack;
+
+  traversalStack.push(triple<int,int,int>(0, 0, 0));
+
+//  int iter = 0;
+  while (true)
+  {
+    triple<int,int,int> stackEntry = traversalStack.top();
+
+//    cout << "====== " << iter++ << " ======" << endl;
+//    printf("Top off stack: %d vs %d. Child index: %x . Stack size: %d\n", stackEntry.first, stackEntry.second, stackEntry.third, (int)traversalStack.size());
+
+    int nodeAID = stackEntry.first;
+    int nodeBID = stackEntry.second;
+    assert(nodeAID >= 0 && nodeAID < sizei(nodes));
+    assert(nodeBID >= 0 && nodeBID < sizei(otherOctree.nodes));
+    const auto * nodeA = &nodes[nodeAID];
+    const auto * nodeB = &otherOctree.nodes[nodeBID];
+
+    bool potentialCollision = nodeA->bb.intersect(nodeB->bb);
+//    cout << "children " << streamRange(nodeA->childrenIDs) << " " << streamRange(nodeB->childrenIDs) << endl;
+//    fout << nodeA->bb << " " << nodeB->bb << endl;
+//    cout << "bb collision: " << potentialCollision << endl;
+
+    // are the nodes on the deepest level
+    // negative sign by convention indicates leaf
+    bool deepestLevelA = nodeA->isLeaf();
+    bool deepestLevelB = nodeB->isLeaf();
+
+    bool BVVT_leaf = deepestLevelA && deepestLevelB;
+
+    if (BVVT_leaf && potentialCollision)
+    {
+      for(int triA : nodeA->indices)
+      {
+        const BoundingBox & bbA = triBBs[triA];
+        for(int triB : nodeB->indices)
+        {
+          const BoundingBox & bbB = otherOctree.triBBs[triB];
+
+          // do tri vs tri intersection
+          if (bbA.intersect(bbB) == false) continue;
+//          cout << "add candidate pair " << triA << " " << triB << endl;
+          candidatePairs.push_back(make_pair(triA, triB));
+        }
+      }
+    }
+
+    if (!potentialCollision || BVVT_leaf) // find next node
+    {
+      int childIndex = 0; // index of this stack entry's node in its parent's childIDs array
+      bool endTraversal = false;
+      bool recurseOnFirstNode = false;
+      do
+      {
+        childIndex = traversalStack.top().third;
+        traversalStack.pop();
+        if (traversalStack.empty())
+        {
+          endTraversal = true;
+          break;
+        }
+        triple<int, int, int> parentEntry = traversalStack.top();
+        nodeAID = parentEntry.first; // now it stores the parentNodeAID
+        nodeBID = parentEntry.second;// now it stores the parentNodeBID
+//        cout << "  back to parent " << nodeAID << " " << nodeBID << endl;
+        assert(nodeAID >= 0 && nodeAID < sizei(nodes));
+        assert(nodeBID >= 0 && nodeBID < sizei(otherOctree.nodes));
+        nodeA = &nodes[nodeAID];             // now it refers to parent nodeA
+        nodeB = &otherOctree.nodes[nodeBID]; // now it refers to parent nodeB
+
+        childIndex += 1; // get next child
+        recurseOnFirstNode = (nodeB->isLeaf() || ((nodeA->depth <= nodeB->depth) && nodeA->isLeaf() == false ));
+        if (recurseOnFirstNode) { childIndex = nodeA->getNextChild(childIndex); }
+        else {childIndex = nodeB->getNextChild(childIndex); }
+      }
+      while (childIndex >= 8);
+      if (endTraversal) break;
+
+//      cout << "  at parent " << nodeAID << " " << nodeBID << ", ";
+      // push the new node
+      // get the child nodeID from parent node ID
+      if (recurseOnFirstNode)
+        nodeAID = nodeA->childrenIDs[childIndex];
+      else
+        nodeBID = nodeB->childrenIDs[childIndex];
+      // now nodeAID and nodeBID stores the node IDs for the next stack entry
+//      cout << "found child " << nodeAID << " " << nodeBID << " " << endl;
+
+      traversalStack.push(triple<int,int,int>(nodeAID, nodeBID, childIndex));
+    }
+    else
+    {
+      bool recurseOnFirstNode = (nodeB->isLeaf() || ((nodeA->depth <= nodeB->depth) && nodeA->isLeaf() == false ));
+      int childIndex = 0;
+      // push the new node
+      if (recurseOnFirstNode)
+      {
+        childIndex = nodeA->getNextChild(0);
+        nodeAID = nodeA->childrenIDs[childIndex];
+      }
+      else
+      {
+        childIndex = nodeB->getNextChild(0);
+        nodeBID = nodeB->childrenIDs[childIndex];
+      }
+      assert(childIndex < 8);
+//      cout << "at a new entry " << nodeAID << " " << nodeBID << endl;
+      traversalStack.push(triple<int,int,int>(nodeAID, nodeBID, childIndex));
+    }
+  } // end traversal loop
+
+  // cout << "Finish finding candidate pairs: " << candidatePairs.size() << endl;
+  sortAndDeduplicate(candidatePairs);
+
+  vector<char> intersected(candidatePairs.size(), 0);
+
+#ifdef USE_TBB
+  tbb::parallel_for(tbb::blocked_range<int>(0, candidatePairs.size()), [&](const tbb::blocked_range<int> & rng)
+  {
+    for (int i = rng.begin(); i != rng.end(); ++i)
+    {
+#else
+    for(size_t i = 0; i < candidatePairs.size(); ++i)
+    {
+#endif
+      int triIDA = candidatePairs[i].first, triIDB = candidatePairs[i].second;
+      assert(triIDA >= 0 && triIDA < numTriangles);
+      assert(triIDB >= 0 && triIDB < otherMesh.numTriangles());
+      if (intersectTriTri(triMesh.pos(triIDA, 0), triMesh.pos(triIDA, 1), triMesh.pos(triIDA,2),
+          otherMesh.pos(triIDB, 0), otherMesh.pos(triIDB, 1), otherMesh.pos(triIDB,2)))
+      {
+        intersected[i] = 1;
+      }
+    }
+#ifdef USE_TBB
+  }, tbb::auto_partitioner()); //end for locations
+#endif
+
+  for(size_t i = 0; i < candidatePairs.size(); i++)
+  {
+    if (intersected[i])
+      triangleIDList.push_back(candidatePairs[i]);
+  }
+}
+
+void ExactTriMeshOctree::lineSegmentIntersectionExact(const TriMeshRef triMesh, Vec3d segStart, Vec3d segEnd, std::vector<int> & triangleIDList) const
+{
+  assert(numVertices == triMesh.numVertices());
+  assert(numTriangles == triMesh.numTriangles());
+
+  auto toBB = [&](const BoundingBox & bb) -> bool
+  {
+    return intersectSegAABB(&segStart[0], &segEnd[0], &bb.bmin()[0], &bb.bmax()[0]);
+  };
+
+  auto toEle = [&](int idx) -> bool
+  {
+    return intersectSegTri(&segStart[0], &segEnd[0], &triMesh.pos(idx, 0)[0], &triMesh.pos(idx, 1)[0], &triMesh.pos(idx, 2)[0]);
+  };
+
+  ExactOctreeBase::rangeQuery(toBB, toEle, triangleIDList);
+}
+
+// note: no tbb threading should be used in triangleIntersectionExact
+// because I use tbb on intersectionExact with another TriMesh
+// and this function calls triangleIntersectionExact in an parallelized loop
+// nested multi-threading can be a problem when I use thread-local data
+void ExactTriMeshOctree::triangleIntersectionExact(const TriMeshRef triMesh, Vec3d t0, Vec3d t1, Vec3d t2, std::vector<int> & triangleIDList) const
+{
+  assert(numVertices == triMesh.numVertices());
+  assert(numTriangles == triMesh.numTriangles());
+
+  auto toBB = [&](const BoundingBox & bb) -> bool
+  {
+    return intersectTriAABB(&t0[0], &t1[0], &t2[0], &bb.bmin()[0], &bb.bmax()[0]);
+  };
+
+  auto toEle = [&](int idx) -> bool
+  {
+    return intersectTriTri(&t0[0], &t1[0], &t2[0], &triMesh.pos(idx, 0)[0], &triMesh.pos(idx, 1)[0], &triMesh.pos(idx, 2)[0]);
+  };
+
+  ExactOctreeBase::rangeQuery(toBB, toEle, triangleIDList);
+}
+
+bool ExactTriMeshOctree::lineSegmentFirstIntersectionPoint(const TriMeshRef triMesh, Vec3d segStart, Vec3d segEnd, double segWeight[2]) const
+{
+  assert(numVertices == triMesh.numVertices());
+  assert(numTriangles == triMesh.numTriangles());
+
+  auto toBB = [&](const BoundingBox & bb) -> bool
+  {
+    return intersectSegAABB(&segStart[0], &segEnd[0], &bb.bmin()[0], &bb.bmax()[0]);
+  };
+
+  segWeight[0] = segWeight[1] = DBL_MAX;
+  auto toEle = [&](int idx) -> void
+  {
+    double alpha[5];
+    if (intersectSegTri(segStart, segEnd, triMesh.pos(idx,0), triMesh.pos(idx,1), triMesh.pos(idx,2), alpha, alpha+2))
+    {
+      if (alpha[0] < segWeight[0])
+      {
+        memcpy(segWeight, alpha, sizeof(double) * 2);
+      }
+    }
+  };
+
+  ExactOctreeBase::rangeQuery(toBB, toEle);
+  return segWeight[0] < DBL_MAX;
+}
+
+vector<int> ExactTriMeshOctree::trianglesUnderNode(int nodeID) const
+{
+  vector<int> ret;
+  vector<int> candidates = { nodeID };
+  int candBegin = 0, candEnd = 1;
+  while(candBegin < candEnd)
+  {
+    for(int i = candBegin; i < candEnd; i++)
+    {
+      int ID = candidates[i];
+      if (nodes[ID].isLeaf())
+      {
+        ret.insert(ret.end(), nodes[ID].indices.begin(), nodes[ID].indices.end());
+      }
+      else
+      {
+        for(int childID : nodes[ID].childrenIDs)
+        {
+          if (childID < 0)
+            continue;
+          candidates.push_back(childID);
+        }
+      }
+    }
+    candBegin = candEnd;
+    candEnd = int(candidates.size());
+  }
+
+  sortAndDeduplicate(ret);
+  return ret;
+}
+
+
+int ExactTriMeshOctree::getClosestTriangle(const TriMeshRef triMesh, const Vec3d & queryPosition, int & feature) const
+{
+  assert(triMesh.numVertices() == numVertices && triMesh.numTriangles() == numTriangles);
+
+  int closestTriID = -1;
+  int closestFeature = -1;
+
+  auto toBB = [&](const BoundingBox & bb) -> pair<double, double>
+  {
+    double dist2 = bb.distanceToPoint2(queryPosition);
+    double furDist2 = bb.furthestDistanceToPoint2(queryPosition);
+    return { dist2, furDist2 };
+  };
+
+
+  auto toElement = [&](int triID, double minDistance) -> double
+  {
+    const Vec3d & v0 = triMesh.pos(triID, 0);
+    const Vec3d & v1 = triMesh.pos(triID, 1);
+    const Vec3d & v2 = triMesh.pos(triID, 2);
+
+    int feature = -1;
+    double dist2 = getSquaredDistanceToTriangle(queryPosition, v0, v1, v2, feature);
+
+    if (dist2 <= minDistance)
+    {
+      closestTriID = triID;
+      closestFeature = feature;
+    }
+    return dist2;
+  };
+
+  double minDist = DBL_MAX;
+  nearestQuery(toBB, toElement, minDist);
+
+  assert(closestTriID >= 0);
+  assert(closestFeature >= 0);
+  feature = closestFeature;
+  return closestTriID;
+}
+
+bool ExactTriMeshOctree::sanityCheck(const TriMeshRef triMesh) const
+{
+  assert(triMesh.numVertices() == numVertices && triMesh.numTriangles() == numTriangles);
+
+  for(size_t i = 0; i < nodes.size(); i++)
+  {
+    const BoundingBox & bb = nodes[i].bb;
+    for(size_t j = 0; j < nodes[i].childrenIDs.size(); j++)
+    {
+      int childID = nodes[i].childrenIDs[j];
+      if (childID < 0) continue;
+      const BoundingBox & childBB = nodes[childID].bb;
+      if (bb.checkInside(childBB) == false)
+      {
+        cout << "Error: boundingBox ID " << childID << " is not inside its parent: ID " << i << endl;
+        bb.print();
+        childBB.print();
+        return false;
+      }
+    }
+    // we don't check whether triangles are inside the box because our construction
+    // allow triangles to be partially outside as long as the outside part are covered by other boxes
+  }
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////
+//                   ExactTetMeshOctree
+/////////////////////////////////////////////////////////////
+
+void ExactTetMeshOctree::clear()
+{
+  nodes.clear();
+  tetBBs.clear();
+}
+
+void ExactTetMeshOctree::build(const TetMeshRef & tetMesh, int maxDepth, int maxNumTetsPerNode)
+{
+  nodes.clear();
+  numVertices = tetMesh.numVertices();
+  numTets = tetMesh.numTets();
+  tetBBs = tetMesh.getTetBoundingBoxes();
+  vector<int> rootIndices(numTets);
+  iota(rootIndices.begin(), rootIndices.end(), 0);
+
+  nodes.emplace_back(0, move(rootIndices));
+  nodes[0].bb = BoundingBox(tetMesh.numVertices(), tetMesh.positions());
+
+  auto bbFromTris = [&](const vector<int> & tetList) -> BoundingBox
+  {
+    // get the bounding box of all triangles inside this childNode
+    vector<int> allVtxIDsInThisNode;
+    tetMesh.getVerticesInTets(tetList, allVtxIDsInThisNode);
+    return BoundingBox (tetMesh.positions(), allVtxIDsInThisNode);
+  };
+  auto subdivide = [&](const vector<int> & elementList, const BoundingBox & bb,
+        vector<int> childIDs[8], BoundingBox subBBs[8]) -> void
+  {
+    boundingBoxPartition(elementList, bb, childIDs, subBBs, tetBBs, bbFromTris);
+  };
+
+  buildFromRoot(subdivide, maxDepth, maxNumTetsPerNode);
+}
+
+//int ExactTetMeshOctree::getClosestTet(const Vec3d & queryPosition, double * minDistance, double distanceHi) const
+//{
+//  auto toBB = [&](const BoundingBox & bb) -> pair<double, double>
+//  {
+//    double dist = bb.distanceToPoint(queryPosition);
+//    return { dist, dist + bb.diameter() };
+//  };
+//
+//  auto toElement = [&](int childIndex, double minDistance) -> double
+//  {
+//    pointInTet(&queryPosition[0], )
+//    Vec3d triangleVertices[3];
+//    for(int vtx=0; vtx<3; vtx++)
+//    {
+//      int vertex = triangles[3*childIndex+vtx];
+//      for(int dim=0; dim<3; dim++)
+//        triangleVertices[vtx][dim] = vertices[3*vertex+dim];
+//    }
+//
+//    TriangleWithCollisionInfo triangle(triangleVertices[0], triangleVertices[1], triangleVertices[2]);
+//
+//    int feature;
+//    double alpha, beta, gamma;
+//    Vec3d queryPosV(queryPos);
+//    double distToTriangle2 = triangle.distanceToPoint2(queryPosV, &feature, &alpha, &beta, &gamma);
+//
+//    //printf("child %d: exactd=%G\n", ch, distToBV);
+//    if (distToTriangle2 <= minDistance * minDistance)
+//    {
+//      *nearestTriangle = childIndex;
+//      *nearestFeature = feature;
+//      *nearestAlpha = alpha;
+//      *nearestBeta = beta;
+//      *nearestGamma = gamma;
+//      return sqrt(distToTriangle2);
+//    }
+//    return DBL_MAX;
+//  };
+//
+//  double minDist = 0.0;
+//  nearestQuery(toBB, toElement, minDist, distanceHi);
+//  if (minDistance) *minDistance = minDist;
+//}
diff --git a/libraries/mesh/exactOctree.h b/libraries/mesh/exactOctree.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a1870da9587371535f96dfc51aacedd7001f863
--- /dev/null
+++ b/libraries/mesh/exactOctree.h
@@ -0,0 +1,251 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef EXACT_OCTREE_H
+#define EXACT_OCTREE_H
+
+// An octree implementation using exact predicates for precise geometry queries.
+// The base class is ExactOctreeBase. It has several derived classes:
+// ExactVertexOctree, ExactTriMeshOctree and ExactTetMeshOctree.
+
+#include "minivector.h"
+#include "boundingBox.h"
+#include "simpleSphere.h"
+#include "halfSpace.h"
+#include "triMeshGeo.h"
+#include "tetMeshGeo.h"
+#include <cstring>
+#include <vector>
+#include <array>
+#include <cassert>
+#include <functional>
+#include <cfloat>
+
+// Base class for exact octrees
+// defines node struct inside the tree
+// defines range query and nearest query algorithm
+
+class ExactOctreeBase
+{
+public:
+  int getDepth() const { return depth; } // get depth of the tree, which equals to the maximum Node::depth in all nodes
+  int getNumNodes() const { return nodes.size(); }
+
+  // return true if the queried object intersects bb
+  using BBFilter = std::function<bool(const BoundingBox & bb)>;
+  // return true if the queried object intersects the element
+  using ElementFilter = std::function<bool(int eleID)>;
+  // process each element, usually is defined to check if the queried object intersects the element
+  using ElementProcess = std::function<void(int eleID)>;
+
+  // Query the octree:
+  // Starting at the root, if the bounding box query toBB returns true,
+  // recursively visit each children node, until reaching a leaf node,
+  // where the element query toEle is called to each elementIDs stored in this leaf node.
+  void rangeQuery(BBFilter toBB, ElementProcess toEle) const;
+
+  // Query the octree and return hit elementID list:
+  // Starting at the root, if the bounding box query toBB returns true,
+  // recursively visit each children node, until reaching a leaf node,
+  // where if the element query toEle returns true on one elementID stored in this leaf node,
+  // the elementID is then pushed into elementIDList.
+  // Note: elementIDList is not sorted or deduplicated.
+  inline void rangeQuery(BBFilter toBB, ElementFilter toEle, std::vector<int> & elementIDList) const;
+
+  // return the near/far distance of the bounding box bb towards the queried object
+  using DistanceToBB = std::function<std::pair<double, double>(const BoundingBox & bb)>;
+  // return the distance of the element with elementID towards the queried object
+  using DistanceToElement = std::function<double(int elementID, double minDistanceSoFar)>;
+
+  // Nearest object query:
+  // Starting at the root, call toBB to check the near/far distance of the bounding box towards the queried object.
+  // If the near distance is farther than the closest distance found so far, then skip.
+  // Otherwise, recursively visit each children node, until reaching a leaf node,
+  // where if calling toElement to compute the distance between the element and the queried object gives
+  // a distance smaller than the closest distance found so far. In this case, the closest distance will be updated.
+  // distanceHi: the value to initialze the "closest distance found so far" value in the algorithm.
+  // Ususally it is set to be DBL_MAX. But it can be other values to cull away elements that are too far away as a priori.
+  void nearestQuery(DistanceToBB toBB, DistanceToElement toElement, double & minDistance, double distanceHi = DBL_MAX) const;
+
+protected:
+  ExactOctreeBase() {}
+  ~ExactOctreeBase() {}
+
+  using Subdivide = std::function<void(const std::vector<int> & elements, const BoundingBox & bb,
+      std::vector<int> childIDs[8], BoundingBox subBBs[8])>;
+
+  // derived class calls this function to build the octree tree
+  void buildFromRoot(Subdivide divideNode, int maxDepth, int maxNumElementsPerNode);
+
+  // whether a bounding box intersects an element
+  using ElementBBIntersect = std::function<bool(int eleID, const BoundingBox & bb)>;
+  // given element ID list, create a bounding box that covers them
+  using BBFromElementList = std::function<BoundingBox(const std::vector<int> & elementList)>;
+
+  // an implementation to divide the elements within the bounding box bb into eight sub groups, store them
+  // in childIDs and their bounding boxes in subBBs
+  void boundingBoxPartition(const std::vector<int> & elements, const BoundingBox & bb,
+      std::vector<int> childIDs[8], BoundingBox subBBs[8],
+      const std::vector<BoundingBox> & elementBBs, BBFromElementList, ElementBBIntersect=nullptr);
+
+  struct Node
+  {
+    int depth; // for the root node of the tree, depth = 0
+    std::vector<int> indices;
+    BoundingBox bb;
+    std::array<int, 8> childrenIDs{ {-1,-1,-1,-1,-1,-1,-1,-1} };
+    Node(int depth, std::vector<int> indices)  : depth(depth), indices(std::move(indices)) {}
+    bool isLeaf() const { return indices.size() > 0; }
+    // get the next available child index in childrenIDs starting at nextChildIndex
+    // return 8 if not available
+    // return nextChildIndex if childrenIDs[nextChildIndex] is available
+    // nextChildIndex: [0, 8]
+    int getNextChild(int nextChildIndex) const { for(; nextChildIndex < 8 && childrenIDs[nextChildIndex] < 0; nextChildIndex++) {} return nextChildIndex; }
+  };
+
+  int depth{0}; // how deep in the hierarchy is this octree
+
+  std::vector<Node> nodes;
+};
+
+// octree to query vertices
+
+class ExactVertexOctree : public ExactOctreeBase
+{
+public:
+  // make empty octree
+  ExactVertexOctree() {}
+  virtual ~ExactVertexOctree() { clear(); }
+
+  // maxDepth: maximum tree depth allowed
+  // build the octree
+  void build(const std::vector<Vec3d> & verticesList, int maxDepth, int maxNumVerticesPerNode);
+
+  // query vertices inside simpleSphere/halfSpace with double-precision
+  // vertexIDList are not sorted or deduplicated
+  void rangeQuery(const SimpleSphere & simpleSphere, std::vector<int> & vertexIDList) const;
+  void rangeQuery(const HalfSpace & halfSpace, std::vector<int> & vertexIDList) const;
+
+protected:
+  std::vector<Vec3d> vertices;
+
+  void clear();
+};
+
+// octree to query a triangle mesh
+
+class ExactTriMeshOctree : public ExactOctreeBase
+{
+public:
+  ExactTriMeshOctree() : numVertices(0), numTriangles(0) {}
+  virtual ~ExactTriMeshOctree() { clear(); }
+
+  void build(const TriMeshRef triMesh, int maxDepth, int maxNumTrianglesPerNode);
+
+  const std::vector<BoundingBox> & triangleBoundingBoxes() const { return triBBs; }
+
+  int numMeshVertices() const { return numVertices; }
+  int numMeshTriangles() const { return numTriangles; }
+
+  // void rangeQuery(const SimpleSphere & simpleSphere, std::vector<int> & vertexIDList);
+
+  // do exact self intersection
+  // input triMesh should be the same mesh as the one used in build()
+  // output triangleID pairs are sorted and deduplicated
+  void selfIntersectionExact(const TriMeshRef triMesh, std::vector<std::pair<int, int>> & triangleIDList) const;
+
+  // do exact intersection with another triangle mesh
+  // output triangleID pairs are sorted and deduplicated
+  void intersectionExact(const TriMeshRef triMesh, const ExactTriMeshOctree & otherOctree, const TriMeshRef & otherMesh,
+      std::vector<std::pair<int,int>> & triangleIDList);
+  void intersectionExact(const TriMeshRef triMesh, const TriMeshRef & otherMesh,
+        std::vector<std::pair<int,int>> & triangleIDList);
+
+  // do exact intersection with line segment / triangle
+  void lineSegmentIntersectionExact(const TriMeshRef triMesh, Vec3d segStart, Vec3d segEnd, std::vector<int> & triangleIDList) const;
+  void triangleIntersectionExact(const TriMeshRef triMesh, Vec3d t0, Vec3d t1, Vec3d t2, std::vector<int> & triangleIDList) const;
+
+  // detect whether the line segment hits a triangle exactly.
+  // if it does, find the segment parameter segWeight[2] to locate the first intersection point from segStart to segEnd by:
+  // intersection_point = segStart * segWeight[0] + segEnd * segWeight[1]
+  bool lineSegmentFirstIntersectionPoint(const TriMeshRef triMesh, Vec3d segStart, Vec3d segEnd, double segWeight[2]) const;
+
+  // get the closest triangle to queryPosition, double-precision
+  int getClosestTriangle(const TriMeshRef triMesh, const Vec3d & queryPosition, int & feature) const;
+
+  // check whether bounding boxes are fine
+  bool sanityCheck(const TriMeshRef triMesh) const;
+
+protected:
+  int numVertices, numTriangles;
+  std::vector<BoundingBox> triBBs;
+
+  void clear();
+  std::vector<int> trianglesUnderNode(int nodeID) const;
+};
+
+// octree to query a tet mesh
+
+class ExactTetMeshOctree : public ExactOctreeBase
+{
+public:
+  ExactTetMeshOctree() : numVertices(0), numTets(0) {}
+
+  void build(const TetMeshRef & tetMesh, int maxDepth, int maxNumTetsPerNode);
+
+//  int getClosestTet(const Vec3d & queryPosition, double * minDistance = nullptr, double distanceHi = DBL_MAX) const;
+
+protected:
+  int numVertices, numTets;
+  std::vector<BoundingBox> tetBBs;
+
+  void clear();
+};
+
+
+// =====================================================
+// ============= BELOW ARE IMPLEMENTATION ==============
+// =====================================================
+
+inline void ExactOctreeBase::rangeQuery(BBFilter toBB, ElementFilter toEle, std::vector<int> & elementIDList) const
+{
+  auto elementProcess = [&](int eleID)
+   {
+    if (toEle(eleID))
+      elementIDList.push_back(eleID);
+  };
+  rangeQuery(toBB, elementProcess);
+}
+
+
+#endif
+
diff --git a/libraries/mesh/geometryQuery.cpp b/libraries/mesh/geometryQuery.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ae4277128ad740c8755552e2961b98c41f3d69cc
--- /dev/null
+++ b/libraries/mesh/geometryQuery.cpp
@@ -0,0 +1,301 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "geometryQuery.h"
+#include "basicAlgorithms.h"
+#include <cassert>
+using namespace std;
+
+double getTriangleAngle(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2)
+{
+  Vec3d e1 = v1 - v0, e2 = v2 - v0;
+  double cosAngle = dot(e1, e2) / (len(e1) * len(e2));
+  cosAngle = clamp(cosAngle, -1.0, 1.0);
+  return acos(cosAngle);
+}
+
+double getVectorAngle(const Vec3d & vec1, const Vec3d vec2)
+{
+  double cosAngle = dot(vec1, vec2) / sqrt(len2(vec1) * len2(vec2));
+  cosAngle = clamp(cosAngle, -1.0, 1.0);
+  return acos(cosAngle);
+}
+
+double getTriangleAngleRobust(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2)
+{
+  Vec3d e1 = v1 - v0;
+  Vec3d e2 = v2 - v0;
+  double l2e1 = len2(e1), l2e2 = len2(e2);
+  double alpha = 0.0;
+  if (l2e1 > 0 && l2e2 > 0)
+  {
+    double cosAlpha = dot(e1,e2) / sqrt(l2e1 * l2e2);
+    cosAlpha = clamp(cosAlpha, -1.0, 1.0);
+    alpha = acos(cosAlpha);
+  }
+  else if (l2e1 == 0 && l2e2 == 0)
+    alpha = M_PI / 3;
+  else
+    alpha = M_PI / 2;
+  return alpha;
+}
+
+double getTwoTriangleDihedralAngle(const Vec3d & e0, const Vec3d & e1, const Vec3d & t0, const Vec3d & t1)
+{
+  Vec3d n0 = getTriangleScaledNormal(e0, e1, t0);
+  Vec3d n1 = getTriangleScaledNormal(e0, e1, t1);
+  return getVectorAngle(n0, n1);
+}
+
+//double getTwoTriangleInnerDihedralAngle(const IndexedTriangle & it0, const IndexedTriangle & it1)
+//{
+//  Vec3d n0 = getTriangleScaledNormal(it0.pos[0], it0.pos[1], it0.pos[2]);
+//  Vec3d n1 = getTriangleScaledNormal(it1.pos[0], it1.pos[1], it1.pos[2]);
+//  Vec3i e = getSharedVertices(it0.vtxID, it1.vtxID);
+//  assert(e[0] >= 0 && e[1] >= 0 && e[2] < 0); // assert shared is an edge
+//  Vec3d t0, t1, edgePos;
+//  for(int i = 0; i < 3; i++)
+//    if (it0.vtxID[i] != e[0] && it0.vtxID[i] != e[1]) {
+//      t0 = it0.pos[i];
+//      edgePos = it0.pos[(i+1)%3]; // one pos on the joint edge
+//      break;
+//    }
+//
+//  for(int i = 0; i < 3; i++)
+//    if (it1.vtxID[i] != e[0] && it1.vtxID[i] != e[1]) {
+//      t1 = it1.pos[i];
+//      break;
+//    }
+//
+//  double normalAngle = getVectorAngle(n0, n1);
+//  double out0 = dot(t0 - edgePos, n1); // whether t0 is outside n1
+//  double out1 = dot(t1 - edgePos, n0); // whether t1 is outside n0
+//  if (out0 > 0 && out1 > 0) { return M_PI + normalAngle; }
+//  else if (out0 < 0 && out1 < 0) { return M_PI - normalAngle; }
+//}
+
+
+// assert len2(normal) > 0
+Vec3d getClosestPointToPlaneWithScaledNormal(const Vec3d & queryPoint, const Vec3d & scaledNormal, const Vec3d & start)
+{
+  Vec3d diff = queryPoint - start;
+  double d = dot(scaledNormal, diff);
+  double normalL2 = len2(scaledNormal);
+  assert(normalL2 > 0);
+  return queryPoint - d * scaledNormal / normalL2;
+}
+
+Vec3d getClosestPointToPlaneWithNormal(const Vec3d & queryPoint, const Vec3d & normal, const Vec3d & start)
+{
+  Vec3d diff = queryPoint - start;
+  double d = dot(normal, diff);
+  return queryPoint - d * normal;
+}
+
+Vec3d getClosestPointToLineSegment(const Vec3d & queryPoint, const Vec3d & lineStart, const Vec3d & lineEnd)
+{
+  Vec3d lineVec = lineEnd - lineStart;
+
+  double d = dot(lineVec, queryPoint - lineStart);
+
+  // the closest point is lineStart
+  if (d <= 0) return lineStart;
+
+  double lineLen2 = len2(lineVec);
+  // the closest point is lineEnd
+  if (d >= lineLen2) return lineEnd;
+
+  return lineStart + (d / lineLen2) * lineVec;
+}
+
+Vec3d getClosestPointToTriangleWithFeature(const Vec3d & queryPoint, const Vec3d & t0, const Vec3d & t1, const Vec3d & t2, int feature)
+{
+  if (feature == 0) return t0;
+  else if (feature == 1) return t1;
+  else if (feature == 2) return t2;
+  else if (feature == 3) // edge 01
+  {
+    return getClosestPointToLineSegment(queryPoint, t0, t1);
+  }
+  else if (feature == 4) // edge 12
+  {
+    return getClosestPointToLineSegment(queryPoint, t1, t2);
+  }
+  else if (feature == 5) // edge 20
+  {
+    return getClosestPointToLineSegment(queryPoint, t2, t0);
+  }
+  // else, feature == 6, on triangle
+  Vec3d scaledNormal = getTriangleScaledNormal(t0, t1, t2);
+  return getClosestPointToPlaneWithScaledNormal(queryPoint, scaledNormal, t0);
+}
+
+Vec3d getClosestPointToTriangleWithNormalAndFeature(const Vec3d & queryPoint, const Vec3d & t0, const Vec3d & t1, const Vec3d & t2, const Vec3d & normal, int feature)
+{
+  if (feature == 0) return t0;
+  else if (feature == 1) return t1;
+  else if (feature == 2) return t2;
+  else if (feature == 3) // edge 01
+  {
+    return getClosestPointToLineSegment(queryPoint, t0, t1);
+  }
+  else if (feature == 4) // edge 12
+  {
+    return getClosestPointToLineSegment(queryPoint, t1, t2);
+  }
+  else if (feature == 5) // edge 20
+  {
+    return getClosestPointToLineSegment(queryPoint, t2, t0);
+  }
+  // else, feature == 6, on triangle
+  return getClosestPointToPlaneWithNormal(queryPoint, normal, t0);
+}
+
+namespace
+{
+
+// scaledTriangleNormal must not be zero-length, but need not be unit length
+// is queryPoint to the left of the edge (edgeStart -> edgeEnd)
+bool isToLeftOfTriangleEdge(const Vec3d & queryPoint, const Vec3d & scaledTriangleNormal, const Vec3d & edgeStart, const Vec3d & edgeEnd)
+{
+  double d = dot(cross(edgeEnd - edgeStart, queryPoint - edgeStart), scaledTriangleNormal);
+  return d > 0;
+}
+
+// delta: vector from an arbitrary point on the plane to the query point
+double getSquaredDistanceToPlaneWithScaledNormalAndDelta(const Vec3d & scaledPlaneNormal, const Vec3d & delta)
+{
+  double d = dot(scaledPlaneNormal, delta);
+  double normalLen2 = len2(scaledPlaneNormal);
+  return (d*d) / normalLen2;
+}
+
+} // anonymous namespace
+
+
+double getSquaredDistanceToLineSegment(const Vec3d & queryPoint, const Vec3d & lineStart, const Vec3d & lineEnd)
+{
+  Vec3d lineVec = lineEnd - lineStart;
+
+  Vec3d segStart2Query = queryPoint - lineStart;
+  double d = dot(lineVec, segStart2Query);
+
+  // return distance from segStart to query point
+  if (d <= 0) return len2(segStart2Query);
+
+  double lineLen2 = len2(lineVec);
+  // return distance from segEnd to query point
+  if (d > lineLen2) return len2(queryPoint - lineEnd);
+
+  // return distance from the infinite line to query point
+  return len2(cross(lineVec, segStart2Query)) / lineLen2;
+}
+
+// also returns the closest feature to the query point:
+//  0: vertex0
+//  1: vertex1
+//  2: vertex2
+//  3: edge among 01
+//  4: edge among 12
+//  5: edge among 20
+//  6: the face itself
+double getSquaredDistanceToTriangle(const Vec3d & queryPoint, const Vec3d & vertex0, const Vec3d & vertex1, const Vec3d & vertex2, int & feature)
+{
+  Vec3d scaledNormal = getTriangleScaledNormal(vertex0, vertex1, vertex2);
+
+  if(scaledNormal != Vec3d(0.0) &&
+      isToLeftOfTriangleEdge(queryPoint, scaledNormal, vertex0, vertex1) &&
+      isToLeftOfTriangleEdge(queryPoint, scaledNormal, vertex1, vertex2) &&
+      isToLeftOfTriangleEdge(queryPoint, scaledNormal, vertex2, vertex0))
+  {
+    // the closest point on triangle to queryPoint is the same closet point on the plane where the triangle lies to queryPoint
+    feature = 6;
+    return getSquaredDistanceToPlaneWithScaledNormalAndDelta(scaledNormal, queryPoint-vertex0);
+  }
+  else // the projection of the queryPoint onto the triangle plane is outside the triangle, or the triangle is degenerate
+    {    // then we query the closest distance from the query point to all the three edges
+      double d0 = getSquaredDistanceToLineSegment(queryPoint, vertex1, vertex0);
+      double d1 = getSquaredDistanceToLineSegment(queryPoint, vertex2, vertex1);
+      double d2 = getSquaredDistanceToLineSegment(queryPoint, vertex0, vertex2);
+
+      pair<double, int> sortBuffer[3] = { { d0, 0 }, { d1, 1 }, { d2, 2 } };
+      sort(sortBuffer, sortBuffer+3);
+      if (sortBuffer[0].first == sortBuffer[1].first)
+      {
+        // closest feature is a vertex
+        int edgeIDToFeatureMap[3][3] = { {-1, 1, 0}, {1, -1, 2}, {0, 2, -1} };
+        feature = edgeIDToFeatureMap[sortBuffer[0].second][sortBuffer[1].second];
+        assert(feature >= 0);
+      }
+      else
+      { // closest feature is an edge
+        feature = sortBuffer[0].second + 3;
+      }
+      return sortBuffer[0].first;
+    }
+}
+
+void getTetBarycentricWeights(const Vec3d & queryPoint, const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d, double weight[4])
+{
+  //       |x1 y1 z1 1|         |x  y  z  1|        |x1 y1 z1 1|        |x1 y1 z1 1|        |x1 y1 z1 1|
+  //  D0 = |x2 y2 z2 1|   D1 =  |x2 y2 z2 1|   D2 = |x  y  z  1|   D3 = |x2 y2 z2 1|   D4 = |x2 y2 z2 1|
+  //       |x3 y3 z3 1|         |x3 y3 z3 1|        |x3 y3 z3 1|        |x  y  z  1|        |x3 y3 z3 1|
+  //       |x4 y4 z4 1|         |x4 y4 z4 1|        |x4 y4 z4 1|        |x4 y4 z4 1|        |x  y  z  1|
+  //  wi = Di / D0
+
+  double tetDet = getTetDeterminant(a, b, c, d);
+
+  for(int i=0; i<4; i++)
+  {
+    // compute D[i+1]
+    Vec3d buf[4] = { a, b, c, d };
+    buf[i] = queryPoint;
+    double D = getTetDeterminant(buf[0], buf[1], buf[2], buf[3]);
+    weight[i] = D / tetDet;
+  }
+}
+
+double getSquaredDistanceToTet(const Vec3d & queryPoint, const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d)
+{
+//  const int OTetKey::tetFaceIndex[4][3] =  { { 1, 2, 3 }, { 0, 3, 2 }, { 0, 1, 3 }, { 0, 2, 1 } };
+  if (getScaledSignedDistanceToTrianglePlane(queryPoint, b, c, d) <= 0.0 ||
+      getScaledSignedDistanceToTrianglePlane(queryPoint, a, d, c) <= 0.0 ||
+      getScaledSignedDistanceToTrianglePlane(queryPoint, a, b, d) <= 0.0 ||
+      getScaledSignedDistanceToTrianglePlane(queryPoint, a, c, b) <= 0.0)
+    return 0.0; // inside the tet
+
+  double d2 = getSquaredDistanceToTriangle(queryPoint, b, c, d);
+  d2 = min(d2, getSquaredDistanceToTriangle(queryPoint, a, d, c));
+  d2 = min(d2, getSquaredDistanceToTriangle(queryPoint, a, b, d));
+  d2 = min(d2, getSquaredDistanceToTriangle(queryPoint, a, c, b));
+  return d2;
+}
diff --git a/libraries/mesh/geometryQuery.h b/libraries/mesh/geometryQuery.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd7c26d0c55fecaa1be825959c9990620b4eed97
--- /dev/null
+++ b/libraries/mesh/geometryQuery.h
@@ -0,0 +1,127 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef GEOMETRYQUERY_H
+#define GEOMETRYQUERY_H
+
+#include "minivector.h"
+#include <cmath>
+
+inline double rad2deg(double x) { return x * (180.0 / M_PI); }
+inline double deg2rad(double x) { return x * (M_PI / 180.0); }
+
+// get angle at v0: /_v1v0v2
+double getTriangleAngle(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2);
+
+// get angle between vec0 and vec1
+double getVectorAngle(const Vec3d & vec0, const Vec3d vec1);
+
+// get the scaled normal of triangle <v0, v1, v2>: cross(v1-v0, v2-v0) = 2 * tri_area * n
+// its length is twice the triangle area
+inline Vec3d getTriangleScaledNormal(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2) { return cross(v1 - v0, v2 - v0); }
+
+// get the normal of triangle <v0, v1, v2>
+inline Vec3d getTriangleNormal(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2) { return norm(getTriangleScaledNormal(v0, v1, v2)); }
+
+inline double getTriangleArea(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2) { return 0.5 * len(getTriangleScaledNormal(v0, v1, v2)); }
+
+// return dot (queryPoint - v0, cross(v1-v0, v2-v0) )
+// the returned value is positive if queryPoint is above triangle, negative if under and zero if on the plane where the triangle lies
+// the returned value is 2 * tri_area * signed_distance
+inline double getScaledSignedDistanceToTrianglePlane(const Vec3d & queryPoint, const Vec3d & v0, const Vec3d & v1, const Vec3d & v2) { return dot(queryPoint - v0, getTriangleScaledNormal(v0,v1,v2)); }
+
+// robust computation of the angle at v0 in a triangle v0, v1, v2
+// if any two points have the same position, it treats the triangle as a degenerate one with angle 0, 90 and 90
+// if all three points have the same position, it treats the triangle angles as 60, 60, 60
+double getTriangleAngleRobust(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2);
+
+// get the dihedral angle between triangle (e0, e1, t0) and (e0, e1, t1), triangle ordering does not matter
+// return value of [0, PI]
+double getTwoTriangleDihedralAngle(const Vec3d & e0, const Vec3d & e1, const Vec3d & t0, const Vec3d & t1);
+
+// assert len(scaledNormal) > 0.0
+Vec3d getClosestPointToPlaneWithScaledNormal(const Vec3d & queryPoint, const Vec3d & scaledNormal, const Vec3d & planeStart);
+// asssert len(normal) == 1.0
+Vec3d getClosestPointToPlaneWithNormal(const Vec3d & queryPoint, const Vec3d & normal, const Vec3d & planeStart);
+
+Vec3d getClosestPointToLineSegment(const Vec3d & queryPoint, const Vec3d & segStart, const Vec3d & segEnd);
+
+// also input the closest feature to the query point:
+//  0: vertex0
+//  1: vertex1
+//  2: vertex2
+//  3: edge among 01
+//  4: edge among 12
+//  5: edge among 20
+//  6: the face itself
+Vec3d getClosestPointToTriangleWithFeature(const Vec3d & queryPoint, const Vec3d & t0, const Vec3d & t1, const Vec3d & t2, int feature);
+Vec3d getClosestPointToTriangleWithNormalAndFeature(const Vec3d & queryPoint, const Vec3d & t0, const Vec3d & t1, const Vec3d & t2, const Vec3d & normal, int feature);
+
+double getSquaredDistanceToTriangle(const Vec3d & queryPoint, const Vec3d & t0, const Vec3d & t1, const Vec3d & t2, int & feature);
+inline double getSquaredDistanceToTriangle(const Vec3d & queryPoint, const Vec3d & t0, const Vec3d & t1, const Vec3d & t2) { int f = 0; return getSquaredDistanceToTriangle(queryPoint,t0,t1,t2, f); }
+
+double getSquaredDistanceToLineSegment(const Vec3d & queryPoint, const Vec3d & segStart, const Vec3d & segEnd);
+
+// compute the inner dihedral angle of the two triangles
+// triangle normals should agree with each other:
+// n0 /                    /                           n0 /
+//  \/                    /\n0                          \/
+//  /            or      /     | n1          but not    /   | n1
+// ----------           ----------                     ----------
+//     | n1
+// IndexedTriangle it0 and it1 should have two shared vtx indices which form the joint edge
+// return [0, 2PI), return nan if normals don't agree
+//double getTwoTriangleInnerDihedralAngle(const IndexedTriangle & it0, const IndexedTriangle & it1);
+
+
+// computes det(A), for the 4x4 matrix A
+//     [ 1 a ]
+// A = [ 1 b ]
+//     [ 1 c ]
+//     [ 1 d ]
+// It can also be computed as det(A) = dot(d - a, cross(b - a, c - a))
+// When det(A) > 0, the tet has positive orientation.
+// When det(A) = 0, the tet is degenerate.
+// When det(A) < 0, the tet has negative orientation.
+// The orientation can also be determined as:
+// if a is under the plane of the triangle formed by <b, c, d>, then it has positive orientation
+inline double getTetDeterminant(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d)
+{
+  return dot(d - a, cross(b - a, c - a));
+}
+
+// compute barycentric weights of tet <a,b,c,d> for queryPoint
+void getTetBarycentricWeights(const Vec3d & queryPoint, const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d, double weight[4]);
+
+double getSquaredDistanceToTet(const Vec3d & queryPoint, const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+
+#endif
diff --git a/libraries/mesh/halfSpace.h b/libraries/mesh/halfSpace.h
new file mode 100644
index 0000000000000000000000000000000000000000..7048969455bf3de0491387140002b3be01804dcf
--- /dev/null
+++ b/libraries/mesh/halfSpace.h
@@ -0,0 +1,87 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef HALFSPACE_H_
+#define HALFSPACE_H_
+
+#include "vec3d.h"
+
+class HalfSpace
+{
+public:
+  // HalfSpace satisfying: dot(normal, X) + d > 0, normal pointing outward
+  // normalization of normal is not needed
+  inline HalfSpace(const Vec3d & normal, double d);
+  // HalfSpace on the positive side of triangle(a,b,c)
+  // normal = cross(b-a, c-a), d = - dot(a, normal)
+  inline HalfSpace(const Vec3d & a, const Vec3d & b, const Vec3d & c);
+
+  // check a point is in this half space or not
+  // return +1 for outside, 0 for on the boundary, -1 for inside
+  inline int outside(const Vec3d & p) const;
+
+  // return true if bb intersect or touch the half space
+  inline bool intersect(const BoundingBox & bb) const;
+
+protected:
+  Vec3d normal;
+  double d;
+};
+
+inline HalfSpace::HalfSpace(const Vec3d & n, double D) : normal(n), d(D) {}
+
+inline HalfSpace::HalfSpace(const Vec3d & a, const Vec3d & b, const Vec3d & c)
+{
+  normal = cross(b-a, c-a);
+  d = - dot(a, normal);
+}
+
+inline int HalfSpace::outside(const Vec3d & p) const
+{
+  double ret = dot(normal, p) + d;
+  return ret > 0 ? +1 : ( ret < 0 ? -1 : 0);
+}
+
+inline bool HalfSpace::intersect(const BoundingBox & bb) const
+{
+  const Vec3d * bound[2] = { & bb.bmin(), & bb.bmax() };
+  for(int i = 0; i < 8; i++)
+  {
+    Vec3d v((*bound[(i & 4) >> 2])[0],(*bound[(i & 2) >> 1])[1], (*bound[i & 1])[3]);
+    if(outside(v) <= 0) return true; // if v is inside the half space
+  }
+  return false;
+}
+
+
+
+#endif
diff --git a/libraries/mesh/initPredicates.h b/libraries/mesh/initPredicates.h
new file mode 100644
index 0000000000000000000000000000000000000000..ca0c969fb19ade47b8efa639ca461dff2fc580e2
--- /dev/null
+++ b/libraries/mesh/initPredicates.h
@@ -0,0 +1,39 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef INITPREDICATES_H
+#define INITPREDICATES_H
+
+// Initialize the variables used for exact arithmetic.
+void initPredicates();
+
+#endif
diff --git a/libraries/mesh/intersection_tunicate.cpp b/libraries/mesh/intersection_tunicate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2293beabc2af5c29508cef0d80e96f188fac9e47
--- /dev/null
+++ b/libraries/mesh/intersection_tunicate.cpp
@@ -0,0 +1,1071 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This file is modified from Robert Bridson's Tunicate library:         *
+ * https://www.cs.ubc.ca/~rbridson/tunicate/                             *
+ * You can redistribute our changes and/or                               *
+ * modify them under the terms of the BSD-style license that is          *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+// Released into the public domain by Robert Bridson, 2009.
+
+#include <cassert>
+#include <cmath>
+
+// Modified Bridson's code to use Shewchuk's orient2/3d because the later is faster
+double orient2d(const double pa[2], const double pb[2], const double pc[2]);
+double orient3d(const double pa[3], const double pb[3], const double pc[3], const double pd[3]);
+
+//==============================================================================
+static bool
+same_sign(double a, double b)
+{
+   return (a<=0 && b<=0) || (a>=0 && b>=0);
+}
+
+//==============================================================================
+int
+simplex_intersection1d(int k,
+                       const double* x0,
+                       const double* x1,
+                       const double* x2,
+                       double* alpha0,
+                       double* alpha1,
+                       double* alpha2)
+{
+   assert(1<=k && k<=2);
+   assert(alpha0 && alpha1 && alpha2);
+   if(k==1)
+   {
+      if(x1[0]<x2[0])
+      {
+         if(x0[0]<x1[0]) return 0;
+         else if(x0[0]>x2[0]) return 0;
+         *alpha0=1;
+         *alpha1=(x2[0]-x0[0])/(x2[0]-x1[0]);
+         *alpha2=(x0[0]-x1[0])/(x2[0]-x1[0]);
+         return 1;
+      }else if(x1[0]>x2[0])
+      {
+         if(x0[0]<x2[0]) return 0;
+         else if(x0[0]>x1[0]) return 0;
+         *alpha0=1;
+         *alpha1=(x2[0]-x0[0])/(x2[0]-x1[0]);
+         *alpha2=(x0[0]-x1[0])/(x2[0]-x1[0]);
+         return 1;
+      }else // x1[0]==x2[0]
+      {
+         if(x0[0]!=x1[0]) return 0;
+         *alpha0=1;
+         *alpha1=0.5;
+         *alpha2=0.5;
+         return 1;
+      }
+   }else
+      return simplex_intersection1d(1, x2, x1, 0, alpha2, alpha1, alpha0);
+}
+
+//==============================================================================
+// degenerate test in 2d - assumes three points lie on the same line
+static int
+simplex_intersection2d(int k,
+                       const double* x0,
+                       const double* x1,
+                       const double* x2,
+                       double* alpha0,
+                       double* alpha1,
+                       double* alpha2)
+{
+   assert(k==1);
+   // try projecting each coordinate out in turn
+   double ax0, ax1, ax2;
+   if(!simplex_intersection1d(1, x0+1, x1+1, x2+1, &ax0, &ax1, &ax2)) return 0;
+   double ay0, ay1, ay2;
+   if(!simplex_intersection1d(1, x0, x1, x2, &ay0, &ay1, &ay2)) return 0;
+   // decide which solution is more accurate for barycentric coordinates
+   double checkx=std::fabs(-ax0*x0[0]+ax1*x1[0]+ax2*x2[0])
+                +std::fabs(-ax0*x0[1]+ax1*x1[1]+ax2*x2[1]);
+   double checky=std::fabs(-ay0*x0[0]+ay1*x1[0]+ay2*x2[0])
+                +std::fabs(-ay0*x0[1]+ay1*x1[1]+ay2*x2[1]);
+   if(checkx<=checky)
+   {
+      *alpha0=ax0;
+      *alpha1=ax1;
+      *alpha2=ax2;
+   }else
+   {
+      *alpha0=ay0;
+      *alpha1=ay1;
+      *alpha2=ay2;
+   }
+   return 1;
+}
+
+//==============================================================================
+int
+simplex_intersection2d(int k,
+                       const double* x0,
+                       const double* x1,
+                       const double* x2,
+                       const double* x3,
+                       double* alpha0,
+                       double* alpha1,
+                       double* alpha2,
+                       double* alpha3)
+{
+   assert(1<=k && k<=3);
+   double sum1, sum2;
+   switch(k)
+   {
+      case 1: // point vs. triangle
+         *alpha1=-orient2d(x0, x2, x3);
+         *alpha2= orient2d(x0, x1, x3);
+         if(!same_sign(*alpha1, *alpha2)) return 0;
+         *alpha3=-orient2d(x0, x1, x2);
+         if(!same_sign(*alpha1, *alpha3)) return 0;
+         if(!same_sign(*alpha2, *alpha3)) return 0;
+         sum2=*alpha1+*alpha2+*alpha3;
+         if(sum2) // triangle not degenerate?
+         {
+            *alpha0=1;
+            *alpha1/=sum2;
+            *alpha2/=sum2;
+            *alpha3/=sum2;
+            return 1;
+         }else // triangle is degenerate and point lies on same line
+         {
+            if(simplex_intersection2d(1, x0, x1, x2, alpha0, alpha1, alpha2))
+            {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection2d(1, x0, x1, x3, alpha0, alpha1, alpha3))
+            {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection2d(1, x0, x2, x3, alpha0, alpha2, alpha3))
+            {
+               *alpha1=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 2: // segment vs. segment
+         *alpha0= orient2d(x1, x2, x3);
+         *alpha1=-orient2d(x0, x2, x3);
+         if(!same_sign(*alpha0, *alpha1)) return 0;
+         *alpha2= orient2d(x0, x1, x3);
+         *alpha3=-orient2d(x0, x1, x2);
+         if(!same_sign(*alpha2, *alpha3)) return 0;
+         sum1=*alpha0+*alpha1;
+         sum2=*alpha2+*alpha3;
+         if(sum1 && sum2)
+         {
+            *alpha0/=sum1;
+            *alpha1/=sum1;
+            *alpha2/=sum2;
+            *alpha3/=sum2;
+            return 1;
+         }else // degenerate: segments lie on the same line
+         {
+            if(simplex_intersection2d(1, x0, x2, x3, alpha0, alpha2, alpha3))
+            {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection2d(1, x1, x2, x3, alpha1, alpha2, alpha3))
+            {
+               *alpha0=0;
+               return 1;
+            }
+            if(simplex_intersection2d(1, x2, x0, x1, alpha2, alpha0, alpha1))
+            {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection2d(1, x3, x0, x1, alpha3, alpha0, alpha1))
+            {
+               *alpha2=0;
+               return 1;
+            }
+            return 0;
+         }
+      case 3: // triangle vs. point
+         return simplex_intersection2d(1, x3, x2, x1, x0,
+                                          alpha3, alpha2, alpha1, alpha0);
+      default:
+         return -1; // should never get here
+   }
+}
+
+//==============================================================================
+// degenerate test in 3d - assumes four points lie on the same plane
+int
+simplex_intersection3d(int k,
+                       const double* x0,
+                       const double* x1,
+                       const double* x2,
+                       const double* x3,
+                       double* alpha0,
+                       double* alpha1,
+                       double* alpha2,
+                       double* alpha3)
+{
+   assert(k<=2);
+   // try projecting each coordinate out in turn
+   double ax0, ax1, ax2, ax3;
+   if(!simplex_intersection2d(k, x0+1, x1+1, x2+1, x3+1, &ax0, &ax1, &ax2,&ax3))
+      return 0;
+   double ay0, ay1, ay2, ay3;
+   double p0[2]={x0[0], x0[2]}, p1[2]={x1[0], x1[2]},
+          p2[2]={x2[0], x2[2]}, p3[2]={x3[0], x3[2]};
+   if(!simplex_intersection2d(k, p0, p1, p2, p3, &ay0, &ay1, &ay2, &ay3))
+      return 0;
+   double az0, az1, az2, az3;
+   if(!simplex_intersection2d(k, x0, x1, x2, x3, &az0, &az1, &az2, &az3))
+      return 0;
+   // decide which solution is more accurate for barycentric coordinates
+   double checkx, checky, checkz;
+   if(k==1)
+   {
+      checkx=std::fabs(-ax0*x0[0]+ax1*x1[0]+ax2*x2[0]+ax3*x3[0])
+            +std::fabs(-ax0*x0[1]+ax1*x1[1]+ax2*x2[1]+ax3*x3[1])
+            +std::fabs(-ax0*x0[2]+ax1*x1[2]+ax2*x2[2]+ax3*x3[2]);
+      checky=std::fabs(-ay0*x0[0]+ay1*x1[0]+ay2*x2[0]+ay3*x3[0])
+            +std::fabs(-ay0*x0[1]+ay1*x1[1]+ay2*x2[1]+ay3*x3[1])
+            +std::fabs(-ay0*x0[2]+ay1*x1[2]+ay2*x2[2]+ay3*x3[2]);
+      checkz=std::fabs(-az0*x0[0]+az1*x1[0]+az2*x2[0]+az3*x3[0])
+            +std::fabs(-az0*x0[1]+az1*x1[1]+az2*x2[1]+az3*x3[1])
+            +std::fabs(-az0*x0[2]+az1*x1[2]+az2*x2[2]+az3*x3[2]);
+   }else
+   {
+      checkx=std::fabs(-ax0*x0[0]-ax1*x1[0]+ax2*x2[0]+ax3*x3[0])
+            +std::fabs(-ax0*x0[1]-ax1*x1[1]+ax2*x2[1]+ax3*x3[1])
+            +std::fabs(-ax0*x0[2]-ax1*x1[2]+ax2*x2[2]+ax3*x3[2]);
+      checky=std::fabs(-ay0*x0[0]-ay1*x1[0]+ay2*x2[0]+ay3*x3[0])
+            +std::fabs(-ay0*x0[1]-ay1*x1[1]+ay2*x2[1]+ay3*x3[1])
+            +std::fabs(-ay0*x0[2]-ay1*x1[2]+ay2*x2[2]+ay3*x3[2]);
+      checkz=std::fabs(-az0*x0[0]-az1*x1[0]+az2*x2[0]+az3*x3[0])
+            +std::fabs(-az0*x0[1]-az1*x1[1]+az2*x2[1]+az3*x3[1])
+            +std::fabs(-az0*x0[2]-az1*x1[2]+az2*x2[2]+az3*x3[2]);
+   }
+   if(checkx<=checky && checkx<=checkz)
+   {
+      *alpha0=ax0;
+      *alpha1=ax1;
+      *alpha2=ax2;
+      *alpha2=ax3;
+   }else if(checky<=checkz)
+   {
+      *alpha0=ay0;
+      *alpha1=ay1;
+      *alpha2=ay2;
+      *alpha2=ay3;
+   }else
+   {
+      *alpha0=az0;
+      *alpha1=az1;
+      *alpha2=az2;
+      *alpha2=az3;
+   }
+   return 1;
+}
+
+//==============================================================================
+int
+simplex_intersection3d(int k,
+                       const double* x0,
+                       const double* x1,
+                       const double* x2,
+                       const double* x3,
+                       const double* x4,
+                       double* alpha0,
+                       double* alpha1,
+                       double* alpha2,
+                       double* alpha3,
+                       double* alpha4)
+{
+   assert(1<=k && k<=4);
+   double sum1, sum2;
+   switch(k)
+   {
+      case 1: // point vs. tetrahedron
+         *alpha1=-orient3d(x0, x2, x3, x4);
+         *alpha2= orient3d(x0, x1, x3, x4);
+         if(!same_sign(*alpha1, *alpha2)) return 0;
+         *alpha3=-orient3d(x0, x1, x2, x4);
+         if(!same_sign(*alpha1, *alpha3)) return 0;
+         if(!same_sign(*alpha2, *alpha3)) return 0;
+         *alpha4= orient3d(x0, x1, x2, x3);
+         if(!same_sign(*alpha1, *alpha4)) return 0;
+         if(!same_sign(*alpha2, *alpha4)) return 0;
+         if(!same_sign(*alpha3, *alpha4)) return 0;
+         *alpha0=1;
+         sum2=*alpha1+*alpha2+*alpha3+*alpha4;
+         if(sum2)
+         {
+            *alpha1/=sum2;
+            *alpha2/=sum2;
+            *alpha3/=sum2;
+            *alpha4/=sum2;
+            return 1;
+         }else // degenerate: point and tetrahedron in same plane
+         {
+            if(simplex_intersection3d(1, x0, x2, x3, x4,
+                                         alpha0, alpha2, alpha3, alpha4))
+            {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection3d(1, x0, x1, x3, x4,
+                                         alpha0, alpha1, alpha3, alpha4))
+            {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection3d(1, x0, x1, x2, x4,
+                                         alpha0, alpha1, alpha2, alpha4))
+            {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection3d(1, x0, x1, x2, x3,
+                                         alpha0, alpha1, alpha2, alpha3))
+            {
+               *alpha4=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 2: // segment (x0, x1) vs. triangle (x2, x3, x4)
+         *alpha0= orient3d(x1, x2, x3, x4);
+         *alpha1=-orient3d(x0, x2, x3, x4);
+         if(!same_sign(*alpha0, *alpha1)) return 0;
+         *alpha2= orient3d(x0, x1, x3, x4);
+         *alpha3=-orient3d(x0, x1, x2, x4);
+         if(!same_sign(*alpha2, *alpha3)) return 0;
+         *alpha4= orient3d(x0, x1, x2, x3);
+         if(!same_sign(*alpha2, *alpha4)) return 0;
+         if(!same_sign(*alpha3, *alpha4)) return 0;
+         sum1=*alpha0+*alpha1;
+         sum2=*alpha2+*alpha3+*alpha4;
+         if(sum1 && sum2)
+         {
+            *alpha0/=sum1;
+            *alpha1/=sum1;
+            *alpha2/=sum2;
+            *alpha3/=sum2;
+            *alpha4/=sum2;
+            return 1;
+         }else // degenerate: segment and triangle in same plane
+         {
+            if(simplex_intersection3d(1, x1, x2, x3, x4,
+                                         alpha1, alpha2, alpha3, alpha4))
+            {
+               *alpha0=0;
+               return 1;
+            }
+            if(simplex_intersection3d(1, x0, x2, x3, x4,
+                                         alpha0, alpha2, alpha3, alpha4))
+            {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection3d(2, x0, x1, x3, x4,
+                                         alpha0, alpha1, alpha3, alpha4))
+            {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection3d(2, x0, x1, x2, x4,
+                                         alpha0, alpha1, alpha2, alpha4))
+            {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection3d(2, x0, x1, x2, x3,
+                                         alpha0, alpha1, alpha2, alpha3))
+            {
+               *alpha4=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 3: // triangle vs. segment
+      case 4: // tetrahedron vs. point
+         return simplex_intersection3d(5-k, x4, x3, x2, x1, x0,
+                                        alpha4, alpha3, alpha2, alpha1, alpha0);
+      default:
+         return -1; // should never get here
+   }
+}
+
+// Currently we don't need 3d+time tests, so I commented out Bridson's code below
+/*
+//==============================================================================
+// degenerate test in 3d+time - assumes five points lie on the same hyper-plane
+static int
+simplex_intersection_time3d(int k,
+                            const double* x0, int time0,
+                            const double* x1, int time1,
+                            const double* x2, int time2,
+                            const double* x3, int time3,
+                            const double* x4, int time4,
+                            double* alpha0,
+                            double* alpha1,
+                            double* alpha2,
+                            double* alpha3,
+                            double* alpha4)
+{
+   assert(k<=2);
+   assert(time0==0 || time0==1);
+   assert(time1==0 || time1==1);
+   assert(time2==0 || time2==1);
+   assert(time3==0 || time3==1);
+   assert(time4==0 || time4==1);
+   // try projecting each coordinate out in turn
+   double ax0, ax1, ax2, ax3, ax4;
+   double r0[3]={x0[0], x0[2], time0}, r1[3]={x1[0], x1[2], time1},
+          r2[3]={x2[0], x2[2], time2}, r3[3]={x3[0], x3[2], time3},
+          r4[3]={x4[0], x4[2], time4};
+   if(!simplex_intersection3d(k, r0, r1, r2, r3, r4,
+                                 &ax0, &ax1, &ax2, &ax3, &ax4)) return 0;
+   double ay0, ay1, ay2, ay3, ay4;
+   double p0[3]={x0[0], x0[2], time0}, p1[3]={x1[0], x1[2], time1},
+          p2[3]={x2[0], x2[2], time2}, p3[3]={x3[0], x3[2], time3},
+          p4[3]={x4[0], x4[2], time4};
+   if(!simplex_intersection3d(k, p0, p1, p2, p3, p4,
+                                 &ay0, &ay1, &ay2, &ay3, &ay4)) return 0;
+   double az0, az1, az2, az3, az4;
+   double q0[3]={x0[0], x0[1], time0}, q1[3]={x1[0], x1[1], time1},
+          q2[3]={x2[0], x2[1], time2}, q3[3]={x3[0], x3[1], time3},
+          q4[3]={x4[0], x4[1], time4};
+   if(!simplex_intersection3d(k, q0, q1, q2, q3, q4,
+                                 &az0, &az1, &az2, &az3, &az4)) return 0;
+   double at0, at1, at2, at3, at4;
+   if(!simplex_intersection3d(k, x0, x1, x2, x3, x4,
+                                 &at0, &at1, &at2, &at3, &at4)) return 0;
+   // decide which solution is more accurate for barycentric coordinates
+   double checkx, checky, checkz, checkt;
+   if(k==1)
+   {
+      checkx=std::fabs(-ax0*x0[0]+ax1*x1[0]+ax2*x2[0]+ax3*x3[0]+ax4*x4[0])
+            +std::fabs(-ax0*x0[1]+ax1*x1[1]+ax2*x2[1]+ax3*x3[1]+ax4*x4[1])
+            +std::fabs(-ax0*x0[2]+ax1*x1[2]+ax2*x2[2]+ax3*x3[2]+ax4*x4[2])
+            +std::fabs(-ax0*time0+ax1*time1+ax2*time2+ax3*time3+ax4*time4);
+      checky=std::fabs(-ay0*x0[0]+ay1*x1[0]+ay2*x2[0]+ay3*x3[0]+ay4*x4[0])
+            +std::fabs(-ay0*x0[1]+ay1*x1[1]+ay2*x2[1]+ay3*x3[1]+ay4*x4[1])
+            +std::fabs(-ay0*x0[2]+ay1*x1[2]+ay2*x2[2]+ay3*x3[2]+ay4*x4[2])
+            +std::fabs(-ay0*time0+ay1*time1+ay2*time2+ay3*time3+ay4*time4);
+      checkz=std::fabs(-az0*x0[0]+az1*x1[0]+az2*x2[0]+az3*x3[0]+az4*x4[0])
+            +std::fabs(-az0*x0[1]+az1*x1[1]+az2*x2[1]+az3*x3[1]+az4*x4[1])
+            +std::fabs(-az0*x0[2]+az1*x1[2]+az2*x2[2]+az3*x3[2]+az4*x4[2])
+            +std::fabs(-az0*time0+az1*time1+az2*time2+az3*time3+az4*time4);
+      checkt=std::fabs(-at0*x0[0]+at1*x1[0]+at2*x2[0]+at3*x3[0]+at4*x4[0])
+            +std::fabs(-at0*x0[1]+at1*x1[1]+at2*x2[1]+at3*x3[1]+at4*x4[1])
+            +std::fabs(-at0*x0[2]+at1*x1[2]+at2*x2[2]+at3*x3[2]+at4*x4[2])
+            +std::fabs(-at0*time0+at1*time1+at2*time2+at3*time3+at4*time4);
+   }else
+   {
+      checkx=std::fabs(-ax0*x0[0]-ax1*x1[0]+ax2*x2[0]+ax3*x3[0]+ax4*x4[0])
+            +std::fabs(-ax0*x0[1]-ax1*x1[1]+ax2*x2[1]+ax3*x3[1]+ax4*x4[1])
+            +std::fabs(-ax0*x0[2]-ax1*x1[2]+ax2*x2[2]+ax3*x3[2]+ax4*x4[2])
+            +std::fabs(-ax0*time0-ax1*time1+ax2*time2+ax3*time3+ax4*time4);
+      checky=std::fabs(-ay0*x0[0]-ay1*x1[0]+ay2*x2[0]+ay3*x3[0]+ay4*x4[0])
+            +std::fabs(-ay0*x0[1]-ay1*x1[1]+ay2*x2[1]+ay3*x3[1]+ay4*x4[1])
+            +std::fabs(-ay0*x0[2]-ay1*x1[2]+ay2*x2[2]+ay3*x3[2]+ay4*x4[2])
+            +std::fabs(-ay0*time0-ay1*time1+ay2*time2+ay3*time3+ay4*time4);
+      checkz=std::fabs(-az0*x0[0]-az1*x1[0]+az2*x2[0]+az3*x3[0]+az4*x4[0])
+            +std::fabs(-az0*x0[1]-az1*x1[1]+az2*x2[1]+az3*x3[1]+az4*x4[1])
+            +std::fabs(-az0*x0[2]-az1*x1[2]+az2*x2[2]+az3*x3[2]+az4*x4[2])
+            +std::fabs(-az0*time0-az1*time1+az2*time2+az3*time3+az4*time4);
+      checkt=std::fabs(-at0*x0[0]-at1*x1[0]+at2*x2[0]+at3*x3[0]+at4*x4[0])
+            +std::fabs(-at0*x0[1]-at1*x1[1]+at2*x2[1]+at3*x3[1]+at4*x4[1])
+            +std::fabs(-at0*x0[2]-at1*x1[2]+at2*x2[2]+at3*x3[2]+at4*x4[2])
+            +std::fabs(-at0*time0-at1*time1+at2*time2+at3*time3+at4*time4);
+   }
+   if(checkx<=checky && checkx<=checkz && checkx<=checkt)
+   {
+      *alpha0=ax0;
+      *alpha1=ax1;
+      *alpha2=ax2;
+      *alpha3=ax3;
+   }else if(checky<=checkz && checky<=checkt)
+   {
+      *alpha0=ay0;
+      *alpha1=ay1;
+      *alpha2=ay2;
+      *alpha3=ay3;
+   }else if(checkz<=checkt)
+   {
+      *alpha0=az0;
+      *alpha1=az1;
+      *alpha2=az2;
+      *alpha3=az3;
+   }else
+   {
+      *alpha0=at0;
+      *alpha1=at1;
+      *alpha2=at2;
+      *alpha3=at3;
+   }
+   return 1;
+}
+
+//==============================================================================
+int
+simplex_intersection_time3d(int k,
+                            const double* x0, int t0,
+                            const double* x1, int t1,
+                            const double* x2, int t2,
+                            const double* x3, int t3,
+                            const double* x4, int t4,
+                            const double* x5, int t5,
+                            double* alpha0,
+                            double* alpha1,
+                            double* alpha2,
+                            double* alpha3,
+                            double* alpha4,
+                            double* alpha5)
+{
+   assert(1<=k && k<=5);
+   assert(t0==0 || t0==1);
+   assert(t1==0 || t1==1);
+   assert(t2==0 || t2==1);
+   assert(t3==0 || t3==1);
+   assert(t4==0 || t4==1);
+   assert(t5==0 || t5==1);
+   double sum1, sum2;
+   switch(k)
+   {
+      case 1: // point vs. pentachoron
+         *alpha1=-orientation_time3d(x0, t0, x2, t2, x3, t3, x4, t4, x5, t5);
+         *alpha2= orientation_time3d(x0, t0, x1, t1, x3, t3, x4, t4, x5, t5);
+         if(!same_sign(*alpha1, *alpha2)) return 0;
+         *alpha3=-orientation_time3d(x0, t0, x1, t1, x2, t3, x4, t4, x5, t5);
+         if(!same_sign(*alpha1, *alpha3)) return 0;
+         *alpha4= orientation_time3d(x0, t0, x1, t1, x2, t2, x3, t3, x5, t5);
+         if(!same_sign(*alpha1, *alpha4)) return 0;
+         *alpha5=-orientation_time3d(x0, t0, x1, t1, x2, t2, x3, t3, x4, t4);
+         if(!same_sign(*alpha1, *alpha5)) return 0;
+         sum2=*alpha1+*alpha2+*alpha3+*alpha4+*alpha5;
+         if(sum2)
+         {
+            *alpha0=1;
+            *alpha1/=sum2;
+            *alpha2/=sum2;
+            *alpha3/=sum2;
+            *alpha4/=sum2;
+            *alpha5/=sum2;
+            return 1;
+         }else
+         {
+            if(simplex_intersection_time3d(1, x0, t0, x2, t2, x3, t3, x4, t4,
+                               x5, t5, alpha0, alpha2, alpha3, alpha4, alpha5))
+                               {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(1, x0, t0, x1, t1, x3, t3, x4, t4,
+                               x5, t5, alpha0, alpha1, alpha3, alpha4, alpha5))
+                               {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(1, x0, t0, x1, t1, x2, t2, x4, t4,
+                               x5, t5, alpha0, alpha1, alpha2, alpha4, alpha5))
+                               {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(1, x0, t0, x1, t1, x2, t2, x3, t3,
+                               x5, t5, alpha0, alpha1, alpha2, alpha3, alpha5))
+                               {
+               *alpha4=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(1, x0, t0, x1, t1, x2, t2, x3, t3,
+                               x4, t4, alpha0, alpha1, alpha2, alpha3, alpha4))
+                               {
+               *alpha5=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 2: // segment vs. tetrahedron
+         *alpha0= orientation_time3d(x1, t1, x2, t2, x3, t3, x4, t4, x5, t5);
+         *alpha1=-orientation_time3d(x0, t0, x2, t2, x3, t3, x4, t4, x5, t5);
+         if(!same_sign(*alpha0, *alpha1)) return 0;
+         *alpha2= orientation_time3d(x0, t0, x1, t1, x3, t3, x4, t4, x5, t5);
+         *alpha3=-orientation_time3d(x0, t0, x1, t1, x2, t2, x4, t4, x5, t5);
+         if(!same_sign(*alpha2, *alpha3)) return 0;
+         *alpha4= orientation_time3d(x0, t0, x1, t1, x2, t2, x3, t3, x5, t5);
+         if(!same_sign(*alpha2, *alpha4)) return 0;
+         *alpha5=-orientation_time3d(x0, t0, x1, t1, x2, t2, x3, t3, x4, t4);
+         if(!same_sign(*alpha2, *alpha5)) return 0;
+         sum1=*alpha0+*alpha1;
+         sum2=*alpha2+*alpha3+*alpha4+*alpha5;
+         if(sum1 && sum2)
+         {
+            *alpha0/=sum1;
+            *alpha1/=sum1;
+            *alpha2/=sum2;
+            *alpha3/=sum2;
+            *alpha4/=sum2;
+            *alpha5/=sum2;
+            return 1;
+         }else
+         {
+            if(simplex_intersection_time3d(1, x1, t1, x2, t2, x3, t3, x4, t4,
+                               x5, t5, alpha1, alpha2, alpha3, alpha4, alpha5))
+                               {
+               *alpha0=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(1, x0, t0, x2, t2, x3, t3, x4, t4,
+                               x5, t5, alpha0, alpha2, alpha3, alpha4, alpha5))
+                               {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x0, t0, x1, t1, x3, t3, x4, t4,
+                               x5, t5, alpha0, alpha1, alpha3, alpha4, alpha5))
+                               {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x0, t0, x1, t1, x2, t2, x4, t4,
+                               x5, t5, alpha0, alpha1, alpha2, alpha4, alpha5))
+                               {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x0, t0, x1, t1, x2, t2, x3, t3,
+                               x5, t5, alpha0, alpha1, alpha2, alpha3, alpha5))
+                               {
+               *alpha4=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x0, t0, x1, t1, x2, t2, x3, t3,
+                               x4, t4, alpha0, alpha1, alpha2, alpha3, alpha4))
+                               {
+               *alpha5=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 3: // triangle vs. triangle
+         *alpha0= orientation_time3d(x1, t1, x2, t2, x3, t3, x4, t4, x5, t5);
+         *alpha1=-orientation_time3d(x0, t0, x2, t2, x3, t3, x4, t4, x5, t5);
+         if(!same_sign(*alpha0, *alpha1)) return 0;
+         *alpha2= orientation_time3d(x0, t0, x1, t1, x3, t3, x4, t4, x5, t5);
+         if(!same_sign(*alpha0, *alpha2)) return 0;
+         *alpha3=-orientation_time3d(x0, t0, x1, t1, x2, t2, x4, t4, x5, t5);
+         *alpha4= orientation_time3d(x0, t0, x1, t1, x2, t2, x3, t3, x5, t5);
+         if(!same_sign(*alpha3, *alpha4)) return 0;
+         *alpha5=-orientation_time3d(x0, t0, x1, t1, x2, t2, x3, t3, x4, t4);
+         if(!same_sign(*alpha3, *alpha5)) return 0;
+         sum1=*alpha0+*alpha1+*alpha2;
+         sum2=*alpha3+*alpha4+*alpha5;
+         if(sum1 && sum2)
+         {
+            *alpha0/=sum1;
+            *alpha1/=sum1;
+            *alpha2/=sum1;
+            *alpha3/=sum2;
+            *alpha4/=sum2;
+            *alpha5/=sum2;
+            return 1;
+         }else
+         {
+            if(simplex_intersection_time3d(2, x1, t1, x2, t2, x3, t3, x4, t4,
+                               x5, t5, alpha1, alpha2, alpha3, alpha4, alpha5))
+                               {
+               *alpha0=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x0, t0, x2, t2, x3, t3, x4, t4,
+                               x5, t5, alpha0, alpha2, alpha3, alpha4, alpha5))
+                               {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x0, t0, x1, t1, x3, t3, x4, t4,
+                               x5, t5, alpha0, alpha1, alpha3, alpha4, alpha5))
+                               {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x4, t4, x5, t5, x0, t0, x1, t1,
+                               x2, t2, alpha4, alpha5, alpha0, alpha1, alpha2))
+                               {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x3, t3, x5, t5, x0, t0, x1, t1,
+                               x2, t2, alpha3, alpha5, alpha0, alpha1, alpha2))
+                               {
+               *alpha4=0;
+               return 1;
+            }
+            if(simplex_intersection_time3d(2, x3, t3, x4, t4, x0, t0, x1, t1,
+                               x2, t2, alpha3, alpha4, alpha0, alpha1, alpha2))
+                               {
+               *alpha5=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 4: // tetrahedron vs. segment
+      case 5: // pentachoron vs. point
+         return simplex_intersection_time3d(6-k, x5, t5, x4, t4, x3, t3, x2, t2,
+                x1, t1, x0, t0, alpha5, alpha4, alpha3, alpha2, alpha1, alpha0);
+      default:
+         return -1; // should never get here
+   }
+}
+
+//==============================================================================
+// degenerate test in 4d - assumes five points lie on the same hyper-plane
+static int
+simplex_intersection4d(int k,
+                       const double* x0,
+                       const double* x1,
+                       const double* x2,
+                       const double* x3,
+                       const double* x4,
+                       double* alpha0,
+                       double* alpha1,
+                       double* alpha2,
+                       double* alpha3,
+                       double* alpha4)
+{
+   assert(k<=2);
+   // try projecting each coordinate out in turn
+   double ax0, ax1, ax2, ax3, ax4;
+   if(!simplex_intersection3d(k, x0+1, x1+1, x2+1, x3+1, x4+1,
+                                 &ax0, &ax1, &ax2, &ax3, &ax4)) return 0;
+   double ay0, ay1, ay2, ay3, ay4;
+   double p0[3]={x0[0], x0[2], x0[3]}, p1[3]={x1[0], x1[2], x1[3]},
+          p2[3]={x2[0], x2[2], x2[3]}, p3[3]={x3[0], x3[2], x3[3]},
+          p4[3]={x4[0], x4[2], x4[3]};
+   if(!simplex_intersection3d(k, p0, p1, p2, p3, p4,
+                                 &ay0, &ay1, &ay2, &ay3, &ay4)) return 0;
+   double az0, az1, az2, az3, az4;
+   double q0[3]={x0[0], x0[1], x0[3]}, q1[3]={x1[0], x1[1], x1[3]},
+          q2[3]={x2[0], x2[1], x2[3]}, q3[3]={x3[0], x3[1], x3[3]},
+          q4[3]={x4[0], x4[1], x4[3]};
+   if(!simplex_intersection3d(k, q0, q1, q2, q3, q4,
+                                 &az0, &az1, &az2, &az3, &az4)) return 0;
+   double at0, at1, at2, at3, at4;
+   if(!simplex_intersection3d(k, x0, x1, x2, x3, x4,
+                                 &at0, &at1, &at2, &at3, &at4)) return 0;
+   // decide which solution is more accurate for barycentric coordinates
+   double checkx, checky, checkz, checkt;
+   if(k==1)
+   {
+      checkx=std::fabs(-ax0*x0[0]+ax1*x1[0]+ax2*x2[0]+ax3*x3[0]+ax4*x4[0])
+            +std::fabs(-ax0*x0[1]+ax1*x1[1]+ax2*x2[1]+ax3*x3[1]+ax4*x4[1])
+            +std::fabs(-ax0*x0[2]+ax1*x1[2]+ax2*x2[2]+ax3*x3[2]+ax4*x4[2])
+            +std::fabs(-ax0*x0[3]+ax1*x1[3]+ax2*x2[3]+ax3*x3[3]+ax4*x4[3]);
+      checky=std::fabs(-ay0*x0[0]+ay1*x1[0]+ay2*x2[0]+ay3*x3[0]+ay4*x4[0])
+            +std::fabs(-ay0*x0[1]+ay1*x1[1]+ay2*x2[1]+ay3*x3[1]+ay4*x4[1])
+            +std::fabs(-ay0*x0[2]+ay1*x1[2]+ay2*x2[2]+ay3*x3[2]+ay4*x4[2])
+            +std::fabs(-ay0*x0[3]+ay1*x1[3]+ay2*x2[3]+ay3*x3[3]+ay4*x4[3]);
+      checkz=std::fabs(-az0*x0[0]+az1*x1[0]+az2*x2[0]+az3*x3[0]+az4*x4[0])
+            +std::fabs(-az0*x0[1]+az1*x1[1]+az2*x2[1]+az3*x3[1]+az4*x4[1])
+            +std::fabs(-az0*x0[2]+az1*x1[2]+az2*x2[2]+az3*x3[2]+az4*x4[2])
+            +std::fabs(-az0*x0[3]+az1*x1[3]+az2*x2[3]+az3*x3[3]+az4*x4[3]);
+      checkt=std::fabs(-at0*x0[0]+at1*x1[0]+at2*x2[0]+at3*x3[0]+at4*x4[0])
+            +std::fabs(-at0*x0[1]+at1*x1[1]+at2*x2[1]+at3*x3[1]+at4*x4[1])
+            +std::fabs(-at0*x0[2]+at1*x1[2]+at2*x2[2]+at3*x3[2]+at4*x4[2])
+            +std::fabs(-at0*x0[3]+at1*x1[3]+at2*x2[3]+at3*x3[3]+at4*x4[3]);
+   }else
+   {
+      checkx=std::fabs(-ax0*x0[0]-ax1*x1[0]+ax2*x2[0]+ax3*x3[0]+ax4*x4[0])
+            +std::fabs(-ax0*x0[1]-ax1*x1[1]+ax2*x2[1]+ax3*x3[1]+ax4*x4[1])
+            +std::fabs(-ax0*x0[2]-ax1*x1[2]+ax2*x2[2]+ax3*x3[2]+ax4*x4[2])
+            +std::fabs(-ax0*x0[3]-ax1*x1[3]+ax2*x2[3]+ax3*x3[3]+ax4*x4[3]);
+      checky=std::fabs(-ay0*x0[0]-ay1*x1[0]+ay2*x2[0]+ay3*x3[0]+ay4*x4[0])
+            +std::fabs(-ay0*x0[1]-ay1*x1[1]+ay2*x2[1]+ay3*x3[1]+ay4*x4[1])
+            +std::fabs(-ay0*x0[2]-ay1*x1[2]+ay2*x2[2]+ay3*x3[2]+ay4*x4[2])
+            +std::fabs(-ay0*x0[3]-ay1*x1[3]+ay2*x2[3]+ay3*x3[3]+ay4*x4[3]);
+      checkz=std::fabs(-az0*x0[0]-az1*x1[0]+az2*x2[0]+az3*x3[0]+az4*x4[0])
+            +std::fabs(-az0*x0[1]-az1*x1[1]+az2*x2[1]+az3*x3[1]+az4*x4[1])
+            +std::fabs(-az0*x0[2]-az1*x1[2]+az2*x2[2]+az3*x3[2]+az4*x4[2])
+            +std::fabs(-az0*x0[3]-az1*x1[3]+az2*x2[3]+az3*x3[3]+az4*x4[3]);
+      checkt=std::fabs(-at0*x0[0]-at1*x1[0]+at2*x2[0]+at3*x3[0]+at4*x4[0])
+            +std::fabs(-at0*x0[1]-at1*x1[1]+at2*x2[1]+at3*x3[1]+at4*x4[1])
+            +std::fabs(-at0*x0[2]-at1*x1[2]+at2*x2[2]+at3*x3[2]+at4*x4[2])
+            +std::fabs(-at0*x0[3]-at1*x1[3]+at2*x2[3]+at3*x3[3]+at4*x4[3]);
+   }
+   if(checkx<=checky && checkx<=checkz && checkx<=checkt)
+   {
+      *alpha0=ax0;
+      *alpha1=ax1;
+      *alpha2=ax2;
+      *alpha3=ax3;
+   }else if(checky<=checkz && checky<=checkt)
+   {
+      *alpha0=ay0;
+      *alpha1=ay1;
+      *alpha2=ay2;
+      *alpha3=ay3;
+   }else if(checkz<=checkt)
+   {
+      *alpha0=az0;
+      *alpha1=az1;
+      *alpha2=az2;
+      *alpha3=az3;
+   }else
+   {
+      *alpha0=at0;
+      *alpha1=at1;
+      *alpha2=at2;
+      *alpha3=at3;
+   }
+   return 1;
+}
+
+//==============================================================================
+int
+simplex_intersection4d(int k,
+                       const double* x0,
+                       const double* x1,
+                       const double* x2,
+                       const double* x3,
+                       const double* x4,
+                       const double* x5,
+                       double* alpha0,
+                       double* alpha1,
+                       double* alpha2,
+                       double* alpha3,
+                       double* alpha4,
+                       double* alpha5)
+{
+   assert(1<=k && k<=5);
+   double sum1, sum2;
+   switch(k)
+   {
+      case 1: // point vs. pentachoron
+         *alpha1=-orientation4d(x0, x2, x3, x4, x5);
+         *alpha2= orientation4d(x0, x1, x3, x4, x5);
+         if(!same_sign(*alpha1, *alpha2)) return 0;
+         *alpha3=-orientation4d(x0, x1, x2, x4, x5);
+         if(!same_sign(*alpha1, *alpha3)) return 0;
+         *alpha4= orientation4d(x0, x1, x2, x3, x5);
+         if(!same_sign(*alpha1, *alpha4)) return 0;
+         *alpha5=-orientation4d(x0, x1, x2, x3, x4);
+         if(!same_sign(*alpha1, *alpha5)) return 0;
+         sum2=*alpha1+*alpha2+*alpha3+*alpha4+*alpha5;
+         if(sum2)
+         {
+            *alpha0=1;
+            *alpha1/=sum2;
+            *alpha2/=sum2;
+            *alpha3/=sum2;
+            *alpha4/=sum2;
+            *alpha5/=sum2;
+            return 1;
+         }else
+         {
+            if(simplex_intersection4d(1, x0, x2, x3, x4, x5,
+                                       alpha0, alpha2, alpha3, alpha4, alpha5))
+                                       {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection4d(1, x0, x1, x3, x4, x5,
+                                       alpha0, alpha1, alpha3, alpha4, alpha5))
+                                       {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection4d(1, x0, x1, x2, x4, x5,
+                                       alpha0, alpha1, alpha2, alpha4, alpha5))
+                                       {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection4d(1, x0, x1, x2, x3, x5,
+                                       alpha0, alpha1, alpha2, alpha3, alpha5))
+                                       {
+               *alpha4=0;
+               return 1;
+            }
+            if(simplex_intersection4d(1, x0, x1, x2, x3, x4,
+                                       alpha0, alpha1, alpha2, alpha3, alpha4))
+                                       {
+               *alpha5=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 2: // segment vs. tetrahedron
+         *alpha0= orientation4d(x1, x2, x3, x4, x5);
+         *alpha1=-orientation4d(x0, x2, x3, x4, x5);
+         if(!same_sign(*alpha0, *alpha1)) return 0;
+         *alpha2= orientation4d(x0, x1, x3, x4, x5);
+         *alpha3=-orientation4d(x0, x1, x2, x4, x5);
+         if(!same_sign(*alpha2, *alpha3)) return 0;
+         *alpha4= orientation4d(x0, x1, x2, x3, x5);
+         if(!same_sign(*alpha2, *alpha4)) return 0;
+         *alpha5=-orientation4d(x0, x1, x2, x3, x4);
+         if(!same_sign(*alpha2, *alpha5)) return 0;
+         sum1=*alpha0+*alpha1;
+         sum2=*alpha2+*alpha3+*alpha4+*alpha5;
+         if(sum1 && sum2)
+         {
+            *alpha0/=sum1;
+            *alpha1/=sum1;
+            *alpha2/=sum2;
+            *alpha3/=sum2;
+            *alpha4/=sum2;
+            *alpha5/=sum2;
+            return 1;
+         }else
+         {
+            if(simplex_intersection4d(1, x1, x2, x3, x4, x5,
+                                       alpha1, alpha2, alpha3, alpha4, alpha5))
+                                       {
+               *alpha0=0;
+               return 1;
+            }
+            if(simplex_intersection4d(1, x0, x2, x3, x4, x5,
+                                       alpha0, alpha2, alpha3, alpha4, alpha5))
+                                       {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x0, x1, x3, x4, x5,
+                                       alpha0, alpha1, alpha3, alpha4, alpha5))
+                                       {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x0, x1, x2, x4, x5,
+                                       alpha0, alpha1, alpha2, alpha4, alpha5))
+                                       {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x0, x1, x2, x3, x5,
+                                       alpha0, alpha1, alpha2, alpha3, alpha5))
+                                       {
+               *alpha4=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x0, x1, x2, x3, x4,
+                                       alpha0, alpha1, alpha2, alpha3, alpha4))
+                                       {
+               *alpha5=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 3: // triangle vs. triangle
+         *alpha0= orientation4d(x1, x2, x3, x4, x5);
+         *alpha1=-orientation4d(x0, x2, x3, x4, x5);
+         if(!same_sign(*alpha0, *alpha1)) return 0;
+         *alpha2= orientation4d(x0, x1, x3, x4, x5);
+         if(!same_sign(*alpha0, *alpha2)) return 0;
+         *alpha3=-orientation4d(x0, x1, x2, x4, x5);
+         *alpha4= orientation4d(x0, x1, x2, x3, x5);
+         if(!same_sign(*alpha3, *alpha4)) return 0;
+         *alpha5=-orientation4d(x0, x1, x2, x3, x4);
+         if(!same_sign(*alpha3, *alpha5)) return 0;
+         sum1=*alpha0+*alpha1+*alpha2;
+         sum2=*alpha3+*alpha4+*alpha5;
+         if(sum1 && sum2)
+         {
+            *alpha0/=sum1;
+            *alpha1/=sum1;
+            *alpha2/=sum1;
+            *alpha3/=sum2;
+            *alpha4/=sum2;
+            *alpha5/=sum2;
+            return 1;
+         }else
+         {
+            if(simplex_intersection4d(2, x1, x2, x3, x4, x5,
+                                       alpha1, alpha2, alpha3, alpha4, alpha5))
+                                       {
+               *alpha0=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x0, x2, x3, x4, x5,
+                                       alpha0, alpha2, alpha3, alpha4, alpha5))
+                                       {
+               *alpha1=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x0, x1, x3, x4, x5,
+                                       alpha0, alpha1, alpha3, alpha4, alpha5))
+                                       {
+               *alpha2=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x4, x5, x0, x1, x2,
+                                       alpha4, alpha5, alpha0, alpha1, alpha2))
+                                       {
+               *alpha3=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x3, x5, x0, x1, x2,
+                                       alpha3, alpha5, alpha0, alpha1, alpha2))
+                                       {
+               *alpha4=0;
+               return 1;
+            }
+            if(simplex_intersection4d(2, x3, x4, x0, x1, x2,
+                                       alpha3, alpha4, alpha0, alpha1, alpha2))
+                                       {
+               *alpha5=0;
+               return 1;
+            }
+            return 0;
+         }
+
+      case 4: // tetrahedron vs. segment
+      case 5: // pentachoron vs. point
+         return simplex_intersection4d(6-k, x5, x4, x3, x2, x1, x0,
+                                alpha5, alpha4, alpha3, alpha2, alpha1, alpha0);
+      default:
+         return -1; // should never get here
+   }
+}
+*/
diff --git a/libraries/mesh/labelOuterTets.cpp b/libraries/mesh/labelOuterTets.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a961994e6ef440b956c4e151fb921d972030fb3
--- /dev/null
+++ b/libraries/mesh/labelOuterTets.cpp
@@ -0,0 +1,142 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "labelOuterTets.h"
+#include <cassert>
+using namespace std;
+
+vector<bool> labelOuterTets(const TetMeshRef & tetMesh, const TetNeighbor & tetNeighbor,
+    function<bool(int tetID)> isTetBoundary, function<bool(int tetID)> istetOuter)
+{
+  enum class TetLabel : char
+  {
+    IN,
+    OUT,
+    UNKNOWN
+  };
+
+  auto tetBoundaries = tetNeighbor.findTetBoundaries(tetMesh.numTets(), tetMesh.tets());
+  vector<int> airTetIDs;
+
+  vector<TetLabel> tetLabel(tetMesh.numTets(), TetLabel::UNKNOWN);
+
+  //  airProfiler.startTimer("initialAirTetLabel");
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    if (isTetBoundary(tetID))
+    {
+      tetLabel[tetID] = TetLabel::IN;
+    }
+  }
+  for(auto p : tetBoundaries)
+  {
+    int tetID = p.first;
+    assert(tetID >= 0 && tetID < tetMesh.numTets());
+    if (tetLabel[tetID] == TetLabel::UNKNOWN)
+    {
+      airTetIDs.push_back(tetID);
+      tetLabel[tetID] = TetLabel::OUT;
+    }
+  }
+  //  airProfiler.stopLastTimer();
+
+//  cout << "Try finding air tets..." << endl;
+  size_t candidateBegin = 0, candidateEnd = airTetIDs.size();
+
+  auto floodFillAir = [&]()
+  {
+    while(candidateBegin != candidateEnd)
+    {
+      for(size_t i = candidateBegin; i < candidateEnd; i++)
+      {
+        int tetID = airTetIDs[i];
+        for(int nbr : tetNeighbor.getTetNeighbors(tetID))
+        {
+          if (nbr < 0) continue;
+          assert(nbr < tetMesh.numTets());
+          if (tetLabel[nbr] != TetLabel::UNKNOWN) continue;
+          tetLabel[nbr] = TetLabel::OUT;
+          airTetIDs.push_back(nbr);
+        }
+      }
+      candidateBegin = candidateEnd;
+      candidateEnd = airTetIDs.size();
+    }
+  };
+
+  auto floodFillInterior = [&](int seedTetID)
+  {
+    vector<int> buffer = { seedTetID }, nextBuffer;
+    tetLabel[seedTetID] = TetLabel::IN;
+    while(buffer.size() > 0)
+    {
+      for(int tetID : buffer)
+      {
+        for(int nbr : tetNeighbor.getTetNeighbors(tetID))
+        {
+          if (nbr < 0) continue;
+          assert(nbr < tetMesh.numTets());
+          if (tetLabel[nbr] != TetLabel::UNKNOWN) continue;
+          tetLabel[nbr] = TetLabel::IN;
+          nextBuffer.push_back(nbr);
+        }
+      }
+      buffer.swap(nextBuffer);
+      nextBuffer.clear();
+    }
+  };
+  //  airProfiler.startTimer("initialFloodFill");
+  floodFillAir();
+
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    if (tetLabel[tetID] != TetLabel::UNKNOWN) continue;
+    if (istetOuter(tetID)) // outside
+    {
+      candidateBegin = airTetIDs.size();
+      airTetIDs.push_back(tetID);
+      tetLabel[tetID] = TetLabel::OUT;
+      candidateEnd = airTetIDs.size();
+      floodFillAir();
+    }
+    else // inside
+    {
+      floodFillInterior(tetID);
+    }
+  }
+
+  vector<bool> ret(tetMesh.numTets());
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+    ret[tetID] = (tetLabel[tetID] == TetLabel::OUT);
+
+  return ret;
+}
diff --git a/libraries/mesh/labelOuterTets.h b/libraries/mesh/labelOuterTets.h
new file mode 100644
index 0000000000000000000000000000000000000000..ec800864421a9573d84b3edb942bcc54ddad4f8a
--- /dev/null
+++ b/libraries/mesh/labelOuterTets.h
@@ -0,0 +1,47 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef LABELOUTERTETS_H
+#define LABELOUTERTETS_H
+
+#include "tetMeshGeo.h"
+#include <functional>
+#include <vector>
+
+// use flood-fill to remove tets which considered outside when embedding a closed surface.
+// isTetBoundary returns true if tetID embeds the surface
+// isTetOuter returns true if tetID is outside the surface. One way to detect this fast is to use winding number.
+// return a vector of size numTets, each bool represents whether tetID is outer
+std::vector<bool> labelOuterTets(const TetMeshRef & tetMesh, const TetNeighbor & tetNeighbor,
+    std::function<bool(int tetID)> isTetBoundary, std::function<bool(int tetID)> isTetOuter);
+
+#endif
diff --git a/libraries/mesh/meshIntersection.cpp b/libraries/mesh/meshIntersection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c3fdcb8342a8673700c8a51764388bdb84dbbe0
--- /dev/null
+++ b/libraries/mesh/meshIntersection.cpp
@@ -0,0 +1,78 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "meshIntersection.h"
+#include "basicAlgorithms.h"
+#include "exactOctree.h"
+#include "predicates.h"
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+using namespace std;
+
+vector<vector<int>> computeTrianglesIntersectingEachTetExact(const TetMeshRef tetMesh, const TriMeshRef triMesh)
+{
+  ExactTriMeshOctree octree;
+  octree.build(triMesh, 5, 10);
+
+  vector<vector<int>> tetEmbedTri(tetMesh.numTets());
+
+#ifdef USE_TBB
+  tbb::parallel_for(tbb::blocked_range<int>(0, tetMesh.numTets()), [&](const tbb::blocked_range<int> & rng)
+  {
+    for (int tetID = rng.begin(); tetID != rng.end(); ++tetID)
+    {
+#else
+    for(int tetID = 0; tetID < tetMesh.numTets(); ++tetID)
+    {
+#endif
+      array<Vec3d, 4> tet;
+      for(int j = 0; j < 4; j++) tet[j] = tetMesh.pos(tetID, j);
+      BoundingBox tetbb(tet);
+
+      auto toBB = [&](const BoundingBox & bb)
+      {
+        return (tetbb.intersect(bb));
+      };
+      auto toTri = [&](int tri)
+      {
+        return intersectTriTet(triMesh.pos(tri, 0), triMesh.pos(tri, 1), triMesh.pos(tri, 2),
+            tet[0], tet[1], tet[2], tet[3]);
+      };
+      octree.rangeQuery(toBB, toTri, tetEmbedTri[tetID]);
+      sortAndDeduplicate(tetEmbedTri[tetID]);
+    }
+#ifdef USE_TBB
+  }, tbb::auto_partitioner()); //end for locations
+#endif
+  return tetEmbedTri;
+}
diff --git a/libraries/mesh/meshIntersection.h b/libraries/mesh/meshIntersection.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c6d970827958c002bd7a3e3a7b04c728857976f
--- /dev/null
+++ b/libraries/mesh/meshIntersection.h
@@ -0,0 +1,42 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef MESHINTERSECTION_H_
+#define MESHINTERSECTION_H_
+
+#include "triMeshGeo.h"
+#include "tetMeshGeo.h"
+
+// return a vector of size #tets, each element is the sorted triangleIDs that intersect this tet
+std::vector<std::vector<int>> computeTrianglesIntersectingEachTetExact(const TetMeshRef tetMesh, const TriMeshRef triMesh);
+
+#endif
diff --git a/libraries/mesh/plane.h b/libraries/mesh/plane.h
new file mode 100644
index 0000000000000000000000000000000000000000..583dbe08489f111c5884a0d4f1ae6861f86b6ae0
--- /dev/null
+++ b/libraries/mesh/plane.h
@@ -0,0 +1,75 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef PLANE_H
+#define PLANE_H
+
+#include "vec3d.h"
+
+// a plane implementation for fast geometry queries
+// no normalization operation is used
+
+struct FastPlane
+{
+  FastPlane(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2);
+  // return 1 if outside according to the normal of the plane
+  //       -1 if inside
+  //        0 if on the plane
+  int outside(const Vec3d & v) const;
+
+  // return the scaled distance between v and the plane
+  // the distance is scaled by len(dir)
+  double scaledDistance(const Vec3d & v) const;
+
+  Vec3d dir;
+  double d;
+};
+
+inline FastPlane::FastPlane(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2)
+{
+  Vec3d e1 = v1 - v0, e2 = v2 - v0;
+  dir = cross(e1, e2);
+  d = dot(v0, dir);
+}
+
+inline int FastPlane::outside(const Vec3d & v) const
+{
+  auto s = (dot(v,dir) - d);
+  return (s > 0 ? 1 : (s < 0) ? -1 : 0);
+}
+
+inline double FastPlane::scaledDistance(const Vec3d & v) const
+{
+  return fabs(dot(v,dir) - d);
+}
+
+#endif
diff --git a/libraries/mesh/predicates.cpp b/libraries/mesh/predicates.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ce60f397dc9f9e7bba6e37722f5359ec17f4b910
--- /dev/null
+++ b/libraries/mesh/predicates.cpp
@@ -0,0 +1,512 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "predicates.h"
+#include <cassert>
+#include <functional>
+#include <cstring>
+#include <algorithm>
+using namespace std;
+
+// use predicates from Shewchuk's predicates
+extern "C" void exactinit();
+extern "C" double orient2d(double* pa, double *pb, double *pc);
+extern "C" double orient3d(double* pa, double *pb, double *pc, double *pd);
+extern "C" double incircle(double* pa, double *pb, double *pc, double *pd);
+extern "C" double insphere(double* pa, double *pb, double *pc, double *pd, double *pe);
+
+extern "C" double orient2dfast(double* pa, double *pb, double *pc);
+extern "C" double orient3dfast(double* pa, double *pb, double *pc, double *pd);
+extern "C" double incirclefast(double* pa, double *pb, double *pc, double *pd);
+extern "C" double inspherefast(double* pa, double *pb, double *pc, double *pd, double *pe);
+
+
+// The following routines are based on Robert Bridson's Tunicate library
+
+namespace
+{
+bool same_sign(double a, double b)
+{
+   return (a<=0 && b<=0) || (a>=0 && b>=0);
+}
+}
+
+int simplex_intersection2d(int k, const double* x0, const double* x1, const double* x2, const double* x3,
+    double* alpha0, double* alpha1, double* alpha2, double* alpha3);
+
+int simplex_intersection3d(int k, const double* x0, const double* x1, const double* x2, const double* x3, const double* x4, double* alpha0,
+    double* alpha1, double* alpha2, double* alpha3, double* alpha4);
+
+// degenerate test in 3d - assumes four points lie on the same plane
+int simplex_intersection3d(int k, const double* x0, const double* x1, const double* x2, const double* x3,
+    double* alpha0, double* alpha1, double* alpha2, double* alpha3);
+
+// -------------------------------------------------------------
+
+void initPredicates()
+{
+  exactinit();
+}
+
+double orient2d(const double pa[2], const double pb[2], const double pc[2])
+{
+  return orient2d((double*)pa, (double*)pb, (double*)pc);
+}
+
+double orient3d(const double pa[3], const double pb[3], const double pc[3], const double pd[3])
+{
+  return orient3d((double*)pa, (double*)pb, (double*)pc, (double*)pd);
+}
+
+double incircle(const double pa[2], const double pb[2], const double pc[2], const double pd[2])
+{
+  return incircle((double*)pa, (double*)pb, (double*)pc, (double*)pd);
+}
+
+double insphere(const double pa[3], const double pb[3], const double pc[3], const double pd[3], const double pe[3])
+{
+  return insphere((double*)pa, (double*)pb, (double*)pc, (double*)pd, (double*)pe);
+}
+
+double orient2dfast(const double pa[2], const double pb[2], const double pc[2])
+{
+  return orient2dfast((double*)pa, (double*)pb, (double*)pc);
+}
+
+double orient3dfast(const double pa[3], const double pb[3], const double pc[3], const double pd[3])
+{
+  return orient3dfast((double*)pa, (double*)pb, (double*)pc, (double*)pd);
+}
+
+double incirclefast(const double pa[2], const double pb[2], const double pc[2], const double pd[2])
+{
+  return incirclefast((double*)pa, (double*)pb, (double*)pc, (double*)pd);
+}
+
+double inspherefast(const double pa[3], const double pb[3], const double pc[3], const double pd[3], const double pe[3])
+{
+  return inspherefast((double*)pa, (double*)pb, (double*)pc, (double*)pd, (double*)pe);
+}
+
+// osega = orient3d(tria, trib, tric, sega)
+// osegb = orient3d(tria, trib, tric, segb)
+bool intersectSegTriWithOrientOnSeg(const double sega[3], const double segb[3], const double trip[3], const double triq[3], const double trir[3],
+    double osega, double osegb)
+{
+  if ((osega > 0 && osegb > 0) || (osega < 0 && osegb < 0)) 
+    return false;
+  double alpha2 = orient3d(sega, segb, trip, triq);
+  double alpha3 = orient3d(sega, segb, triq, trir);
+  if (!same_sign(alpha2, alpha3)) 
+    return false;
+  double alpha4 = orient3d(sega, segb, trir, trip);
+  if (!same_sign(alpha2, alpha4)) 
+    return false;
+  if (!same_sign(alpha3, alpha4)) 
+    return false;
+
+  double sum2 =alpha2+alpha3+alpha4;
+
+  if (osega != osegb && sum2 != 0.0) 
+    return true;
+
+  // degenerate: segment and triangle in same plane
+  double a[4];
+  if (simplex_intersection3d(1, segb, trip, triq, trir, a, a+1, a+2, a+3)) 
+    return true;
+  if (simplex_intersection3d(1, sega, trip, triq, trir, a, a+1, a+2, a+3)) 
+    return true;
+  if (simplex_intersection3d(2, sega, segb, triq, trir, a, a+1, a+2, a+3)) 
+    return true;
+  if (simplex_intersection3d(2, sega, segb, trip, trir, a, a+1, a+2, a+3)) 
+    return true;
+  if (simplex_intersection3d(2, sega, segb, trip, triq, a, a+1, a+2, a+3)) 
+    return true;
+  return false;
+}
+
+bool pointInTet(const double point[3], const double teta[3], const double tetb[3], const double tetc[3], const double tetd[3])
+{
+  double alpha[5];
+  return simplex_intersection3d(1, point, teta, tetb, tetc, tetd, alpha, alpha+1, alpha+2, alpha+3, alpha+4) != 0;
+}
+
+bool intersectSegTri(const double sega[3], const double segb[3], const double tria[3], const double trib[3], const double tric[3])
+{
+  double osega = orient3d(tria, trib, tric, sega);
+  double osegb = orient3d(tria, trib, tric, segb);
+  return intersectSegTriWithOrientOnSeg(sega, segb, tria, trib, tric, osega, osegb);
+}
+
+bool intersectSegTri(const double sega[3], const double segb[3], const double tria[3], const double trib[3], const double tric[3],
+    double segWeight[2], double triangleWeight[3])
+{
+  return simplex_intersection3d(2, sega, segb, tria, trib, tric, segWeight, segWeight+1, triangleWeight, triangleWeight+1, triangleWeight+2) != 0;
+}
+
+// check each edge against the other triangle
+bool intersectTriTri(const double pa[3], const double pb[3], const double pc[3], const double qx[3], const double qy[3], const double qz[3])
+{
+  double oqa = orient3d(pa, pb, pc, qx);
+  double oqb = orient3d(pa, pb, pc, qy);
+  double oqc = orient3d(pa, pb, pc, qz);
+
+  // if qa, qb, qc are all on the same side of triangle p, then disjoint
+  if ((oqa > 0 && oqb > 0 && oqc > 0) || (oqa < 0 && oqb < 0 && oqc < 0)) 
+    return false;
+
+  double opa = orient3d(qx, qy, qz, pa);
+  double opb = orient3d(qx, qy, qz, pb);
+  double opc = orient3d(qx, qy, qz, pc);
+
+  // if pa, pb, pc are all on the same side of triangle q, then disjoint
+  if ((opa > 0 && opb > 0 && opc > 0) || (opa < 0 && opb < 0 && opc < 0)) return false;
+
+  return intersectSegTriWithOrientOnSeg(qx, qy, pa, pb, pc, oqa, oqb) ||
+         intersectSegTriWithOrientOnSeg(qy, qz, pa, pb, pc, oqb, oqc) ||
+         intersectSegTriWithOrientOnSeg(qz, qx, pa, pb, pc, oqc, oqa) ||
+         intersectSegTriWithOrientOnSeg(pa, pb, qx, qy, qz, opa, opb) ||
+         intersectSegTriWithOrientOnSeg(pb, pc, qx, qy, qz, opb, opc) ||
+         intersectSegTriWithOrientOnSeg(pc, pa, qx, qy, qz, opc, opa);
+
+  ////////
+//  double otherOrient[9] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
+//  bool otherOrientComputed[9];
+//  memset(otherOrientComputed, 0, sizeof(otherOrientComputed));
+//
+//  auto intersectSegTriWithOrient = [&](const double sega[3], const double segb[3], const double trip[3], const double triq[3], const double trir[3],
+//      double osega, double osegb, int oabpqID, int oabqrID, int oabrpID)->bool {
+//    if ((osega > 0 && osegb > 0) || (osega < 0 && osegb < 0)) return false;
+//
+//    auto computeOrient = [&otherOrientComputed, &otherOrient](const double a[3], const double b[3], const double c[3], const double d[3], int ID) -> double {
+//      double alpha = 0.0;
+//      if (otherOrientComputed[ID] == false) {
+//        alpha = orient3d(a, b, c, d);
+//        otherOrient[ID] = alpha;
+//        otherOrientComputed[ID] = true;
+//      }
+//      else { alpha = otherOrient[ID]; }
+//      return alpha;
+//    };
+//    double alpha2 = computeOrient(sega, segb, trip, triq, oabpqID);
+//    double alpha3 = computeOrient(sega, segb, triq, trir, oabqrID);
+//
+//    if(!same_sign(alpha2, alpha3)) return 0;
+//
+//    double alpha4 = computeOrient(sega, segb, trir, trip, oabrpID);
+//
+//    if(!same_sign(alpha2, alpha4)) return 0;
+//    if(!same_sign(alpha3, alpha4)) return 0;
+//
+//    double sum2=alpha2+alpha3+alpha4;
+//
+//    if(osega != osegb && sum2) { return true; }
+//
+//    // degenerate: segment and triangle in same plane
+//    double a[4];
+//    if(simplex_intersection3d(1, segb, trip, triq, trir,
+//        a, a+1, a+2, a+3)) { return true; }
+//    if(simplex_intersection3d(1, sega, trip, triq, trir,
+//        a, a+1, a+2, a+3)) { return true; }
+//    if(simplex_intersection3d(2, sega, segb, triq, trir,
+//        a, a+1, a+2, a+3)) { return true; }
+//    if(simplex_intersection3d(2, sega, segb, trip, trir,
+//        a, a+1, a+2, a+3)){ return true; }
+//    if(simplex_intersection3d(2, sega, segb, trip, triq,
+//        a, a+1, a+2, a+3)){ return true; }
+//    return false;
+//  };
+//
+//  // order of otherOrient:
+////           0     1     2     3     4     5     6     7     8
+////  double xyab, xybc, xyca, yzab, yzbc, yzca, zxab, zxbc, zxca;
+//  //         0     3     6     1     4     7     2     5     8
+//  //       abxy, abyz, abzx, bcxy, bcyz, bczx, caxy, cayz, cazx
+//
+//  return intersectSegTriWithOrient(qx, qy, pa, pb, pc, oqa, oqb, 0, 1, 2) ||
+//         intersectSegTriWithOrient(qy, qz, pa, pb, pc, oqb, oqc, 3, 4, 5) ||
+//         intersectSegTriWithOrient(qz, qx, pa, pb, pc, oqc, oqa, 6 ,7, 8) ||
+//         intersectSegTriWithOrient(pa, pb, qx, qy, qz, opa, opb, 0, 3, 6) ||
+//         intersectSegTriWithOrient(pb, pc, qx, qy, qz, opb, opc, 1, 4, 7) ||
+//         intersectSegTriWithOrient(pc, pa, qx, qy, qz, opc, opa, 2, 5, 8);
+}
+
+bool intersectTriTet(const double tria[3], const double trib[3], const double tric[3],
+    const double teta[3], const double tetb[3], const double tetc[3], const double tetd[3])
+{
+  return pointInTet(tria, teta, tetb, tetc, tetd) ||
+         // triangle edges against tet faces
+         intersectSegTri(tria, trib, teta, tetb, tetc) || intersectSegTri(tria, trib, teta, tetb, tetd) ||
+         intersectSegTri(tria, trib, teta, tetc, tetd) || intersectSegTri(tria, trib, tetb, tetc, tetd) ||
+         intersectSegTri(tria, tric, teta, tetb, tetc) || intersectSegTri(tria, tric, teta, tetb, tetd) ||
+         intersectSegTri(tria, tric, teta, tetc, tetd) || intersectSegTri(tria, tric, tetb, tetc, tetd) ||
+         intersectSegTri(trib, tric, teta, tetb, tetc) || intersectSegTri(trib, tric, teta, tetb, tetd) ||
+         intersectSegTri(trib, tric, teta, tetc, tetd) || intersectSegTri(trib, tric, tetb, tetc, tetd) ||
+         // tet edges against triangle
+         intersectSegTri(teta, tetb, tria, trib, tric) || intersectSegTri(teta, tetc, tria, trib, tric) ||
+         intersectSegTri(teta, tetd, tria, trib, tric) || intersectSegTri(tetb, tetc, tria, trib, tric) ||
+         intersectSegTri(tetb, tetd, tria, trib, tric) || intersectSegTri(tetc, tetd, tria, trib, tric);
+}
+
+// d: [0,3), direction (X,Y,Z) which this face is perpendicular to
+bool intersectSegAABBFace(int d, const double sa[3], const double sb[3],
+    const double ra[3], const double rb[3], const double rc[3], const double rd[3])
+{
+  double x = ra[d];
+  if (sa[d] < x && sb[d] < x) return 0;
+  if (sa[d] > x && sb[d] > x) return 0;
+  double a2 = orient3d(sa, sb, rc, rd);
+  double a3 = orient3d(sa, sb, rd, ra);
+  if(!same_sign(a2, a3)) return 0;
+  double a4 = orient3d(sa, sb, ra, rb);
+  if(!same_sign(a2, a4)) return 0;
+  if(!same_sign(a3, a4)) return 0;
+  double a5 = orient3d(sa, sb, rb, rc);
+  if(!same_sign(a2, a5)) return 0;
+  if(!same_sign(a3, a5)) return 0;
+  if(!same_sign(a4, a5)) return 0;
+
+  double sum1 = a2 + a3 + a4 + a5;
+  if ((sa[d] != x || sb[d] != x) && sum1 != 0) // non-degenerate
+    return 1;
+  // process degeneracy
+
+  if(simplex_intersection3d(1, sa, ra, rb, rc, &a2, &a3, &a4, &a5)) // whether sa inside triangle ra,rb,rc
+  {
+     return 1;
+  }
+  if(simplex_intersection3d(1, sa, ra, rc, rd, &a2, &a3, &a4, &a5)) // whether sa inside triangle ra,rc,rd
+  {
+     return 1;
+  }
+  if(simplex_intersection3d(1, sb, ra, rb, rc, &a2, &a3, &a4, &a5)) // whether sb inside triangle ra,rb,rc
+  {
+     return 1;
+  }
+  if(simplex_intersection3d(1, sb, ra, rc, rd, &a2, &a3, &a4, &a5)) // whether sb inside triangle ra,rc,rd
+  {
+     return 1;
+  }
+  if(simplex_intersection3d(2, sa, sb, ra, rb, &a2, &a3, &a4, &a5)) // whether segment sa, sb intersect segment ra, rb
+  {
+     return 1;
+  }
+  if(simplex_intersection3d(2, sa, sb, rb, rc, &a2, &a3, &a4, &a5)) // whether segment sa, sb intersect segment rb, rc
+  {
+     return 1;
+  }
+  if(simplex_intersection3d(2, sa, sb, rc, rd, &a2, &a3, &a4, &a5)) // whether segment sa, sb intersect segment rc, rd
+  {
+     return 1;
+  }
+  if(simplex_intersection3d(2, sa, sb, rd, ra, &a2, &a3, &a4, &a5)) // whether segment sa, sb intersect segment rd, ra
+  {
+     return 1;
+  }
+  return 0;
+}
+
+// d: [0,3), direction (X,Y,Z) which this face is perpendicular to
+bool intersectTriAABBFace(int d, const double ta[3], const double tb[3], const double tc[3], // t for triangle
+    const double ra[3], const double rb[3], const double rc[3], const double rd[3])          // r for rectangle
+{
+//  return intersectSegTri(pa, pb, qa, qb, qc) || intersectSegTri(pa, pc, qa, qb, qc) || intersectSegTri(pb, pc, qa, qb, qc) ||
+//      intersectSegTri(qa, qb, pa, pb, pc) || intersectSegTri(qa, qc, pa, pb, pc) || intersectSegTri(qb, qc, pa, pb, pc);
+  return intersectSegAABBFace(d, ta, tb, ra, rb, rc, rd) ||
+         intersectSegAABBFace(d, ta, tc, ra, rb, rc, rd) ||
+         intersectSegAABBFace(d, tb, tc, ra, rb, rc, rd) ||
+         intersectSegTri(ra, rb, ta, tb, tc) ||
+         intersectSegTri(rb, rc, ta, tb, tc) ||
+         intersectSegTri(rc, rd, ta, tb, tc) ||
+         intersectSegTri(rd, ra, ta, tb, tc);
+}
+
+// local variable const double p[8][3] in the following functions represent the AABB vertic4es
+// order of p is:
+//     3 - - - 2
+//    /|      /|
+//   7 - - - 6 |       y
+//   | |     | |       |
+//   | 0 - - | 1       |_ _ _x
+//   |/      |/       /
+//   4 - - - 5       z
+
+bool intersectTriAABB(const double ta[3], const double tb[3], const double tc[3], const double bmin[3], const double bmax[3])
+{
+  assert(bmin[0] <= bmax[0] && bmin[1] <= bmax[1] && bmin[2] <= bmax[2]);
+  if (bmin[0] <= ta[0] && ta[0] <= bmax[0] && bmin[1] <= ta[1] && ta[1] <= bmax[1] && bmin[2] <= ta[2] && ta[2] <= bmax[2]) return true;
+  if (bmin[0] <= tb[0] && tb[0] <= bmax[0] && bmin[1] <= tb[1] && tb[1] <= bmax[1] && bmin[2] <= tb[2] && tb[2] <= bmax[2]) return true;
+  if (bmin[0] <= tc[0] && tc[0] <= bmax[0] && bmin[1] <= tc[1] && tc[1] <= bmax[1] && bmin[2] <= tc[2] && tc[2] <= bmax[2]) return true;
+
+  double minx = min(min(ta[0], tb[0]), tc[0]);
+  double miny = min(min(ta[1], tb[1]), tc[1]);
+  double minz = min(min(ta[2], tb[2]), tc[2]);
+  double maxx = max(max(ta[0], tb[0]), tc[0]);
+  double maxy = max(max(ta[1], tb[1]), tc[1]);
+  double maxz = max(max(ta[2], tb[2]), tc[2]);
+
+  if ( ( bmax[0] < minx ) || ( maxx < bmin[0] ) || ( bmax[1] < miny ) || ( maxy < bmin[1] ) || ( bmax[2] < minz ) || ( maxz < bmin[2] ) )
+    return false; //no overlap
+
+  // create a box mesh for AABB
+  const double p[8][3] = { {bmin[0], bmin[1], bmin[2]}, { bmax[0], bmin[1], bmin[2] }, { bmax[0], bmax[1], bmin[2] }, { bmin[0], bmax[1], bmin[2] },
+                            { bmin[0], bmin[1], bmax[2] }, { bmax[0], bmin[1], bmax[2] }, {bmax[0], bmax[1], bmax[2]}, { bmin[0], bmax[1], bmax[2] } };
+
+  if (intersectSegAABBFace(0, ta, tb, p[0], p[4], p[7], p[3]) ||
+      intersectSegAABBFace(0, ta, tc, p[0], p[4], p[7], p[3]) ||
+      intersectSegAABBFace(0, tb, tc, p[0], p[4], p[7], p[3])) return true;
+  if (intersectSegAABBFace(0, ta, tb, p[1], p[2], p[6], p[5]) ||
+      intersectSegAABBFace(0, ta, tc, p[1], p[2], p[6], p[5]) ||
+      intersectSegAABBFace(0, tb, tc, p[1], p[2], p[6], p[5])) return true;
+
+  if (intersectSegAABBFace(1, ta, tb, p[0], p[1], p[5], p[4]) ||
+      intersectSegAABBFace(1, ta, tc, p[0], p[1], p[5], p[4]) ||
+      intersectSegAABBFace(1, tb, tc, p[0], p[1], p[5], p[4])) return true;
+  if (intersectSegAABBFace(1, ta, tb, p[2], p[3], p[7], p[6]) ||
+      intersectSegAABBFace(1, ta, tc, p[2], p[3], p[7], p[6]) ||
+      intersectSegAABBFace(1, tb, tc, p[2], p[3], p[7], p[6])) return true;
+
+  if (intersectSegAABBFace(2, ta, tb, p[0], p[3], p[2], p[1]) ||
+      intersectSegAABBFace(2, ta, tc, p[0], p[3], p[2], p[1]) ||
+      intersectSegAABBFace(2, tb, tc, p[0], p[3], p[2], p[1])) return true;
+  if (intersectSegAABBFace(2, ta, tb, p[4], p[5], p[6], p[7]) ||
+      intersectSegAABBFace(2, ta, tc, p[4], p[5], p[6], p[7]) ||
+      intersectSegAABBFace(2, tb, tc, p[4], p[5], p[6], p[7])) return true;
+
+  if (intersectSegTri(p[0], p[4], ta, tb, tc) || intersectSegTri(p[4], p[7], ta, tb, tc) ||
+      intersectSegTri(p[7], p[3], ta, tb, tc) || intersectSegTri(p[3], p[0], ta, tb, tc) ||
+      intersectSegTri(p[1], p[2], ta, tb, tc) || intersectSegTri(p[2], p[6], ta, tb, tc) ||
+      intersectSegTri(p[6], p[5], ta, tb, tc) || intersectSegTri(p[5], p[1], ta, tb, tc) ||
+      intersectSegTri(p[0], p[1], ta, tb, tc) || intersectSegTri(p[4], p[5], ta, tb, tc) ||
+      intersectSegTri(p[6], p[7], ta, tb, tc) || intersectSegTri(p[2], p[3], ta, tb, tc)) return true;
+//
+//  if (intersectTriAABBFace(0, ta, tb, tc, p[0], p[4], p[7], p[3])) return true; // check intersection with face 0-4-7-3
+//  if (intersectTriAABBFace(0, ta, tb, tc, p[1], p[2], p[6], p[5])) return true;
+//
+//  if (intersectTriAABBFace(1, ta, tb, tc, p[0], p[1], p[5], p[4])) return true;
+//  if (intersectTriAABBFace(1, ta, tb, tc, p[2], p[3], p[7], p[6])) return true;
+//
+//  if (intersectTriAABBFace(2, ta, tb, tc, p[0], p[3], p[2], p[1])) return true;
+//  if (intersectTriAABBFace(2, ta, tb, tc, p[4], p[5], p[6], p[7])) return true;
+  return false;
+}
+
+
+bool intersectSegAABB(const double sa[3], const double sb[3], const double bmin[3], const double bmax[3])
+{
+  assert(bmin[0] <= bmax[0] && bmin[1] <= bmax[1] && bmin[2] <= bmax[2]);
+  auto getOutcode = [](const double pos[3], const double bmin[3], const double bmax[3])
+  {
+    char code = 0;
+    code += (pos[0] < bmin[0]);
+    code += (pos[0] > bmax[0]) << 1;
+    code += (pos[1] < bmin[1]) << 2;
+    code += (pos[1] > bmax[1]) << 3;
+    code += (pos[2] < bmin[2]) << 4;
+    code += (pos[2] > bmax[2]) << 5;
+    return code;
+  };
+  char codea = getOutcode(sa, bmin, bmax), codeb = getOutcode(sb, bmin, bmax);
+  if (codea == 0 || codeb == 0) return true;
+  if ((codea & codeb) != 0) return false;
+
+  const double p[8][3] = { {bmin[0], bmin[1], bmin[2]}, { bmax[0], bmin[1], bmin[2] }, { bmax[0], bmax[1], bmin[2] }, { bmin[0], bmax[1], bmin[2] },
+      { bmin[0], bmin[1], bmax[2] }, { bmax[0], bmin[1], bmax[2] }, {bmax[0], bmax[1], bmax[2]}, { bmin[0], bmax[1], bmax[2] } };
+//  const int t[12][3] = { {0,3,2}, {0,2,1}, {4,5,6}, {4,6,7}, {0,1,5}, {0,5,4},
+//      {3,7,6}, {3,6,2}, {1,2,6}, {1,6,5}, {0,4,7}, {0,7,3} };
+//  for(int i = 0; i < 12; i++) {
+//    if (intersectSegTri(sa, sb, p[t[i][0]], p[t[i][1]], p[t[i][2]])) return true;
+//  }
+//  return false;
+
+  if (intersectSegAABBFace(0, sa, sb, p[0], p[4], p[7], p[3])) return true; // check intersection with face 0-4-7-3
+  if (intersectSegAABBFace(0, sa, sb, p[1], p[2], p[6], p[5])) return true;
+  if (intersectSegAABBFace(1, sa, sb, p[0], p[1], p[5], p[4])) return true;
+  if (intersectSegAABBFace(1, sa, sb, p[2], p[3], p[7], p[6])) return true;
+  if (intersectSegAABBFace(2, sa, sb, p[0], p[3], p[2], p[1])) return true;
+  if (intersectSegAABBFace(2, sa, sb, p[4], p[5], p[6], p[7])) return true;
+  return false;
+}
+
+namespace
+{
+
+void getXY(const double v[3], double o[2]) { o[0] = v[0]; o[1] = v[1]; }
+void getYZ(const double v[3], double o[2]) { o[0] = v[1]; o[1] = v[2]; }
+void getZX(const double v[3], double o[2]) { o[0] = v[2]; o[1] = v[0]; }
+std::function<void(const double v[3], double o[2])> getComp[3] = { getXY, getYZ, getZX };
+
+}
+
+bool isTriangleDegenerate(const double ta[3], const double tb[3], const double tc[3])
+{
+  for(int i = 0; i < 3; i++)
+  {
+    auto f = getComp[i];
+    double a[2], b[2], c[2];
+    f(ta, a);
+    f(tb, b);
+    f(tc, c);
+
+    if (orient2d(a,b,c) != 0.0) return false;
+  }
+  return true;
+}
+
+int inCircumsphereOnPlane(const double ta[3], const double tb[3], const double tc[3], const double td[3])
+{
+  for(int i = 0; i < 3; i++)
+  {
+    auto f = getComp[i];
+    double a[2], b[2], c[2], d[2];
+    f(ta, a);
+    f(tb, b);
+    f(tc, c);
+    f(td, d);
+    double ret = incircle(a, b, c, d);
+    if (ret < 0) return -1;
+    if (ret > 0) return 1;
+  }
+  return 0;
+}
+
+bool intersectSegSeg2d(const double sa[2], const double sb[2], const double ta[2], const double tb[2])
+{
+  double alpha[4];
+  return simplex_intersection2d(2, sa, sb, ta, tb, alpha, alpha+1, alpha+2, alpha+3);
+}
+
+bool intersectSegSeg2d(const double sa[2], const double sb[2], const double ta[2], const double tb[2], double sw[2], double tw[2])
+{
+  return simplex_intersection2d(2, sa, sb, ta, tb, sw, sw+1, tw, tw+1);
+}
+
diff --git a/libraries/mesh/predicates.h b/libraries/mesh/predicates.h
new file mode 100644
index 0000000000000000000000000000000000000000..7464ad601dd476e86b0c6d761dcf9f12fe667fbb
--- /dev/null
+++ b/libraries/mesh/predicates.h
@@ -0,0 +1,111 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef PREDICATES_H
+#define PREDICATES_H
+
+// Predicates for robust geometric queries.
+// Interface to Shewchuk's exact predicates code.
+
+#include "initPredicates.h"
+
+// !!!!!!!!!! IMPORTANT !!!!!!!!!!
+// You must first call "initPredicates" defined in initPredicates.h to initialize variables used for exact predicates.
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+// Return a positive value if the points pa, pb, and pc occur
+// in counterclockwise order; a negative value if they occur
+// in clockwise order; and zero if they are collinear.  The
+// result is also a rough approximation of twice the signed
+// area of the triangle defined by the three points.
+double orient2d(const double pa[2], const double pb[2], const double pc[2]);
+double orient2dfast(const double pa[2], const double pb[2], const double pc[2]);
+
+// Return a positive value if the point pd lies below the
+// plane passing through pa, pb, and pc; "below" is defined so
+// that pa, pb, and pc appear in counterclockwise order when
+// viewed from above the plane.  Returns a negative value if
+// pd lies above the plane.  Returns zero if the points are
+// coplanar.  The result is also a rough approximation of six
+// times the signed volume of the tetrahedron defined by the
+// four points.
+double orient3d(const double pa[3], const double pb[3], const double pc[3], const double pd[3]);
+double orient3dfast(const double pa[3], const double pb[3], const double pc[3], const double pd[3]);
+
+// Return a positive value if the point pd lies inside the
+// circle passing through pa, pb, and pc; a negative value if
+// it lies outside; and zero if the four points are cocircular.
+// The points pa, pb, and pc must be in counterclockwise
+// order, or the sign of the result will be reversed.
+double incircle(const double pa[2], const double pb[2], const double pc[2], const double pd[2]);
+double incirclefast(const double pa[2], const double pb[2], const double pc[2], const double pd[2]);
+
+// Return a positive value if the point pe lies inside the
+// sphere passing through pa, pb, pc, and pd; a negative value
+// if it lies outside; and zero if the five points are
+// cospherical.  The points pa, pb, pc, and pd must be ordered
+// so that they have a positive orientation (as defined by
+// orient3d()), or the sign of the result will be reversed.
+double insphere(const double pa[3], const double pb[3], const double pc[3], const double pd[3], const double pe[3]);
+double inspherefast(const double pa[3], const double pb[3], const double pc[3], const double pd[3], const double pe[3]);
+
+// assuming pa, pb, pc and pd are coplanar
+// return +1 if pd lies inside the circle passing through pa, pb, pc on the plane
+// return -1 if pd lies outside the circle passing through pa, pb, pc on the plane
+// return 0 if they are cocircular
+int inCircumsphereOnPlane(const double pa[3], const double pb[3], const double pc[3], const double pd[3]);
+
+bool pointInTet(const double point[3], const double teta[3], const double tetb[3], const double tetc[3], const double tetd[3]);
+
+bool intersectSegTri(const double sega[3], const double segb[3], const double tria[3], const double trib[3], const double tric[3]);
+bool intersectSegTri(const double sega[3], const double segb[3], const double tria[3], const double trib[3], const double tric[3],
+    double segWeight[2], double triangleWeight[3]);
+
+bool intersectTriTri(const double pa[3], const double pb[3], const double pc[3], const double qa[3], const double qb[3], const double qc[3]);
+
+bool intersectTriTet(const double tria[3], const double trib[3], const double tric[3],
+    const double teta[3], const double tetb[3], const double tetc[3], const double tetd[3]);
+
+// triangle completely inside AABB also considered as intersecting
+bool intersectTriAABB(const double tria[3], const double trib[3], const double tric[3], const double bmin[3], const double bmax[3]);
+
+// line segment completely inside AABB also considered as intersecting
+bool intersectSegAABB(const double sa[3], const double sb[3], const double bmin[3], const double bmax[3]);
+
+bool isTriangleDegenerate(const double ta[3], const double tb[3], const double tc[3]);
+
+bool intersectSegSeg2d(const double sa[2], const double sb[2], const double ta[2], const double tb[2]);
+// sw[2] and tw[2] are weight of the intersection
+bool intersectSegSeg2d(const double sa[2], const double sb[2], const double ta[2], const double tb[2], double sw[2], double tw[2]);
+
+#endif
+
diff --git a/libraries/mesh/predicates_Shewchuk.c b/libraries/mesh/predicates_Shewchuk.c
new file mode 100644
index 0000000000000000000000000000000000000000..bb4f8371585ff6bbfc2cbca6221a1070038e3a59
--- /dev/null
+++ b/libraries/mesh/predicates_Shewchuk.c
@@ -0,0 +1,4512 @@
+/*****************************************************************************/
+/*                                                                           */
+/*  Routines for Arbitrary Precision Floating-point Arithmetic               */
+/*  and Fast Robust Geometric Predicates                                     */
+/*  (predicates.c)                                                           */
+/*                                                                           */
+/*  May 18, 1996                                                             */
+/*                                                                           */
+/*  Placed in the public domain by                                           */
+/*  Jonathan Richard Shewchuk                                                */
+/*  School of Computer Science                                               */
+/*  Carnegie Mellon University                                               */
+/*  5000 Forbes Avenue                                                       */
+/*  Pittsburgh, Pennsylvania  15213-3891                                     */
+/*  jrs@cs.cmu.edu                                                           */
+/*                                                                           */
+/*  This file contains C implementation of algorithms for exact addition     */
+/*    and multiplication of floating-point numbers, and predicates for       */
+/*    robustly performing the orientation and incircle tests used in         */
+/*    computational geometry.  The algorithms and underlying theory are      */
+/*    described in Jonathan Richard Shewchuk.  "Adaptive Precision Floating- */
+/*    Point Arithmetic and Fast Robust Geometric Predicates."  Technical     */
+/*    Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon      */
+/*    University, Pittsburgh, Pennsylvania, May 1996.  (Submitted to         */
+/*    Discrete & Computational Geometry.)                                    */
+/*                                                                           */
+/*  This file, the paper listed above, and other information are available   */
+/*    from the Web page http://www.cs.cmu.edu/~quake/robust.html .           */
+/*                                                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Using this code:                                                         */
+/*                                                                           */
+/*  First, read the short or long version of the paper (from the Web page    */
+/*    above).                                                                */
+/*                                                                           */
+/*  Be sure to call exactinit() once, before calling any of the arithmetic   */
+/*    functions or geometric predicates.  Also be sure to turn on the        */
+/*    optimizer when compiling this file.                                    */
+/*                                                                           */
+/*                                                                           */
+/*  Several geometric predicates are defined.  Their parameters are all      */
+/*    points.  Each point is an array of two or three floating-point         */
+/*    numbers.  The geometric predicates, described in the papers, are       */
+/*                                                                           */
+/*    orient2d(pa, pb, pc)                                                   */
+/*    orient2dfast(pa, pb, pc)                                               */
+/*    orient3d(pa, pb, pc, pd)                                               */
+/*    orient3dfast(pa, pb, pc, pd)                                           */
+/*    incircle(pa, pb, pc, pd)                                               */
+/*    incirclefast(pa, pb, pc, pd)                                           */
+/*    insphere(pa, pb, pc, pd, pe)                                           */
+/*    inspherefast(pa, pb, pc, pd, pe)                                       */
+/*                                                                           */
+/*  Those with suffix "fast" are approximate, non-robust versions.  Those    */
+/*    without the suffix are adaptive precision, robust versions.  There     */
+/*    are also versions with the suffices "exact" and "slow", which are      */
+/*    non-adaptive, exact arithmetic versions, which I use only for timings  */
+/*    in my arithmetic papers.                                               */
+/*                                                                           */
+/*                                                                           */
+/*  An expansion is represented by an array of floating-point numbers,       */
+/*    sorted from smallest to largest magnitude (possibly with interspersed  */
+/*    zeros).  The length of each expansion is stored as a separate integer, */
+/*    and each arithmetic function returns an integer which is the length    */
+/*    of the expansion it created.                                           */
+/*                                                                           */
+/*  Several arithmetic functions are defined.  Their parameters are          */
+/*                                                                           */
+/*    e, f           Input expansions                                        */
+/*    elen, flen     Lengths of input expansions (must be >= 1)              */
+/*    h              Output expansion                                        */
+/*    b              Input scalar                                            */
+/*                                                                           */
+/*  The arithmetic functions are                                             */
+/*                                                                           */
+/*    grow_expansion(elen, e, b, h)                                          */
+/*    grow_expansion_zeroelim(elen, e, b, h)                                 */
+/*    expansion_sum(elen, e, flen, f, h)                                     */
+/*    expansion_sum_zeroelim1(elen, e, flen, f, h)                           */
+/*    expansion_sum_zeroelim2(elen, e, flen, f, h)                           */
+/*    fast_expansion_sum(elen, e, flen, f, h)                                */
+/*    fast_expansion_sum_zeroelim(elen, e, flen, f, h)                       */
+/*    linear_expansion_sum(elen, e, flen, f, h)                              */
+/*    linear_expansion_sum_zeroelim(elen, e, flen, f, h)                     */
+/*    scale_expansion(elen, e, b, h)                                         */
+/*    scale_expansion_zeroelim(elen, e, b, h)                                */
+/*    compress(elen, e, h)                                                   */
+/*                                                                           */
+/*  All of these are described in the long version of the paper; some are    */
+/*    described in the short version.  All return an integer that is the     */
+/*    length of h.  Those with suffix _zeroelim perform zero elimination,    */
+/*    and are recommended over their counterparts.  The procedure            */
+/*    fast_expansion_sum_zeroelim() (or linear_expansion_sum_zeroelim() on   */
+/*    processors that do not use the round-to-even tiebreaking rule) is      */
+/*    recommended over expansion_sum_zeroelim().  Each procedure has a       */
+/*    little note next to it (in the code below) that tells you whether or   */
+/*    not the output expansion may be the same array as one of the input     */
+/*    expansions.                                                            */
+/*                                                                           */
+/*                                                                           */
+/*  If you look around below, you'll also find macros for a bunch of         */
+/*    simple unrolled arithmetic operations, and procedures for printing     */
+/*    expansions (commented out because they don't work with all C           */
+/*    compilers) and for generating random floating-point numbers whose      */
+/*    significand bits are all random.  Most of the macros have undocumented */
+/*    requirements that certain of their parameters should not be the same   */
+/*    variable; for safety, better to make sure all the parameters are       */
+/*    distinct variables.  Feel free to send email to jrs@cs.cmu.edu if you  */
+/*    have questions.                                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+//#include <sys/time.h>
+
+/* On some machines, the exact arithmetic routines might be defeated by the  */
+/*   use of internal extended precision floating-point registers.  Sometimes */
+/*   this problem can be fixed by defining certain values to be volatile,    */
+/*   thus forcing them to be stored to memory and rounded off.  This isn't   */
+/*   a great solution, though, as it slows the arithmetic down.              */
+/*                                                                           */
+/* To try this out, write "#define INEXACT volatile" below.  Normally,       */
+/*   however, INEXACT should be defined to be nothing.  ("#define INEXACT".) */
+
+#define INEXACT                          /* Nothing */
+/* #define INEXACT volatile */
+
+#define REAL double                      /* float or double */
+#define REALPRINT doubleprint
+#define REALRAND doublerand
+#define NARROWRAND narrowdoublerand
+#define UNIFORMRAND uniformdoublerand
+
+/* Which of the following two methods of finding the absolute values is      */
+/*   fastest is compiler-dependent.  A few compilers can inline and optimize */
+/*   the fabs() call; but most will incur the overhead of a function call,   */
+/*   which is disastrously slow.  A faster way on IEEE machines might be to  */
+/*   mask the appropriate bit, but that's difficult to do in C.              */
+
+#define Absolute(a)  ((a) >= 0.0 ? (a) : -(a))
+/* #define Absolute(a)  fabs(a) */
+
+/* Many of the operations are broken up into two pieces, a main part that    */
+/*   performs an approximate operation, and a "tail" that computes the       */
+/*   roundoff error of that operation.                                       */
+/*                                                                           */
+/* The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(),    */
+/*   Split(), and Two_Product() are all implemented as described in the      */
+/*   reference.  Each of these macros requires certain variables to be       */
+/*   defined in the calling routine.  The variables `bvirt', `c', `abig',    */
+/*   `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because   */
+/*   they store the result of an operation that may incur roundoff error.    */
+/*   The input parameter `x' (or the highest numbered `x_' parameter) must   */
+/*   also be declared `INEXACT'.                                             */
+
+#define Fast_Two_Sum_Tail(a, b, x, y) \
+  bvirt = x - a; \
+  y = b - bvirt
+
+#define Fast_Two_Sum(a, b, x, y) \
+  x = (REAL) (a + b); \
+  Fast_Two_Sum_Tail(a, b, x, y)
+
+#define Fast_Two_Diff_Tail(a, b, x, y) \
+  bvirt = a - x; \
+  y = bvirt - b
+
+#define Fast_Two_Diff(a, b, x, y) \
+  x = (REAL) (a - b); \
+  Fast_Two_Diff_Tail(a, b, x, y)
+
+#define Two_Sum_Tail(a, b, x, y) \
+  bvirt = (REAL) (x - a); \
+  avirt = x - bvirt; \
+  bround = b - bvirt; \
+  around = a - avirt; \
+  y = around + bround
+
+#define Two_Sum(a, b, x, y) \
+  x = (REAL) (a + b); \
+  Two_Sum_Tail(a, b, x, y)
+
+#define Two_Diff_Tail(a, b, x, y) \
+  bvirt = (REAL) (a - x); \
+  avirt = x + bvirt; \
+  bround = bvirt - b; \
+  around = a - avirt; \
+  y = around + bround
+
+#define Two_Diff(a, b, x, y) \
+  x = (REAL) (a - b); \
+  Two_Diff_Tail(a, b, x, y)
+
+#define Split(a, ahi, alo) \
+  c = (REAL) (splitter * a); \
+  abig = (REAL) (c - a); \
+  ahi = c - abig; \
+  alo = a - ahi
+
+#define Two_Product_Tail(a, b, x, y) \
+  Split(a, ahi, alo); \
+  Split(b, bhi, blo); \
+  err1 = x - (ahi * bhi); \
+  err2 = err1 - (alo * bhi); \
+  err3 = err2 - (ahi * blo); \
+  y = (alo * blo) - err3
+
+#define Two_Product(a, b, x, y) \
+  x = (REAL) (a * b); \
+  Two_Product_Tail(a, b, x, y)
+
+/* Two_Product_Presplit() is Two_Product() where one of the inputs has       */
+/*   already been split.  Avoids redundant splitting.                        */
+
+#define Two_Product_Presplit(a, b, bhi, blo, x, y) \
+  x = (REAL) (a * b); \
+  Split(a, ahi, alo); \
+  err1 = x - (ahi * bhi); \
+  err2 = err1 - (alo * bhi); \
+  err3 = err2 - (ahi * blo); \
+  y = (alo * blo) - err3
+
+/* Two_Product_2Presplit() is Two_Product() where both of the inputs have    */
+/*   already been split.  Avoids redundant splitting.                        */
+
+#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \
+  x = (REAL) (a * b); \
+  err1 = x - (ahi * bhi); \
+  err2 = err1 - (alo * bhi); \
+  err3 = err2 - (ahi * blo); \
+  y = (alo * blo) - err3
+
+/* Square() can be done more quickly than Two_Product().                     */
+
+#define Square_Tail(a, x, y) \
+  Split(a, ahi, alo); \
+  err1 = x - (ahi * ahi); \
+  err3 = err1 - ((ahi + ahi) * alo); \
+  y = (alo * alo) - err3
+
+#define Square(a, x, y) \
+  x = (REAL) (a * a); \
+  Square_Tail(a, x, y)
+
+/* Macros for summing expansions of various fixed lengths.  These are all    */
+/*   unrolled versions of Expansion_Sum().                                   */
+
+#define Two_One_Sum(a1, a0, b, x2, x1, x0) \
+  Two_Sum(a0, b , _i, x0); \
+  Two_Sum(a1, _i, x2, x1)
+
+#define Two_One_Diff(a1, a0, b, x2, x1, x0) \
+  Two_Diff(a0, b , _i, x0); \
+  Two_Sum( a1, _i, x2, x1)
+
+#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \
+  Two_One_Sum(a1, a0, b0, _j, _0, x0); \
+  Two_One_Sum(_j, _0, b1, x3, x2, x1)
+
+#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \
+  Two_One_Diff(a1, a0, b0, _j, _0, x0); \
+  Two_One_Diff(_j, _0, b1, x3, x2, x1)
+
+#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \
+  Two_One_Sum(a1, a0, b , _j, x1, x0); \
+  Two_One_Sum(a3, a2, _j, x4, x3, x2)
+
+#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \
+  Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \
+  Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1)
+
+#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, \
+                      x1, x0) \
+  Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \
+  Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2)
+
+#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, \
+                      x3, x2, x1, x0) \
+  Four_One_Sum(a3, a2, a1, a0, b , _j, x3, x2, x1, x0); \
+  Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4)
+
+#define Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, \
+                      x6, x5, x4, x3, x2, x1, x0) \
+  Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, \
+                _1, _0, x0); \
+  Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, \
+                x3, x2, x1)
+
+#define Eight_Four_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b4, b3, b1, b0, x11, \
+                       x10, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \
+  Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, \
+                _2, _1, _0, x1, x0); \
+  Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, \
+                x7, x6, x5, x4, x3, x2)
+
+/* Macros for multiplying expansions of various fixed lengths.               */
+
+#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \
+  Split(b, bhi, blo); \
+  Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+  Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, x1); \
+  Fast_Two_Sum(_j, _k, x3, x2)
+
+#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \
+  Split(b, bhi, blo); \
+  Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \
+  Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, x1); \
+  Fast_Two_Sum(_j, _k, _i, x2); \
+  Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, x3); \
+  Fast_Two_Sum(_j, _k, _i, x4); \
+  Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, x5); \
+  Fast_Two_Sum(_j, _k, x7, x6)
+
+#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \
+  Split(a0, a0hi, a0lo); \
+  Split(b0, bhi, blo); \
+  Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \
+  Split(a1, a1hi, a1lo); \
+  Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _k, _1); \
+  Fast_Two_Sum(_j, _k, _l, _2); \
+  Split(b1, bhi, blo); \
+  Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \
+  Two_Sum(_1, _0, _k, x1); \
+  Two_Sum(_2, _k, _j, _1); \
+  Two_Sum(_l, _j, _m, _2); \
+  Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \
+  Two_Sum(_i, _0, _n, _0); \
+  Two_Sum(_1, _0, _i, x2); \
+  Two_Sum(_2, _i, _k, _1); \
+  Two_Sum(_m, _k, _l, _2); \
+  Two_Sum(_j, _n, _k, _0); \
+  Two_Sum(_1, _0, _j, x3); \
+  Two_Sum(_2, _j, _i, _1); \
+  Two_Sum(_l, _i, _m, _2); \
+  Two_Sum(_1, _k, _i, x4); \
+  Two_Sum(_2, _i, _k, x5); \
+  Two_Sum(_m, _k, x7, x6)
+
+/* An expansion of length two can be squared more quickly than finding the   */
+/*   product of two different expansions of length two, and the result is    */
+/*   guaranteed to have no more than six (rather than eight) components.     */
+
+#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \
+  Square(a0, _j, x0); \
+  _0 = a0 + a0; \
+  Two_Product(a1, _0, _k, _1); \
+  Two_One_Sum(_k, _1, _j, _l, _2, x1); \
+  Square(a1, _j, _1); \
+  Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2)
+
+REAL splitter;     /* = 2^ceiling(p / 2) + 1.  Used to split floats in half. */
+REAL epsilon;                /* = 2^(-p).  Used to estimate roundoff errors. */
+/* A set of coefficients used to calculate maximum roundoff errors.          */
+REAL resulterrbound;
+REAL ccwerrboundA, ccwerrboundB, ccwerrboundC;
+REAL o3derrboundA, o3derrboundB, o3derrboundC;
+REAL iccerrboundA, iccerrboundB, iccerrboundC;
+REAL isperrboundA, isperrboundB, isperrboundC;
+
+/*****************************************************************************/
+/*                                                                           */
+/*  doubleprint()   Print the bit representation of a double.                */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void doubleprint(number)
+double number;
+{
+  unsigned long long no;
+  unsigned long long sign, expo;
+  int exponent;
+  int i, bottomi;
+
+  no = *(unsigned long long *) &number;
+  sign = no & 0x8000000000000000ll;
+  expo = (no >> 52) & 0x7ffll;
+  exponent = (int) expo;
+  exponent = exponent - 1023;
+  if (sign)
+  {
+    printf("-");
+  } else
+  {
+    printf(" ");
+  }
+  if (exponent == -1023)
+  {
+    printf(
+      "0.0000000000000000000000000000000000000000000000000000_     (   )");
+  } else
+  {
+    printf("1.");
+    bottomi = -1;
+    for (i = 0; i < 52; i++)
+    {
+      if (no & 0x0008000000000000ll)
+      {
+        printf("1");
+        bottomi = i;
+      } else
+      {
+        printf("0");
+      }
+      no <<= 1;
+    }
+    printf("_%d  (%d)", exponent, exponent - 1 - bottomi);
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  floatprint()   Print the bit representation of a float.                  */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void floatprint(number)
+float number;
+{
+  unsigned no;
+  unsigned sign, expo;
+  int exponent;
+  int i, bottomi;
+
+  no = *(unsigned *) &number;
+  sign = no & 0x80000000;
+  expo = (no >> 23) & 0xff;
+  exponent = (int) expo;
+  exponent = exponent - 127;
+  if (sign)
+  {
+    printf("-");
+  } else
+  {
+    printf(" ");
+  }
+  if (exponent == -127)
+  {
+    printf("0.00000000000000000000000_     (   )");
+  } else
+  {
+    printf("1.");
+    bottomi = -1;
+    for (i = 0; i < 23; i++)
+    {
+      if (no & 0x00400000)
+      {
+        printf("1");
+        bottomi = i;
+      } else
+      {
+        printf("0");
+      }
+      no <<= 1;
+    }
+    printf("_%3d  (%3d)", exponent, exponent - 1 - bottomi);
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_print()   Print the bit representation of an expansion.        */
+/*                                                                           */
+/*  Useful for debugging exact arithmetic routines.                          */
+/*                                                                           */
+/*****************************************************************************/
+
+/*
+void expansion_print(elen, e)
+int elen;
+REAL *e;
+{
+  int i;
+
+  for (i = elen - 1; i >= 0; i--)
+  {
+    REALPRINT(e[i]);
+    if (i > 0)
+    {
+      printf(" +\n");
+    } else
+    {
+      printf("\n");
+    }
+  }
+}
+*/
+
+/*****************************************************************************/
+/*                                                                           */
+/*  doublerand()   Generate a double with random 53-bit significand and a    */
+/*                 random exponent in [0, 511].                              */
+/*                                                                           */
+/*****************************************************************************/
+
+double doublerand()
+{
+  double result;
+  double expo;
+  long a, b, c;
+  long i;
+
+#if defined(WIN32) || defined(_WIN32)
+  a = rand();
+  b = rand();
+  c = rand();
+#else
+  a = random();
+  b = random();
+  c = random();
+#endif
+
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  for (i = 512, expo = 2; i <= 131072; i *= 2, expo = expo * expo)
+  {
+    if (c & i)
+    {
+      result *= expo;
+    }
+  }
+  return result;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  narrowdoublerand()   Generate a double with random 53-bit significand    */
+/*                       and a random exponent in [0, 7].                    */
+/*                                                                           */
+/*****************************************************************************/
+
+double narrowdoublerand()
+{
+  double result;
+  double expo;
+  long a, b, c;
+  long i;
+
+#if defined(WIN32) || defined(_WIN32)
+  a = rand();
+  b = rand();
+  c = rand();
+#else
+  a = random();
+  b = random();
+  c = random();
+#endif
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo)
+  {
+    if (c & i)
+    {
+      result *= expo;
+    }
+  }
+  return result;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  uniformdoublerand()   Generate a double with random 53-bit significand.  */
+/*                                                                           */
+/*****************************************************************************/
+
+double uniformdoublerand()
+{
+  double result;
+  long a, b;
+
+#if defined(WIN32) || defined(_WIN32)
+  a = rand();
+  b = rand();
+#else
+  a = random();
+  b = random();
+#endif
+
+  result = (double) (a - 1073741824) * 8388608.0 + (double) (b >> 8);
+  return result;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  floatrand()   Generate a float with random 24-bit significand and a      */
+/*                random exponent in [0, 63].                                */
+/*                                                                           */
+/*****************************************************************************/
+
+float floatrand()
+{
+  float result;
+  float expo;
+  long a, c;
+  long i;
+
+#if defined(WIN32) || defined(_WIN32)
+  a = rand();
+  c = rand();
+#else
+  a = random();
+  c = random();
+#endif
+  result = (float) ((a - 1073741824) >> 6);
+  for (i = 512, expo = 2; i <= 16384; i *= 2, expo = expo * expo)
+  {
+    if (c & i)
+    {
+      result *= expo;
+    }
+  }
+  return result;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  narrowfloatrand()   Generate a float with random 24-bit significand and  */
+/*                      a random exponent in [0, 7].                         */
+/*                                                                           */
+/*****************************************************************************/
+
+float narrowfloatrand()
+{
+  float result;
+  float expo;
+  long a, c;
+  long i;
+
+#if defined(WIN32) || defined(_WIN32)
+  a = rand();
+  c = rand();
+#else
+  a = random();
+  c = random();
+#endif
+  result = (float) ((a - 1073741824) >> 6);
+  for (i = 512, expo = 2; i <= 2048; i *= 2, expo = expo * expo)
+  {
+    if (c & i)
+    {
+      result *= expo;
+    }
+  }
+  return result;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  uniformfloatrand()   Generate a float with random 24-bit significand.    */
+/*                                                                           */
+/*****************************************************************************/
+
+float uniformfloatrand()
+{
+  float result;
+  long a;
+
+#if defined(WIN32) || defined(_WIN32)
+  a = rand();
+#else
+  a = random();
+#endif
+  result = (float) ((a - 1073741824) >> 6);
+  return result;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  exactinit()   Initialize the variables used for exact arithmetic.        */
+/*                                                                           */
+/*  `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in   */
+/*  floating-point arithmetic.  `epsilon' bounds the relative roundoff       */
+/*  error.  It is used for floating-point error analysis.                    */
+/*                                                                           */
+/*  `splitter' is used to split floating-point numbers into two half-        */
+/*  length significands for exact multiplication.                            */
+/*                                                                           */
+/*  I imagine that a highly optimizing compiler might be too smart for its   */
+/*  own good, and somehow cause this routine to fail, if it pretends that    */
+/*  floating-point arithmetic is too much like real arithmetic.              */
+/*                                                                           */
+/*  Don't change this routine unless you fully understand it.                */
+/*                                                                           */
+/*****************************************************************************/
+
+void exactinit()
+{
+  REAL half;
+  REAL check, lastcheck;
+  int every_other;
+
+  every_other = 1;
+  half = 0.5;
+  epsilon = 1.0;
+  splitter = 1.0;
+  check = 1.0;
+  /* Repeatedly divide `epsilon' by two until it is too small to add to    */
+  /*   one without causing roundoff.  (Also check if the sum is equal to   */
+  /*   the previous sum, for machines that round up instead of using exact */
+  /*   rounding.  Not that this library will work on such machines anyway. */
+  do
+  {
+    lastcheck = check;
+    epsilon *= half;
+    if (every_other)
+    {
+      splitter *= 2.0;
+    }
+    every_other = !every_other;
+    check = 1.0 + epsilon;
+  } while ((check != 1.0) && (check != lastcheck));
+  splitter += 1.0;
+
+  /* Error bounds for orientation and incircle tests. */
+  resulterrbound = (3.0 + 8.0 * epsilon) * epsilon;
+  ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon;
+  ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon;
+  ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon;
+  o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon;
+  o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon;
+  o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon;
+  iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon;
+  iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon;
+  iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon;
+  isperrboundA = (16.0 + 224.0 * epsilon) * epsilon;
+  isperrboundB = (5.0 + 72.0 * epsilon) * epsilon;
+  isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  grow_expansion()   Add a scalar to an expansion.                         */
+/*                                                                           */
+/*  Sets h = e + b.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+int grow_expansion(elen, e, b, h)                /* e and h can be the same. */
+int elen;
+REAL *e;
+REAL b;
+REAL *h;
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  int eindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  Q = b;
+  for (eindex = 0; eindex < elen; eindex++)
+  {
+    enow = e[eindex];
+    Two_Sum(Q, enow, Qnew, h[eindex]);
+    Q = Qnew;
+  }
+  h[eindex] = Q;
+  return eindex + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  grow_expansion_zeroelim()   Add a scalar to an expansion, eliminating    */
+/*                              zero components from the output expansion.   */
+/*                                                                           */
+/*  Sets h = e + b.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+int grow_expansion_zeroelim(elen, e, b, h)       /* e and h can be the same. */
+int elen;
+REAL *e;
+REAL b;
+REAL *h;
+{
+  REAL Q, hh;
+  INEXACT REAL Qnew;
+  int eindex, hindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  hindex = 0;
+  Q = b;
+  for (eindex = 0; eindex < elen; eindex++)
+  {
+    enow = e[eindex];
+    Two_Sum(Q, enow, Qnew, hh);
+    Q = Qnew;
+    if (hh != 0.0)
+    {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0))
+  {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_sum()   Sum two expansions.                                    */
+/*                                                                           */
+/*  Sets h = e + f.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the nonadjacent property as well.  (That is,   */
+/*  if e has one of these properties, so will h.)  Does NOT maintain the     */
+/*  strongly nonoverlapping property.                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+int expansion_sum(elen, e, flen, f, h)
+/* e and h can be the same, but f and h cannot. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  int findex, hindex, hlast;
+  REAL hnow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  Q = f[0];
+  for (hindex = 0; hindex < elen; hindex++)
+  {
+    hnow = e[hindex];
+    Two_Sum(Q, hnow, Qnew, h[hindex]);
+    Q = Qnew;
+  }
+  h[hindex] = Q;
+  hlast = hindex;
+  for (findex = 1; findex < flen; findex++)
+  {
+    Q = f[findex];
+    for (hindex = findex; hindex <= hlast; hindex++)
+    {
+      hnow = h[hindex];
+      Two_Sum(Q, hnow, Qnew, h[hindex]);
+      Q = Qnew;
+    }
+    h[++hlast] = Q;
+  }
+  return hlast + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_sum_zeroelim1()   Sum two expansions, eliminating zero         */
+/*                              components from the output expansion.        */
+/*                                                                           */
+/*  Sets h = e + f.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the nonadjacent property as well.  (That is,   */
+/*  if e has one of these properties, so will h.)  Does NOT maintain the     */
+/*  strongly nonoverlapping property.                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+int expansion_sum_zeroelim1(elen, e, flen, f, h)
+/* e and h can be the same, but f and h cannot. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  int index, findex, hindex, hlast;
+  REAL hnow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  Q = f[0];
+  for (hindex = 0; hindex < elen; hindex++)
+  {
+    hnow = e[hindex];
+    Two_Sum(Q, hnow, Qnew, h[hindex]);
+    Q = Qnew;
+  }
+  h[hindex] = Q;
+  hlast = hindex;
+  for (findex = 1; findex < flen; findex++)
+  {
+    Q = f[findex];
+    for (hindex = findex; hindex <= hlast; hindex++)
+    {
+      hnow = h[hindex];
+      Two_Sum(Q, hnow, Qnew, h[hindex]);
+      Q = Qnew;
+    }
+    h[++hlast] = Q;
+  }
+  hindex = -1;
+  for (index = 0; index <= hlast; index++)
+  {
+    hnow = h[index];
+    if (hnow != 0.0)
+    {
+      h[++hindex] = hnow;
+    }
+  }
+  if (hindex == -1)
+  {
+    return 1;
+  } else
+  {
+    return hindex + 1;
+  }
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  expansion_sum_zeroelim2()   Sum two expansions, eliminating zero         */
+/*                              components from the output expansion.        */
+/*                                                                           */
+/*  Sets h = e + f.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the nonadjacent property as well.  (That is,   */
+/*  if e has one of these properties, so will h.)  Does NOT maintain the     */
+/*  strongly nonoverlapping property.                                        */
+/*                                                                           */
+/*****************************************************************************/
+
+int expansion_sum_zeroelim2(elen, e, flen, f, h)
+/* e and h can be the same, but f and h cannot. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+{
+  REAL Q, hh;
+  INEXACT REAL Qnew;
+  int eindex, findex, hindex, hlast;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+
+  hindex = 0;
+  Q = f[0];
+  for (eindex = 0; eindex < elen; eindex++)
+  {
+    enow = e[eindex];
+    Two_Sum(Q, enow, Qnew, hh);
+    Q = Qnew;
+    if (hh != 0.0)
+    {
+      h[hindex++] = hh;
+    }
+  }
+  h[hindex] = Q;
+  hlast = hindex;
+  for (findex = 1; findex < flen; findex++)
+  {
+    hindex = 0;
+    Q = f[findex];
+    for (eindex = 0; eindex <= hlast; eindex++)
+    {
+      enow = h[eindex];
+      Two_Sum(Q, enow, Qnew, hh);
+      Q = Qnew;
+      if (hh != 0)
+      {
+        h[hindex++] = hh;
+      }
+    }
+    h[hindex] = Q;
+    hlast = hindex;
+  }
+  return hlast + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  fast_expansion_sum()   Sum two expansions.                               */
+/*                                                                           */
+/*  Sets h = e + f.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  If round-to-even is used (as with IEEE 754), maintains the strongly      */
+/*  nonoverlapping property.  (That is, if e is strongly nonoverlapping, h   */
+/*  will be also.)  Does NOT maintain the nonoverlapping or nonadjacent      */
+/*  properties.                                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+int fast_expansion_sum(elen, e, flen, f, h)           /* h cannot be e or f. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  REAL enow, fnow;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow))
+  {
+    Q = enow;
+    enow = e[++eindex];
+  } else
+  {
+    Q = fnow;
+    fnow = f[++findex];
+  }
+  hindex = 0;
+  if ((eindex < elen) && (findex < flen))
+  {
+    if ((fnow > enow) == (fnow > -enow))
+    {
+      Fast_Two_Sum(enow, Q, Qnew, h[0]);
+      enow = e[++eindex];
+    } else
+    {
+      Fast_Two_Sum(fnow, Q, Qnew, h[0]);
+      fnow = f[++findex];
+    }
+    Q = Qnew;
+    hindex = 1;
+    while ((eindex < elen) && (findex < flen))
+    {
+      if ((fnow > enow) == (fnow > -enow))
+      {
+        Two_Sum(Q, enow, Qnew, h[hindex]);
+        enow = e[++eindex];
+      } else
+      {
+        Two_Sum(Q, fnow, Qnew, h[hindex]);
+        fnow = f[++findex];
+      }
+      Q = Qnew;
+      hindex++;
+    }
+  }
+  while (eindex < elen)
+  {
+    Two_Sum(Q, enow, Qnew, h[hindex]);
+    enow = e[++eindex];
+    Q = Qnew;
+    hindex++;
+  }
+  while (findex < flen)
+  {
+    Two_Sum(Q, fnow, Qnew, h[hindex]);
+    fnow = f[++findex];
+    Q = Qnew;
+    hindex++;
+  }
+  h[hindex] = Q;
+  return hindex + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  fast_expansion_sum_zeroelim()   Sum two expansions, eliminating zero     */
+/*                                  components from the output expansion.    */
+/*                                                                           */
+/*  Sets h = e + f.  See the long version of my paper for details.           */
+/*                                                                           */
+/*  If round-to-even is used (as with IEEE 754), maintains the strongly      */
+/*  nonoverlapping property.  (That is, if e is strongly nonoverlapping, h   */
+/*  will be also.)  Does NOT maintain the nonoverlapping or nonadjacent      */
+/*  properties.                                                              */
+/*                                                                           */
+/*****************************************************************************/
+
+int fast_expansion_sum_zeroelim(elen, e, flen, f, h)  /* h cannot be e or f. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+{
+  REAL Q;
+  INEXACT REAL Qnew;
+  INEXACT REAL hh;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  REAL enow, fnow;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow))
+  {
+    Q = enow;
+    enow = e[++eindex];
+  } else
+  {
+    Q = fnow;
+    fnow = f[++findex];
+  }
+  hindex = 0;
+  if ((eindex < elen) && (findex < flen))
+  {
+    if ((fnow > enow) == (fnow > -enow))
+    {
+      Fast_Two_Sum(enow, Q, Qnew, hh);
+      enow = e[++eindex];
+    } else
+    {
+      Fast_Two_Sum(fnow, Q, Qnew, hh);
+      fnow = f[++findex];
+    }
+    Q = Qnew;
+    if (hh != 0.0)
+    {
+      h[hindex++] = hh;
+    }
+    while ((eindex < elen) && (findex < flen))
+    {
+      if ((fnow > enow) == (fnow > -enow))
+      {
+        Two_Sum(Q, enow, Qnew, hh);
+        enow = e[++eindex];
+      } else
+      {
+        Two_Sum(Q, fnow, Qnew, hh);
+        fnow = f[++findex];
+      }
+      Q = Qnew;
+      if (hh != 0.0)
+      {
+        h[hindex++] = hh;
+      }
+    }
+  }
+  while (eindex < elen)
+  {
+    Two_Sum(Q, enow, Qnew, hh);
+    enow = e[++eindex];
+    Q = Qnew;
+    if (hh != 0.0)
+    {
+      h[hindex++] = hh;
+    }
+  }
+  while (findex < flen)
+  {
+    Two_Sum(Q, fnow, Qnew, hh);
+    fnow = f[++findex];
+    Q = Qnew;
+    if (hh != 0.0)
+    {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0))
+  {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  linear_expansion_sum()   Sum two expansions.                             */
+/*                                                                           */
+/*  Sets h = e + f.  See either version of my paper for details.             */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  (That is, if e is                */
+/*  nonoverlapping, h will be also.)                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+int linear_expansion_sum(elen, e, flen, f, h)         /* h cannot be e or f. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+{
+  REAL Q, q;
+  INEXACT REAL Qnew;
+  INEXACT REAL R;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  REAL enow, fnow;
+  REAL g0;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  if ((fnow > enow) == (fnow > -enow))
+  {
+    g0 = enow;
+    enow = e[++eindex];
+  } else
+  {
+    g0 = fnow;
+    fnow = f[++findex];
+  }
+  if ((eindex < elen) && ((findex >= flen)
+                          || ((fnow > enow) == (fnow > -enow))))
+                          {
+    Fast_Two_Sum(enow, g0, Qnew, q);
+    enow = e[++eindex];
+  } else
+  {
+    Fast_Two_Sum(fnow, g0, Qnew, q);
+    fnow = f[++findex];
+  }
+  Q = Qnew;
+  for (hindex = 0; hindex < elen + flen - 2; hindex++)
+  {
+    if ((eindex < elen) && ((findex >= flen)
+                            || ((fnow > enow) == (fnow > -enow))))
+                            {
+      Fast_Two_Sum(enow, q, R, h[hindex]);
+      enow = e[++eindex];
+    } else
+    {
+      Fast_Two_Sum(fnow, q, R, h[hindex]);
+      fnow = f[++findex];
+    }
+    Two_Sum(Q, R, Qnew, q);
+    Q = Qnew;
+  }
+  h[hindex] = q;
+  h[hindex + 1] = Q;
+  return hindex + 2;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  linear_expansion_sum_zeroelim()   Sum two expansions, eliminating zero   */
+/*                                    components from the output expansion.  */
+/*                                                                           */
+/*  Sets h = e + f.  See either version of my paper for details.             */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  (That is, if e is                */
+/*  nonoverlapping, h will be also.)                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+int linear_expansion_sum_zeroelim(elen, e, flen, f, h)/* h cannot be e or f. */
+int elen;
+REAL *e;
+int flen;
+REAL *f;
+REAL *h;
+{
+  REAL Q, q, hh;
+  INEXACT REAL Qnew;
+  INEXACT REAL R;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  int eindex, findex, hindex;
+  int count;
+  REAL enow, fnow;
+  REAL g0;
+
+  enow = e[0];
+  fnow = f[0];
+  eindex = findex = 0;
+  hindex = 0;
+  if ((fnow > enow) == (fnow > -enow))
+  {
+    g0 = enow;
+    enow = e[++eindex];
+  } else
+  {
+    g0 = fnow;
+    fnow = f[++findex];
+  }
+  if ((eindex < elen) && ((findex >= flen)
+                          || ((fnow > enow) == (fnow > -enow))))
+                          {
+    Fast_Two_Sum(enow, g0, Qnew, q);
+    enow = e[++eindex];
+  } else
+  {
+    Fast_Two_Sum(fnow, g0, Qnew, q);
+    fnow = f[++findex];
+  }
+  Q = Qnew;
+  for (count = 2; count < elen + flen; count++)
+  {
+    if ((eindex < elen) && ((findex >= flen)
+                            || ((fnow > enow) == (fnow > -enow))))
+                            {
+      Fast_Two_Sum(enow, q, R, hh);
+      enow = e[++eindex];
+    } else
+    {
+      Fast_Two_Sum(fnow, q, R, hh);
+      fnow = f[++findex];
+    }
+    Two_Sum(Q, R, Qnew, q);
+    Q = Qnew;
+    if (hh != 0)
+    {
+      h[hindex++] = hh;
+    }
+  }
+  if (q != 0)
+  {
+    h[hindex++] = q;
+  }
+  if ((Q != 0.0) || (hindex == 0))
+  {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scale_expansion()   Multiply an expansion by a scalar.                   */
+/*                                                                           */
+/*  Sets h = be.  See either version of my paper for details.                */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+int scale_expansion(elen, e, b, h)            /* e and h cannot be the same. */
+int elen;
+REAL *e;
+REAL b;
+REAL *h;
+{
+  INEXACT REAL Q;
+  INEXACT REAL sum;
+  INEXACT REAL product1;
+  REAL product0;
+  int eindex, hindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+
+  Split(b, bhi, blo);
+  Two_Product_Presplit(e[0], b, bhi, blo, Q, h[0]);
+  hindex = 1;
+  for (eindex = 1; eindex < elen; eindex++)
+  {
+    enow = e[eindex];
+    Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+    Two_Sum(Q, product0, sum, h[hindex]);
+    hindex++;
+    Two_Sum(product1, sum, Q, h[hindex]);
+    hindex++;
+  }
+  h[hindex] = Q;
+  return elen + elen;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  scale_expansion_zeroelim()   Multiply an expansion by a scalar,          */
+/*                               eliminating zero components from the        */
+/*                               output expansion.                           */
+/*                                                                           */
+/*  Sets h = be.  See either version of my paper for details.                */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), maintains the strongly nonoverlapping and nonadjacent    */
+/*  properties as well.  (That is, if e has one of these properties, so      */
+/*  will h.)                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+int scale_expansion_zeroelim(elen, e, b, h)   /* e and h cannot be the same. */
+int elen;
+REAL *e;
+REAL b;
+REAL *h;
+{
+  INEXACT REAL Q, sum;
+  REAL hh;
+  INEXACT REAL product1;
+  REAL product0;
+  int eindex, hindex;
+  REAL enow;
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+
+  Split(b, bhi, blo);
+  Two_Product_Presplit(e[0], b, bhi, blo, Q, hh);
+  hindex = 0;
+  if (hh != 0)
+  {
+    h[hindex++] = hh;
+  }
+  for (eindex = 1; eindex < elen; eindex++)
+  {
+    enow = e[eindex];
+    Two_Product_Presplit(enow, b, bhi, blo, product1, product0);
+    Two_Sum(Q, product0, sum, hh);
+    if (hh != 0)
+    {
+      h[hindex++] = hh;
+    }
+    Fast_Two_Sum(product1, sum, Q, hh);
+    if (hh != 0)
+    {
+      h[hindex++] = hh;
+    }
+  }
+  if ((Q != 0.0) || (hindex == 0))
+  {
+    h[hindex++] = Q;
+  }
+  return hindex;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  compress()   Compress an expansion.                                      */
+/*                                                                           */
+/*  See the long version of my paper for details.                            */
+/*                                                                           */
+/*  Maintains the nonoverlapping property.  If round-to-even is used (as     */
+/*  with IEEE 754), then any nonoverlapping expansion is converted to a      */
+/*  nonadjacent expansion.                                                   */
+/*                                                                           */
+/*****************************************************************************/
+
+int compress(elen, e, h)                         /* e and h may be the same. */
+int elen;
+REAL *e;
+REAL *h;
+{
+  REAL Q, q;
+  INEXACT REAL Qnew;
+  int eindex, hindex;
+  INEXACT REAL bvirt;
+  REAL enow, hnow;
+  int top, bottom;
+
+  bottom = elen - 1;
+  Q = e[bottom];
+  for (eindex = elen - 2; eindex >= 0; eindex--)
+  {
+    enow = e[eindex];
+    Fast_Two_Sum(Q, enow, Qnew, q);
+    if (q != 0)
+    {
+      h[bottom--] = Qnew;
+      Q = q;
+    } else
+    {
+      Q = Qnew;
+    }
+  }
+  top = 0;
+  for (hindex = bottom + 1; hindex < elen; hindex++)
+  {
+    hnow = h[hindex];
+    Fast_Two_Sum(hnow, Q, Qnew, q);
+    if (q != 0)
+    {
+      h[top++] = q;
+    }
+    Q = Qnew;
+  }
+  h[top] = Q;
+  return top + 1;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  estimate()   Produce a one-word estimate of an expansion's value.        */
+/*                                                                           */
+/*  See either version of my paper for details.                              */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL estimate(elen, e)
+int elen;
+REAL *e;
+{
+  REAL Q;
+  int eindex;
+
+  Q = e[0];
+  for (eindex = 1; eindex < elen; eindex++)
+  {
+    Q += e[eindex];
+  }
+  return Q;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  orient2dfast()   Approximate 2D orientation test.  Nonrobust.            */
+/*  orient2dexact()   Exact 2D orientation test.  Robust.                    */
+/*  orient2dslow()   Another exact 2D orientation test.  Robust.             */
+/*  orient2d()   Adaptive exact 2D orientation test.  Robust.                */
+/*                                                                           */
+/*               Return a positive value if the points pa, pb, and pc occur  */
+/*               in counterclockwise order; a negative value if they occur   */
+/*               in clockwise order; and zero if they are collinear.  The    */
+/*               result is also a rough approximation of twice the signed    */
+/*               area of the triangle defined by the three points.           */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In orient2d() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, orient2d() is usually quite */
+/*  fast, but will run more slowly when the input points are collinear or    */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL orient2dfast(pa, pb, pc)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+{
+  REAL acx, bcx, acy, bcy;
+
+  acx = pa[0] - pc[0];
+  bcx = pb[0] - pc[0];
+  acy = pa[1] - pc[1];
+  bcy = pb[1] - pc[1];
+  return acx * bcy - acy * bcx;
+}
+
+REAL orient2dexact(pa, pb, pc)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+{
+  INEXACT REAL axby1, axcy1, bxcy1, bxay1, cxay1, cxby1;
+  REAL axby0, axcy0, bxcy0, bxay0, cxay0, cxby0;
+  REAL aterms[4], bterms[4], cterms[4];
+  INEXACT REAL aterms3, bterms3, cterms3;
+  REAL v[8], w[12];
+  int vlength, wlength;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  Two_Product(pa[0], pb[1], axby1, axby0);
+  Two_Product(pa[0], pc[1], axcy1, axcy0);
+  Two_Two_Diff(axby1, axby0, axcy1, axcy0,
+               aterms3, aterms[2], aterms[1], aterms[0]);
+  aterms[3] = aterms3;
+
+  Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+  Two_Product(pb[0], pa[1], bxay1, bxay0);
+  Two_Two_Diff(bxcy1, bxcy0, bxay1, bxay0,
+               bterms3, bterms[2], bterms[1], bterms[0]);
+  bterms[3] = bterms3;
+
+  Two_Product(pc[0], pa[1], cxay1, cxay0);
+  Two_Product(pc[0], pb[1], cxby1, cxby0);
+  Two_Two_Diff(cxay1, cxay0, cxby1, cxby0,
+               cterms3, cterms[2], cterms[1], cterms[0]);
+  cterms[3] = cterms3;
+
+  vlength = fast_expansion_sum_zeroelim(4, aterms, 4, bterms, v);
+  wlength = fast_expansion_sum_zeroelim(vlength, v, 4, cterms, w);
+
+  return w[wlength - 1];
+}
+
+REAL orient2dslow(pa, pb, pc)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+{
+  INEXACT REAL acx, acy, bcx, bcy;
+  REAL acxtail, acytail;
+  REAL bcxtail, bcytail;
+  REAL negate, negatetail;
+  REAL axby[8], bxay[8];
+  INEXACT REAL axby7, bxay7;
+  REAL deter[16];
+  int deterlen;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k, _l, _m, _n;
+  REAL _0, _1, _2;
+
+  Two_Diff(pa[0], pc[0], acx, acxtail);
+  Two_Diff(pa[1], pc[1], acy, acytail);
+  Two_Diff(pb[0], pc[0], bcx, bcxtail);
+  Two_Diff(pb[1], pc[1], bcy, bcytail);
+
+  Two_Two_Product(acx, acxtail, bcy, bcytail,
+                  axby7, axby[6], axby[5], axby[4],
+                  axby[3], axby[2], axby[1], axby[0]);
+  axby[7] = axby7;
+  negate = -acy;
+  negatetail = -acytail;
+  Two_Two_Product(bcx, bcxtail, negate, negatetail,
+                  bxay7, bxay[6], bxay[5], bxay[4],
+                  bxay[3], bxay[2], bxay[1], bxay[0]);
+  bxay[7] = bxay7;
+
+  deterlen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL orient2dadapt(pa, pb, pc, detsum)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL detsum;
+{
+  INEXACT REAL acx, acy, bcx, bcy;
+  REAL acxtail, acytail, bcxtail, bcytail;
+  INEXACT REAL detleft, detright;
+  REAL detlefttail, detrighttail;
+  REAL det, errbound;
+  REAL B[4], C1[8], C2[12], D[16];
+  INEXACT REAL B3;
+  int C1length, C2length, Dlength;
+  REAL u[4];
+  INEXACT REAL u3;
+  INEXACT REAL s1, t1;
+  REAL s0, t0;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  acx = (REAL) (pa[0] - pc[0]);
+  bcx = (REAL) (pb[0] - pc[0]);
+  acy = (REAL) (pa[1] - pc[1]);
+  bcy = (REAL) (pb[1] - pc[1]);
+
+  Two_Product(acx, bcy, detleft, detlefttail);
+  Two_Product(acy, bcx, detright, detrighttail);
+
+  Two_Two_Diff(detleft, detlefttail, detright, detrighttail,
+               B3, B[2], B[1], B[0]);
+  B[3] = B3;
+
+  det = estimate(4, B);
+  errbound = ccwerrboundB * detsum;
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pc[0], acx, acxtail);
+  Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail);
+  Two_Diff_Tail(pa[1], pc[1], acy, acytail);
+  Two_Diff_Tail(pb[1], pc[1], bcy, bcytail);
+
+  if ((acxtail == 0.0) && (acytail == 0.0)
+      && (bcxtail == 0.0) && (bcytail == 0.0))
+      {
+    return det;
+  }
+
+  errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det);
+  det += (acx * bcytail + bcy * acxtail)
+       - (acy * bcxtail + bcx * acytail);
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  Two_Product(acxtail, bcy, s1, s0);
+  Two_Product(acytail, bcx, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1);
+
+  Two_Product(acx, bcytail, s1, s0);
+  Two_Product(acy, bcxtail, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2);
+
+  Two_Product(acxtail, bcytail, s1, s0);
+  Two_Product(acytail, bcxtail, t1, t0);
+  Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]);
+  u[3] = u3;
+  Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D);
+
+  return(D[Dlength - 1]);
+}
+
+REAL orient2d(pa, pb, pc)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+{
+  REAL detleft, detright, det;
+  REAL detsum, errbound;
+
+  detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]);
+  detright = (pa[1] - pc[1]) * (pb[0] - pc[0]);
+  det = detleft - detright;
+
+  if (detleft > 0.0)
+  {
+    if (detright <= 0.0)
+    {
+      return det;
+    } else
+    {
+      detsum = detleft + detright;
+    }
+  } else if (detleft < 0.0)
+  {
+    if (detright >= 0.0)
+    {
+      return det;
+    } else
+    {
+      detsum = -detleft - detright;
+    }
+  } else
+  {
+    return det;
+  }
+
+  errbound = ccwerrboundA * detsum;
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  return orient2dadapt(pa, pb, pc, detsum);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  orient3dfast()   Approximate 3D orientation test.  Nonrobust.            */
+/*  orient3dexact()   Exact 3D orientation test.  Robust.                    */
+/*  orient3dslow()   Another exact 3D orientation test.  Robust.             */
+/*  orient3d()   Adaptive exact 3D orientation test.  Robust.                */
+/*                                                                           */
+/*               Return a positive value if the point pd lies below the      */
+/*               plane passing through pa, pb, and pc; "below" is defined so */
+/*               that pa, pb, and pc appear in counterclockwise order when   */
+/*               viewed from above the plane.  Returns a negative value if   */
+/*               pd lies above the plane.  Returns zero if the points are    */
+/*               coplanar.  The result is also a rough approximation of six  */
+/*               times the signed volume of the tetrahedron defined by the   */
+/*               four points.                                                */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In orient3d() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, orient3d() is usually quite */
+/*  fast, but will run more slowly when the input points are coplanar or     */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL orient3dfast(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  REAL adx, bdx, cdx;
+  REAL ady, bdy, cdy;
+  REAL adz, bdz, cdz;
+
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+  adz = pa[2] - pd[2];
+  bdz = pb[2] - pd[2];
+  cdz = pc[2] - pd[2];
+
+  return adx * (bdy * cdz - bdz * cdy)
+       + bdx * (cdy * adz - cdz * ady)
+       + cdx * (ady * bdz - adz * bdy);
+}
+
+REAL orient3dexact(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1;
+  INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1;
+  REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0;
+  REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0;
+  REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+  REAL temp8[8];
+  int templen;
+  REAL abc[12], bcd[12], cda[12], dab[12];
+  int abclen, bcdlen, cdalen, dablen;
+  REAL adet[24], bdet[24], cdet[24], ddet[24];
+  int alen, blen, clen, dlen;
+  REAL abdet[48], cddet[48];
+  int ablen, cdlen;
+  REAL deter[96];
+  int deterlen;
+  int i;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  Two_Product(pa[0], pb[1], axby1, axby0);
+  Two_Product(pb[0], pa[1], bxay1, bxay0);
+  Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
+
+  Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+  Two_Product(pc[0], pb[1], cxby1, cxby0);
+  Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
+
+  Two_Product(pc[0], pd[1], cxdy1, cxdy0);
+  Two_Product(pd[0], pc[1], dxcy1, dxcy0);
+  Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
+
+  Two_Product(pd[0], pa[1], dxay1, dxay0);
+  Two_Product(pa[0], pd[1], axdy1, axdy0);
+  Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
+
+  Two_Product(pa[0], pc[1], axcy1, axcy0);
+  Two_Product(pc[0], pa[1], cxay1, cxay0);
+  Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
+
+  Two_Product(pb[0], pd[1], bxdy1, bxdy0);
+  Two_Product(pd[0], pb[1], dxby1, dxby0);
+  Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
+
+  templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8);
+  cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda);
+  templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8);
+  dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab);
+  for (i = 0; i < 4; i++)
+  {
+    bd[i] = -bd[i];
+    ac[i] = -ac[i];
+  }
+  templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8);
+  abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc);
+  templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8);
+  bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd);
+
+  alen = scale_expansion_zeroelim(bcdlen, bcd, pa[2], adet);
+  blen = scale_expansion_zeroelim(cdalen, cda, -pb[2], bdet);
+  clen = scale_expansion_zeroelim(dablen, dab, pc[2], cdet);
+  dlen = scale_expansion_zeroelim(abclen, abc, -pd[2], ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL orient3dslow(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  INEXACT REAL adx, ady, adz, bdx, bdy, bdz, cdx, cdy, cdz;
+  REAL adxtail, adytail, adztail;
+  REAL bdxtail, bdytail, bdztail;
+  REAL cdxtail, cdytail, cdztail;
+  REAL negate, negatetail;
+  INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7;
+  REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8];
+  REAL temp16[16], temp32[32], temp32t[32];
+  int temp16len, temp32len, temp32tlen;
+  REAL adet[64], bdet[64], cdet[64];
+  int alen, blen, clen;
+  REAL abdet[128];
+  int ablen;
+  REAL deter[192];
+  int deterlen;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k, _l, _m, _n;
+  REAL _0, _1, _2;
+
+  Two_Diff(pa[0], pd[0], adx, adxtail);
+  Two_Diff(pa[1], pd[1], ady, adytail);
+  Two_Diff(pa[2], pd[2], adz, adztail);
+  Two_Diff(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff(pb[1], pd[1], bdy, bdytail);
+  Two_Diff(pb[2], pd[2], bdz, bdztail);
+  Two_Diff(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff(pc[1], pd[1], cdy, cdytail);
+  Two_Diff(pc[2], pd[2], cdz, cdztail);
+
+  Two_Two_Product(adx, adxtail, bdy, bdytail,
+                  axby7, axby[6], axby[5], axby[4],
+                  axby[3], axby[2], axby[1], axby[0]);
+  axby[7] = axby7;
+  negate = -ady;
+  negatetail = -adytail;
+  Two_Two_Product(bdx, bdxtail, negate, negatetail,
+                  bxay7, bxay[6], bxay[5], bxay[4],
+                  bxay[3], bxay[2], bxay[1], bxay[0]);
+  bxay[7] = bxay7;
+  Two_Two_Product(bdx, bdxtail, cdy, cdytail,
+                  bxcy7, bxcy[6], bxcy[5], bxcy[4],
+                  bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
+  bxcy[7] = bxcy7;
+  negate = -bdy;
+  negatetail = -bdytail;
+  Two_Two_Product(cdx, cdxtail, negate, negatetail,
+                  cxby7, cxby[6], cxby[5], cxby[4],
+                  cxby[3], cxby[2], cxby[1], cxby[0]);
+  cxby[7] = cxby7;
+  Two_Two_Product(cdx, cdxtail, ady, adytail,
+                  cxay7, cxay[6], cxay[5], cxay[4],
+                  cxay[3], cxay[2], cxay[1], cxay[0]);
+  cxay[7] = cxay7;
+  negate = -cdy;
+  negatetail = -cdytail;
+  Two_Two_Product(adx, adxtail, negate, negatetail,
+                  axcy7, axcy[6], axcy[5], axcy[4],
+                  axcy[3], axcy[2], axcy[1], axcy[0]);
+  axcy[7] = axcy7;
+
+  temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16);
+  temp32len = scale_expansion_zeroelim(temp16len, temp16, adz, temp32);
+  temp32tlen = scale_expansion_zeroelim(temp16len, temp16, adztail, temp32t);
+  alen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+                                     adet);
+
+  temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16);
+  temp32len = scale_expansion_zeroelim(temp16len, temp16, bdz, temp32);
+  temp32tlen = scale_expansion_zeroelim(temp16len, temp16, bdztail, temp32t);
+  blen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+                                     bdet);
+
+  temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16);
+  temp32len = scale_expansion_zeroelim(temp16len, temp16, cdz, temp32);
+  temp32tlen = scale_expansion_zeroelim(temp16len, temp16, cdztail, temp32t);
+  clen = fast_expansion_sum_zeroelim(temp32len, temp32, temp32tlen, temp32t,
+                                     cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL orient3dadapt(pa, pb, pc, pd, permanent)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+REAL permanent;
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+  REAL det, errbound;
+
+  INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+  REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+  REAL bc[4], ca[4], ab[4];
+  INEXACT REAL bc3, ca3, ab3;
+  REAL adet[8], bdet[8], cdet[8];
+  int alen, blen, clen;
+  REAL abdet[16];
+  int ablen;
+  REAL *finnow, *finother, *finswap;
+  REAL fin1[192], fin2[192];
+  int finlength;
+
+  REAL adxtail, bdxtail, cdxtail;
+  REAL adytail, bdytail, cdytail;
+  REAL adztail, bdztail, cdztail;
+  INEXACT REAL at_blarge, at_clarge;
+  INEXACT REAL bt_clarge, bt_alarge;
+  INEXACT REAL ct_alarge, ct_blarge;
+  REAL at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4];
+  int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen;
+  INEXACT REAL bdxt_cdy1, cdxt_bdy1, cdxt_ady1;
+  INEXACT REAL adxt_cdy1, adxt_bdy1, bdxt_ady1;
+  REAL bdxt_cdy0, cdxt_bdy0, cdxt_ady0;
+  REAL adxt_cdy0, adxt_bdy0, bdxt_ady0;
+  INEXACT REAL bdyt_cdx1, cdyt_bdx1, cdyt_adx1;
+  INEXACT REAL adyt_cdx1, adyt_bdx1, bdyt_adx1;
+  REAL bdyt_cdx0, cdyt_bdx0, cdyt_adx0;
+  REAL adyt_cdx0, adyt_bdx0, bdyt_adx0;
+  REAL bct[8], cat[8], abt[8];
+  int bctlen, catlen, abtlen;
+  INEXACT REAL bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1;
+  INEXACT REAL adxt_cdyt1, adxt_bdyt1, bdxt_adyt1;
+  REAL bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0;
+  REAL adxt_cdyt0, adxt_bdyt0, bdxt_adyt0;
+  REAL u[4], v[12], w[16];
+  INEXACT REAL u3;
+  int vlength, wlength;
+  REAL negate;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k;
+  REAL _0;
+
+  adx = (REAL) (pa[0] - pd[0]);
+  bdx = (REAL) (pb[0] - pd[0]);
+  cdx = (REAL) (pc[0] - pd[0]);
+  ady = (REAL) (pa[1] - pd[1]);
+  bdy = (REAL) (pb[1] - pd[1]);
+  cdy = (REAL) (pc[1] - pd[1]);
+  adz = (REAL) (pa[2] - pd[2]);
+  bdz = (REAL) (pb[2] - pd[2]);
+  cdz = (REAL) (pc[2] - pd[2]);
+
+  Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+  Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+  Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+  alen = scale_expansion_zeroelim(4, bc, adz, adet);
+
+  Two_Product(cdx, ady, cdxady1, cdxady0);
+  Two_Product(adx, cdy, adxcdy1, adxcdy0);
+  Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+  ca[3] = ca3;
+  blen = scale_expansion_zeroelim(4, ca, bdz, bdet);
+
+  Two_Product(adx, bdy, adxbdy1, adxbdy0);
+  Two_Product(bdx, ady, bdxady1, bdxady0);
+  Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+  clen = scale_expansion_zeroelim(4, ab, cdz, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = o3derrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+  Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+  Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+  Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+  Two_Diff_Tail(pa[2], pd[2], adz, adztail);
+  Two_Diff_Tail(pb[2], pd[2], bdz, bdztail);
+  Two_Diff_Tail(pc[2], pd[2], cdz, cdztail);
+
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+      && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0)
+      && (adztail == 0.0) && (bdztail == 0.0) && (cdztail == 0.0))
+      {
+    return det;
+  }
+
+  errbound = o3derrboundC * permanent + resulterrbound * Absolute(det);
+  det += (adz * ((bdx * cdytail + cdy * bdxtail)
+                 - (bdy * cdxtail + cdx * bdytail))
+          + adztail * (bdx * cdy - bdy * cdx))
+       + (bdz * ((cdx * adytail + ady * cdxtail)
+                 - (cdy * adxtail + adx * cdytail))
+          + bdztail * (cdx * ady - cdy * adx))
+       + (cdz * ((adx * bdytail + bdy * adxtail)
+                 - (ady * bdxtail + bdx * adytail))
+          + cdztail * (adx * bdy - ady * bdx));
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  finnow = fin1;
+  finother = fin2;
+
+  if (adxtail == 0.0)
+  {
+    if (adytail == 0.0)
+    {
+      at_b[0] = 0.0;
+      at_blen = 1;
+      at_c[0] = 0.0;
+      at_clen = 1;
+    } else
+    {
+      negate = -adytail;
+      Two_Product(negate, bdx, at_blarge, at_b[0]);
+      at_b[1] = at_blarge;
+      at_blen = 2;
+      Two_Product(adytail, cdx, at_clarge, at_c[0]);
+      at_c[1] = at_clarge;
+      at_clen = 2;
+    }
+  } else
+  {
+    if (adytail == 0.0)
+    {
+      Two_Product(adxtail, bdy, at_blarge, at_b[0]);
+      at_b[1] = at_blarge;
+      at_blen = 2;
+      negate = -adxtail;
+      Two_Product(negate, cdy, at_clarge, at_c[0]);
+      at_c[1] = at_clarge;
+      at_clen = 2;
+    } else
+    {
+      Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0);
+      Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0);
+      Two_Two_Diff(adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0,
+                   at_blarge, at_b[2], at_b[1], at_b[0]);
+      at_b[3] = at_blarge;
+      at_blen = 4;
+      Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0);
+      Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0);
+      Two_Two_Diff(adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0,
+                   at_clarge, at_c[2], at_c[1], at_c[0]);
+      at_c[3] = at_clarge;
+      at_clen = 4;
+    }
+  }
+  if (bdxtail == 0.0)
+  {
+    if (bdytail == 0.0)
+    {
+      bt_c[0] = 0.0;
+      bt_clen = 1;
+      bt_a[0] = 0.0;
+      bt_alen = 1;
+    } else
+    {
+      negate = -bdytail;
+      Two_Product(negate, cdx, bt_clarge, bt_c[0]);
+      bt_c[1] = bt_clarge;
+      bt_clen = 2;
+      Two_Product(bdytail, adx, bt_alarge, bt_a[0]);
+      bt_a[1] = bt_alarge;
+      bt_alen = 2;
+    }
+  } else
+  {
+    if (bdytail == 0.0)
+    {
+      Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]);
+      bt_c[1] = bt_clarge;
+      bt_clen = 2;
+      negate = -bdxtail;
+      Two_Product(negate, ady, bt_alarge, bt_a[0]);
+      bt_a[1] = bt_alarge;
+      bt_alen = 2;
+    } else
+    {
+      Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0);
+      Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0);
+      Two_Two_Diff(bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0,
+                   bt_clarge, bt_c[2], bt_c[1], bt_c[0]);
+      bt_c[3] = bt_clarge;
+      bt_clen = 4;
+      Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0);
+      Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0);
+      Two_Two_Diff(bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0,
+                  bt_alarge, bt_a[2], bt_a[1], bt_a[0]);
+      bt_a[3] = bt_alarge;
+      bt_alen = 4;
+    }
+  }
+  if (cdxtail == 0.0)
+  {
+    if (cdytail == 0.0)
+    {
+      ct_a[0] = 0.0;
+      ct_alen = 1;
+      ct_b[0] = 0.0;
+      ct_blen = 1;
+    } else
+    {
+      negate = -cdytail;
+      Two_Product(negate, adx, ct_alarge, ct_a[0]);
+      ct_a[1] = ct_alarge;
+      ct_alen = 2;
+      Two_Product(cdytail, bdx, ct_blarge, ct_b[0]);
+      ct_b[1] = ct_blarge;
+      ct_blen = 2;
+    }
+  } else
+  {
+    if (cdytail == 0.0)
+    {
+      Two_Product(cdxtail, ady, ct_alarge, ct_a[0]);
+      ct_a[1] = ct_alarge;
+      ct_alen = 2;
+      negate = -cdxtail;
+      Two_Product(negate, bdy, ct_blarge, ct_b[0]);
+      ct_b[1] = ct_blarge;
+      ct_blen = 2;
+    } else
+    {
+      Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0);
+      Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0);
+      Two_Two_Diff(cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0,
+                   ct_alarge, ct_a[2], ct_a[1], ct_a[0]);
+      ct_a[3] = ct_alarge;
+      ct_alen = 4;
+      Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0);
+      Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0);
+      Two_Two_Diff(cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0,
+                   ct_blarge, ct_b[2], ct_b[1], ct_b[0]);
+      ct_b[3] = ct_blarge;
+      ct_blen = 4;
+    }
+  }
+
+  bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct);
+  wlength = scale_expansion_zeroelim(bctlen, bct, adz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat);
+  wlength = scale_expansion_zeroelim(catlen, cat, bdz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt);
+  wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w);
+  finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                          finother);
+  finswap = finnow; finnow = finother; finother = finswap;
+
+  if (adztail != 0.0)
+  {
+    vlength = scale_expansion_zeroelim(4, bc, adztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdztail != 0.0)
+  {
+    vlength = scale_expansion_zeroelim(4, ca, bdztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdztail != 0.0)
+  {
+    vlength = scale_expansion_zeroelim(4, ab, cdztail, v);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  if (adxtail != 0.0)
+  {
+    if (bdytail != 0.0)
+    {
+      Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0);
+      Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdztail != 0.0)
+      {
+        Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (cdytail != 0.0)
+    {
+      negate = -adxtail;
+      Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0);
+      Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdztail != 0.0)
+      {
+        Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+  if (bdxtail != 0.0)
+  {
+    if (cdytail != 0.0)
+    {
+      Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0);
+      Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adztail != 0.0)
+      {
+        Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (adytail != 0.0)
+    {
+      negate = -bdxtail;
+      Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0);
+      Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdztail != 0.0)
+      {
+        Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+  if (cdxtail != 0.0)
+  {
+    if (adytail != 0.0)
+    {
+      Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0);
+      Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdztail != 0.0)
+      {
+        Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+    if (bdytail != 0.0)
+    {
+      negate = -cdxtail;
+      Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0);
+      Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                              finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adztail != 0.0)
+      {
+        Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]);
+        u[3] = u3;
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u,
+                                                finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+    }
+  }
+
+  if (adztail != 0.0)
+  {
+    wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdztail != 0.0)
+  {
+    wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdztail != 0.0)
+  {
+    wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w,
+                                            finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  return finnow[finlength - 1];
+}
+
+REAL orient3d(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL det;
+  REAL permanent, errbound;
+
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+  adz = pa[2] - pd[2];
+  bdz = pb[2] - pd[2];
+  cdz = pc[2] - pd[2];
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+
+  det = adz * (bdxcdy - cdxbdy)
+      + bdz * (cdxady - adxcdy)
+      + cdz * (adxbdy - bdxady);
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz)
+            + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz)
+            + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz);
+  errbound = o3derrboundA * permanent;
+  if ((det > errbound) || (-det > errbound))
+  {
+    return det;
+  }
+
+  return orient3dadapt(pa, pb, pc, pd, permanent);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  incirclefast()   Approximate 2D incircle test.  Nonrobust.               */
+/*  incircleexact()   Exact 2D incircle test.  Robust.                       */
+/*  incircleslow()   Another exact 2D incircle test.  Robust.                */
+/*  incircle()   Adaptive exact 2D incircle test.  Robust.                   */
+/*                                                                           */
+/*               Return a positive value if the point pd lies inside the     */
+/*               circle passing through pa, pb, and pc; a negative value if  */
+/*               it lies outside; and zero if the four points are cocircular.*/
+/*               The points pa, pb, and pc must be in counterclockwise       */
+/*               order, or the sign of the result will be reversed.          */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In incircle() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, incircle() is usually quite */
+/*  fast, but will run more slowly when the input points are cocircular or   */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL incirclefast(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  REAL adx, ady, bdx, bdy, cdx, cdy;
+  REAL abdet, bcdet, cadet;
+  REAL alift, blift, clift;
+
+  adx = pa[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdx = pb[0] - pd[0];
+  bdy = pb[1] - pd[1];
+  cdx = pc[0] - pd[0];
+  cdy = pc[1] - pd[1];
+
+  abdet = adx * bdy - bdx * ady;
+  bcdet = bdx * cdy - cdx * bdy;
+  cadet = cdx * ady - adx * cdy;
+  alift = adx * adx + ady * ady;
+  blift = bdx * bdx + bdy * bdy;
+  clift = cdx * cdx + cdy * cdy;
+
+  return alift * bcdet + blift * cadet + clift * abdet;
+}
+
+REAL incircleexact(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  INEXACT REAL axby1, bxcy1, cxdy1, dxay1, axcy1, bxdy1;
+  INEXACT REAL bxay1, cxby1, dxcy1, axdy1, cxay1, dxby1;
+  REAL axby0, bxcy0, cxdy0, dxay0, axcy0, bxdy0;
+  REAL bxay0, cxby0, dxcy0, axdy0, cxay0, dxby0;
+  REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+  REAL temp8[8];
+  int templen;
+  REAL abc[12], bcd[12], cda[12], dab[12];
+  int abclen, bcdlen, cdalen, dablen;
+  REAL det24x[24], det24y[24], det48x[48], det48y[48];
+  int xlen, ylen;
+  REAL adet[96], bdet[96], cdet[96], ddet[96];
+  int alen, blen, clen, dlen;
+  REAL abdet[192], cddet[192];
+  int ablen, cdlen;
+  REAL deter[384];
+  int deterlen;
+  int i;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  Two_Product(pa[0], pb[1], axby1, axby0);
+  Two_Product(pb[0], pa[1], bxay1, bxay0);
+  Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
+
+  Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+  Two_Product(pc[0], pb[1], cxby1, cxby0);
+  Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
+
+  Two_Product(pc[0], pd[1], cxdy1, cxdy0);
+  Two_Product(pd[0], pc[1], dxcy1, dxcy0);
+  Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
+
+  Two_Product(pd[0], pa[1], dxay1, dxay0);
+  Two_Product(pa[0], pd[1], axdy1, axdy0);
+  Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
+
+  Two_Product(pa[0], pc[1], axcy1, axcy0);
+  Two_Product(pc[0], pa[1], cxay1, cxay0);
+  Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
+
+  Two_Product(pb[0], pd[1], bxdy1, bxdy0);
+  Two_Product(pd[0], pb[1], dxby1, dxby0);
+  Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
+
+  templen = fast_expansion_sum_zeroelim(4, cd, 4, da, temp8);
+  cdalen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, cda);
+  templen = fast_expansion_sum_zeroelim(4, da, 4, ab, temp8);
+  dablen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, dab);
+  for (i = 0; i < 4; i++)
+  {
+    bd[i] = -bd[i];
+    ac[i] = -ac[i];
+  }
+  templen = fast_expansion_sum_zeroelim(4, ab, 4, bc, temp8);
+  abclen = fast_expansion_sum_zeroelim(templen, temp8, 4, ac, abc);
+  templen = fast_expansion_sum_zeroelim(4, bc, 4, cd, temp8);
+  bcdlen = fast_expansion_sum_zeroelim(templen, temp8, 4, bd, bcd);
+
+  xlen = scale_expansion_zeroelim(bcdlen, bcd, pa[0], det24x);
+  xlen = scale_expansion_zeroelim(xlen, det24x, pa[0], det48x);
+  ylen = scale_expansion_zeroelim(bcdlen, bcd, pa[1], det24y);
+  ylen = scale_expansion_zeroelim(ylen, det24y, pa[1], det48y);
+  alen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, adet);
+
+  xlen = scale_expansion_zeroelim(cdalen, cda, pb[0], det24x);
+  xlen = scale_expansion_zeroelim(xlen, det24x, -pb[0], det48x);
+  ylen = scale_expansion_zeroelim(cdalen, cda, pb[1], det24y);
+  ylen = scale_expansion_zeroelim(ylen, det24y, -pb[1], det48y);
+  blen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, bdet);
+
+  xlen = scale_expansion_zeroelim(dablen, dab, pc[0], det24x);
+  xlen = scale_expansion_zeroelim(xlen, det24x, pc[0], det48x);
+  ylen = scale_expansion_zeroelim(dablen, dab, pc[1], det24y);
+  ylen = scale_expansion_zeroelim(ylen, det24y, pc[1], det48y);
+  clen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, cdet);
+
+  xlen = scale_expansion_zeroelim(abclen, abc, pd[0], det24x);
+  xlen = scale_expansion_zeroelim(xlen, det24x, -pd[0], det48x);
+  ylen = scale_expansion_zeroelim(abclen, abc, pd[1], det24y);
+  ylen = scale_expansion_zeroelim(ylen, det24y, -pd[1], det48y);
+  dlen = fast_expansion_sum_zeroelim(xlen, det48x, ylen, det48y, ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL incircleslow(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL adxtail, bdxtail, cdxtail;
+  REAL adytail, bdytail, cdytail;
+  REAL negate, negatetail;
+  INEXACT REAL axby7, bxcy7, axcy7, bxay7, cxby7, cxay7;
+  REAL axby[8], bxcy[8], axcy[8], bxay[8], cxby[8], cxay[8];
+  REAL temp16[16];
+  int temp16len;
+  REAL detx[32], detxx[64], detxt[32], detxxt[64], detxtxt[64];
+  int xlen, xxlen, xtlen, xxtlen, xtxtlen;
+  REAL x1[128], x2[192];
+  int x1len, x2len;
+  REAL dety[32], detyy[64], detyt[32], detyyt[64], detytyt[64];
+  int ylen, yylen, ytlen, yytlen, ytytlen;
+  REAL y1[128], y2[192];
+  int y1len, y2len;
+  REAL adet[384], bdet[384], cdet[384], abdet[768], deter[1152];
+  int alen, blen, clen, ablen, deterlen;
+  int i;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k, _l, _m, _n;
+  REAL _0, _1, _2;
+
+  Two_Diff(pa[0], pd[0], adx, adxtail);
+  Two_Diff(pa[1], pd[1], ady, adytail);
+  Two_Diff(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff(pb[1], pd[1], bdy, bdytail);
+  Two_Diff(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff(pc[1], pd[1], cdy, cdytail);
+
+  Two_Two_Product(adx, adxtail, bdy, bdytail,
+                  axby7, axby[6], axby[5], axby[4],
+                  axby[3], axby[2], axby[1], axby[0]);
+  axby[7] = axby7;
+  negate = -ady;
+  negatetail = -adytail;
+  Two_Two_Product(bdx, bdxtail, negate, negatetail,
+                  bxay7, bxay[6], bxay[5], bxay[4],
+                  bxay[3], bxay[2], bxay[1], bxay[0]);
+  bxay[7] = bxay7;
+  Two_Two_Product(bdx, bdxtail, cdy, cdytail,
+                  bxcy7, bxcy[6], bxcy[5], bxcy[4],
+                  bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
+  bxcy[7] = bxcy7;
+  negate = -bdy;
+  negatetail = -bdytail;
+  Two_Two_Product(cdx, cdxtail, negate, negatetail,
+                  cxby7, cxby[6], cxby[5], cxby[4],
+                  cxby[3], cxby[2], cxby[1], cxby[0]);
+  cxby[7] = cxby7;
+  Two_Two_Product(cdx, cdxtail, ady, adytail,
+                  cxay7, cxay[6], cxay[5], cxay[4],
+                  cxay[3], cxay[2], cxay[1], cxay[0]);
+  cxay[7] = cxay7;
+  negate = -cdy;
+  negatetail = -cdytail;
+  Two_Two_Product(adx, adxtail, negate, negatetail,
+                  axcy7, axcy[6], axcy[5], axcy[4],
+                  axcy[3], axcy[2], axcy[1], axcy[0]);
+  axcy[7] = axcy7;
+
+
+  temp16len = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, temp16);
+
+  xlen = scale_expansion_zeroelim(temp16len, temp16, adx, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, adx, detxx);
+  xtlen = scale_expansion_zeroelim(temp16len, temp16, adxtail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, adx, detxxt);
+  for (i = 0; i < xxtlen; i++)
+  {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, adxtail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+
+  ylen = scale_expansion_zeroelim(temp16len, temp16, ady, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, ady, detyy);
+  ytlen = scale_expansion_zeroelim(temp16len, temp16, adytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, ady, detyyt);
+  for (i = 0; i < yytlen; i++)
+  {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, adytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+
+  alen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, adet);
+
+
+  temp16len = fast_expansion_sum_zeroelim(8, cxay, 8, axcy, temp16);
+
+  xlen = scale_expansion_zeroelim(temp16len, temp16, bdx, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, bdx, detxx);
+  xtlen = scale_expansion_zeroelim(temp16len, temp16, bdxtail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, bdx, detxxt);
+  for (i = 0; i < xxtlen; i++)
+  {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, bdxtail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+
+  ylen = scale_expansion_zeroelim(temp16len, temp16, bdy, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, bdy, detyy);
+  ytlen = scale_expansion_zeroelim(temp16len, temp16, bdytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, bdy, detyyt);
+  for (i = 0; i < yytlen; i++)
+  {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, bdytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+
+  blen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, bdet);
+
+
+  temp16len = fast_expansion_sum_zeroelim(8, axby, 8, bxay, temp16);
+
+  xlen = scale_expansion_zeroelim(temp16len, temp16, cdx, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, cdx, detxx);
+  xtlen = scale_expansion_zeroelim(temp16len, temp16, cdxtail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, cdx, detxxt);
+  for (i = 0; i < xxtlen; i++)
+  {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, cdxtail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+
+  ylen = scale_expansion_zeroelim(temp16len, temp16, cdy, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, cdy, detyy);
+  ytlen = scale_expansion_zeroelim(temp16len, temp16, cdytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, cdy, detyyt);
+  for (i = 0; i < yytlen; i++)
+  {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, cdytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+
+  clen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL incircleadapt(pa, pb, pc, pd, permanent)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+REAL permanent;
+{
+  INEXACT REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL det, errbound;
+
+  INEXACT REAL bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1;
+  REAL bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0;
+  REAL bc[4], ca[4], ab[4];
+  INEXACT REAL bc3, ca3, ab3;
+  REAL axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32];
+  int axbclen, axxbclen, aybclen, ayybclen, alen;
+  REAL bxca[8], bxxca[16], byca[8], byyca[16], bdet[32];
+  int bxcalen, bxxcalen, bycalen, byycalen, blen;
+  REAL cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32];
+  int cxablen, cxxablen, cyablen, cyyablen, clen;
+  REAL abdet[64];
+  int ablen;
+  REAL fin1[1152], fin2[1152];
+  REAL *finnow, *finother, *finswap;
+  int finlength;
+
+  REAL adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail;
+  INEXACT REAL adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1;
+  REAL adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0;
+  REAL aa[4], bb[4], cc[4];
+  INEXACT REAL aa3, bb3, cc3;
+  INEXACT REAL ti1, tj1;
+  REAL ti0, tj0;
+  REAL u[4], v[4];
+  INEXACT REAL u3, v3;
+  REAL temp8[8], temp16a[16], temp16b[16], temp16c[16];
+  REAL temp32a[32], temp32b[32], temp48[48], temp64[64];
+  int temp8len, temp16alen, temp16blen, temp16clen;
+  int temp32alen, temp32blen, temp48len, temp64len;
+  REAL axtbb[8], axtcc[8], aytbb[8], aytcc[8];
+  int axtbblen, axtcclen, aytbblen, aytcclen;
+  REAL bxtaa[8], bxtcc[8], bytaa[8], bytcc[8];
+  int bxtaalen, bxtcclen, bytaalen, bytcclen;
+  REAL cxtaa[8], cxtbb[8], cytaa[8], cytbb[8];
+  int cxtaalen, cxtbblen, cytaalen, cytbblen;
+  REAL axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8];
+  int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen;
+  REAL axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16];
+  int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen;
+  REAL axtbctt[8], aytbctt[8], bxtcatt[8];
+  REAL bytcatt[8], cxtabtt[8], cytabtt[8];
+  int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen;
+  REAL abt[8], bct[8], cat[8];
+  int abtlen, bctlen, catlen;
+  REAL abtt[4], bctt[4], catt[4];
+  int abttlen, bcttlen, cattlen;
+  INEXACT REAL abtt3, bctt3, catt3;
+  REAL negate;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  adx = (REAL) (pa[0] - pd[0]);
+  bdx = (REAL) (pb[0] - pd[0]);
+  cdx = (REAL) (pc[0] - pd[0]);
+  ady = (REAL) (pa[1] - pd[1]);
+  bdy = (REAL) (pb[1] - pd[1]);
+  cdy = (REAL) (pc[1] - pd[1]);
+
+  Two_Product(bdx, cdy, bdxcdy1, bdxcdy0);
+  Two_Product(cdx, bdy, cdxbdy1, cdxbdy0);
+  Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+  axbclen = scale_expansion_zeroelim(4, bc, adx, axbc);
+  axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc);
+  aybclen = scale_expansion_zeroelim(4, bc, ady, aybc);
+  ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc);
+  alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet);
+
+  Two_Product(cdx, ady, cdxady1, cdxady0);
+  Two_Product(adx, cdy, adxcdy1, adxcdy0);
+  Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]);
+  ca[3] = ca3;
+  bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca);
+  bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca);
+  bycalen = scale_expansion_zeroelim(4, ca, bdy, byca);
+  byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca);
+  blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet);
+
+  Two_Product(adx, bdy, adxbdy1, adxbdy0);
+  Two_Product(bdx, ady, bdxady1, bdxady0);
+  Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+  cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab);
+  cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab);
+  cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab);
+  cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab);
+  clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = iccerrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pd[0], adx, adxtail);
+  Two_Diff_Tail(pa[1], pd[1], ady, adytail);
+  Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail);
+  Two_Diff_Tail(pb[1], pd[1], bdy, bdytail);
+  Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail);
+  Two_Diff_Tail(pc[1], pd[1], cdy, cdytail);
+  if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0)
+      && (adytail == 0.0) && (bdytail == 0.0) && (cdytail == 0.0))
+      {
+    return det;
+  }
+
+  errbound = iccerrboundC * permanent + resulterrbound * Absolute(det);
+  det += ((adx * adx + ady * ady) * ((bdx * cdytail + cdy * bdxtail)
+                                     - (bdy * cdxtail + cdx * bdytail))
+          + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx))
+       + ((bdx * bdx + bdy * bdy) * ((cdx * adytail + ady * cdxtail)
+                                     - (cdy * adxtail + adx * cdytail))
+          + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx))
+       + ((cdx * cdx + cdy * cdy) * ((adx * bdytail + bdy * adxtail)
+                                     - (ady * bdxtail + bdx * adytail))
+          + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx));
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  finnow = fin1;
+  finother = fin2;
+
+  if ((bdxtail != 0.0) || (bdytail != 0.0)
+      || (cdxtail != 0.0) || (cdytail != 0.0))
+      {
+    Square(adx, adxadx1, adxadx0);
+    Square(ady, adyady1, adyady0);
+    Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]);
+    aa[3] = aa3;
+  }
+  if ((cdxtail != 0.0) || (cdytail != 0.0)
+      || (adxtail != 0.0) || (adytail != 0.0))
+      {
+    Square(bdx, bdxbdx1, bdxbdx0);
+    Square(bdy, bdybdy1, bdybdy0);
+    Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]);
+    bb[3] = bb3;
+  }
+  if ((adxtail != 0.0) || (adytail != 0.0)
+      || (bdxtail != 0.0) || (bdytail != 0.0))
+      {
+    Square(cdx, cdxcdx1, cdxcdx0);
+    Square(cdy, cdycdy1, cdycdy0);
+    Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]);
+    cc[3] = cc3;
+  }
+
+  if (adxtail != 0.0)
+  {
+    axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc);
+    temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx,
+                                          temp16a);
+
+    axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc);
+    temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b);
+
+    axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb);
+    temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (adytail != 0.0)
+  {
+    aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc);
+    temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady,
+                                          temp16a);
+
+    aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb);
+    temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b);
+
+    aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc);
+    temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdxtail != 0.0)
+  {
+    bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca);
+    temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx,
+                                          temp16a);
+
+    bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa);
+    temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b);
+
+    bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc);
+    temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (bdytail != 0.0)
+  {
+    bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca);
+    temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy,
+                                          temp16a);
+
+    bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc);
+    temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b);
+
+    bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa);
+    temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdxtail != 0.0)
+  {
+    cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab);
+    temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx,
+                                          temp16a);
+
+    cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb);
+    temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b);
+
+    cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa);
+    temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+  if (cdytail != 0.0)
+  {
+    cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab);
+    temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy,
+                                          temp16a);
+
+    cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa);
+    temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b);
+
+    cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb);
+    temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c);
+
+    temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                            temp16blen, temp16b, temp32a);
+    temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c,
+                                            temp32alen, temp32a, temp48);
+    finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                            temp48, finother);
+    finswap = finnow; finnow = finother; finother = finswap;
+  }
+
+  if ((adxtail != 0.0) || (adytail != 0.0))
+  {
+    if ((bdxtail != 0.0) || (bdytail != 0.0)
+        || (cdxtail != 0.0) || (cdytail != 0.0))
+        {
+      Two_Product(bdxtail, cdy, ti1, ti0);
+      Two_Product(bdx, cdytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -bdy;
+      Two_Product(cdxtail, negate, ti1, ti0);
+      negate = -bdytail;
+      Two_Product(cdx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct);
+
+      Two_Product(bdxtail, cdytail, ti1, ti0);
+      Two_Product(cdxtail, bdytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]);
+      bctt[3] = bctt3;
+      bcttlen = 4;
+    } else
+    {
+      bct[0] = 0.0;
+      bctlen = 1;
+      bctt[0] = 0.0;
+      bcttlen = 1;
+    }
+
+    if (adxtail != 0.0)
+    {
+      temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a);
+      axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct);
+      temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (bdytail != 0.0)
+      {
+        temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (cdytail != 0.0)
+      {
+        temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail,
+                                            temp32a);
+      axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt);
+      temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (adytail != 0.0)
+    {
+      temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a);
+      aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct);
+      temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail,
+                                            temp32a);
+      aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt);
+      temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+  if ((bdxtail != 0.0) || (bdytail != 0.0))
+  {
+    if ((cdxtail != 0.0) || (cdytail != 0.0)
+        || (adxtail != 0.0) || (adytail != 0.0))
+        {
+      Two_Product(cdxtail, ady, ti1, ti0);
+      Two_Product(cdx, adytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -cdy;
+      Two_Product(adxtail, negate, ti1, ti0);
+      negate = -cdytail;
+      Two_Product(adx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat);
+
+      Two_Product(cdxtail, adytail, ti1, ti0);
+      Two_Product(adxtail, cdytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]);
+      catt[3] = catt3;
+      cattlen = 4;
+    } else
+    {
+      cat[0] = 0.0;
+      catlen = 1;
+      catt[0] = 0.0;
+      cattlen = 1;
+    }
+
+    if (bdxtail != 0.0)
+    {
+      temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a);
+      bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat);
+      temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (cdytail != 0.0)
+      {
+        temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (adytail != 0.0)
+      {
+        temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail,
+                                            temp32a);
+      bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt);
+      temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (bdytail != 0.0)
+    {
+      temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a);
+      bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat);
+      temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail,
+                                            temp32a);
+      bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt);
+      temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+  if ((cdxtail != 0.0) || (cdytail != 0.0))
+  {
+    if ((adxtail != 0.0) || (adytail != 0.0)
+        || (bdxtail != 0.0) || (bdytail != 0.0))
+        {
+      Two_Product(adxtail, bdy, ti1, ti0);
+      Two_Product(adx, bdytail, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]);
+      u[3] = u3;
+      negate = -ady;
+      Two_Product(bdxtail, negate, ti1, ti0);
+      negate = -adytail;
+      Two_Product(bdx, negate, tj1, tj0);
+      Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]);
+      v[3] = v3;
+      abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt);
+
+      Two_Product(adxtail, bdytail, ti1, ti0);
+      Two_Product(bdxtail, adytail, tj1, tj0);
+      Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]);
+      abtt[3] = abtt3;
+      abttlen = 4;
+    } else
+    {
+      abt[0] = 0.0;
+      abtlen = 1;
+      abtt[0] = 0.0;
+      abttlen = 1;
+    }
+
+    if (cdxtail != 0.0)
+    {
+      temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a);
+      cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt);
+      temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+      if (adytail != 0.0)
+      {
+        temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+      if (bdytail != 0.0)
+      {
+        temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8);
+        temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail,
+                                              temp16a);
+        finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen,
+                                                temp16a, finother);
+        finswap = finnow; finnow = finother; finother = finswap;
+      }
+
+      temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail,
+                                            temp32a);
+      cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt);
+      temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+    if (cdytail != 0.0)
+    {
+      temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a);
+      cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt);
+      temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy,
+                                            temp32a);
+      temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp32alen, temp32a, temp48);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len,
+                                              temp48, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+
+
+      temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail,
+                                            temp32a);
+      cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt);
+      temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy,
+                                            temp16a);
+      temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail,
+                                            temp16b);
+      temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a,
+                                              temp16blen, temp16b, temp32b);
+      temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                              temp32blen, temp32b, temp64);
+      finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len,
+                                              temp64, finother);
+      finswap = finnow; finnow = finother; finother = finswap;
+    }
+  }
+
+  return finnow[finlength - 1];
+}
+
+REAL incircle(pa, pb, pc, pd)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+{
+  REAL adx, bdx, cdx, ady, bdy, cdy;
+  REAL bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady;
+  REAL alift, blift, clift;
+  REAL det;
+  REAL permanent, errbound;
+
+  adx = pa[0] - pd[0];
+  bdx = pb[0] - pd[0];
+  cdx = pc[0] - pd[0];
+  ady = pa[1] - pd[1];
+  bdy = pb[1] - pd[1];
+  cdy = pc[1] - pd[1];
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+  alift = adx * adx + ady * ady;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+  blift = bdx * bdx + bdy * bdy;
+
+  adxbdy = adx * bdy;
+  bdxady = bdx * ady;
+  clift = cdx * cdx + cdy * cdy;
+
+  det = alift * (bdxcdy - cdxbdy)
+      + blift * (cdxady - adxcdy)
+      + clift * (adxbdy - bdxady);
+
+  permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift
+            + (Absolute(cdxady) + Absolute(adxcdy)) * blift
+            + (Absolute(adxbdy) + Absolute(bdxady)) * clift;
+  errbound = iccerrboundA * permanent;
+  if ((det > errbound) || (-det > errbound))
+  {
+    return det;
+  }
+
+  return incircleadapt(pa, pb, pc, pd, permanent);
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  inspherefast()   Approximate 3D insphere test.  Nonrobust.               */
+/*  insphereexact()   Exact 3D insphere test.  Robust.                       */
+/*  insphereslow()   Another exact 3D insphere test.  Robust.                */
+/*  insphere()   Adaptive exact 3D insphere test.  Robust.                   */
+/*                                                                           */
+/*               Return a positive value if the point pe lies inside the     */
+/*               sphere passing through pa, pb, pc, and pd; a negative value */
+/*               if it lies outside; and zero if the five points are         */
+/*               cospherical.  The points pa, pb, pc, and pd must be ordered */
+/*               so that they have a positive orientation (as defined by     */
+/*               orient3d()), or the sign of the result will be reversed.    */
+/*                                                                           */
+/*  Only the first and last routine should be used; the middle two are for   */
+/*  timings.                                                                 */
+/*                                                                           */
+/*  The last three use exact arithmetic to ensure a correct answer.  The     */
+/*  result returned is the determinant of a matrix.  In insphere() only,     */
+/*  this determinant is computed adaptively, in the sense that exact         */
+/*  arithmetic is used only to the degree it is needed to ensure that the    */
+/*  returned value has the correct sign.  Hence, insphere() is usually quite */
+/*  fast, but will run more slowly when the input points are cospherical or  */
+/*  nearly so.                                                               */
+/*                                                                           */
+/*****************************************************************************/
+
+REAL inspherefast(pa, pb, pc, pd, pe)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+REAL *pe;
+{
+  REAL aex, bex, cex, dex;
+  REAL aey, bey, cey, dey;
+  REAL aez, bez, cez, dez;
+  REAL alift, blift, clift, dlift;
+  REAL ab, bc, cd, da, ac, bd;
+  REAL abc, bcd, cda, dab;
+
+  aex = pa[0] - pe[0];
+  bex = pb[0] - pe[0];
+  cex = pc[0] - pe[0];
+  dex = pd[0] - pe[0];
+  aey = pa[1] - pe[1];
+  bey = pb[1] - pe[1];
+  cey = pc[1] - pe[1];
+  dey = pd[1] - pe[1];
+  aez = pa[2] - pe[2];
+  bez = pb[2] - pe[2];
+  cez = pc[2] - pe[2];
+  dez = pd[2] - pe[2];
+
+  ab = aex * bey - bex * aey;
+  bc = bex * cey - cex * bey;
+  cd = cex * dey - dex * cey;
+  da = dex * aey - aex * dey;
+
+  ac = aex * cey - cex * aey;
+  bd = bex * dey - dex * bey;
+
+  abc = aez * bc - bez * ac + cez * ab;
+  bcd = bez * cd - cez * bd + dez * bc;
+  cda = cez * da + dez * ac + aez * cd;
+  dab = dez * ab + aez * bd + bez * da;
+
+  alift = aex * aex + aey * aey + aez * aez;
+  blift = bex * bex + bey * bey + bez * bez;
+  clift = cex * cex + cey * cey + cez * cez;
+  dlift = dex * dex + dey * dey + dez * dez;
+
+  return (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+}
+
+REAL insphereexact(pa, pb, pc, pd, pe)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+REAL *pe;
+{
+  INEXACT REAL axby1, bxcy1, cxdy1, dxey1, exay1;
+  INEXACT REAL bxay1, cxby1, dxcy1, exdy1, axey1;
+  INEXACT REAL axcy1, bxdy1, cxey1, dxay1, exby1;
+  INEXACT REAL cxay1, dxby1, excy1, axdy1, bxey1;
+  REAL axby0, bxcy0, cxdy0, dxey0, exay0;
+  REAL bxay0, cxby0, dxcy0, exdy0, axey0;
+  REAL axcy0, bxdy0, cxey0, dxay0, exby0;
+  REAL cxay0, dxby0, excy0, axdy0, bxey0;
+  REAL ab[4], bc[4], cd[4], de[4], ea[4];
+  REAL ac[4], bd[4], ce[4], da[4], eb[4];
+  REAL temp8a[8], temp8b[8], temp16[16];
+  int temp8alen, temp8blen, temp16len;
+  REAL abc[24], bcd[24], cde[24], dea[24], eab[24];
+  REAL abd[24], bce[24], cda[24], deb[24], eac[24];
+  int abclen, bcdlen, cdelen, dealen, eablen;
+  int abdlen, bcelen, cdalen, deblen, eaclen;
+  REAL temp48a[48], temp48b[48];
+  int temp48alen, temp48blen;
+  REAL abcd[96], bcde[96], cdea[96], deab[96], eabc[96];
+  int abcdlen, bcdelen, cdealen, deablen, eabclen;
+  REAL temp192[192];
+  REAL det384x[384], det384y[384], det384z[384];
+  int xlen, ylen, zlen;
+  REAL detxy[768];
+  int xylen;
+  REAL adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152];
+  int alen, blen, clen, dlen, elen;
+  REAL abdet[2304], cddet[2304], cdedet[3456];
+  int ablen, cdlen;
+  REAL deter[5760];
+  int deterlen;
+  int i;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  Two_Product(pa[0], pb[1], axby1, axby0);
+  Two_Product(pb[0], pa[1], bxay1, bxay0);
+  Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]);
+
+  Two_Product(pb[0], pc[1], bxcy1, bxcy0);
+  Two_Product(pc[0], pb[1], cxby1, cxby0);
+  Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]);
+
+  Two_Product(pc[0], pd[1], cxdy1, cxdy0);
+  Two_Product(pd[0], pc[1], dxcy1, dxcy0);
+  Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]);
+
+  Two_Product(pd[0], pe[1], dxey1, dxey0);
+  Two_Product(pe[0], pd[1], exdy1, exdy0);
+  Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]);
+
+  Two_Product(pe[0], pa[1], exay1, exay0);
+  Two_Product(pa[0], pe[1], axey1, axey0);
+  Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]);
+
+  Two_Product(pa[0], pc[1], axcy1, axcy0);
+  Two_Product(pc[0], pa[1], cxay1, cxay0);
+  Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]);
+
+  Two_Product(pb[0], pd[1], bxdy1, bxdy0);
+  Two_Product(pd[0], pb[1], dxby1, dxby0);
+  Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]);
+
+  Two_Product(pc[0], pe[1], cxey1, cxey0);
+  Two_Product(pe[0], pc[1], excy1, excy0);
+  Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]);
+
+  Two_Product(pd[0], pa[1], dxay1, dxay0);
+  Two_Product(pa[0], pd[1], axdy1, axdy0);
+  Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]);
+
+  Two_Product(pe[0], pb[1], exby1, exby0);
+  Two_Product(pb[0], pe[1], bxey1, bxey0);
+  Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]);
+
+  temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a);
+  abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       abc);
+
+  temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a);
+  bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       bcd);
+
+  temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a);
+  cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       cde);
+
+  temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a);
+  dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       dea);
+
+  temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a);
+  eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       eab);
+
+  temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a);
+  abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       abd);
+
+  temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a);
+  bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       bce);
+
+  temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a);
+  cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       cda);
+
+  temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a);
+  deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       deb);
+
+  temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b,
+                                          temp16);
+  temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a);
+  eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16,
+                                       eac);
+
+  temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b);
+  for (i = 0; i < temp48blen; i++)
+  {
+    temp48b[i] = -temp48b[i];
+  }
+  bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, bcde);
+  xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x);
+  ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y);
+  zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet);
+
+  temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b);
+  for (i = 0; i < temp48blen; i++)
+  {
+    temp48b[i] = -temp48b[i];
+  }
+  cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, cdea);
+  xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x);
+  ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y);
+  zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet);
+
+  temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b);
+  for (i = 0; i < temp48blen; i++)
+  {
+    temp48b[i] = -temp48b[i];
+  }
+  deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, deab);
+  xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x);
+  ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y);
+  zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet);
+
+  temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b);
+  for (i = 0; i < temp48blen; i++)
+  {
+    temp48b[i] = -temp48b[i];
+  }
+  eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, eabc);
+  xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x);
+  ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y);
+  zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet);
+
+  temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a);
+  temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b);
+  for (i = 0; i < temp48blen; i++)
+  {
+    temp48b[i] = -temp48b[i];
+  }
+  abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a,
+                                        temp48blen, temp48b, abcd);
+  xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192);
+  xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x);
+  ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192);
+  ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y);
+  zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192);
+  zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z);
+  xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy);
+  elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL insphereslow(pa, pb, pc, pd, pe)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+REAL *pe;
+{
+  INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
+  REAL aextail, bextail, cextail, dextail;
+  REAL aeytail, beytail, ceytail, deytail;
+  REAL aeztail, beztail, ceztail, deztail;
+  REAL negate, negatetail;
+  INEXACT REAL axby7, bxcy7, cxdy7, dxay7, axcy7, bxdy7;
+  INEXACT REAL bxay7, cxby7, dxcy7, axdy7, cxay7, dxby7;
+  REAL axby[8], bxcy[8], cxdy[8], dxay[8], axcy[8], bxdy[8];
+  REAL bxay[8], cxby[8], dxcy[8], axdy[8], cxay[8], dxby[8];
+  REAL ab[16], bc[16], cd[16], da[16], ac[16], bd[16];
+  int ablen, bclen, cdlen, dalen, aclen, bdlen;
+  REAL temp32a[32], temp32b[32], temp64a[64], temp64b[64], temp64c[64];
+  int temp32alen, temp32blen, temp64alen, temp64blen, temp64clen;
+  REAL temp128[128], temp192[192];
+  int temp128len, temp192len;
+  REAL detx[384], detxx[768], detxt[384], detxxt[768], detxtxt[768];
+  int xlen, xxlen, xtlen, xxtlen, xtxtlen;
+  REAL x1[1536], x2[2304];
+  int x1len, x2len;
+  REAL dety[384], detyy[768], detyt[384], detyyt[768], detytyt[768];
+  int ylen, yylen, ytlen, yytlen, ytytlen;
+  REAL y1[1536], y2[2304];
+  int y1len, y2len;
+  REAL detz[384], detzz[768], detzt[384], detzzt[768], detztzt[768];
+  int zlen, zzlen, ztlen, zztlen, ztztlen;
+  REAL z1[1536], z2[2304];
+  int z1len, z2len;
+  REAL detxy[4608];
+  int xylen;
+  REAL adet[6912], bdet[6912], cdet[6912], ddet[6912];
+  int alen, blen, clen, dlen;
+  REAL abdet[13824], cddet[13824], deter[27648];
+  int deterlen;
+  int i;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL a0hi, a0lo, a1hi, a1lo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j, _k, _l, _m, _n;
+  REAL _0, _1, _2;
+
+  Two_Diff(pa[0], pe[0], aex, aextail);
+  Two_Diff(pa[1], pe[1], aey, aeytail);
+  Two_Diff(pa[2], pe[2], aez, aeztail);
+  Two_Diff(pb[0], pe[0], bex, bextail);
+  Two_Diff(pb[1], pe[1], bey, beytail);
+  Two_Diff(pb[2], pe[2], bez, beztail);
+  Two_Diff(pc[0], pe[0], cex, cextail);
+  Two_Diff(pc[1], pe[1], cey, ceytail);
+  Two_Diff(pc[2], pe[2], cez, ceztail);
+  Two_Diff(pd[0], pe[0], dex, dextail);
+  Two_Diff(pd[1], pe[1], dey, deytail);
+  Two_Diff(pd[2], pe[2], dez, deztail);
+
+  Two_Two_Product(aex, aextail, bey, beytail,
+                  axby7, axby[6], axby[5], axby[4],
+                  axby[3], axby[2], axby[1], axby[0]);
+  axby[7] = axby7;
+  negate = -aey;
+  negatetail = -aeytail;
+  Two_Two_Product(bex, bextail, negate, negatetail,
+                  bxay7, bxay[6], bxay[5], bxay[4],
+                  bxay[3], bxay[2], bxay[1], bxay[0]);
+  bxay[7] = bxay7;
+  ablen = fast_expansion_sum_zeroelim(8, axby, 8, bxay, ab);
+  Two_Two_Product(bex, bextail, cey, ceytail,
+                  bxcy7, bxcy[6], bxcy[5], bxcy[4],
+                  bxcy[3], bxcy[2], bxcy[1], bxcy[0]);
+  bxcy[7] = bxcy7;
+  negate = -bey;
+  negatetail = -beytail;
+  Two_Two_Product(cex, cextail, negate, negatetail,
+                  cxby7, cxby[6], cxby[5], cxby[4],
+                  cxby[3], cxby[2], cxby[1], cxby[0]);
+  cxby[7] = cxby7;
+  bclen = fast_expansion_sum_zeroelim(8, bxcy, 8, cxby, bc);
+  Two_Two_Product(cex, cextail, dey, deytail,
+                  cxdy7, cxdy[6], cxdy[5], cxdy[4],
+                  cxdy[3], cxdy[2], cxdy[1], cxdy[0]);
+  cxdy[7] = cxdy7;
+  negate = -cey;
+  negatetail = -ceytail;
+  Two_Two_Product(dex, dextail, negate, negatetail,
+                  dxcy7, dxcy[6], dxcy[5], dxcy[4],
+                  dxcy[3], dxcy[2], dxcy[1], dxcy[0]);
+  dxcy[7] = dxcy7;
+  cdlen = fast_expansion_sum_zeroelim(8, cxdy, 8, dxcy, cd);
+  Two_Two_Product(dex, dextail, aey, aeytail,
+                  dxay7, dxay[6], dxay[5], dxay[4],
+                  dxay[3], dxay[2], dxay[1], dxay[0]);
+  dxay[7] = dxay7;
+  negate = -dey;
+  negatetail = -deytail;
+  Two_Two_Product(aex, aextail, negate, negatetail,
+                  axdy7, axdy[6], axdy[5], axdy[4],
+                  axdy[3], axdy[2], axdy[1], axdy[0]);
+  axdy[7] = axdy7;
+  dalen = fast_expansion_sum_zeroelim(8, dxay, 8, axdy, da);
+  Two_Two_Product(aex, aextail, cey, ceytail,
+                  axcy7, axcy[6], axcy[5], axcy[4],
+                  axcy[3], axcy[2], axcy[1], axcy[0]);
+  axcy[7] = axcy7;
+  negate = -aey;
+  negatetail = -aeytail;
+  Two_Two_Product(cex, cextail, negate, negatetail,
+                  cxay7, cxay[6], cxay[5], cxay[4],
+                  cxay[3], cxay[2], cxay[1], cxay[0]);
+  cxay[7] = cxay7;
+  aclen = fast_expansion_sum_zeroelim(8, axcy, 8, cxay, ac);
+  Two_Two_Product(bex, bextail, dey, deytail,
+                  bxdy7, bxdy[6], bxdy[5], bxdy[4],
+                  bxdy[3], bxdy[2], bxdy[1], bxdy[0]);
+  bxdy[7] = bxdy7;
+  negate = -bey;
+  negatetail = -beytail;
+  Two_Two_Product(dex, dextail, negate, negatetail,
+                  dxby7, dxby[6], dxby[5], dxby[4],
+                  dxby[3], dxby[2], dxby[1], dxby[0]);
+  dxby[7] = dxby7;
+  bdlen = fast_expansion_sum_zeroelim(8, bxdy, 8, dxby, bd);
+
+  temp32alen = scale_expansion_zeroelim(cdlen, cd, -bez, temp32a);
+  temp32blen = scale_expansion_zeroelim(cdlen, cd, -beztail, temp32b);
+  temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64a);
+  temp32alen = scale_expansion_zeroelim(bdlen, bd, cez, temp32a);
+  temp32blen = scale_expansion_zeroelim(bdlen, bd, ceztail, temp32b);
+  temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64b);
+  temp32alen = scale_expansion_zeroelim(bclen, bc, -dez, temp32a);
+  temp32blen = scale_expansion_zeroelim(bclen, bc, -deztail, temp32b);
+  temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64c);
+  temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
+                                           temp64blen, temp64b, temp128);
+  temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
+                                           temp128len, temp128, temp192);
+  xlen = scale_expansion_zeroelim(temp192len, temp192, aex, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, aex, detxx);
+  xtlen = scale_expansion_zeroelim(temp192len, temp192, aextail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, aex, detxxt);
+  for (i = 0; i < xxtlen; i++)
+  {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, aextail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+  ylen = scale_expansion_zeroelim(temp192len, temp192, aey, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, aey, detyy);
+  ytlen = scale_expansion_zeroelim(temp192len, temp192, aeytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, aey, detyyt);
+  for (i = 0; i < yytlen; i++)
+  {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, aeytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+  zlen = scale_expansion_zeroelim(temp192len, temp192, aez, detz);
+  zzlen = scale_expansion_zeroelim(zlen, detz, aez, detzz);
+  ztlen = scale_expansion_zeroelim(temp192len, temp192, aeztail, detzt);
+  zztlen = scale_expansion_zeroelim(ztlen, detzt, aez, detzzt);
+  for (i = 0; i < zztlen; i++)
+  {
+    detzzt[i] *= 2.0;
+  }
+  ztztlen = scale_expansion_zeroelim(ztlen, detzt, aeztail, detztzt);
+  z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
+  z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
+  xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
+  alen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, adet);
+
+  temp32alen = scale_expansion_zeroelim(dalen, da, cez, temp32a);
+  temp32blen = scale_expansion_zeroelim(dalen, da, ceztail, temp32b);
+  temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64a);
+  temp32alen = scale_expansion_zeroelim(aclen, ac, dez, temp32a);
+  temp32blen = scale_expansion_zeroelim(aclen, ac, deztail, temp32b);
+  temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64b);
+  temp32alen = scale_expansion_zeroelim(cdlen, cd, aez, temp32a);
+  temp32blen = scale_expansion_zeroelim(cdlen, cd, aeztail, temp32b);
+  temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64c);
+  temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
+                                           temp64blen, temp64b, temp128);
+  temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
+                                           temp128len, temp128, temp192);
+  xlen = scale_expansion_zeroelim(temp192len, temp192, bex, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, bex, detxx);
+  xtlen = scale_expansion_zeroelim(temp192len, temp192, bextail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, bex, detxxt);
+  for (i = 0; i < xxtlen; i++)
+  {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, bextail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+  ylen = scale_expansion_zeroelim(temp192len, temp192, bey, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, bey, detyy);
+  ytlen = scale_expansion_zeroelim(temp192len, temp192, beytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, bey, detyyt);
+  for (i = 0; i < yytlen; i++)
+  {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, beytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+  zlen = scale_expansion_zeroelim(temp192len, temp192, bez, detz);
+  zzlen = scale_expansion_zeroelim(zlen, detz, bez, detzz);
+  ztlen = scale_expansion_zeroelim(temp192len, temp192, beztail, detzt);
+  zztlen = scale_expansion_zeroelim(ztlen, detzt, bez, detzzt);
+  for (i = 0; i < zztlen; i++)
+  {
+    detzzt[i] *= 2.0;
+  }
+  ztztlen = scale_expansion_zeroelim(ztlen, detzt, beztail, detztzt);
+  z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
+  z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
+  xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
+  blen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, bdet);
+
+  temp32alen = scale_expansion_zeroelim(ablen, ab, -dez, temp32a);
+  temp32blen = scale_expansion_zeroelim(ablen, ab, -deztail, temp32b);
+  temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64a);
+  temp32alen = scale_expansion_zeroelim(bdlen, bd, -aez, temp32a);
+  temp32blen = scale_expansion_zeroelim(bdlen, bd, -aeztail, temp32b);
+  temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64b);
+  temp32alen = scale_expansion_zeroelim(dalen, da, -bez, temp32a);
+  temp32blen = scale_expansion_zeroelim(dalen, da, -beztail, temp32b);
+  temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64c);
+  temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
+                                           temp64blen, temp64b, temp128);
+  temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
+                                           temp128len, temp128, temp192);
+  xlen = scale_expansion_zeroelim(temp192len, temp192, cex, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, cex, detxx);
+  xtlen = scale_expansion_zeroelim(temp192len, temp192, cextail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, cex, detxxt);
+  for (i = 0; i < xxtlen; i++)
+  {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, cextail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+  ylen = scale_expansion_zeroelim(temp192len, temp192, cey, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, cey, detyy);
+  ytlen = scale_expansion_zeroelim(temp192len, temp192, ceytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, cey, detyyt);
+  for (i = 0; i < yytlen; i++)
+  {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, ceytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+  zlen = scale_expansion_zeroelim(temp192len, temp192, cez, detz);
+  zzlen = scale_expansion_zeroelim(zlen, detz, cez, detzz);
+  ztlen = scale_expansion_zeroelim(temp192len, temp192, ceztail, detzt);
+  zztlen = scale_expansion_zeroelim(ztlen, detzt, cez, detzzt);
+  for (i = 0; i < zztlen; i++)
+  {
+    detzzt[i] *= 2.0;
+  }
+  ztztlen = scale_expansion_zeroelim(ztlen, detzt, ceztail, detztzt);
+  z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
+  z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
+  xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
+  clen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, cdet);
+
+  temp32alen = scale_expansion_zeroelim(bclen, bc, aez, temp32a);
+  temp32blen = scale_expansion_zeroelim(bclen, bc, aeztail, temp32b);
+  temp64alen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64a);
+  temp32alen = scale_expansion_zeroelim(aclen, ac, -bez, temp32a);
+  temp32blen = scale_expansion_zeroelim(aclen, ac, -beztail, temp32b);
+  temp64blen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64b);
+  temp32alen = scale_expansion_zeroelim(ablen, ab, cez, temp32a);
+  temp32blen = scale_expansion_zeroelim(ablen, ab, ceztail, temp32b);
+  temp64clen = fast_expansion_sum_zeroelim(temp32alen, temp32a,
+                                           temp32blen, temp32b, temp64c);
+  temp128len = fast_expansion_sum_zeroelim(temp64alen, temp64a,
+                                           temp64blen, temp64b, temp128);
+  temp192len = fast_expansion_sum_zeroelim(temp64clen, temp64c,
+                                           temp128len, temp128, temp192);
+  xlen = scale_expansion_zeroelim(temp192len, temp192, dex, detx);
+  xxlen = scale_expansion_zeroelim(xlen, detx, dex, detxx);
+  xtlen = scale_expansion_zeroelim(temp192len, temp192, dextail, detxt);
+  xxtlen = scale_expansion_zeroelim(xtlen, detxt, dex, detxxt);
+  for (i = 0; i < xxtlen; i++)
+  {
+    detxxt[i] *= 2.0;
+  }
+  xtxtlen = scale_expansion_zeroelim(xtlen, detxt, dextail, detxtxt);
+  x1len = fast_expansion_sum_zeroelim(xxlen, detxx, xxtlen, detxxt, x1);
+  x2len = fast_expansion_sum_zeroelim(x1len, x1, xtxtlen, detxtxt, x2);
+  ylen = scale_expansion_zeroelim(temp192len, temp192, dey, dety);
+  yylen = scale_expansion_zeroelim(ylen, dety, dey, detyy);
+  ytlen = scale_expansion_zeroelim(temp192len, temp192, deytail, detyt);
+  yytlen = scale_expansion_zeroelim(ytlen, detyt, dey, detyyt);
+  for (i = 0; i < yytlen; i++)
+  {
+    detyyt[i] *= 2.0;
+  }
+  ytytlen = scale_expansion_zeroelim(ytlen, detyt, deytail, detytyt);
+  y1len = fast_expansion_sum_zeroelim(yylen, detyy, yytlen, detyyt, y1);
+  y2len = fast_expansion_sum_zeroelim(y1len, y1, ytytlen, detytyt, y2);
+  zlen = scale_expansion_zeroelim(temp192len, temp192, dez, detz);
+  zzlen = scale_expansion_zeroelim(zlen, detz, dez, detzz);
+  ztlen = scale_expansion_zeroelim(temp192len, temp192, deztail, detzt);
+  zztlen = scale_expansion_zeroelim(ztlen, detzt, dez, detzzt);
+  for (i = 0; i < zztlen; i++)
+  {
+    detzzt[i] *= 2.0;
+  }
+  ztztlen = scale_expansion_zeroelim(ztlen, detzt, deztail, detztzt);
+  z1len = fast_expansion_sum_zeroelim(zzlen, detzz, zztlen, detzzt, z1);
+  z2len = fast_expansion_sum_zeroelim(z1len, z1, ztztlen, detztzt, z2);
+  xylen = fast_expansion_sum_zeroelim(x2len, x2, y2len, y2, detxy);
+  dlen = fast_expansion_sum_zeroelim(z2len, z2, xylen, detxy, ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, deter);
+
+  return deter[deterlen - 1];
+}
+
+REAL insphereadapt(pa, pb, pc, pd, pe, permanent)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+REAL *pe;
+REAL permanent;
+{
+  INEXACT REAL aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez;
+  REAL det, errbound;
+
+  INEXACT REAL aexbey1, bexaey1, bexcey1, cexbey1;
+  INEXACT REAL cexdey1, dexcey1, dexaey1, aexdey1;
+  INEXACT REAL aexcey1, cexaey1, bexdey1, dexbey1;
+  REAL aexbey0, bexaey0, bexcey0, cexbey0;
+  REAL cexdey0, dexcey0, dexaey0, aexdey0;
+  REAL aexcey0, cexaey0, bexdey0, dexbey0;
+  REAL ab[4], bc[4], cd[4], da[4], ac[4], bd[4];
+  INEXACT REAL ab3, bc3, cd3, da3, ac3, bd3;
+  REAL abeps, bceps, cdeps, daeps, aceps, bdeps;
+  REAL temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48];
+  int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len;
+  REAL xdet[96], ydet[96], zdet[96], xydet[192];
+  int xlen, ylen, zlen, xylen;
+  REAL adet[288], bdet[288], cdet[288], ddet[288];
+  int alen, blen, clen, dlen;
+  REAL abdet[576], cddet[576];
+  int ablen, cdlen;
+  REAL fin1[1152];
+  int finlength;
+
+  REAL aextail, bextail, cextail, dextail;
+  REAL aeytail, beytail, ceytail, deytail;
+  REAL aeztail, beztail, ceztail, deztail;
+
+  INEXACT REAL bvirt;
+  REAL avirt, bround, around;
+  INEXACT REAL c;
+  INEXACT REAL abig;
+  REAL ahi, alo, bhi, blo;
+  REAL err1, err2, err3;
+  INEXACT REAL _i, _j;
+  REAL _0;
+
+  aex = (REAL) (pa[0] - pe[0]);
+  bex = (REAL) (pb[0] - pe[0]);
+  cex = (REAL) (pc[0] - pe[0]);
+  dex = (REAL) (pd[0] - pe[0]);
+  aey = (REAL) (pa[1] - pe[1]);
+  bey = (REAL) (pb[1] - pe[1]);
+  cey = (REAL) (pc[1] - pe[1]);
+  dey = (REAL) (pd[1] - pe[1]);
+  aez = (REAL) (pa[2] - pe[2]);
+  bez = (REAL) (pb[2] - pe[2]);
+  cez = (REAL) (pc[2] - pe[2]);
+  dez = (REAL) (pd[2] - pe[2]);
+
+  Two_Product(aex, bey, aexbey1, aexbey0);
+  Two_Product(bex, aey, bexaey1, bexaey0);
+  Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]);
+  ab[3] = ab3;
+
+  Two_Product(bex, cey, bexcey1, bexcey0);
+  Two_Product(cex, bey, cexbey1, cexbey0);
+  Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]);
+  bc[3] = bc3;
+
+  Two_Product(cex, dey, cexdey1, cexdey0);
+  Two_Product(dex, cey, dexcey1, dexcey0);
+  Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]);
+  cd[3] = cd3;
+
+  Two_Product(dex, aey, dexaey1, dexaey0);
+  Two_Product(aex, dey, aexdey1, aexdey0);
+  Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]);
+  da[3] = da3;
+
+  Two_Product(aex, cey, aexcey1, aexcey0);
+  Two_Product(cex, aey, cexaey1, cexaey0);
+  Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]);
+  ac[3] = ac3;
+
+  Two_Product(bex, dey, bexdey1, bexdey0);
+  Two_Product(dex, bey, dexbey1, dexbey0);
+  Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]);
+  bd[3] = bd3;
+
+  temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet);
+
+  temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet);
+
+  temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet);
+
+  temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a);
+  temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b);
+  temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c);
+  temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a,
+                                          temp8blen, temp8b, temp16);
+  temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c,
+                                          temp16len, temp16, temp24);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48);
+  xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48);
+  ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet);
+  temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48);
+  zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet);
+  xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet);
+  dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet);
+
+  ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet);
+  cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet);
+  finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1);
+
+  det = estimate(finlength, fin1);
+  errbound = isperrboundB * permanent;
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  Two_Diff_Tail(pa[0], pe[0], aex, aextail);
+  Two_Diff_Tail(pa[1], pe[1], aey, aeytail);
+  Two_Diff_Tail(pa[2], pe[2], aez, aeztail);
+  Two_Diff_Tail(pb[0], pe[0], bex, bextail);
+  Two_Diff_Tail(pb[1], pe[1], bey, beytail);
+  Two_Diff_Tail(pb[2], pe[2], bez, beztail);
+  Two_Diff_Tail(pc[0], pe[0], cex, cextail);
+  Two_Diff_Tail(pc[1], pe[1], cey, ceytail);
+  Two_Diff_Tail(pc[2], pe[2], cez, ceztail);
+  Two_Diff_Tail(pd[0], pe[0], dex, dextail);
+  Two_Diff_Tail(pd[1], pe[1], dey, deytail);
+  Two_Diff_Tail(pd[2], pe[2], dez, deztail);
+  if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0)
+      && (bextail == 0.0) && (beytail == 0.0) && (beztail == 0.0)
+      && (cextail == 0.0) && (ceytail == 0.0) && (ceztail == 0.0)
+      && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0))
+      {
+    return det;
+  }
+
+  errbound = isperrboundC * permanent + resulterrbound * Absolute(det);
+  abeps = (aex * beytail + bey * aextail)
+        - (aey * bextail + bex * aeytail);
+  bceps = (bex * ceytail + cey * bextail)
+        - (bey * cextail + cex * beytail);
+  cdeps = (cex * deytail + dey * cextail)
+        - (cey * dextail + dex * ceytail);
+  daeps = (dex * aeytail + aey * dextail)
+        - (dey * aextail + aex * deytail);
+  aceps = (aex * ceytail + cey * aextail)
+        - (aey * cextail + cex * aeytail);
+  bdeps = (bex * deytail + dey * bextail)
+        - (bey * dextail + dex * beytail);
+  det += (((bex * bex + bey * bey + bez * bez)
+           * ((cez * daeps + dez * aceps + aez * cdeps)
+              + (ceztail * da3 + deztail * ac3 + aeztail * cd3))
+           + (dex * dex + dey * dey + dez * dez)
+           * ((aez * bceps - bez * aceps + cez * abeps)
+              + (aeztail * bc3 - beztail * ac3 + ceztail * ab3)))
+          - ((aex * aex + aey * aey + aez * aez)
+           * ((bez * cdeps - cez * bdeps + dez * bceps)
+              + (beztail * cd3 - ceztail * bd3 + deztail * bc3))
+           + (cex * cex + cey * cey + cez * cez)
+           * ((dez * abeps + aez * bdeps + bez * daeps)
+              + (deztail * ab3 + aeztail * bd3 + beztail * da3))))
+       + 2.0 * (((bex * bextail + bey * beytail + bez * beztail)
+                 * (cez * da3 + dez * ac3 + aez * cd3)
+                 + (dex * dextail + dey * deytail + dez * deztail)
+                 * (aez * bc3 - bez * ac3 + cez * ab3))
+                - ((aex * aextail + aey * aeytail + aez * aeztail)
+                 * (bez * cd3 - cez * bd3 + dez * bc3)
+                 + (cex * cextail + cey * ceytail + cez * ceztail)
+                 * (dez * ab3 + aez * bd3 + bez * da3)));
+  if ((det >= errbound) || (-det >= errbound))
+  {
+    return det;
+  }
+
+  return insphereexact(pa, pb, pc, pd, pe);
+}
+
+REAL insphere(pa, pb, pc, pd, pe)
+REAL *pa;
+REAL *pb;
+REAL *pc;
+REAL *pd;
+REAL *pe;
+{
+  REAL aex, bex, cex, dex;
+  REAL aey, bey, cey, dey;
+  REAL aez, bez, cez, dez;
+  REAL aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey;
+  REAL aexcey, cexaey, bexdey, dexbey;
+  REAL alift, blift, clift, dlift;
+  REAL ab, bc, cd, da, ac, bd;
+  REAL abc, bcd, cda, dab;
+  REAL aezplus, bezplus, cezplus, dezplus;
+  REAL aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus;
+  REAL cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus;
+  REAL aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus;
+  REAL det;
+  REAL permanent, errbound;
+
+  aex = pa[0] - pe[0];
+  bex = pb[0] - pe[0];
+  cex = pc[0] - pe[0];
+  dex = pd[0] - pe[0];
+  aey = pa[1] - pe[1];
+  bey = pb[1] - pe[1];
+  cey = pc[1] - pe[1];
+  dey = pd[1] - pe[1];
+  aez = pa[2] - pe[2];
+  bez = pb[2] - pe[2];
+  cez = pc[2] - pe[2];
+  dez = pd[2] - pe[2];
+
+  aexbey = aex * bey;
+  bexaey = bex * aey;
+  ab = aexbey - bexaey;
+  bexcey = bex * cey;
+  cexbey = cex * bey;
+  bc = bexcey - cexbey;
+  cexdey = cex * dey;
+  dexcey = dex * cey;
+  cd = cexdey - dexcey;
+  dexaey = dex * aey;
+  aexdey = aex * dey;
+  da = dexaey - aexdey;
+
+  aexcey = aex * cey;
+  cexaey = cex * aey;
+  ac = aexcey - cexaey;
+  bexdey = bex * dey;
+  dexbey = dex * bey;
+  bd = bexdey - dexbey;
+
+  abc = aez * bc - bez * ac + cez * ab;
+  bcd = bez * cd - cez * bd + dez * bc;
+  cda = cez * da + dez * ac + aez * cd;
+  dab = dez * ab + aez * bd + bez * da;
+
+  alift = aex * aex + aey * aey + aez * aez;
+  blift = bex * bex + bey * bey + bez * bez;
+  clift = cex * cex + cey * cey + cez * cez;
+  dlift = dex * dex + dey * dey + dez * dez;
+
+  det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd);
+
+  aezplus = Absolute(aez);
+  bezplus = Absolute(bez);
+  cezplus = Absolute(cez);
+  dezplus = Absolute(dez);
+  aexbeyplus = Absolute(aexbey);
+  bexaeyplus = Absolute(bexaey);
+  bexceyplus = Absolute(bexcey);
+  cexbeyplus = Absolute(cexbey);
+  cexdeyplus = Absolute(cexdey);
+  dexceyplus = Absolute(dexcey);
+  dexaeyplus = Absolute(dexaey);
+  aexdeyplus = Absolute(aexdey);
+  aexceyplus = Absolute(aexcey);
+  cexaeyplus = Absolute(cexaey);
+  bexdeyplus = Absolute(bexdey);
+  dexbeyplus = Absolute(dexbey);
+  permanent = ((cexdeyplus + dexceyplus) * bezplus
+               + (dexbeyplus + bexdeyplus) * cezplus
+               + (bexceyplus + cexbeyplus) * dezplus)
+            * alift
+            + ((dexaeyplus + aexdeyplus) * cezplus
+               + (aexceyplus + cexaeyplus) * dezplus
+               + (cexdeyplus + dexceyplus) * aezplus)
+            * blift
+            + ((aexbeyplus + bexaeyplus) * dezplus
+               + (bexdeyplus + dexbeyplus) * aezplus
+               + (dexaeyplus + aexdeyplus) * bezplus)
+            * clift
+            + ((bexceyplus + cexbeyplus) * aezplus
+               + (cexaeyplus + aexceyplus) * bezplus
+               + (aexbeyplus + bexaeyplus) * cezplus)
+            * dlift;
+  errbound = isperrboundA * permanent;
+  if ((det > errbound) || (-det > errbound))
+  {
+    return det;
+  }
+
+  return insphereadapt(pa, pb, pc, pd, pe, permanent);
+}
diff --git a/libraries/mesh/rectKey.cpp b/libraries/mesh/rectKey.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b4da8f94481859e3c089519d79d6a92f890f8c31
--- /dev/null
+++ b/libraries/mesh/rectKey.cpp
@@ -0,0 +1,38 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "rectKey.h"
+
+const int URectKey::rectEdgeIndex[4][2] = { {0,1}, {1,2},{2,3},{3,0} };
+
+const int ORectKey::rectEdgeIndex[4][2] = { {0,1}, {1,2},{2,3},{3,0} };
+
diff --git a/libraries/mesh/rectKey.h b/libraries/mesh/rectKey.h
new file mode 100644
index 0000000000000000000000000000000000000000..dbf034faecd1b850fd7942381f1c61c19b305f24
--- /dev/null
+++ b/libraries/mesh/rectKey.h
@@ -0,0 +1,270 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef RECTKEY_H
+#define RECTKEY_H
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include "vec4i.h"
+#include "edgeKey.h"
+
+
+// unoriented rectangle key based on vtx indices
+// v[0], v[1], v[2] and v[3] will be sorted to ensure v[0] <= v[1] <= v[2] <= v[3]
+struct URectKey
+{
+  inline URectKey(const int v[4]);
+  inline URectKey(int v0, int v1, int v2, int v3);
+  inline URectKey(); // creates an invalid key with v = {-1,-1,-1, -1}
+  inline bool operator < (const URectKey & o) const { return v < o.v; }
+  inline bool operator == (const URectKey & o) const { return v == o.v; }
+  inline bool operator != (const URectKey & o) const { return v != o.v; }
+  const int & operator [] (int index) const { return v[index]; }
+  const int * indices() const { return &v[0]; }
+  // return the unordered edge opposite to v[i], i: [0,4)
+  inline UEdgeKey uEdgeKey(int i) const { return UEdgeKey(v[rectEdgeIndex[i][0]], v[rectEdgeIndex[i][1]]); }
+  // given the global vtx index, return its first index in v [0,2]
+  // else return -1
+  inline int getInvertedIndex(const int globalVtxIndex) const { return v.getInvertedIndex(globalVtxIndex); }
+
+  inline bool shareUEdge(const URectKey & nbr) const;
+
+  static const int rectEdgeIndex[4][2];
+protected:
+  Vec4i v;
+};
+
+inline std::ostream & operator << (std::ostream & s, const URectKey & v);
+
+// oriented rectangle key that remember its orientation
+// v[0],v[1] and v[2] will be reordered to ensure v[0] <= v[1] and v[0] <= v[2] and (v[0], v[1], v[2]) remain the same orientation as the input
+struct ORectKey
+{
+  inline ORectKey(const int v[4]);
+  inline ORectKey(int v0, int v1, int v2, int v3);
+  inline ORectKey(); // creates an invalid key with v = {-1,-1,-1,-1}
+  inline bool operator < (const ORectKey & o) const { return v < o.v; }
+  inline bool operator == (const ORectKey & o) const { return v == o.v; }
+  inline bool operator != (const ORectKey & o) const { return v != o.v; }
+  const int & operator [] (int i) const { return v[i]; }
+  const int * indices() const { return &v[0]; }
+
+  inline URectKey uRectKey() const { return URectKey(&v[0]); }
+
+  // return the ordered/unordered edge opposite to v[ind]
+  inline UEdgeKey uEdgeKey(int i) const { return UEdgeKey(v[rectEdgeIndex[i][0]], v[rectEdgeIndex[i][1]]); }
+  inline OEdgeKey oEdgeKey(int i) const { return OEdgeKey(v[rectEdgeIndex[i][0]], v[rectEdgeIndex[i][1]]); }
+
+  // given the global vtx index, return the index in v [0,2]; otherwise return -1
+  inline int getInvertedIndex(const int globalVtxIndex) const { return v.getInvertedIndex(globalVtxIndex); }
+
+  inline bool shareUEdge(const ORectKey & nbr) const;
+  inline bool shareOEdge(const ORectKey & nbr) const;
+  inline bool shareOppositeOEdge(const ORectKey & nbr) const;
+  inline OEdgeKey getSharedOppositeOEdge(const ORectKey & nbr) const; // return first owned OEdge whose reverse is owned by nbr; return (-1,-1) otherwise
+  inline UEdgeKey getSharedUEdge(const ORectKey & nbr) const; // return the first shared UEdge; return (-1,-1) if no shared UEdge
+
+  inline int getInvertedOEdgeIndex(const OEdgeKey & edge) const;
+
+  bool hasOEdge(const OEdgeKey & edge) const;
+  bool hasUEdge(const UEdgeKey & edge) const;
+
+  inline void reverse() { std::swap(v[1], v[2]); } // reverse the orientation
+  inline ORectKey getReversedRectKey() const { return ORectKey(v[0], v[3], v[2], v[1]); } // return reversed rectKey
+
+  // rotate v0-v3 so that they share the same cyclic order but v0 = min(v0,v1,v2,v3)
+  inline static void rotate(Vec4i & v);
+
+  static const int rectEdgeIndex[4][2];
+protected:
+  Vec4i v;
+};
+
+inline std::ostream & operator << (std::ostream & s, const ORectKey & v);
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                             IMPLEMENTATION                                //
+///////////////////////////////////////////////////////////////////////////////
+
+inline URectKey::URectKey(int v0, int v1, int v2, int v3)
+{
+  v[0] = v0; v[1] = v1; v[2] = v2; v[3] = v3;
+  std::sort(&v[0], &v[0]+4);
+}
+
+inline URectKey::URectKey(const int vtx[4])
+{
+  memcpy(&v[0], vtx, sizeof(int) * 4);
+  std::sort(&v[0], &v[0]+4);
+}
+
+inline URectKey::URectKey()
+{
+  v[0] = v[1] = v[2] = v[3] = -1;
+}
+
+inline ORectKey::ORectKey(int v0, int v1, int v2, int v3)
+{
+  v[0] = v0; v[1] = v1; v[2] = v2; v[3] = v3;
+  rotate(v);
+}
+
+inline ORectKey::ORectKey(const int vtx[4])
+{
+  memcpy(&v[0], vtx, sizeof(int) * 4);
+  rotate(v);
+}
+
+inline ORectKey::ORectKey()
+{
+  v[0] = v[1] = v[2] = v[3] = -1;
+}
+
+inline void ORectKey::rotate(Vec4i & v)
+{
+  int * vmin = std::min_element(&v[0], &v[0]+4);
+  int minIdx = (vmin - &v[0]);
+  v.rotate(minIdx);
+}
+
+inline bool URectKey::shareUEdge(const URectKey & nbr) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    UEdgeKey key = uEdgeKey(i);
+    for(int j = 0; j < 4; j++)
+      if (key == nbr.uEdgeKey(j))
+        return true;
+  }
+  return false;
+}
+
+inline bool ORectKey::shareUEdge(const ORectKey & nbr) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    UEdgeKey key = uEdgeKey(i);
+    for(int j = 0; j < 4; j++)
+      if (key == nbr.uEdgeKey(j))
+        return true;
+  }
+  return false;
+}
+
+inline bool ORectKey::shareOEdge(const ORectKey & nbr) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    OEdgeKey key = oEdgeKey(i);
+    for(int j = 0; j < 4; j++)
+      if (key == nbr.oEdgeKey(j))
+        return true;
+  }
+  return false;
+}
+
+inline bool ORectKey::shareOppositeOEdge(const ORectKey & nbr) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    OEdgeKey key = oEdgeKey(i);
+    key.reverse();
+    for(int j = 0; j < 4; j++)
+      if (key == nbr.oEdgeKey(j))
+        return true;
+  }
+  return false;
+}
+
+inline OEdgeKey ORectKey::getSharedOppositeOEdge(const ORectKey & nbr) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    OEdgeKey key = oEdgeKey(i);
+    key.reverse();
+    for(int j = 0; j < 4; j++)
+      if (key == nbr.oEdgeKey(j))
+        return key.getReversedEdgeKey();
+  }
+  return OEdgeKey();
+}
+
+inline UEdgeKey ORectKey::getSharedUEdge(const ORectKey & nbr) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    UEdgeKey key = uEdgeKey(i);
+    for(int j = 0; j < 4; j++)
+      if (key == nbr.uEdgeKey(j))
+        return key;
+  }
+  return UEdgeKey(); // return a default invalid UEdgeKey
+}
+
+inline bool ORectKey::hasOEdge(const OEdgeKey & edge) const
+{
+  for(int i = 0; i < 4; i++)
+    if (edge == oEdgeKey(i))
+      return true;
+  return false;
+}
+
+inline bool ORectKey::hasUEdge(const UEdgeKey & edge) const
+{
+  for(int i = 0; i < 4; i++)
+    if (edge == uEdgeKey(i))
+      return true;
+  return false;
+}
+
+inline int ORectKey::getInvertedOEdgeIndex(const OEdgeKey & edge) const
+{
+  for(int i = 0; i < 4; i++)
+    if (edge == oEdgeKey(i))
+      return i;
+  return -1;
+}
+
+inline std::ostream & operator << (std::ostream & s, const URectKey & v)
+{
+  return s << '(' << v[0] << ' ' << v[1] << ' '  << v[2] << ' ' << v[3] << ')';
+}
+
+inline std::ostream & operator << (std::ostream & s, const ORectKey & v)
+{
+  return s << '(' << v[0] << ' ' << v[1] << ' '  << v[2] << ' ' << v[3] << ')';
+}
+
+#endif
diff --git a/src/libobjMesh/simpleSphere.cpp b/libraries/mesh/simpleSphere.cpp
similarity index 67%
rename from src/libobjMesh/simpleSphere.cpp
rename to libraries/mesh/simpleSphere.cpp
index a79c72d897c2aa865d1f67b2538d2b27a04a5bdd..3d134a249d6a8dfe9bc03e75dd745925781d77a4 100644
--- a/src/libobjMesh/simpleSphere.cpp
+++ b/libraries/mesh/simpleSphere.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,15 +32,13 @@
 
 #include "simpleSphere.h"
 
-namespace vega
-{
 /*
   A sphere
   Author: Jernej Barbic, CMU
 */
 
 // does the sphere intersect the bounding box
-bool SimpleSphere::doesBoundingBoxIntersect(BoundingBox & box) const
+bool SimpleSphere::doesBoundingBoxIntersect(const BoundingBox & box) const
 {
   Vec3d bmin,bmax;
   bmin = box.bmin();
@@ -45,10 +47,10 @@ bool SimpleSphere::doesBoundingBoxIntersect(BoundingBox & box) const
   double d;
 
   #define COORDINATE_TEST(i)\
-  d = bmin[i] - center[i];\
+  d = bmin[i] - center_[i];\
   if (d > 0)\
     dmin += d * d;\
-  d = center[i] - bmax[i];\
+  d = center_[i] - bmax[i];\
   if (d > 0)\
     dmin += d * d;
 
@@ -59,7 +61,15 @@ bool SimpleSphere::doesBoundingBoxIntersect(BoundingBox & box) const
 
   #undef COORDINATE_TEST
 
-  return (dmin <= radius * radius);
+  return (dmin <= radius_ * radius_);
+}
+
+bool SimpleSphere::inside(const Vec3d & queryPoint) const
+{
+  return len2(center_- queryPoint) <= radius_ * radius_;
 }
-    
+
+double SimpleSphere::signedDistance(const Vec3d & queryPoint) const
+{
+  return len(center_ - queryPoint) - radius_;
 }
diff --git a/src/libobjMesh/simpleSphere.h b/libraries/mesh/simpleSphere.h
similarity index 62%
rename from src/libobjMesh/simpleSphere.h
rename to libraries/mesh/simpleSphere.h
index ab317d434e68b60a1dd889ed071fad8d246b97be..60038897a8d1caf209fa4dfd9368280fdb36aedf 100644
--- a/src/libobjMesh/simpleSphere.h
+++ b/libraries/mesh/simpleSphere.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,10 +33,6 @@
 #ifndef _SIMPLESPHERE_H_
 #define _SIMPLESPHERE_H_
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 /*
   A sphere
   Author: Jernej Barbic, CMU
@@ -41,22 +41,27 @@
 #include "minivector.h"
 #include "boundingBox.h"
 
-namespace vega
-{
 class SimpleSphere
 {
 public:
-  SimpleSphere(Vec3d center_, double radius_) : center(center_), radius(radius_) {}
+  SimpleSphere(Vec3d center, double radius) : center_(center), radius_(radius) {}
 
-  SimpleSphere(double x, double y, double z, double radius_) : center(Vec3d(x,y,z)), radius(radius_) {}
+  SimpleSphere(double x, double y, double z, double radius) : center_(Vec3d(x,y,z)), radius_(radius) {}
 
+  const Vec3d & center() const { return center_; }
+  double radius() const { return radius_; }
   // does the sphere intersect the bounding box
-  bool doesBoundingBoxIntersect(BoundingBox & box) const;
+  bool doesBoundingBoxIntersect(const BoundingBox & box) const;
+
+  // return true if queryPoint is inside or touches the sphere
+  bool inside(const Vec3d & queryPoint) const;
+
+  double signedDistance(const Vec3d & queryPoint) const;
 
 private:
-  Vec3d center;
-  double radius;
+  Vec3d center_;
+  double radius_;
 };
-}
+
 #endif
 
diff --git a/libraries/mesh/tetKey.cpp b/libraries/mesh/tetKey.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ef7701bb6fdbc22affca043d7fbae49efb9895a0
--- /dev/null
+++ b/libraries/mesh/tetKey.cpp
@@ -0,0 +1,88 @@
+/*
+  This code is based on code from the Geometric Tools library,
+  which is licensed under a boost license.
+  Such usage is permitted by the boost license; for details,
+  please see the boost license below.
+*/
+
+// Geometric Tools, LLC
+// Copyright (c) 1998-2014
+// Distributed under the Boost Software License, Version 1.0.
+// http://www.boost.org/LICENSE_1_0.txt
+// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
+
+/*************************************************************************
+ *                                                                       *
+ * We release our improvements to the wildMagic code under our standard  *
+ * Vega FEM license, as follows:                                         *
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "improvements to the wildMagic library" , Copyright (C) 2018 USC      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "tetKey.h"
+
+const int OTetKey::tetFaceIndex[4][3] =  { { 1, 2, 3 }, { 0, 3, 2 }, { 0, 1, 3 }, { 0, 2, 1 } };
+
+const int OTetKey::tetEdgeIndex[6][2] =
+{
+ { 0, 1 }, { 1, 2 }, { 2, 0 },
+ { 0, 3 }, { 1, 3 }, { 2, 3 } };
+
+
+void OTetKey::permute(int v0, int v1, int v2, int v3, int & r0, int & r1, int & r2, int & r3)
+{
+  int minInd = 0;
+  r0 = v0;
+  if (v1 < r0)
+  {
+    r0 = v1;
+    minInd = 1;
+  }
+  if (v2 < r0)
+  {
+    r0 = v2;
+    minInd = 2;
+  }
+  if (v3 < r0)
+  {
+    r0 = v3;
+    minInd = 3;
+  }
+
+  if (minInd == 0)
+    OTriKey::permute(v1, v2, v3, r1, r2, r3);
+  else if (minInd == 1)
+    OTriKey::permute(v0, v3, v2, r1, r2, r3);
+  else if (minInd == 2)
+    OTriKey::permute(v0, v1, v3, r1, r2, r3);
+  else  // minInd == 3
+      OTriKey::permute(v0, v2, v1, r1, r2, r3);
+}
+
diff --git a/libraries/mesh/tetKey.h b/libraries/mesh/tetKey.h
new file mode 100644
index 0000000000000000000000000000000000000000..99275df29ab96969e380d0499b293b8df71c1868
--- /dev/null
+++ b/libraries/mesh/tetKey.h
@@ -0,0 +1,248 @@
+/*
+  This code is based on code from the Geometric Tools library,
+  which is licensed under a boost license.
+  Such usage is permitted by the boost license; for details,
+  please see the boost license below.
+*/
+
+// Geometric Tools, LLC
+// Copyright (c) 1998-2014
+// Distributed under the Boost Software License, Version 1.0.
+// http://www.boost.org/LICENSE_1_0.txt
+// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
+
+/*************************************************************************
+ *                                                                       *
+ * We release our improvements to the wildMagic code under our standard  *
+ * Vega FEM license, as follows:                                         *
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "improvements to the wildMagic library" , Copyright (C) 2018 USC      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TETKEYS_H
+#define TETKEYS_H
+
+#include "triKey.h"
+#include "vec4i.h"
+#include <algorithm>
+#include <ostream>
+
+// unoriented tet key based on vtx indices
+struct UTetKey
+{
+  inline UTetKey();  // creates an invalid key with v = {-1,-1,-1,-1}
+  inline UTetKey(int v0, int v1, int v2, int v3);
+  inline UTetKey(const int v[4]) : UTetKey(v[0], v[1], v[2], v[3]) {}
+  inline UTetKey(const Vec4i & v) : UTetKey(v[0], v[1], v[2], v[3]) {}
+
+  inline bool operator < (const UTetKey & o) const { return v < o.v; }
+  inline bool operator == (const UTetKey & o) const { return v == o.v; }
+  inline bool operator != (const UTetKey & o) const { return v != o.v; }
+  const int & operator[](int index) const { return v[index]; }
+  const int * indices() const { return &v[0]; }
+  UTriKey uFaceKey(int ind) const;
+
+  inline bool shareUFace(const UTetKey & nbr) const;
+  inline UTriKey getSharedUFace(const UTetKey & nbr) const; // return the first shared UTriKey; return (-1,-1, -1) if no shared UTriKey
+
+  // if all indices are >= 0
+  inline bool allVerticesValid() const { return v[0] >= 0; }
+  // if this tet is valid: v[i] >=0 && v[i] != v[j]
+  inline bool isValidTet() const;
+  inline bool hasIndex(int vtxIndex) const { return v[0] == vtxIndex || v[1] == vtxIndex || v[2] == vtxIndex || v[3] == vtxIndex; }
+
+  // opposite face for each vtx in a tet. The faces are ordered so that its normals pointing outside the tet if tet has positive orientation
+  // static const int tetFaceIndex[4][3];
+protected:
+  Vec4i v;
+};
+
+inline std::ostream & operator << (std::ostream & s, const UTetKey & v);
+
+// oriented tet key based on vtx indices
+struct OTetKey
+{
+  inline OTetKey();  // creates an invalid key with v = {-1,-1,-1,-1}
+  inline OTetKey(int v0, int v1, int v2, int v3);
+  inline OTetKey(const int v[4]) : OTetKey(v[0], v[1], v[2], v[3]) {}
+  inline OTetKey(const Vec4i & v) : OTetKey(v[0], v[1], v[2], v[3]) {}
+
+  inline bool operator < (const OTetKey & o) const { return v < o.v; }
+  inline bool operator == (const OTetKey & o) const { return v == o.v; }
+  inline bool operator != (const OTetKey & o) const { return v != o.v; }
+  const int & operator[](int index) const { return v[index]; }
+  const int * indices() const { return &v[0]; }
+
+  // get the TriKey opposite to i-th vtx,  // 0 <= i < 4
+  OTriKey oFaceKey(int i) const;
+  UTriKey uFaceKey(int i) const;
+  // get the i-th EdgeKey, // 0 <= i < 6
+//  OEdgeKey oEdgeKey(int i) const; // there's no meaning in producing an OEdgeKey, so it's commented out
+  UEdgeKey uEdgeKey(int i) const;
+  UTetKey uTetKey() const { return UTetKey(&v[0]); }
+  // given the global vtx index, return its first index in v [0,3]
+  // else return -1
+  inline int getInvertedIndex(const int globalVtxIndex) const { return v.getInvertedIndex(globalVtxIndex); }
+
+  inline int getInvertedEdgeIndex(const UEdgeKey & edge) const;
+  inline int getInvertedTriIndex(const OTriKey & tri) const;
+  inline int getInvertedTriIndex(const UTriKey & tri) const;
+
+  inline bool hasIndex(int vtxIndex) const { return v[0] == vtxIndex || v[1] == vtxIndex || v[2] == vtxIndex || v[3] == vtxIndex; }
+  // permute v0, v1, v2, v3 and store into r0, r1, r2, r3 so that they share the same orientation but r0 = min(v0,v1,v2,v3)
+  static void permute(int v0, int v1, int v2, int v3, int & r0, int & r1, int & r2, int & r3);
+  // opposite face for each vtx in a tet. The faces are ordered so that its normals pointing outside the tet if tet has positive orientation
+  static const int tetFaceIndex[4][3];
+  static const int tetEdgeIndex[6][2];
+
+  Vec4i v;
+};
+
+inline std::ostream & operator << (std::ostream & s, const OTetKey & v);
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                             IMPLEMENTATION                                //
+///////////////////////////////////////////////////////////////////////////////
+
+inline UTetKey::UTetKey()
+{
+  v[0] = v[1] = v[2] = v[3] = -1;
+}
+
+inline UTetKey::UTetKey(int v0, int v1, int v2, int v3)
+{
+  v[0] = v0;
+  v[1] = v1;
+  v[2] = v2;
+  v[3] = v3;
+  std::sort(&v[0], &v[4]);
+}
+
+
+inline UTriKey UTetKey::uFaceKey(int ind) const
+{
+ return UTriKey(v[OTetKey::tetFaceIndex[ind][0]], v[OTetKey::tetFaceIndex[ind][1]], v[OTetKey::tetFaceIndex[ind][2]]);
+}
+
+inline bool UTetKey::shareUFace(const UTetKey & nbr) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    UTriKey key = uFaceKey(i);
+    for(int j = 0; j < 4; j++)
+      if (key == nbr.uFaceKey(j))
+        return true;
+  }
+  return false;
+}
+
+inline UTriKey UTetKey::getSharedUFace(const UTetKey & nbr) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    UTriKey key = uFaceKey(i);
+    for(int j = 0; j < 4; j++)
+      if (key == nbr.uFaceKey(j))
+        return key;
+  }
+  return UTriKey(); // return a default invalid UTriKey
+}
+
+inline bool UTetKey::isValidTet() const
+{
+  return v[0] >= 0 && v[0] != v[1] && v[1] != v[2] && v[2] != v[3];
+}
+
+inline OTetKey::OTetKey()
+{
+  v[0] = v[1] = v[2] = v[3] = -1;
+}
+
+inline OTetKey::OTetKey(int v0, int v1, int v2, int v3)
+{
+  permute(v0, v1, v2, v3, v[0], v[1], v[2], v[3]);
+}
+
+inline OTriKey OTetKey::oFaceKey(int ind) const
+{
+ return OTriKey(v[tetFaceIndex[ind][0]], v[tetFaceIndex[ind][1]], v[tetFaceIndex[ind][2]]);
+}
+
+inline UTriKey OTetKey::uFaceKey(int ind) const
+{
+ return UTriKey(v[tetFaceIndex[ind][0]], v[tetFaceIndex[ind][1]], v[tetFaceIndex[ind][2]]);
+}
+
+//inline OEdgeKey OTetKey::oEdgeKey(int i) const
+//{
+//  return OEdgeKey(v[tetEdgeIndex[i][0]], v[tetEdgeIndex[i][1]]);
+//}
+
+inline UEdgeKey OTetKey::uEdgeKey(int i) const
+{
+  return UEdgeKey(v[tetEdgeIndex[i][0]], v[tetEdgeIndex[i][1]]);
+}
+
+inline std::ostream & operator << (std::ostream & s, const UTetKey & v)
+{
+  return s << '(' << v[0] << ' ' << v[1] << ' '  << v[2] << ' ' << v[3] << ')';
+}
+
+inline std::ostream & operator << (std::ostream & s, const OTetKey & v)
+{
+  return s << '(' << v[0] << ' ' << v[1] << ' '  << v[2] << ' ' << v[3] << ')';
+}
+
+inline int OTetKey::getInvertedEdgeIndex(const UEdgeKey & edge) const
+{
+  for(int i = 0; i < 6; i++)
+    if (edge == uEdgeKey(i))
+      return i;
+  return -1;
+}
+
+inline int OTetKey::getInvertedTriIndex(const OTriKey & tri) const
+{
+  for(int i = 0; i < 4; i++)
+    if (oFaceKey(i) == tri)
+      return i;
+  return -1;
+}
+
+inline int OTetKey::getInvertedTriIndex(const UTriKey & tri) const
+{
+  for(int i = 0; i < 4; i++)
+    if (uFaceKey(i) == tri)
+      return i;
+  return -1;
+}
+
+#endif
diff --git a/libraries/mesh/tetMeshGeo.cpp b/libraries/mesh/tetMeshGeo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f2f1ee498a33b2db84a88318eb5e9a03c6d3f7b0
--- /dev/null
+++ b/libraries/mesh/tetMeshGeo.cpp
@@ -0,0 +1,298 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "tetMeshGeo.h"
+#include "tetKey.h"
+#include "basicAlgorithms.h"
+#include "containerHelper.h"
+#include "geometryQuery.h"
+#include <cstring>
+#include <cassert>
+#include <iostream>
+#include <string>
+using namespace std;
+
+TetMeshRef::TetMeshRef(int numVertices, const double * vertices, int numTets, const int * t) :
+    numVertices_(numVertices), numTets_(numTets)
+{
+  positions_ = (const Vec3d*) vertices;
+  tets_ = (const Vec4i*) t;
+}
+
+TetMeshRef::TetMeshRef(const std::vector<Vec3d> & vertices, const std::vector<Vec4i> & tets) :
+    numVertices_(vertices.size()), numTets_(tets.size())
+{
+  positions_ = vertices.data();
+  tets_ = tets.data();
+}
+
+Vec3d TetMeshRef::computeTetCenter(int tetID) const
+{
+  return (pos(tetID, 0) + pos(tetID, 1) + pos(tetID, 2) + pos(tetID, 3)) / 4.0;
+}
+
+std::vector<BoundingBox> TetMeshRef::getTetBoundingBoxes() const
+{
+  vector<BoundingBox> bbs;
+  bbs.reserve(numTets());
+  for(int i = 0; i < numTets(); i++)
+  {
+    bbs.emplace_back(positions_, tets_[i]);
+  }
+  return bbs;
+}
+
+Tetrahedron TetMeshRef::getTetrahedron(int tetID) const
+{
+  return { pos(tetID, 0), pos(tetID, 1), pos(tetID, 2), pos(tetID, 3) };
+}
+
+// save to obj mesh
+bool TetMeshRef::save(const std::string & filename) const
+{
+  // open file
+  FILE * fout = fopen(filename.c_str(), "w");
+
+  if (!fout)
+  {
+    cout << "Error: could not write to file " << filename << endl;
+    return false;
+  }
+
+  fprintf(fout, "# Vega mesh file.\n");
+  fprintf(fout, "# %d vertices, %d elements\n", numVertices(), numTets());
+  fprintf(fout, "\n");
+
+  // write vertices
+  fprintf(fout,"*VERTICES\n");
+  fprintf(fout,"%d 3 0 0\n", numVertices());
+
+  for(int i=0; i < numVertices(); i++)
+  {
+    const Vec3d & v = pos(i);
+    fprintf(fout,"%d %.15G %.15G %.15G\n", i+1, v[0], v[1], v[2]);
+  }
+  fprintf(fout, "\n");
+
+  // write elements
+  fprintf(fout,"*ELEMENTS\n");
+
+  fprintf(fout,"TET\n");
+
+  fprintf(fout,"%d %d 0\n", numTets(), 4);
+
+  for(int el=0; el < numTets(); el++)
+  {
+    fprintf(fout,"%d ", el+1);
+    for(int j=0; j < 4; j++)
+    {
+      fprintf(fout, "%d", tet(el)[j] + 1);
+      if (j != 3)
+        fprintf(fout," ");
+    }
+    fprintf(fout,"\n");
+  }
+  fprintf(fout, "\n");
+  fclose(fout);
+
+  cout << "Saved mesh (#v: " << numVertices() << ", #t: " << numTets() << ") to " << filename << "." << endl;
+  return true;
+}
+
+TetMeshGeo::TetMeshGeo(int numVertices, const double * vertices, int numTets, const int * t) :
+    positions_(numVertices), tets_(numTets)
+{
+  memcpy(&positions_[0][0], vertices, sizeof(double) * numVertices * 3);
+  memcpy(&tets_[0][0], t, sizeof(int) * numTets * 4);
+}
+
+TetMeshGeo::TetMeshGeo(std::vector<Vec3d> vertices, std::vector<Vec4i> tets) :
+    positions_(move(vertices)), tets_(move(tets)) {}
+
+
+TetMeshGeo::TetMeshGeo(int numVertices, const Vec3d * vertices, std::vector<Vec4i> tets) : 
+    positions_(numVertices), tets_(move(tets))
+{
+  memcpy(&positions_[0][0], vertices, sizeof(double) * numVertices * 3);
+}
+
+
+/////////////////////////////////////////////////////////////
+//                    TetNeighbor
+/////////////////////////////////////////////////////////////
+
+TetNeighbor::TetNeighbor(const vector<Vec4i> & tets) : nTets(tets.size()),
+    tetNbrs(tets.size(), Vec4i(-1))
+{
+  for(int tetID = 0; tetID < nTets; tetID++)
+  {
+    OTetKey tetkey(tets[tetID]);
+    for(int j = 0; j < 4; j++)
+    {
+      int v0 = tets[tetID][j];
+      OTriKey key = tetkey.oFaceKey(tetkey.getInvertedIndex(v0));
+      if (key.isValidTriangle() == false) // // error, TetMesh is not valid!
+        throw 1;
+
+      if (otriTet.find(key) != otriTet.end())
+      {
+        cout << "Error, the tet neighbor is not manifold, tetID " << tetID << " tet " << tets[tetID] << " "
+            << "key " << key << " prevTetID: " << otriTet[key] << " prevTet " << tets[otriTet[key]] << endl;
+        throw 1; // this ordered face appears twice, not manifold!
+      }
+      otriTet.emplace(key, tetID);
+    }
+  }
+
+  for(int tetID = 0; tetID < nTets; tetID++)
+  {
+    OTetKey tetkey(tets[tetID]);
+    for(int j = 0; j < 4; j++)
+    {
+      if (tetNbrs[tetID][j] < 0)
+      {
+        int v0 = tets[tetID][j];
+        OTriKey key = tetkey.oFaceKey(tetkey.getInvertedIndex(v0));
+        auto iter = otriTet.find(key.getReversedTriKey()); // get the revserse key
+        if (iter != otriTet.end())
+        {
+          int tetID2 = iter->second;
+          tetNbrs[tetID][j] = tetID2;
+        }
+      }
+    }
+  }
+}
+
+vector<pair<int, OTriKey>> TetNeighbor::findTetBoundaries(int numTets, const Vec4i * tets) const
+{
+  assert(tetNbrs.size() == (size_t)numTets);
+  vector<pair<int, OTriKey>> ret;
+  for(size_t tetID = 0; tetID < tetNbrs.size(); tetID++)
+  {
+    const auto & n = tetNbrs[tetID];
+    const auto & t = tets[tetID];
+    for(int i = 0; i < 4; i++)
+    {
+      if (n[i] < 0) // find a bounary
+      {
+        ret.emplace_back(tetID, OTriKey(t[i], t[(i+1)%4], t[(i+2)%4]));
+      }
+    }
+  }
+  return ret;
+}
+
+NonManifoldTetNeighbor::NonManifoldTetNeighbor(const vector<Vec4i> & tets) : nTets(tets.size())
+{
+  for(int tetID = 0; tetID < nTets; tetID++)
+  {
+    OTetKey tetkey(tets[tetID]);
+    for(int j = 0; j < 4; j++)
+    {
+      int v0 = tets[tetID][j];
+      OTriKey key = tetkey.oFaceKey(tetkey.getInvertedIndex(v0));
+      if (key.isValidTriangle() == false) // // error, TetMesh is not valid!
+        throw 1;
+      otriTets[key].push_back(tetID);
+    }
+  }
+}
+
+vector<int> NonManifoldTetNeighbor::getTetNeighbors(const std::vector<Vec4i> & tets, int tetID) const
+{
+  assert(tets.size() == (size_t)nTets);
+  vector<int> ret;
+  OTetKey tetkey(tets[tetID]);
+  for(int i = 0; i < 4; i++)
+  {
+    OTriKey nbrKey = tetkey.oFaceKey(i).getReversedTriKey();
+    auto it = otriTets.find(nbrKey);
+    if (it != otriTets.end())
+    {
+      vectorInsertRangeBack(ret, it->second);
+    }
+  }
+  sortAndDeduplicate(ret);
+  return ret;
+}
+
+TetMeshGeo getSubTetMesh(const TetMeshRef & tetMesh, const std::vector<int> & subTetIDs,
+    std::vector<int> * subVtxID2FullVtxID, std::map<int, int> * fullTetID2SubTetID)
+{
+  vector<int> usedVtxIDs;
+  for(int tetID : subTetIDs)
+  {
+    for(int i = 0; i < 4; i++) usedVtxIDs.push_back(tetMesh.tet(tetID)[i]);
+  }
+  sortAndDeduplicate(usedVtxIDs);
+
+  map<int,int> oldToNewVtxMap;
+  for(size_t i = 0; i < usedVtxIDs.size(); i++)
+  {
+    oldToNewVtxMap[usedVtxIDs[i]] = i;
+  }
+
+  vector<Vec4i> newTets(subTetIDs.size());
+  int newTetID = 0;
+  for(int tetID : subTetIDs)
+  {
+    for(int i = 0; i < 4; i++)
+    {
+      int newID = oldToNewVtxMap[tetMesh.tet(tetID)[i]];
+      newTets[newTetID][i] = newID;
+    }
+    newTetID++;
+  }
+
+  vector<Vec3d> newPos(usedVtxIDs.size());
+  for(size_t i = 0; i < usedVtxIDs.size(); i++) newPos[i] = tetMesh.pos(usedVtxIDs[i]);
+  if (subVtxID2FullVtxID) { *subVtxID2FullVtxID = move(usedVtxIDs); }
+  if (fullTetID2SubTetID) { *fullTetID2SubTetID = move(oldToNewVtxMap); }
+
+  return TetMeshGeo(move(newPos), move(newTets));
+}
+
+double TetMeshRef::computeTetDeterminant(int tetID) const
+{
+  return getTetDeterminant(pos(tetID, 0), pos(tetID, 1), pos(tetID, 2), pos(tetID, 3));
+}
+
+void TetMeshRef::computeTetBarycentricWeights(int tetID, const Vec3d & queryPos, double w[4]) const
+{
+  return getTetBarycentricWeights(queryPos, pos(tetID, 0), pos(tetID, 1), pos(tetID, 2), pos(tetID, 3), w);
+}
+
+double TetMeshRef::computeSquaredDistanceToTet(int tetID, const Vec3d & queryPos) const
+{
+  return getSquaredDistanceToTet(queryPos, pos(tetID, 0), pos(tetID, 1), pos(tetID, 2), pos(tetID, 3));
+}
diff --git a/libraries/mesh/tetMeshGeo.h b/libraries/mesh/tetMeshGeo.h
new file mode 100644
index 0000000000000000000000000000000000000000..688ef77bc72a0470d0cbbd1708a3e30cfde3642e
--- /dev/null
+++ b/libraries/mesh/tetMeshGeo.h
@@ -0,0 +1,202 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TETMESHGEO_H
+#define TETMESHGEO_H
+
+#include "minivector.h"
+#include "boundingBox.h"
+#include "tetrahedron.h"
+#include "triKey.h"
+#include <string>
+#include <set>
+#include <map>
+#include <cmath>
+
+// class to reference an external triangle mesh
+class TetMeshRef
+{
+public:
+  TetMeshRef() {} // empty mesh
+  TetMeshRef(int numVertices, const double * vertices, int numTets, const int * tets);
+  TetMeshRef(const std::vector<Vec3d> & vertices, const std::vector<Vec4i> & tets);
+
+  int numVertices() const { return numVertices_; }
+  int numTets() const { return numTets_; }
+
+  const Vec3d & pos(int vtxID) const { return positions_[vtxID]; }
+  const Vec4i & tet(int tetID) const { return tets_[tetID]; }
+
+  int tetVtxID(int tetID, int i) const { return tets_[tetID][i]; }
+  const Vec3d & pos(int tetID, int i) const { return positions_[tets_[tetID][i]]; }
+
+  Vec3d computeTetCenter(int tetID) const;
+
+  const Vec3d * positions() const { return positions_; }
+  const Vec4i * tets() const { return tets_; }
+
+  Tetrahedron getTetrahedron(int tetID) const;
+
+  template<class TetIDContainer>
+  void getVerticesInTets(const TetIDContainer & tetIDs, std::vector<int> & vertexIDs) const;
+
+  // get bounding box for every tet
+  std::vector<BoundingBox> getTetBoundingBoxes() const;
+
+  double computeTetDeterminant(int tetID) const;
+
+  void computeTetBarycentricWeights(int tetID, const Vec3d & queryPos, double weight[4]) const;
+
+  double computeSquaredDistanceToTet(int tetID, const Vec3d & queryPos) const;
+  double computeDistanceToTet(int tetID, const Vec3d & queryPos) const { return std::sqrt(computeSquaredDistanceToTet(tetID, queryPos)); }
+
+
+  // save to obj mesh
+  bool save(const std::string & filename) const;
+
+protected:
+  int numVertices_ = 0, numTets_ = 0;
+  const Vec3d * positions_ = nullptr;
+  const Vec4i * tets_ = nullptr;
+};
+
+class TetMeshGeo
+{
+public:
+  TetMeshGeo() {} // empty mesh
+  TetMeshGeo(int numVertices, const double * vertices, int numTets, const int * tets);
+  TetMeshGeo(std::vector<Vec3d> vertices, std::vector<Vec4i> tets);
+  TetMeshGeo(int numVertices, const Vec3d * vertices, std::vector<Vec4i> tets);
+
+  int numVertices() const { return positions_.size(); }
+  int numTets() const { return tets_.size(); }
+
+  const Vec3d & pos(int vtxID) const { return positions_[vtxID]; }
+  Vec3d & pos(int vtxID) { return positions_[vtxID]; }
+  const Vec4i & tet(int tetID) const { return tets_[tetID]; }
+  Vec4i & tet(int tetID) { return tets_[tetID]; }
+
+  int tetVtxID(int tetID, int i) const { return tets_[tetID][i]; }
+  const Vec3d & pos(int tetID, int i) const { return positions_[tets_[tetID][i]]; }
+  Vec3d & pos(int tetID, int i) { return positions_[tets_[tetID][i]]; }
+
+  const std::vector<Vec3d> & positions() const { return positions_; }
+  const std::vector<Vec4i> & tets() const { return tets_; }
+
+  TetMeshRef ref() const { return { positions_, tets_ }; }
+//  implicit conversion
+  operator TetMeshRef() const { return ref(); }
+
+  // save to obj mesh
+  bool save(const std::string & filename) const { return ref().save(filename); }
+
+protected:
+  std::vector<Vec3d> positions_;
+  std::vector<Vec4i> tets_;
+};
+
+class TetNeighbor
+{
+public:
+  TetNeighbor() {}
+  TetNeighbor(const std::vector<Vec4i> & tets);
+  int numTets() const { return nTets; }
+
+  const Vec4i & getTetNeighbors(int tetID) const { return tetNbrs[tetID]; }
+
+  std::vector<std::pair<int, OTriKey>> findTetBoundaries(int numTets, const Vec4i * tets) const;
+  inline std::vector<std::pair<int, OTriKey>> findTetBoundaries(const std::vector<Vec4i> & tets) const { return findTetBoundaries(tets.size(), tets.data()); }
+
+protected:
+  int nTets = 0;
+  std::vector<Vec4i> tetNbrs;
+  std::map<OTriKey, int> otriTet; // <v0,v1,v2> -> tet with ordered face <v0, v1, v2>
+};
+
+class NonManifoldTetNeighbor
+{
+public:
+  NonManifoldTetNeighbor() {} // empty
+  NonManifoldTetNeighbor(const std::vector<Vec4i> & tets);
+
+  // return sorted buffer of all tets neighboring tetID
+  std::vector<int> getTetNeighbors(const std::vector<Vec4i> & tets, int tetID) const;
+
+protected:
+  int nTets = 0;
+  std::map<OTriKey, std::vector<int>> otriTets;
+};
+
+
+// return a sub mesh which are triangles from selected triangleIDs
+// isolated vertices in the sub mesh ARE NOT removed
+// call removeIsolatedVertices to remove them from the result TriMesh
+template<class TetIDContainer>
+TetMeshGeo getSubTetMeshWithSameVertices(const TetMeshRef & tetMesh, const TetIDContainer & tetIDs);
+
+TetMeshGeo getSubTetMesh(const TetMeshRef & tetMesh, const std::vector<int> & subTetIDs,
+    std::vector<int> * subVtxID2FullVtxID = nullptr,
+    std::map<int, int> * fullTetID2SubTetID = nullptr);
+
+//////////////////////////////////////////////////////////
+//                  Implementation
+//////////////////////////////////////////////////////////
+
+template<class TetIDContainer>
+void TetMeshRef::getVerticesInTets(const TetIDContainer & tetIDs, std::vector<int> & vertexIDs) const
+{
+  int numPrevIDs = vertexIDs.size();
+  for(int tetID: tetIDs)
+  {
+    for(int i = 0; i < 4; i++)
+    {
+      vertexIDs.push_back(tets_[tetID][i]);
+    }
+  }
+  // remove duplicate vertex IDs
+  std::sort(vertexIDs.begin() + numPrevIDs, vertexIDs.end());
+  auto newEnd = std::unique(vertexIDs.begin() + numPrevIDs, vertexIDs.end());
+  vertexIDs.resize(std::distance(vertexIDs.begin(), newEnd));
+}
+
+template<class TetIDContainer>
+TetMeshGeo getSubTetMeshWithSameVertices(const TetMeshRef & tetMesh, const TetIDContainer & tetIDs)
+{
+  std::vector<Vec4i> subTets;
+  for(int tetID: tetIDs)
+  {
+    subTets.push_back(tetMesh.tet(tetID));
+  }
+  return TetMeshGeo(tetMesh.numVertices(), tetMesh.positions(), move(subTets));
+}
+
+#endif
diff --git a/libraries/mesh/tetMeshManifold.cpp b/libraries/mesh/tetMeshManifold.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cada8ec7106e7ced7fed4f0d9edecf5e20a64a24
--- /dev/null
+++ b/libraries/mesh/tetMeshManifold.cpp
@@ -0,0 +1,294 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "tetMeshManifold.h"
+#include <algorithm>
+#include <iostream>
+#include <cstring>
+#include <cassert>
+using namespace std;
+
+TetMeshManifold::TetMeshManifold() {}
+
+TetMeshManifold::~TetMeshManifold()
+{
+  clear();
+}
+
+void TetMeshManifold::clear()
+{
+  for(TetIter it = tets.begin(); it != tets.end(); it++)
+    delete it->second;
+
+  for(TriIter it = triangles.begin(); it != triangles.end(); it++)
+    delete it->second;
+
+  tets.clear();
+  triangles.clear();
+  surface.clear();
+}
+
+const TetMeshManifold::Tetrahedron * TetMeshManifold::add(const int vtx[4], int id)
+{
+  return add(vtx[0],vtx[1],vtx[2],vtx[3], id);
+}
+
+const TetMeshManifold::Tetrahedron * TetMeshManifold::add(int v0, int v1, int v2, int v3, int id)
+{
+  UTetKey tetkey(v0,v1,v2,v3);
+  TetIter it = tets.find(tetkey);
+  // found this tet in the manifold
+  if (it != tets.end())
+    return it->second;
+
+  Tetrahedron * tet = new Tetrahedron(v0,v1,v2,v3);
+  tet->id = id;
+
+  UTriKey utrikeys[4];
+  OTriKey otrikeys[4];
+  Triangle * faces[4];
+  bool fail = false;
+  // find all four faces in the manifold and test potential non-manifold adding
+  for(int i = 0; i < 4; i++)
+  {
+    utrikeys[i] = tet->uFaceKey(i);
+    otrikeys[i] = tet->oFaceKey(i);
+    TriIter triit = triangles.find(utrikeys[i]);
+    if (triit == triangles.end()) // this triangle face is not inside triangles; it's new
+    {
+      faces[i] = NULL;
+      assert(surface.find(otrikeys[i]) == surface.end()); // it should also not inside surface
+    }
+    else
+    {
+      faces[i] = triit->second;
+      assert(faces[i]->tet[0] != NULL);
+      if (faces[i]->tet[1] != NULL)
+      {
+        // this triangle has two tets already. Mesh would become non-manifold if the tet is added
+        fail = true;
+        break;
+      }
+      if (surface.find(otrikeys[i].getReversedTriKey()) == surface.end()) // the orientation of this face is wrong
+      {
+        fail = true;
+        break;
+      }
+    }
+  }
+  if (fail)
+  {
+    delete tet;
+    return NULL;
+  }
+
+  // Now it's safe. Add this tet to the manifold
+  tets[tetkey] = tet;
+  // OTriKey surfaceFaceToAdd[4];
+  // int numSurfaceFaceToAdd = 0;
+
+  for(int i = 0; i < 4; i++)
+  {
+    Triangle * tri = faces[i];
+    UTriKey & facekey = utrikeys[i];
+    OTriKey & okey = otrikeys[i];
+
+    if (tri == NULL)
+    {
+      // no this triangle exist. We add a new triangle
+      tri = new Triangle(facekey);
+      triangles[facekey] = tri;
+      tri->tet[0] = tet;
+      tet->face[i] = tri;
+     // assert(surface.find(okey) == surface.end());
+     surface.insert(pair<OTriKey, Tetrahedron *>(okey, tet));
+//      cout << "Begin to add surface triangle: " << endl;
+      //
+      // surfaceFaceToAdd[numSurfaceFaceToAdd++] = okey;
+      // we don't add okey to surfaceManifold here now because this might cause the surfaceManifold to become non-manifold
+//      surfaceManifold.add(okey);
+    }
+    else
+    { // tet has a neighbor on this face
+      assert(surface.find(okey) == surface.end());
+      tri->tet[1] = tet;
+      tet->face[i] = tri;
+      Tetrahedron * nbr = tri->tet[0];
+      for(int j = 0; j < 4; j++)
+        if (nbr->face[j] == tri)
+        {
+          assert(nbr->nbr[j] == NULL);
+          nbr->nbr[j] = tet;
+          tet->nbr[i] = nbr;
+          break;
+        }
+      okey.reverse(); // now okey is the neighbor's face
+     assert(surface.find(okey) != surface.end());
+     surface.erase(okey);
+      // bool success = surfaceManifold.remove(okey);
+      // assert(success == true);
+    }
+  }
+
+  // for(int i = 0; i < numSurfaceFaceToAdd; i++) {
+  //   const TriMeshManifold::Triangle * ret = surfaceManifold.add(surfaceFaceToAdd[i]);
+  //   assert(ret != NULL);
+  // }
+  return tet;
+}
+
+bool TetMeshManifold::remove(const int vtx[4])
+{
+  return remove(vtx[0],vtx[1],vtx[2],vtx[3]);
+}
+
+bool TetMeshManifold::remove(int v0, int v1, int v2, int v3)
+{
+  UTetKey tetkey(v0,v1,v2,v3);
+  TetIter it = tets.find(tetkey);
+  // can't find this tet in the manifold
+  if (it == tets.end())
+    return false;
+
+  // OTriKey surfaceFaceToAdd[4];
+  // int numSurfaceFaceToAdd = 0;
+
+  Tetrahedron * tet = it->second;
+  for(int i = 0; i < 4; i++)
+  {
+    UTriKey trikey = tet->uFaceKey(i);
+    TriIter triit = triangles.find(trikey);
+    assert(triit != triangles.end());
+    Triangle * tri = triit->second;
+    assert(tri->tet[0] == tet || tri->tet[1] == tet);
+    assert(!(tri->tet[1] == tet && tri->tet[0] == NULL) );
+    assert(!(tri->tet[0] == tet && tri->tet[1] == tet) );
+
+    OTriKey okey = tet->oFaceKey(i);
+    Tetrahedron * nbr = NULL;
+    if (tri->tet[0] != tet)
+      nbr = tri->tet[0];
+    else
+      nbr = tri->tet[1];
+
+    if (nbr)
+    {
+      // we have a neighbor for this tet
+      // we'll reset the corresponding neighbor var
+      assert(tet->nbr[i] == nbr);
+      for(int j = 0; j < 4; j++)
+        if (nbr->face[j] == tri)
+        {
+          nbr->nbr[j] = NULL;
+          break;
+        }
+      // we remove this tet's reference in tri
+      // and move neighbor to tet[0] so that tri->tet[0] is always not NULL
+      tri->tet[0] = nbr;
+      tri->tet[1] = NULL;
+
+      okey.reverse(); // now it's neighbor's face
+      assert(surface.find(okey) == surface.end());
+      surface.insert(pair<OTriKey, Tetrahedron *>(okey, tet));
+
+      // surfaceFaceToAdd[numSurfaceFaceToAdd++] = okey;
+      // we don't add okey to surfaceManifold here now because this might cause the surfaceManifold to become non-manifold
+//      surfaceManifold.add(okey);
+    }
+    else
+    {
+      // only this tet shares this face
+      // we'll delete this face
+      triangles.erase(triit);
+      delete tri;
+      assert(surface.find(okey) != surface.end());
+      surface.erase(okey);
+      // bool success = surfaceManifold.remove(okey);
+      // assert(success == true);
+    }
+  }
+
+  // for(int i = 0; i < numSurfaceFaceToAdd; i++) {
+  //   const TriMeshManifold::Triangle * ret = surfaceManifold.add(surfaceFaceToAdd[i]);
+  //   assert(ret != NULL);
+  // }
+
+  tets.erase(it);
+  delete tet;
+  return true;
+}
+
+void TetMeshManifold::getSurface(std::set<OTriKey> & outputSurface) const
+{
+  outputSurface.clear();
+  for(map<OTriKey, Tetrahedron *>::const_iterator it = surface.begin(); it != surface.end(); it++)
+    outputSurface.insert(it->first);
+}
+
+TetMeshManifold::Tetrahedron::Tetrahedron(int v0, int v1, int v2, int v3) : OTetKey(v0,v1,v2,v3), id(-1)
+{
+//  v[0] = v0; v[1] = v1; v[2] = v2; v[3] = v3;
+  memset(face, 0, sizeof(face));
+  memset(nbr, 0, sizeof(nbr));
+  // cout << "tetrahedron created: " << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << endl;
+}
+
+int TetMeshManifold::Tetrahedron::getOppositeVtx(const Triangle * tri) const
+{
+  for(int i = 0; i < 4; i++)
+    if (face[i] == tri)
+      return v[i];
+  return -1;
+}
+
+bool TetMeshManifold::Tetrahedron::isNeighbor(const TetMeshManifold::Tetrahedron * other) const
+{
+  for(int i = 0; i < 4; i++)
+    if (nbr[i] == other)
+      return true;
+  return false;
+}
+
+int TetMeshManifold::Tetrahedron::getNeighborInvertedIndex(const TetMeshManifold::Tetrahedron * other) const
+{
+  for(int i = 0; i < 4; i++)
+    if (nbr[i] == other)
+      return i;
+  return -1;
+}
+
+TetMeshManifold::Triangle::Triangle(const UTriKey & key) : UTriKey(key)
+{
+  memset(tet, 0, sizeof(tet));
+}
+
+
diff --git a/libraries/mesh/tetMeshManifold.h b/libraries/mesh/tetMeshManifold.h
new file mode 100644
index 0000000000000000000000000000000000000000..9028abdee7648a714beddcae750945194d61291c
--- /dev/null
+++ b/libraries/mesh/tetMeshManifold.h
@@ -0,0 +1,134 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TETMESHMANIFOLD_H
+#define TETMESHMANIFOLD_H
+#include <map>
+#include <set>
+#include "tetKey.h"
+
+// maintain a tet mesh to enfore manifoldness on tet-face connection
+// it can also query neighboring relationship on tets
+// note that it only detects non-manifoldness created by one face shared by more than two tets,
+// does not consider the case where two tets sharing no face but one vertex
+
+
+// define tet(v0,v1,v2,v3) orientation:
+//                                                  |(p1 - p0)^T|
+// ((p1 - p0) x (p2 - p0)) dot (p3 - p0) > 0, <==>  |(p2 - p0)^T| > 0
+//                                                  |(p3 - p0)^T|
+// in struct UTetKey, we order v[4] to be v0 < v1 < v2 < v3,
+// in struct OTetKey, we order v[4] to be v0 < v1 < (v2, v3) and the tet orientation is preserved
+// we also preserve the input tet vertex order in class TetMeshManifold
+// so that we can get correct tet face orientation from Tetrahedron::getOppositeFace(size_t)
+//
+// in a UTriKey, we order v[3] to be v0 < v1 < v2,
+// no ordering is preserved in class TetMeshManifold::Triangle
+
+class TetMeshManifold
+{
+public:
+  TetMeshManifold();
+  virtual ~TetMeshManifold();
+
+  void clear();
+
+  class Triangle;
+  class Tetrahedron;
+  typedef std::map<UTetKey, Tetrahedron *> TetMap;
+  typedef std::map<UTriKey, Triangle *> TriMap;
+
+  // add a new tet (v0,v1,v2, v3) into the mesh, return the new built Tetrahedron
+  // if the tet already exists, it just return the tet
+  // return NULL if the new tet can cause one face shared by more than two tets (non-manifoldness)
+  // an id can also be given to the input tet
+  const Tetrahedron * add(int v0, int v1, int v2, int v3, int id = -1);
+  const Tetrahedron * add(const int vtx[4], int id = -1);
+
+  // remove a tet from the mesh, return false if it cannot be found
+  bool remove(int v0, int v1, int v2, int v3);
+  bool remove(const int vtx[4]);
+
+  // get the connection structure for tets and triangles
+  const TetMap & getTetMap() const { return tets; }
+  const TriMap & getTriMap() const { return triangles; }
+  // get the surface (boundary faces) of the mesh
+  const std::map<OTriKey, Tetrahedron *> & getSurfaceMap() const { return surface; }
+  void getSurface(std::set<OTriKey> & surface) const;
+
+  class Triangle : public UTriKey
+  {
+  public:
+    Triangle(const UTriKey & key);
+    const Tetrahedron * getTet(std::size_t i) const { return tet[i]; }
+    int getVtx(std::size_t i) const { return v[i]; }
+  protected:
+    Tetrahedron * tet[2];
+    friend class TetMeshManifold;
+  };
+
+  class Tetrahedron : public OTetKey
+  {
+  public:
+    Tetrahedron(int v0, int v1, int v2, int v3);
+    int getVtx(std::size_t i) const { return v[i]; }
+    // get the index of vtx in v[4], return -1 if not found
+    int getVertexInvertedIndex(int vtx) const { return getInvertedIndex(vtx); }
+    const Tetrahedron * getNeighbor(std::size_t i) const { return nbr[i]; }
+    bool isNeighbor(const Tetrahedron * other) const;
+    int getNeighborInvertedIndex(const Tetrahedron * other) const; // get the index of other in this->nbr[4], return -1 if not found
+    // get oriented opposite face for each vtx in the tet, vtx: 0-3
+    void getOppositeFace(int vtx, int face[3]) const;
+    OTriKey getOppositeOFace(int vtx) const; // vtx: 0-3
+    UTriKey getOppositeUFace(int vtx) const; // vtx: 0-3
+    int getOppositeVtx(const Triangle * face) const;
+    int getID() const { return id; }
+  protected:
+    int id;
+    Triangle * face[4];
+    Tetrahedron * nbr[4];
+    friend class TetMeshManifold;
+  };
+
+
+  typedef TetMap::iterator TetIter;
+  typedef TetMap::const_iterator TetCIter;
+  typedef TriMap::iterator TriIter;
+  typedef TriMap::const_iterator TriCIter;
+
+protected:
+  TetMap tets;
+  TriMap triangles;
+  std::map<OTriKey, Tetrahedron *> surface;
+};
+
+#endif
diff --git a/libraries/mesh/tetrahedron.cpp b/libraries/mesh/tetrahedron.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d20a0925c92af9b38aad98ce12bc3df38c521f3d
--- /dev/null
+++ b/libraries/mesh/tetrahedron.cpp
@@ -0,0 +1,34 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "tetrahedron.h"
+
diff --git a/libraries/mesh/tetrahedron.h b/libraries/mesh/tetrahedron.h
new file mode 100644
index 0000000000000000000000000000000000000000..97fe5a51ec9c583c768569c0f75d571de62e60be
--- /dev/null
+++ b/libraries/mesh/tetrahedron.h
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TETRAHEDRON_H
+#define TETRAHEDRON_H
+
+#include "vec3d.h"
+#include <array>
+
+class Tetrahedron
+{
+public:
+  Tetrahedron(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2, const Vec3d & v3) : v({v0, v1, v2, v3}) {}
+
+  const Vec3d & operator [] (int i) const { return v[i]; }
+  Vec3d & operator [] (int i) { return v[i]; }
+
+  const Vec3d * begin() const { return &v[0]; }
+  Vec3d * begin() { return &v[0]; }
+
+  const Vec3d * end() const { return &v[0] + 4; }
+  Vec3d * end() { return &v[0] + 4; }
+
+protected:
+  std::array<Vec3d, 4> v;
+};
+
+#endif
+
diff --git a/libraries/mesh/triKey.cpp b/libraries/mesh/triKey.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f93d24f4cb2254719a8aa616d5ee375e7548ccbf
--- /dev/null
+++ b/libraries/mesh/triKey.cpp
@@ -0,0 +1,80 @@
+/*
+  This code is based on code from the Geometric Tools library,
+  which is licensed under a boost license.
+  Such usage is permitted by the boost license; for details,
+  please see the boost license below.
+*/
+
+// Geometric Tools, LLC
+// Copyright (c) 1998-2014
+// Distributed under the Boost Software License, Version 1.0.
+// http://www.boost.org/LICENSE_1_0.txt
+// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
+
+/*************************************************************************
+ *                                                                       *
+ * We release our improvements to the wildMagic code under our standard  *
+ * Vega FEM license, as follows:                                         *
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "improvements to the wildMagic library" , Copyright (C) 2018 USC      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "triKey.h"
+
+const int UTriKey::triEdgeIndex[3][2] =  { { 1, 2 }, { 0, 2 }, { 0, 1 } };
+
+const int OTriKey::triEdgeIndex[3][2] =  { { 1, 2 }, { 2, 0 }, { 0, 1 } };
+
+void OTriKey::permute(int v0, int v1, int v2, int & r0, int & r1, int & r2)
+{
+  if (v0 < v1)
+  {
+    if (v0 < v2)
+    {
+      r0 = v0; r1 = v1; r2 = v2;
+    }
+    else
+    { // v2 <= v0
+      r0 = v2; r1 = v0; r2 = v1;
+    }
+  }
+  else
+  { // v1 <= v0
+    if (v1 < v2)
+    {
+      r0 = v1; r1 = v2; r2 = v0;
+    }
+    else
+    { // v2 <= v1
+      r0 = v2; r1 = v0; r2 = v1;
+    }
+  }
+}
+
diff --git a/libraries/mesh/triKey.h b/libraries/mesh/triKey.h
new file mode 100644
index 0000000000000000000000000000000000000000..07f0ce563d3ef3a280ade404dcd4d5fc06c01845
--- /dev/null
+++ b/libraries/mesh/triKey.h
@@ -0,0 +1,428 @@
+/*
+  This code is based on code from the Geometric Tools library,
+  which is licensed under a boost license.
+  Such usage is permitted by the boost license; for details,
+  please see the boost license below.
+*/
+
+// Geometric Tools, LLC
+// Copyright (c) 1998-2014
+// Distributed under the Boost Software License, Version 1.0.
+// http://www.boost.org/LICENSE_1_0.txt
+// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
+
+/*************************************************************************
+ *                                                                       *
+ * We release our improvements to the wildMagic code under our standard  *
+ * Vega FEM license, as follows:                                         *
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "improvements to the wildMagic library" , Copyright (C) 2018 USC      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TRIKEY_H
+#define TRIKEY_H
+
+#include <algorithm>
+#include <ostream>
+#include "vec3i.h"
+#include "edgeKey.h"
+
+
+
+// unoriented triangle key based on vtx indices
+// v[0], v[1] and v[2] will be sorted to ensure v[0] <= v[1] <= v[2]
+struct UTriKey
+{
+  inline UTriKey(); // creates an invalid key with v = {-1,-1,-1}
+  inline UTriKey(int v0, int v1, int v2);
+  inline UTriKey(const int v[3]) : UTriKey(v[0], v[1], v[2]) {}
+  inline UTriKey(const Vec3i & v) : UTriKey(v[0], v[1], v[2]) {}
+
+  inline bool operator < (const UTriKey & o) const { return v < o.v; }
+  inline bool operator == (const UTriKey & o) const { return v == o.v; }
+  inline bool operator != (const UTriKey & o) const { return v != o.v; }
+  const int & operator [] (int index) const { return v[index]; }
+  const int * indices() const { return &v[0]; }
+  // return the unordered edge opposite to v[i], i:[0,3)
+  inline UEdgeKey uEdgeKey(int i) const { return UEdgeKey(v[triEdgeIndex[i][0]], v[triEdgeIndex[i][1]]); }
+  // given the global vtx index, return its first index in v [0,2]
+  // else return -1
+  inline int getInvertedIndex(const int globalVtxIndex) const { return v.getInvertedIndex(globalVtxIndex); }
+
+  inline bool shareUEdge(const UTriKey & nbr) const;
+  inline UEdgeKey getSharedUEdge(const UTriKey & nbr) const; // return the first shared UEdge; return (-1,-1) if no shared UEdge
+
+  inline bool hasUEdge(const UEdgeKey & edge) const;
+  inline bool hasIndex(int vtxIndex) const { return v[0] == vtxIndex || v[1] == vtxIndex || v[2] == vtxIndex; }
+
+  // return -1 if edge not found
+  inline int getVertexOppositeEdge(const UEdgeKey & edge) const;
+
+  // if all indices are >= 0
+  inline bool allVerticesValid() const { return v[0] >= 0; }
+  // if this triangle is valid: v[i] >=0 && v[i] != v[j]
+  inline bool isValidTriangle() const;
+
+  // index: { { 1, 2 }, { 0, 2 }, { 0, 1 } }
+  static const int triEdgeIndex[3][2];
+protected:
+  Vec3i v;
+};
+
+inline std::ostream & operator << (std::ostream & s, const UTriKey & v);
+
+// oriented triangle key that remember its orientation
+// v[0],v[1] and v[2] will be reordered to ensure v[0] <= v[1] and v[0] <= v[2] and (v[0], v[1], v[2]) remain the same orientation as the input
+struct OTriKey
+{
+  inline OTriKey(); // creates an invalid key with v = {-1,-1,-1}
+  inline OTriKey(int v0, int v1, int v2);
+  inline OTriKey(const int v[3]) : OTriKey(v[0], v[1], v[2]) {}
+  inline OTriKey(const Vec3i & v) : OTriKey(v[0], v[1], v[2]) {}
+
+  inline bool operator < (const OTriKey & o) const { return v < o.v; }
+  inline bool operator == (const OTriKey & o) const { return v == o.v; }
+  inline bool operator != (const OTriKey & o) const { return v != o.v; }
+  const int & operator [] (int index) const { return v[index]; }
+  const int * indices() const { return &v[0]; }
+
+  inline UTriKey uTriKey() const { return UTriKey(&v[0]); }
+
+  // return the ordered/unordered edge opposite to v[i], i:[0,3)
+  inline UEdgeKey uEdgeKey(int i) const { return UEdgeKey(v[triEdgeIndex[i][0]], v[triEdgeIndex[i][1]]); }
+  inline OEdgeKey oEdgeKey(int i) const { return OEdgeKey(v[triEdgeIndex[i][0]], v[triEdgeIndex[i][1]]); }
+
+  // given the global vtx index, return the index in v [0,2]; otherwise return -1
+  inline int getInvertedIndex(const int globalVtxIndex) const { return v.getInvertedIndex(globalVtxIndex); }
+
+  inline bool shareUEdge(const OTriKey & nbr) const;
+  inline bool shareOEdge(const OTriKey & nbr) const;
+  inline bool shareOppositeOEdge(const OTriKey & nbr) const;
+  inline OEdgeKey getSharedOppositeOEdge(const OTriKey & nbr) const; // return first owned OEdge whose reverse is owned by nbr; return (-1,-1) otherwise
+  inline UEdgeKey getSharedUEdge(const OTriKey & nbr) const; // return the first shared UEdge; return (-1,-1) if no shared UEdge
+
+  inline int getInvertedOEdgeIndex(const OEdgeKey & edge) const;
+
+  bool hasOEdge(const OEdgeKey & edge) const;
+  bool hasUEdge(const UEdgeKey & edge) const;
+
+  inline void reverse() { std::swap(v[1], v[2]); } // reverse the orientation
+  inline OTriKey getReversedTriKey() const { return OTriKey(v[0], v[2], v[1]); } // return reversed tri key
+
+  // if all indices are >= 0
+  inline bool allVerticesValid() const { return v[0] >= 0; }
+  // if this triangle is valid: v[i] >=0 && v[i] != v[j]
+  inline bool isValidTriangle() const;
+
+  // permute v0, v1, v2 and store into r0, r1, r2 so that they share the same orientation but r0 = min(v0,v1,v2)
+  static void permute(int v0, int v1, int v2, int & r0, int & r1, int & r2);
+
+  // index: { { 1, 2 }, { 2, 0 }, { 0, 1 } }
+  static const int triEdgeIndex[3][2];
+protected:
+  Vec3i v;
+};
+
+inline std::ostream & operator << (std::ostream & s, const OTriKey & v);
+
+
+///////////////////////////////////////////////////////////////////////////////
+//                             IMPLEMENTATION                                //
+///////////////////////////////////////////////////////////////////////////////
+
+inline UTriKey::UTriKey()
+{
+  v[0] = v[1] = v[2] = -1;
+}
+
+inline UTriKey::UTriKey(int v0, int v1, int v2)
+{
+  v[0] = v0;
+  v[1] = v1;
+  v[2] = v2;
+  std::sort(&v[0], &v[3]);
+}
+
+// find whether two elemetns are shared among the two tuples: (v0, v1, v2) and (n0, n1, n2)
+// we utilize the fact that vertices are pre-sorted in UTriKey
+inline bool UTriKey::shareUEdge(const UTriKey & nbr) const
+{
+  for(int i = 0; i < 3; i++)
+  {
+    UEdgeKey key = uEdgeKey(i);
+    for(int j = 0; j < 3; j++)
+      if (key == nbr.uEdgeKey(j))
+        return true;
+  }
+  return false;
+
+  // const int * n = &nbr.v[0];
+
+  // if (v[0] == n[0])
+  // {
+  //   if (v[1] == n[1] || v[1] == n[2] || v[2] == n[1] || v[2] == n[2])
+  //     return true;
+  //   return false;
+  // }
+  // else if (v[0] == n[1])
+  // {
+  //   // if enthering this branch (v[0] == n[1]), then n[0] <= v[1]. we can prove v[1] != n[0], otherwise v[0]==v[1]==n[0]==n[1], should enter the first branch already
+  //   // same case for v[2] != n[0]
+  //   if (v[1] == n[2] || v[2] == n[2])
+  //     return true;
+  //   return false;
+  // }
+  // // if v[0] == n[2], then n[1] <= v[1]
+  // // if n[1] < v[1], these means (v1, v2) will never have one same element as (n0, n1), cannot share UEdge
+  // // else, n[1] == v[1], then v0 == n1, should go to the second branch already
+  // // so we don't consider the case where v0 == v2
+  // if (v[1] == n[1] && v[2] == n[2])
+  //   return true;
+  // return false;
+}
+
+inline UEdgeKey UTriKey::getSharedUEdge(const UTriKey & nbr) const
+{
+  for(int i = 0; i < 3; i++)
+  {
+    UEdgeKey key = uEdgeKey(i);
+    for(int j = 0; j < 3; j++)
+      if (key == nbr.uEdgeKey(j))
+        return key;
+  }
+  return UEdgeKey(); // return a default invalid UEdgeKey
+}
+
+inline bool UTriKey::isValidTriangle() const
+{
+  return v[0] >= 0 && v[0] != v[1] && v[1] != v[2]; // because v[0] <= v[1] <= v[2]
+}
+
+inline bool UTriKey::hasUEdge(const UEdgeKey & edge) const
+{
+  for(int i = 0; i < 3; i++)
+    if (edge == uEdgeKey(i))
+      return true;
+  return false;
+}
+
+inline int UTriKey::getVertexOppositeEdge(const UEdgeKey & edge) const
+{
+  for(int i = 0; i < 3; i++)
+    if (edge == uEdgeKey(i))
+      return v[i];
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//                         OTriKey
+////////////////////////////////////////////////////////////////////
+
+inline OTriKey::OTriKey()
+{
+  v[0] = v[1] = v[2] = -1;
+}
+
+inline OTriKey::OTriKey(int v0, int v1, int v2)
+{
+  permute(v0, v1, v2, v[0], v[1], v[2]);
+}
+
+// find whether two elemetns are shared among the two tuples: (v0, v1, v2) and (n0, n1, n2)
+// we utilize the fact that v0 <= min(v1, v2) and n0 <= min(n1, n2)
+inline bool OTriKey::shareUEdge(const OTriKey & nbr) const
+{
+  for(int i = 0; i < 3; i++)
+  {
+    UEdgeKey key = uEdgeKey(i);
+    for(int j = 0; j < 3; j++)
+      if (key == nbr.uEdgeKey(j))
+        return true;
+  }
+  return false;
+
+  // const int * n = &nbr.v[0];
+  // if (v[0] == n[0])
+  // {
+  //   if (v[1] == n[1] || v[1] == n[2] || v[2] == n[1] || v[2] == n[2])
+  //     return true;
+  //   return false;
+  // }
+  // else if (v[0] == n[1])
+  // {
+  //   // if enthering this branch (v[0] == n[1]), then v[1] != n[0], otherwise v[0]==v[1]==n[0]==n[1], should enter the first branch already
+  //   // same case for v[2] != n[0]
+  //   if (v[1] == n[2] || v[2] == n[2])
+  //     return true;
+  //   return false;
+  // }
+  // else if (v[0] == n[2])
+  // {
+  //   // if enthering this branch (v[0] == n[2]), then v[1] != n[0], otherwise v[0]==v[1]==n[0]==n[1], should enter the first branch already
+  //   // same case for v[2] != n[0]
+  //   if (v[1] == n[1] || v[2] == n[1])
+  //     return true;
+  //   return false;
+  // }
+
+  // if (v[1] == n[1] && v[2] == n[2])
+  //   return true;
+  // return false;
+}
+
+// find whether two TriKeys share the same OEdge
+// we utilize the fact that v0 <= min(v1, v2) and n0 <= min(n1, n2)
+// only nine cases
+// case 1, (v1, v2) == (n1, n2)
+// case 2, (v1, v2) == (n2, n0)
+// case 3, (v1, v2) == (n0, n1)
+// case 4, (v2, v0) == (n1, n2)
+// case 5, (v2, v0) == (n2, n0)
+// case 6, (v2, v0) == (n0, n1)
+// case 7, (v0, v1) == (n1, n2)
+// case 8, (v0, v1) == (n2, n0)
+// case 9, (v0, v1) == (n0, n1)
+inline bool OTriKey::shareOEdge(const OTriKey & nbr) const
+{
+  // const int * n = nbr.v;
+  // if (v[0] == n[0]) // case 5, 9
+  // {
+  //   if (v[1] == n[1] || v[1] == n[2])
+  //     return true;
+  // }
+  // else if (v[0] == n[1]) // case 6, 7
+  // {
+  //   // if enthering this branch (v[0] == n[1]), then v[2] != n[0], otherwise v[0]==v[2]==n[0]==n[1], should enter the first branch already
+  //   if (v[1] == n[2])
+  //     return true;
+  // }
+  // else if (v[0] == n[2]) // case 4, 8
+  // {
+  //   // if enthering this branch (v[0] == n[2]), then v[1] != n[0], otherwise v[0]==v[1]==n[0]==n[1], should enter the first branch already
+  //   if (v[2] == n[1])
+  //     return true;
+  // }
+  // // check case 1, 2, 3
+  // if (v[1] == n[1] && v[2] == n[2])
+  //   return true;
+  // if (v[1] == n[2] && v[2] == n[0])
+  //   return true;
+  // if (v[1] == n[0] && v[2] == n[1])
+  //   return true;
+
+  for(int i = 0; i < 3; i++)
+  {
+    OEdgeKey key = oEdgeKey(i);
+    for(int j = 0; j < 3; j++)
+      if (key == nbr.oEdgeKey(j))
+        return true;
+  }
+
+  return false;
+}
+
+inline bool OTriKey::shareOppositeOEdge(const OTriKey & nbr) const
+{
+  for(int i = 0; i < 3; i++)
+  {
+    OEdgeKey key = oEdgeKey(i);
+    key.reverse();
+    for(int j = 0; j < 3; j++)
+      if (key == nbr.oEdgeKey(j))
+        return true;
+  }
+  return false;
+}
+
+inline OEdgeKey OTriKey::getSharedOppositeOEdge(const OTriKey & nbr) const
+{
+  for(int i = 0; i < 3; i++)
+  {
+    OEdgeKey key = oEdgeKey(i);
+    key.reverse();
+    for(int j = 0; j < 3; j++)
+      if (key == nbr.oEdgeKey(j))
+        return key.getReversedEdgeKey();
+  }
+  return OEdgeKey();
+}
+
+inline UEdgeKey OTriKey::getSharedUEdge(const OTriKey & nbr) const
+{
+  for(int i = 0; i < 3; i++)
+  {
+    UEdgeKey key = uEdgeKey(i);
+    for(int j = 0; j < 3; j++)
+      if (key == nbr.uEdgeKey(j))
+        return key;
+  }
+  return UEdgeKey(); // return a default invalid UEdgeKey
+}
+
+inline bool OTriKey::hasOEdge(const OEdgeKey & edge) const
+{
+  for(int i = 0; i < 3; i++)
+    if (edge == oEdgeKey(i))
+      return true;
+  return false;
+}
+
+inline bool OTriKey::hasUEdge(const UEdgeKey & edge) const
+{
+  for(int i = 0; i < 3; i++)
+    if (edge == uEdgeKey(i))
+      return true;
+  return false;
+}
+
+inline int OTriKey::getInvertedOEdgeIndex(const OEdgeKey & edge) const
+{
+  for(int i = 0; i < 3; i++)
+    if (edge == oEdgeKey(i))
+      return i;
+  return -1;
+}
+
+inline bool OTriKey::isValidTriangle() const
+{
+  return v[0] >= 0 && v[0] != v[1] && v[1] != v[2] && v[0] != v[2]; // because v[0] <= v[1], v[0] <= v[2]
+}
+
+inline std::ostream & operator << (std::ostream & s, const UTriKey & v)
+{
+  return s << '(' << v[0] << ' ' << v[1] << ' '  << v[2] << ')';
+}
+
+inline std::ostream & operator << (std::ostream & s, const OTriKey & v)
+{
+  return s << '(' << v[0] << ' ' << v[1] << ' '  << v[2] << ')';
+}
+
+#endif
diff --git a/libraries/mesh/triMeshGeo.cpp b/libraries/mesh/triMeshGeo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0845e2f221a61ace71415d63410da4e562d43479
--- /dev/null
+++ b/libraries/mesh/triMeshGeo.cpp
@@ -0,0 +1,768 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "triMeshGeo.h"
+#include "triKey.h"
+#include "basicAlgorithms.h"
+#include "geometryQuery.h"
+#include "valueIndex.h"
+#include "stringHelper.h"
+
+#include <iostream>
+#include <cstring>
+#include <cassert>
+#include <fstream>
+#include <cmath>
+#include <functional>
+#include <limits>
+#include <unordered_set>
+#include <map>
+#include <numeric>
+using namespace std;
+
+
+/////////////////////////////////////////////////////////////////
+//                   TriMeshRef functions
+/////////////////////////////////////////////////////////////////
+
+TriMeshRef::TriMeshRef(int numVertices, const double * vertices, int numTriangles, const int * t) :
+    numVertices_(numVertices), numTriangles_(numTriangles)
+{
+  positions_ = (const Vec3d*) vertices;
+  triangles_ = (const Vec3i*) t;
+}
+
+TriMeshRef::TriMeshRef(int numVertices, const Vec3d * vertices, int numTriangles, const Vec3i * t) :
+    numVertices_(numVertices), numTriangles_(numTriangles)
+{
+  positions_ = vertices;
+  triangles_ = t;
+}
+
+TriMeshRef::TriMeshRef(const std::vector<Vec3d> & vertices, const std::vector<Vec3i> & triangles) :
+    numVertices_(vertices.size()), numTriangles_(triangles.size())
+{
+  positions_ = vertices.data();
+  triangles_ = triangles.data();
+}
+
+TriMeshRef::TriMeshRef(int numVertices, const Vec3d * vertices, const std::vector<Vec3i> & triangles) :
+    numVertices_(numVertices), numTriangles_(triangles.size())
+{
+  positions_ = vertices;
+  triangles_ = triangles.data();
+}
+
+IndexedTriangle TriMeshRef::getIndexedTriangle(int triID) const
+{
+  return IndexedTriangle(triID, tri(triID), pos(triID, 0), pos(triID, 1), pos(triID, 2));
+}
+
+std::vector<BoundingBox> TriMeshRef::getTriangleBoundingBoxes() const
+{
+  vector<BoundingBox> bbs;
+  bbs.reserve(numTriangles());
+  for(int i = 0; i < numTriangles(); i++)
+  {
+    bbs.emplace_back(positions_, triangles_[i]);
+  }
+  return bbs;
+}
+
+BoundingBox TriMeshRef::computeTriangleBoundingBox() const
+{
+  if (numTriangles() == 0) return BoundingBox();
+  const int * ts = (int*)triangles();
+  const int * te = ts + numTriangles() * 3;
+  auto r = makeRange(ts, te);
+  return BoundingBox(positions(), r);
+}
+
+Vec3d TriMeshRef::computeTriangleNormal(int triID) const
+{
+  Vec3d normal = cross(pos(triID, 1) - pos(triID, 0), pos(triID, 2) - pos(triID, 0));
+  normal.normalize();
+  return normal;
+}
+
+Vec3d TriMeshRef::computeAverageVertexPosition() const
+{
+  Vec3d center(0.0);
+  if (numVertices() > 0)
+  {
+    for(int i = 0; i < numVertices(); i++)
+      center += pos(i);
+    center /= numVertices();
+  }
+  return center;
+}
+
+Vec3d TriMeshRef::computeTriangleCentroid(int triID) const
+{
+  return (pos(triID, 0) + pos(triID, 1) + pos(triID, 2)) / 3.0;
+}
+
+Vec3d TriMeshRef::computeAverageTriangleCentroid() const
+{
+  Vec3d centroid(0.0);
+  if (numTriangles() == 0) return centroid;
+  for(int i = 0; i < numTriangles(); i++)
+  {
+    centroid += computeTriangleCentroid(i);
+  }
+  centroid /= numTriangles();
+  return centroid;
+}
+
+double TriMeshRef::getTriangleAngleAtVertex(int triID, int vtxID) const
+{
+  const auto & tri = triangles_[triID];
+  int i = tri.getInvertedIndex(vtxID);
+  assert(i >= 0);
+  int j = (i+1)%3, k = (i+2)%3;
+  return getTriangleAngle(pos(tri[i]), pos(tri[j]), pos(tri[k]));
+}
+
+double TriMeshRef::getTriangleAngleAtVertexRobust(int triID, int vtxID) const
+{
+  const auto & tri = triangles_[triID];
+  int i = tri.getInvertedIndex(vtxID);
+  assert(i >= 0);
+  int j = (i+1)%3, k = (i+2)%3;
+  return getTriangleAngleRobust(pos(tri[i]), pos(tri[j]), pos(tri[k]));
+}
+
+double TriMeshRef::computeSurfaceArea() const
+{
+  double area = 0.0;
+  for(int i = 0; i < numTriangles(); i++)
+  {
+    area += getTriangleArea(pos(i, 0), pos(i,1), pos(i, 2));
+  }
+  return area;
+}
+
+double TriMeshRef::computeWindingNumber(const Vec3d & queryPos) const
+{
+  double w = 0;
+  for(int j = 0; j < numTriangles(); j++)
+  {
+    Vec3d a = pos(j, 0) - queryPos;
+    Vec3d b = pos(j, 1) - queryPos;
+    Vec3d c = pos(j, 2) - queryPos;
+    double la = len(a), lb = len(b), lc = len(c);
+    Mat3d mat(a,b,c);
+    double omega = 2*atan2(det(mat), (la * lb * lc + dot(a,b) * lc + dot(b,c) * la + dot(c,a) * lb));
+    assert(isnan(omega) == false);
+    w += omega;
+  }
+
+  w /= 4 * M_PI;
+  return w;
+}
+
+namespace
+{
+
+bool saveToAscii(const TriMeshRef & mesh, const std::string & filename)
+{
+  // open file
+  ofstream fout(filename.c_str());
+
+  if (!fout)
+  {
+    cout << "Error: could not write to file " << filename << "." << endl;
+    return false;
+  }
+
+  fout << "# Generated by the TriMeshRef class" << endl;
+  fout << "# Number of vertices: " << mesh.numVertices() << endl;
+  fout << "# Number of faces: " << mesh.numTriangles() << endl;
+
+  // vertices...
+  for(int i = 0; i < mesh.numVertices(); i++)
+  {
+    const Vec3d & pos = mesh.pos(i);
+    fout << "v " << pos[0] << " " << pos[1] << " " << pos[2] << endl;
+  }
+
+  // groups and faces...
+  //  fout << "g default"<< endl;
+  for(int i = 0; i < mesh.numTriangles(); i++)
+  {
+    const Vec3i & t = mesh.tri(i);
+    fout << "f " << t[0]+1 << " " << t[1]+1 << " " << t[2]+1 << endl;
+  }
+
+  cout << "Saved mesh (#v: " << mesh.numVertices() << ", #t: " << mesh.numTriangles() << ") to " << filename << "." << endl;
+  fout.close();
+  return true;
+}
+
+bool saveToBinary(const TriMeshRef & mesh, const std::string & filename)
+{
+  FILE * fout = fopen(filename.c_str(), "wb");
+  if (fout == nullptr)
+  {
+    cout << "Error: could not write to file " << filename << endl;
+    return false;
+  }
+
+  // first pass: count the total number of bytes to be written to the file
+  // second pass: do the actual writing
+  enum {COUNT_BYTES, WRITE_TO_DISK, NUM_PASSES};
+  int totalPasses = NUM_PASSES;
+
+  unsigned int totalBytes = 0;
+  for(int pass = 0; pass < totalPasses; pass++)
+  {
+    unsigned int bytes = 0;
+    unsigned int items = 0;
+
+    // the header will be the number of bytes (including the totalbytes itself)
+    if (pass == WRITE_TO_DISK && fwrite(&totalBytes, sizeof(unsigned int), 1, fout) != 1)
+      return false;
+    bytes += sizeof(unsigned int);
+
+    // save the flag that determines whether to output materials or not
+    int outputMaterials = 0;
+    if (pass == WRITE_TO_DISK && fwrite(&outputMaterials, sizeof(int), 1, fout) != 1)
+      return 1;
+    bytes += sizeof(int);
+
+    // save the number of vertices
+    unsigned int numVertices = mesh.numVertices();
+    if (pass == WRITE_TO_DISK && fwrite(&numVertices, sizeof(unsigned int), 1, fout) != 1)
+      return 1;
+    bytes += sizeof(unsigned int);
+
+    // save vertices
+    items = 3 * numVertices;
+    if (pass == WRITE_TO_DISK && fwrite(mesh.positions(), sizeof(double), items, fout) != items)
+      return 1;
+    bytes += items * sizeof(double);
+
+    // save the number of texture coordinates
+    unsigned int numTexCoordinates = 0;
+    if (pass == WRITE_TO_DISK && fwrite(&numTexCoordinates, sizeof(unsigned int), 1, fout) != 1)
+      return 1;
+    bytes += sizeof(unsigned int);
+
+    // save the number of normals
+    unsigned int numNormals = 0;
+    if (pass == WRITE_TO_DISK && fwrite(&numNormals, sizeof(unsigned int), 1, fout) != 1)
+      return 1;
+    bytes += sizeof(unsigned int);
+
+    // save the number of groups
+    unsigned int numGroups = 1;
+    if (pass == WRITE_TO_DISK && fwrite(&numGroups, sizeof(unsigned int), 1, fout) != 1)
+      return 1;
+    bytes += sizeof(unsigned int);
+
+    // save group name
+    const char * groupName = "defaultGroup";
+    unsigned int strLength = strlen(groupName);
+    if (pass == WRITE_TO_DISK && fwrite(&strLength, sizeof(unsigned int), 1, fout) != 1)
+      return 1;
+    bytes += sizeof(unsigned int);
+
+    items = strLength;
+    if (pass == WRITE_TO_DISK && fwrite(groupName, sizeof(char), items, fout) != items)
+      return 1;
+    bytes += items * sizeof(char);
+
+    // save the number of faces of the current group
+    unsigned int numFaces = mesh.numTriangles();
+    if (pass == WRITE_TO_DISK && fwrite(&numFaces, sizeof(unsigned int), 1, fout) != 1)
+      return 1;
+    bytes += sizeof(unsigned int);
+
+    // save the number of vertices of each face in current group
+    const unsigned int numVtxPerFace = 3;
+    if (pass == WRITE_TO_DISK)
+    {
+      for(int i = 0; i < mesh.numTriangles(); i++)
+      {
+        if (fwrite(&numVtxPerFace, sizeof(unsigned int), 1, fout) != 1)
+          return 1;
+      }
+    }
+    bytes += mesh.numTriangles() * sizeof(unsigned int);
+
+    // save the vertices of each face
+    items = mesh.numTriangles() * 3;
+    vector<unsigned int> vtxIndices(mesh.numTriangles() * 3);
+    for(int i = 0; i < mesh.numTriangles(); i++)
+      for(int j = 0; j < 3; j++)
+        vtxIndices[i*3+j] = mesh.triVtxID(i, j) + 1; // 1-index
+    if (pass == WRITE_TO_DISK && fwrite(vtxIndices.data(), sizeof(unsigned int), items, fout) != items)
+      return 1;
+    bytes += items * sizeof(unsigned int);
+
+    vector<unsigned int> buffer(mesh.numTriangles() * 3, 0.0);
+
+    // save texture coordinates
+    if (pass == WRITE_TO_DISK && fwrite(buffer.data(), sizeof(unsigned int), items, fout) != items)
+      return 1;
+    bytes += items * sizeof(unsigned int);
+
+    // save normal coordinates
+    if (pass == WRITE_TO_DISK && fwrite(buffer.data(), sizeof(unsigned int), items, fout) != items)
+      return 1;
+    bytes += items * sizeof(unsigned int);
+
+    if (pass == COUNT_BYTES)
+      totalBytes = bytes;
+  } // for pass
+
+  fclose(fout);
+  return true;
+}
+
+}
+
+bool TriMeshRef::save(const string & filename) const
+{
+  if (iendWith(filename, ".objb"))
+    return saveToBinary(*this, filename);
+  if (iendWith(filename, ".obj"))
+    return saveToAscii(*this, filename);
+
+  printf("Unknown file extension when saving %s, try ASCII format...\n", filename.c_str());
+  return saveToAscii(*this, filename);
+}
+
+/////////////////////////////////////////////////////////////////
+//                   TriMesh functions
+/////////////////////////////////////////////////////////////////
+
+TriMeshGeo::TriMeshGeo(int numVertices, const double * vertices, int numTriangles, const int * t) :
+    positions_(numVertices), triangles_(numTriangles)
+{
+  memcpy(&positions_[0][0], vertices, sizeof(double) * numVertices * 3);
+  memcpy(&triangles_[0][0], t, sizeof(int) * numTriangles * 3);
+}
+
+TriMeshGeo::TriMeshGeo(int numVertices, const Vec3d * vertices, int numTriangles, const Vec3i * t) :
+    positions_(numVertices), triangles_(numTriangles)
+{
+  memcpy(&positions_[0], vertices, sizeof(double) * numVertices * 3);
+  memcpy(&triangles_[0], t, sizeof(int) * numTriangles * 3);
+}
+
+TriMeshGeo::TriMeshGeo(int numVertices, const Vec3d * vertices, std::vector<Vec3i> triangles) : positions_(numVertices)
+{
+  memcpy(&positions_[0], vertices, sizeof(double) * numVertices * 3);
+  triangles_ = triangles;
+}
+
+TriMeshGeo::TriMeshGeo(std::vector<Vec3d> vertices, std::vector<Vec3i> triangles) :
+    positions_(move(vertices)), triangles_(move(triangles)) {}
+
+TriMeshGeo::TriMeshGeo(const TriMeshRef meshRef) : TriMeshGeo(meshRef.numVertices(), meshRef.positions(), meshRef.numTriangles(), meshRef.triangles())
+{}
+
+/////////////////////////////////////////////////////////////////
+//                   Other functions
+/////////////////////////////////////////////////////////////////
+
+int getTriangleVertexOppositeEdge(const Vec3i & tri, int e0, int e1)
+{
+  for(int i = 0; i < 3; i++)
+  {
+    if (tri[i] != e0 && tri[i] != e1) return tri[i];
+  }
+  assert(0);
+  return -1;
+}
+
+int getTriangleVertexOppositeEdge(const Vec3i & tri, const UEdgeKey & edge)
+{
+  return getTriangleVertexOppositeEdge(tri, edge[0], edge[1]);
+}
+
+OEdgeKey getTriangleOEdge(const Vec3i & tri, const UEdgeKey & uedge)
+{
+  for(int i = 0; i < 3; i++)
+  {
+    UEdgeKey curUEdge(tri[i], tri[(i+1)%3]);
+    if (curUEdge == uedge) return OEdgeKey(tri[i], tri[(i+1)%3]);
+  }
+  return OEdgeKey();
+}
+
+Vec3i getSharedVertices(const Vec3i & t0, const Vec3i & t1)
+{
+  Vec3i ret(-1);
+  int index = 0;
+  for(int i = 0; i < 3; i++)
+  {
+    for(int j = 0; j < 3; j++)
+    {
+      if (t0[i] == t1[j]) { ret[index++] = t0[i]; break; }
+    }
+  }
+  return ret;
+}
+
+std::vector<Vec3i> triangulatePolygon(const std::vector<int> & polygon)
+{
+  vector<Vec3i> ret;
+  for(size_t i = 2; i < polygon.size(); i++)
+  {
+    ret.emplace_back(polygon[0], polygon[i-1], polygon[i]);
+  }
+  return ret;
+}
+
+std::vector<Vec3i> triangulatePolygonRobust(const std::vector<int> & polygon)
+{
+  if (polygon.size() > 2) return triangulatePolygon(polygon);
+  if (polygon.size() == 2) return { Vec3i(polygon[0], polygon[1], polygon[1]) };
+  if (polygon.size() == 1) return { Vec3i(polygon[0]) };
+  return {};
+}
+
+vector<Vec3d> removeIdenticalVertices(const std::vector<Vec3d> & vertices,
+    std::vector<int> * newVtxID2OldVtxID, std::vector<int> * oldVtxID2NewVtxID)
+{
+  if (vertices.size() == 0)
+  {
+    if (newVtxID2OldVtxID) newVtxID2OldVtxID->clear();
+    if (oldVtxID2NewVtxID) oldVtxID2NewVtxID->clear();
+    return {};
+  }
+
+  vector<int> indices(vertices.size()); // eventually stores newVtxID->oldVtxID
+  iota(indices.begin(), indices.end(), 0);
+  sort(indices.begin(), indices.end(), [&](int a, int b) { return vertices[a] < vertices[b]; } );
+
+  vector<Vec3d> ret;
+  ret.push_back(vertices[indices[0]]);
+  if (newVtxID2OldVtxID) *newVtxID2OldVtxID = { indices[0] };
+  if (oldVtxID2NewVtxID) oldVtxID2NewVtxID->assign(vertices.size(), -1);
+
+  for(size_t i = 0, j = 1; j < indices.size(); j++)
+  {
+    if (vertices[indices[i]] == vertices[indices[j]])
+    {
+      if (oldVtxID2NewVtxID) { oldVtxID2NewVtxID->at(indices[j]) = ret.size()-1; }
+    }
+    else
+    {
+      if (oldVtxID2NewVtxID) { oldVtxID2NewVtxID->at(indices[j]) = ret.size(); }
+      ret.push_back(vertices[indices[j]]);
+      if (newVtxID2OldVtxID) { newVtxID2OldVtxID->push_back(indices[j]); }
+      i = j-1;
+    }
+  }
+
+  if (oldVtxID2NewVtxID)
+    assert(find(oldVtxID2NewVtxID->begin(), oldVtxID2NewVtxID->end(), -1) == oldVtxID2NewVtxID->end());
+  return ret;
+}
+
+bool hasInvalidTriangles(const std::vector<Vec3i> & triangles)
+{
+  for(const auto & t: triangles)
+  {
+    if (!(t[0] >= 0 && t[1] >= 0 && t[2] >= 0 && t[0] != t[1] && t[1] != t[2] && t[2] != t[0])) return true;
+  }
+  return false;
+}
+
+vector<Vec3i> getInvalidTriangles(const std::vector<Vec3i> & triangles)
+{
+  vector<Vec3i> ret;
+  for(const auto & t: triangles)
+  {
+    if (!(t[0] >= 0 && t[1] >= 0 && t[2] >= 0 && t[0] != t[1] && t[1] != t[2] && t[2] != t[0])) ret.push_back(t);
+  }
+  return ret;
+}
+
+
+vector<Vec3i> getOnlyValidTriangles(const std::vector<Vec3i> & triangles)
+{
+  vector<Vec3i> ret;
+  for(const auto & t: triangles)
+  {
+    if (t[0] >= 0 && t[1] >= 0 && t[2] >= 0 && t[0] != t[1] && t[1] != t[2] && t[2] != t[0])
+      ret.push_back(t);
+  }
+  return ret;
+}
+
+vector<Vec3i> getOnlyValidTriangles(const std::vector<Vec3i> & triangles, std::vector<int> & validIDs)
+{
+  validIDs.clear();
+  vector<Vec3i> ret;
+  for(size_t i = 0; i < triangles.size(); i++)
+  {
+    const auto & t = triangles[i];
+    if (t[0] >= 0 && t[1] >= 0 && t[2] >= 0 && t[0] != t[1] && t[1] != t[2] && t[2] != t[0])
+    {
+      ret.push_back(t);
+      validIDs.push_back(i);
+    }
+  }
+  return ret;
+}
+
+TriMeshGeo removeIsolatedVertices(const TriMeshRef meshRef, vector<int> * newVtxID2OldVtxID, vector<int> * oldVtxID2NewVtxID)
+{
+  vector<int> usedVtxIDs;
+  for(int triID = 0; triID < meshRef.numTriangles(); triID++)
+  {
+    for(int i = 0; i < 3; i++) usedVtxIDs.push_back(meshRef.tri(triID)[i]);
+  }
+  sortAndDeduplicate(usedVtxIDs);
+  if (usedVtxIDs.size() == (size_t)meshRef.numVertices())
+  {
+    // now usedVtxIDs contains [0, meshRef.numVertices)
+    if (newVtxID2OldVtxID) { *newVtxID2OldVtxID = usedVtxIDs; }
+    if (oldVtxID2NewVtxID) { *oldVtxID2NewVtxID = move(usedVtxIDs); }
+    return TriMeshGeo(meshRef);
+  }
+
+  vector<int> oldToNewVtxMap(meshRef.numVertices(), -1);
+  for(size_t i = 0; i < usedVtxIDs.size(); i++)
+  {
+    oldToNewVtxMap[usedVtxIDs[i]] = i;
+  }
+
+  vector<Vec3i> newTriangles(meshRef.numTriangles());
+  for(int triID = 0; triID < meshRef.numTriangles(); triID++)
+  {
+    for(int i = 0; i < 3; i++)
+    {
+      int newID = oldToNewVtxMap[meshRef.tri(triID)[i]];
+      assert(newID >= 0);
+      newTriangles[triID][i] = newID;
+    }
+  }
+
+  vector<Vec3d> newPos(usedVtxIDs.size());
+  for(size_t i = 0; i < usedVtxIDs.size(); i++) newPos[i] = meshRef.pos(usedVtxIDs[i]);
+  if (newVtxID2OldVtxID) { *newVtxID2OldVtxID = move(usedVtxIDs); }
+  if (oldVtxID2NewVtxID) { *oldVtxID2NewVtxID = move(oldToNewVtxMap); }
+
+  return TriMeshGeo(move(newPos), move(newTriangles));
+}
+
+TriMeshGeo removeIsolatedVerticesWithMap(const TriMeshRef meshRef, vector<int> * newVtxID2OldVtxID, map<int,int> * oldVtxID2NewVtxID)
+{
+  vector<int> usedVtxIDs;
+  for(int triID = 0; triID < meshRef.numTriangles(); triID++)
+  {
+    for(int i = 0; i < 3; i++) usedVtxIDs.push_back(meshRef.tri(triID)[i]);
+  }
+  sortAndDeduplicate(usedVtxIDs);
+  if (usedVtxIDs.size() == (size_t)meshRef.numVertices())
+  {
+    // now usedVtxIDs contains [0, meshRef.numVertices)
+    if (newVtxID2OldVtxID) { *newVtxID2OldVtxID = move(usedVtxIDs); }
+    if (oldVtxID2NewVtxID) { for(int i = 0; i < meshRef.numVertices(); i++) (*oldVtxID2NewVtxID)[i] = i; }
+    return TriMeshGeo(meshRef);
+  }
+
+  map<int,int> oldToNewVtxMap;
+  for(size_t i = 0; i < usedVtxIDs.size(); i++)
+  {
+    oldToNewVtxMap[usedVtxIDs[i]] = i;
+  }
+
+  vector<Vec3i> newTriangles(meshRef.numTriangles());
+  for(int triID = 0; triID < meshRef.numTriangles(); triID++)
+  {
+    for(int i = 0; i < 3; i++)
+    {
+      int newID = oldToNewVtxMap[meshRef.tri(triID)[i]];
+      newTriangles[triID][i] = newID;
+    }
+  }
+
+  vector<Vec3d> newPos(usedVtxIDs.size());
+  for(size_t i = 0; i < usedVtxIDs.size(); i++) newPos[i] = meshRef.pos(usedVtxIDs[i]);
+  if (newVtxID2OldVtxID) { *newVtxID2OldVtxID = move(usedVtxIDs); }
+  if (oldVtxID2NewVtxID) { *oldVtxID2NewVtxID = move(oldToNewVtxMap); }
+
+  return TriMeshGeo(move(newPos), move(newTriangles));
+}
+
+TriMeshGeo removeIdenticalVertices(const TriMeshRef meshRef, std::vector<int> * newVtxID2OldVtxID,
+    std::vector<int> * oldVtxID2NewVtxID)
+{
+  if (meshRef.numVertices() == 0)
+  {
+    if (newVtxID2OldVtxID) newVtxID2OldVtxID->clear();
+    if (oldVtxID2NewVtxID) oldVtxID2NewVtxID->clear();
+    return TriMeshGeo(meshRef);
+  }
+
+  vector<int> indices(meshRef.numVertices()); // eventually stores newVtxID->oldVtxID
+  iota(indices.begin(), indices.end(), 0);
+  sort(indices.begin(), indices.end(), [&](int a, int b) { return meshRef.pos(a) < meshRef.pos(b); } );
+
+  vector<Vec3d> newVertices;
+  newVertices.push_back(meshRef.pos(indices[0]));
+  vector<int> old2new(meshRef.numVertices(), -1);
+  if (newVtxID2OldVtxID) *newVtxID2OldVtxID = { indices[0] };
+  old2new[indices[0]] = 0;
+
+  for(size_t i = 0, j = 1; j < indices.size(); j++)
+  {
+    if (meshRef.pos(indices[i]) == meshRef.pos(indices[j]))
+    {
+      old2new[indices[j]] = newVertices.size()-1;
+    }
+    else
+    {
+      old2new[indices[j]] = newVertices.size();
+      newVertices.push_back(meshRef.pos(indices[j]));
+      if (newVtxID2OldVtxID) { newVtxID2OldVtxID->push_back(indices[j]); }
+      i = j-1;
+    }
+  }
+
+  assert(find(old2new.begin(), old2new.end(), -1) == old2new.end());
+
+  vector<Vec3i> newTri(meshRef.numTriangles());
+  for(int i = 0; i < meshRef.numTriangles(); i++)
+  {
+    for(int j = 0; j < 3; j++)
+      newTri[i][j] = old2new[meshRef.tri(i)[j]];
+  }
+  if (oldVtxID2NewVtxID) *oldVtxID2NewVtxID = old2new;
+
+  return TriMeshGeo(move(newVertices), move(newTri));
+}
+
+TriMeshGeo removeInvalidTriangles(const TriMeshRef meshRef)
+{
+  vector<int> dt;
+  for(int i = 0; i < meshRef.numTriangles(); i++)
+  {
+    Vec3i t = meshRef.tri(i);
+    if ((t[0] >= 0 && t[1] >= 0 && t[2] >= 0 && t[0] != t[1] && t[1] != t[2] && t[2] != t[0]) == false)
+      dt.push_back(i);
+  }
+  vector<Vec3i> triangles = meshRef.exportTriangles();
+  removeByIndices(triangles, dt);
+  return {meshRef.numVertices(), meshRef.positions(), move(triangles) };
+}
+
+TriMeshGeo removeTriangles(const TriMeshRef meshRef, const std::vector<int> & triangleIDsToRemove)
+{
+  vector<Vec3i> triangles = meshRef.exportTriangles();
+  removeByIndices(triangles, triangleIDsToRemove);
+  vector<Vec3d> pos = meshRef.exportPositions();
+  return { move(pos), move(triangles) };
+}
+
+TriMeshGeo mergeVertices(const TriMeshRef meshRef, std::vector<int> & oldVtxID2NewVtxID)
+{
+  assert(oldVtxID2NewVtxID.size() == (size_t)meshRef.numVertices());
+  vector<Vec3i> newTriangles = meshRef.exportTriangles();
+  for(int triID = 0; triID < meshRef.numTriangles(); triID++)
+  {
+    for(int i = 0; i < 3; i++)
+      newTriangles[triID][i] = oldVtxID2NewVtxID[meshRef.triVtxID(triID, i)];
+  }
+  vector<Vec3d> newVertices;
+  for(int vID = 0; vID < meshRef.numVertices(); vID++)
+  {
+    int newID = oldVtxID2NewVtxID[vID];
+    if (newID >= (int)newVertices.size()) newVertices.resize(newID+1);
+    newVertices[newID] = meshRef.pos(vID);
+  }
+  return { move(newVertices), move(newTriangles) };
+}
+
+TriMeshGeo mergeMesh(const TriMeshRef mesh1, const TriMeshRef mesh2)
+{
+  vector<Vec3d> vertices = mesh1.exportPositions();
+  vector<Vec3i> triangles = mesh1.exportTriangles();
+  int vtxStart = vertices.size();
+  int triStart = triangles.size();
+  mesh2.exportPositions(vertices);
+  mesh2.exportTriangles(triangles);
+  for(size_t i = triStart; i < triangles.size(); i++)
+  {
+    for(int j = 0; j < 3; j++)
+      triangles[i][j] += vtxStart;
+  }
+  return { move(vertices), move(triangles) };
+}
+
+double TriMeshRef::computeSquaredDistanceToPoint(const Vec3d & pt, int * closestTriangle, int * feature)
+{
+  static_assert(numeric_limits<double>::has_quiet_NaN, "No quiet NaN");
+  if (numTriangles() == 0)
+  {
+    if (closestTriangle) *closestTriangle = -1;
+    if (feature) *feature = -1;
+    return numeric_limits<double>::quiet_NaN();
+  }
+
+  MinValueKey<pair<int,int>> vi(make_pair(-1,-1));
+  for(int i = 0; i < numTriangles(); i++)
+  {
+    int f = -1;
+    double d2 = getSquaredDistanceToTriangle(pt, pos(i,0), pos(i,1), pos(i,2), f);
+    vi.update(d2, make_pair(i, f));
+  }
+  assert(vi.key.first >= 0);
+  if (closestTriangle) *closestTriangle = vi.key.first;
+  if (feature) *feature = vi.key.second;
+  return vi.value;
+}
+
+int TriMeshRef::computeEulerCharacteristic() const
+{
+  int ret = numVertices() + numTriangles();
+  unordered_set<UEdgeKey> ues;
+  for(int i = 0; i < numTriangles(); i++)
+  {
+    for(int j = 0; j < 3; j++)
+      ues.emplace(triangles_[i][j], triangles_[i][(j+1)%3]);
+  }
+  ret -= ues.size();
+  return ret;
+}
+
+int TriMeshRef::computeEulerCharacteristicAssumingClosedManifold() const
+{
+  assert(numTriangles() % 2 == 0);
+  return numVertices() - numTriangles() / 2;
+}
diff --git a/libraries/mesh/triMeshGeo.h b/libraries/mesh/triMeshGeo.h
new file mode 100644
index 0000000000000000000000000000000000000000..2980a82ba2f22641d6c1e938cd372b9218a91690
--- /dev/null
+++ b/libraries/mesh/triMeshGeo.h
@@ -0,0 +1,378 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TRIMESHGEO_H
+#define TRIMESHGEO_H
+
+#include "vec3d.h"
+#include "vec3i.h"
+#include "boundingBox.h"
+#include "edgeKey.h"
+#include <vector>
+#include <algorithm>
+#include <map>
+
+// a triangle struct to hold triangle index, its vertex indices and positions
+struct IndexedTriangle
+{
+  int triID = -1;
+  Vec3i vtxID {-1 ,-1, -1};
+  Vec3d pos[3] {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}};
+
+  IndexedTriangle() {}
+  IndexedTriangle(int triID, const Vec3i & vtxID, const Vec3d & p0, const Vec3d & p1, const Vec3d & p2) : triID(triID), vtxID(vtxID), pos{p0, p1, p2} {}
+};
+
+// class to reference an external triangle mesh
+class TriMeshRef
+{
+public:
+  TriMeshRef() {} // empty mesh
+  TriMeshRef(int numVertices, const double * vertices, int numTriangles, const int * triangles);
+  TriMeshRef(int numVertices, const Vec3d * vertices, int numTriangles, const Vec3i * triangles);
+  TriMeshRef(const std::vector<Vec3d> & vertices, const std::vector<Vec3i> & triangles);
+  TriMeshRef(int numVertices, const Vec3d * vertices, const std::vector<Vec3i> & triangles);
+
+  int numVertices() const { return numVertices_; }
+  int numTriangles() const { return numTriangles_; }
+
+  const Vec3d & pos(int vtxID) const { return positions_[vtxID]; }
+  const Vec3i & tri(int triID) const { return triangles_[triID]; }
+
+  int triVtxID(int triID, int i) const { return triangles_[triID][i]; }
+  const Vec3d & pos(int triID, int i) const { return positions_[triangles_[triID][i]]; }
+
+  IndexedTriangle getIndexedTriangle(int triID) const;
+
+  const Vec3d * positions() const { return positions_; }
+  const Vec3i * triangles() const { return triangles_; }
+
+  std::vector<Vec3d> exportPositions() const { return std::vector<Vec3d>(positions_, positions_ + numVertices_); }
+  std::vector<Vec3i> exportTriangles() const { return std::vector<Vec3i>(triangles_, triangles_ + numTriangles_); }
+  void exportPositions(std::vector<Vec3d> & v) const { v.insert(v.end(), positions_, positions_ + numVertices_); }
+  void exportTriangles(std::vector<Vec3i> & t) const { t.insert(t.end(), triangles_, triangles_ + numTriangles_); }
+
+  template<class TriangleIDContainer>
+  void getVerticesInTriangles(const TriangleIDContainer & triangleIDs, std::vector<int> & vertexIDs) const;
+
+  // get bounding box for every triangle
+  std::vector<BoundingBox> getTriangleBoundingBoxes() const;
+  // compute bounding box for all the triangles
+  // return a default BoundingBox if no triangles
+  BoundingBox computeTriangleBoundingBox() const;
+
+  Vec3d computeTriangleNormal(int triID) const;
+
+  Vec3d computeAverageVertexPosition() const;
+
+  // compute barycenter of each triangle
+  Vec3d computeTriangleCentroid(int triID) const;
+
+  Vec3d computeAverageTriangleCentroid() const;
+
+  // x = V - E + F
+  // for closed connected orientable manifold polygon, x = 2 - 2 g, where g = genus
+  // for connected orientable manifold polygon with b boundaries, x = 2 - 2 g - b
+  int computeEulerCharacteristic() const;
+  // it the mesh is closed edge-manifold, then E = F * 3 / 2
+  int computeEulerCharacteristicAssumingClosedManifold() const;
+
+  // vtxID: [0, TriMesh::numVertices())
+  double getTriangleAngleAtVertex(int triID, int vtxID) const;
+  double getTriangleAngleAtVertexRobust(int triID, int vtxID) const;
+
+  double computeSurfaceArea() const;
+  // use an O(n) method to compute winding number
+  double computeWindingNumber(const Vec3d & pos) const;
+
+  // use an O(n) method to compute distance, not exact
+  // return quiet nan if no triangles
+  double computeSquaredDistanceToPoint(const Vec3d & pos, int * closestTriangle = nullptr, int * feature = nullptr);
+
+  // save to obj mesh
+  bool save(const std::string & filename) const;
+
+protected:
+  int numVertices_ = 0, numTriangles_ = 0;
+  const Vec3d * positions_ = nullptr;
+  const Vec3i * triangles_ = nullptr;
+};
+
+
+// class to store basic triangle mesh data
+class TriMeshGeo
+{
+public:
+  TriMeshGeo() {} // empty mesh
+  TriMeshGeo(int numVertices, const double * vertices, int numTriangles, const int * triangles);
+  TriMeshGeo(int numVertices, const Vec3d * vertices, int numTriangles, const Vec3i * triangles);
+  TriMeshGeo(int numVertices, const Vec3d * vertices, std::vector<Vec3i> triangles);
+  TriMeshGeo(std::vector<Vec3d> vertices, std::vector<Vec3i> triangles);
+  TriMeshGeo(const TriMeshRef meshRef);
+
+  int numVertices() const { return positions_.size(); }
+  int numTriangles() const { return triangles_.size(); }
+
+  const Vec3d & pos(int vtxID) const { return positions_[vtxID]; }
+  Vec3d & pos(int vtxID) { return positions_[vtxID]; }
+  const Vec3i & tri(int triID) const { return triangles_[triID]; }
+  Vec3i & tri(int triID) { return triangles_[triID]; }
+
+  int triVtxID(int triID, int i) const { return triangles_[triID][i]; }
+  const Vec3d & pos(int triID, int i) const { return positions_[triangles_[triID][i]]; }
+  Vec3d & pos(int triID, int i) { return positions_[triangles_[triID][i]]; }
+
+  void addPos(const Vec3d & p) { positions_.push_back(p); }
+  void addTri(const Vec3i & t) { triangles_.push_back(t); }
+
+  const std::vector<Vec3d> & positions() const { return positions_; }
+  const std::vector<Vec3i> & triangles() const { return triangles_; }
+
+  TriMeshRef ref() const { return { positions_, triangles_ }; }
+  // implicit conversion
+  operator TriMeshRef() const { return ref(); }
+
+  // save to obj mesh
+  bool save(const std::string & filename) const { return ref().save(filename); }
+
+protected:
+  std::vector<Vec3d> positions_;
+  std::vector<Vec3i> triangles_;
+};
+
+// =========================================================
+//              Single Triangle Utilities
+// =========================================================
+// they will eventually go to a triangle related header
+
+// get vertex ID opposite the edge of (e0, e1)
+// assume tri is valid and contains e0 and e1
+int getTriangleVertexOppositeEdge(const Vec3i & tri, int e0, int e1);
+int getTriangleVertexOppositeEdge(const Vec3i & tri, const UEdgeKey & edge);
+
+// give an uedge, get the oedge on the tri
+// assume tri is valid
+// return an invalid key {-1,-1} if no such uedge on tri
+OEdgeKey getTriangleOEdge(const Vec3i & tri, const UEdgeKey & uedge);
+
+// get the shared vertex IDs from two triangles, assuming input t0 and t1 are valid triangles (v[i] >= 0 && v[i] != v[j])
+// if no vtx shared, return {-1, -1, -1}
+// if v0 is shared, return {v0, -1, -1}
+// if v0, v1 are shared, return {v0, v1, -1}
+// if v0, v1, v2 are shared, return {v0, v1, v2}
+Vec3i getSharedVertices(const Vec3i & t0, const Vec3i & t1);
+
+// =========================================================
+//                  Polygon Utilities
+// =========================================================
+// they will eventually go to a polygon related header
+
+
+// given a polygon formed by a vertexID list, triangulate it
+// return an empty vector if the polygon is degenerate
+std::vector<Vec3i> triangulatePolygon(const std::vector<int> & polygon);
+// if polygon is degenerate (contains 1 or 2 vtx), create a degenerate triangle
+// if only one vtx (v0) in polygon, return a triangle of (v0, v0, v0)
+// if only two vtx (v0, v1) in polygon, return a triangle of (v0, v1, v1)
+std::vector<Vec3i> triangulatePolygonRobust(const std::vector<int> & polygon);
+
+// remove vertices of the same positions
+// return new vertices
+std::vector<Vec3d> removeIdenticalVertices(const std::vector<Vec3d> & vertices,
+    std::vector<int> * newVtxID2OldVtxID = nullptr, std::vector<int> * oldVtxID2NewVtxID = nullptr);
+
+// =========================================================
+//                  TriMesh Utilities
+// =========================================================
+
+// if there are triangles that are invalid:  v[i] < 0 || v[i] == v[j]
+bool hasInvalidTriangles(const std::vector<Vec3i> & triangles);
+
+// return triangles that are invalid
+std::vector<Vec3i> getInvalidTriangles(const std::vector<Vec3i> & triangles);
+
+// return triangles that are valid:  v[i] >=0 && v[i] != v[j]
+std::vector<Vec3i> getOnlyValidTriangles(const std::vector<Vec3i> & triangles);
+// also return valid triID in input triangles
+std::vector<Vec3i> getOnlyValidTriangles(const std::vector<Vec3i> & triangles, std::vector<int> & validIDs);
+
+template<class TriangleIDContainer>
+void getVerticesInSelectedTriangles(const std::vector<Vec3i> & triangles, const TriangleIDContainer & triangleIDs,
+  std::vector<int> & vertexIDs);
+template<class TriangleIDContainer>
+void getVerticesInSelectedTriangles(const Vec3i * triangles, const TriangleIDContainer & triangleIDs,
+  std::vector<int> & vertexIDs);
+
+template<class Vec3iContainer>
+void getVerticesInTriangles(const Vec3iContainer & triangles, std::vector<int> & vertexIDs);
+
+// remove those vertices in the mesh that do not contribute to a triangle
+// the returned TriMesh has different vertices, but the triangle ordering is the same as meshRef
+// newVtxID2OldVtxID has size #returnedMeshVertices
+// oldVtxID2NewVtxID has size meshRef.numVertices()
+TriMeshGeo removeIsolatedVertices(const TriMeshRef meshRef, std::vector<int> * newVtxID2OldVtxID = nullptr,
+    std::vector<int> * oldVtxID2NewVtxID = nullptr);
+// for a small sub mesh, it is wasteful to construct a vector for old->new mapping
+// this function uses map to realize the old->new mapping
+TriMeshGeo removeIsolatedVerticesWithMap(const TriMeshRef meshRef, std::vector<int> * newVtxID2OldVtxID = nullptr,
+    std::map<int, int> * oldVtxID2NewVtxID = nullptr);
+
+// keep only one vertex if several vertices are on the same position
+TriMeshGeo removeIdenticalVertices(const TriMeshRef meshRef, std::vector<int> * newVtxID2OldVtxID = nullptr,
+    std::vector<int> * oldVtxID2NewVtxID = nullptr);
+
+TriMeshGeo removeInvalidTriangles(const TriMeshRef meshRef);
+
+// triangleIDsToRemove must be ordered
+TriMeshGeo removeTriangles(const TriMeshRef meshRef, const std::vector<int> & triangleIDsToRemove);
+
+// oldVtxID2NewVtxID is of size meshRef.numVertices(), it creates a mapping from old vtxID to the new vtxID
+// it asserts the mapping of oldVtxID2NewVtxID is valid, new VtxID are continuous, ranging [0, #newVtxID)
+TriMeshGeo mergeVertices(const TriMeshRef meshRef, std::vector<int> & oldVtxID2NewVtxID);
+
+// return a sub mesh which are triangles from selected triangleIDs
+// isolated vertices in the sub mesh ARE NOT removed
+// call removeIsolatedVertices to remove them from the result TriMesh
+template<class TriangleIDContainer>
+TriMeshGeo getSubTriMeshWithSameVertices(const TriMeshRef meshRef, const TriangleIDContainer & triangleIDs);
+template<class TriangleIDContainer>
+void getSubMesh(const TriMeshRef meshRef, const TriangleIDContainer & triangleIDs, std::vector<Vec3i> & outputTriangles);
+
+template<class TriangleIDContainer>
+TriMeshGeo getSubTriMesh(const TriMeshRef & mesh, const TriangleIDContainer & subTriIDs,
+    std::vector<int> * subVtxID2FullVtxID = nullptr,
+    std::map<int, int> * fullTriID2SubTriID = nullptr);
+
+// simple merge of mesh by retaining all vertices and triangles from both meshes
+TriMeshGeo mergeMesh(const TriMeshRef mesh1, const TriMeshRef mesh2);
+
+// =========================================================
+//                  Implementations
+// =========================================================
+
+template<class TriangleIDContainer>
+void getVerticesInSelectedTriangles(const std::vector<Vec3i> & triangles, const TriangleIDContainer & triangleIDs,
+  std::vector<int> & vertexIDs)
+{
+  return getVerticesInSelectedTriangles(triangles.data(), triangleIDs, vertexIDs);
+}
+
+template<class TriangleIDContainer>
+void getVerticesInSelectedTriangles(const Vec3i * triangles, const TriangleIDContainer & triangleIDs,
+  std::vector<int> & vertexIDs)
+{
+  int numPrevIDs = vertexIDs.size();
+  for(int triID : triangleIDs)
+  {
+    for(int j = 0; j < 3; j++) vertexIDs.push_back(triangles[triID][j]);
+  }
+  // remove duplicate vertex IDs
+  std::sort(vertexIDs.begin() + numPrevIDs, vertexIDs.end());
+  auto newEnd = std::unique(vertexIDs.begin() + numPrevIDs, vertexIDs.end());
+  vertexIDs.resize(std::distance(vertexIDs.begin(), newEnd));
+}
+
+template<class Vec3iContainer>
+void getVerticesInTriangles(const Vec3iContainer & triangles, std::vector<int> & vertexIDs)
+{
+  int numPrevIDs = vertexIDs.size();
+  for(Vec3i t : triangles)
+  {
+    for(int j = 0; j < 3; j++) vertexIDs.push_back(t[j]);
+  }
+  // remove duplicate vertex IDs
+  std::sort(vertexIDs.begin() + numPrevIDs, vertexIDs.end());
+  auto newEnd = std::unique(vertexIDs.begin() + numPrevIDs, vertexIDs.end());
+  vertexIDs.resize(std::distance(vertexIDs.begin(), newEnd));
+}
+
+template<class TriangleIDContainer>
+void TriMeshRef::getVerticesInTriangles(const TriangleIDContainer & triangleIDs, std::vector<int> & vertexIDs) const
+{
+  getVerticesInSelectedTriangles(triangles_, triangleIDs, vertexIDs);
+}
+
+template<class TriangleIDContainer>
+TriMeshGeo getSubTriMeshWithSameVertices(const TriMeshRef meshRef, const TriangleIDContainer & triangleIDs)
+{
+  std::vector<Vec3i> subTris;
+  for(int triID: triangleIDs)
+  {
+    subTris.push_back(meshRef.tri(triID));
+  }
+  return TriMeshGeo(meshRef.numVertices(), meshRef.positions(), subTris.size(), subTris.data());
+}
+
+template<class TriangleIDContainer>
+void getSubMesh(const TriMeshRef meshRef, const TriangleIDContainer & triangleIDs, std::vector<Vec3i> & outputTriangles)
+{
+  for(int triID: triangleIDs) { outputTriangles.push_back(meshRef.tri(triID)); }
+}
+
+template<class TriangleIDContainer>
+TriMeshGeo getSubTriMesh(const TriMeshRef & mesh, const TriangleIDContainer & subTriIDs,
+    std::vector<int> * subVtxID2FullVtxID, std::map<int, int> * fullTriID2SubTriID)
+{
+  std::vector<int> usedVtxIDs;
+  for(int triID : subTriIDs)
+  {
+    for(int i = 0; i < 3; i++) usedVtxIDs.push_back(mesh.tri(triID)[i]);
+  }
+  // remove duplicated vertices
+  std::sort(usedVtxIDs.begin(), usedVtxIDs.end());
+  auto newEnd = std::unique(usedVtxIDs.begin(), usedVtxIDs.end());
+  usedVtxIDs.resize(std::distance(usedVtxIDs.begin(), newEnd));
+
+  std::map<int,int> oldToNewVtxMap;
+  for(size_t i = 0; i < usedVtxIDs.size(); i++) { oldToNewVtxMap[usedVtxIDs[i]] = i; }
+
+  std::vector<Vec3i> newTris(subTriIDs.size());
+  int newTriID = 0;
+  for(int triID : subTriIDs)
+  {
+    for(int i = 0; i < 3; i++)
+    {
+      int newID = oldToNewVtxMap[mesh.tri(triID)[i]];
+      newTris[newTriID][i] = newID;
+    }
+    newTriID++;
+  }
+
+  std::vector<Vec3d> newPos(usedVtxIDs.size());
+  for(size_t i = 0; i < usedVtxIDs.size(); i++) newPos[i] = mesh.pos(usedVtxIDs[i]);
+  if (subVtxID2FullVtxID) { *subVtxID2FullVtxID = move(usedVtxIDs); }
+  if (fullTriID2SubTriID) { *fullTriID2SubTriID = move(oldToNewVtxMap); }
+
+  return TriMeshGeo(move(newPos), move(newTris));
+}
+
+#endif
diff --git a/libraries/mesh/triMeshManifold.cpp b/libraries/mesh/triMeshManifold.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..14b4701eb62f0eb7261495ae42e97fe31c3c80aa
--- /dev/null
+++ b/libraries/mesh/triMeshManifold.cpp
@@ -0,0 +1,253 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "triMeshManifold.h"
+#include <algorithm>
+#include <iostream>
+#include <cstring>
+#include <cassert>
+using namespace std;
+
+TriMeshManifold::TriMeshManifold() {}
+
+TriMeshManifold::~TriMeshManifold()
+{
+  clear();
+}
+
+void TriMeshManifold::clear()
+{
+  for(TriIter it = triangles.begin(); it != triangles.end(); it++)
+    delete it->second;
+
+  for(EdgeIter it = edges.begin(); it != edges.end(); it++)
+    delete it->second;
+
+  triangles.clear();
+  edges.clear();
+  boundary.clear();
+}
+
+const TriMeshManifold::Triangle * TriMeshManifold::add(int v0, int v1, int v2)
+{
+  OTriKey trikey(v0,v1,v2);
+  return add(trikey);
+}
+
+const TriMeshManifold::Triangle * TriMeshManifold::add(const OTriKey & trikey)
+{
+//  cout << "adding OTriKey: " << trikey[0] << " " << trikey[1] << " " << trikey[2] << endl;
+  TriIter it = triangles.find(trikey);
+  // found this triangle in the manifold
+  if (it != triangles.end())
+  {
+//    cout << "Already exists" << endl;
+    return it->second;
+  }
+
+  Triangle * tri = new Triangle(trikey);
+  UEdgeKey edgekeys[3];
+  Edge * ownEdges[3];
+
+  bool fail = false;
+  // find all three edges in the manifold and test potential non-manifold adding
+  for(int i = 0; i < 3; i++)
+  {
+    edgekeys[i] = tri->uEdgeKey(i);
+    EdgeIter edgeit = edges.find(edgekeys[i]);
+    if (edgeit == edges.end()) // this edge is new
+    {
+      ownEdges[i] = NULL;
+      assert(boundary.find(tri->oEdgeKey(i)) == boundary.end()); // assert this edge is not inside boundary
+    }
+    else
+    {
+      ownEdges[i] = edgeit->second;
+      assert(ownEdges[i]->face[0] != NULL);
+      if (ownEdges[i]->face[1] != NULL)
+      {
+        // this edge has two faces already. Mesh would become non-manifold if this triangle is added
+        fail = true;
+        break;
+      }
+      // test orientation of this edge is correct or not.
+      if (boundary.find(tri->oEdgeKey(i).getReversedEdgeKey()) == boundary.end())
+      {
+        fail = true;
+        break;
+      }
+    }
+  }
+  if (fail)
+  {
+    delete tri;
+    return NULL;
+  }
+
+  // Now it's safe. Add this triangle to the manifold
+  triangles[trikey] = tri;
+
+  for(int i = 0; i < 3; i++)
+  {
+    Edge * edge = ownEdges[i];
+    UEdgeKey & edgekey = edgekeys[i];
+    OEdgeKey okey = tri->oEdgeKey(i);
+    assert(edgekey == UEdgeKey(okey[0], okey[1]));
+    if (edge == NULL)
+    {
+      // this edge does not exist yet. We add a new Edge
+      edge = new Edge(edgekey);
+      assert(edge->v[0] <= edge->v[1]);
+      edges[edgekey] = edge;
+      edge->face[0] = tri;
+      tri->edge[i] = edge;
+
+      // modify boundary
+      // assert(boundary.find(okey) == boundary.end()); // this assert has been checked above; so it's commented out
+//      cout << "boundary insertion: " << okey.v[0] << " " << okey.v[1] << endl;
+      boundary.insert(okey);
+    }
+    else // triangle has a neighbor on this edge
+    {
+      assert(boundary.find(okey) == boundary.end());
+      edge->face[1] = tri;
+      tri->edge[i] = edge;
+      Triangle * nbr = edge->face[0];
+      for(int j = 0; j < 3; j++)
+        if (nbr->edge[j] == edge)
+        {
+          assert(nbr->nbr[j] == NULL);
+          nbr->nbr[j] = tri;
+          tri->nbr[i] = nbr;
+          break;
+        }
+
+      // update boundary
+      okey.reverse(); // now okey is the neighbor's edge
+//      cout << "removing boundary edge: " << okey.v[0] << " " << okey.v[1] << endl;
+      assert(boundary.find(okey) != boundary.end());
+      boundary.erase(okey);
+    }
+  }
+  return tri;
+}
+
+const TriMeshManifold::Triangle * TriMeshManifold::getTriangle(const OTriKey & key) const
+{
+  TriCIter it = triangles.find(key);
+  if (it != triangles.end())
+    return it->second;
+  return NULL;
+}
+
+bool TriMeshManifold::remove(int v0, int v1, int v2)
+{
+  OTriKey trikey(v0,v1,v2);
+  return remove(trikey);
+}
+
+bool TriMeshManifold::remove(const OTriKey & trikey)
+{
+  cout << "remove OTriKey: " << trikey[0] <<  " " << trikey[1] << " " << trikey[2] << endl;
+  TriIter it = triangles.find(trikey);
+  // can't find this tet in the manifold
+  if (it == triangles.end())
+    return false;
+
+  Triangle * tri = it->second;
+  for(int i = 0; i < 3; i++)
+  {
+    UEdgeKey edgekey = tri->uEdgeKey(i);
+    EdgeIter edgeit = edges.find(edgekey);
+    assert(edgeit != edges.end());
+    Edge * edge = edgeit->second;
+    assert(edge->face[0] == tri || edge->face[1] == tri);
+    assert(!(edge->face[1] == tri && edge->face[0] == NULL) );
+    assert(!(edge->face[0] == tri && edge->face[1] == tri) );
+
+    OEdgeKey okey = tri->oEdgeKey(i);
+    Triangle * nbr = NULL;
+    if (edge->face[0] != tri)
+      nbr = edge->face[0];
+    else
+      nbr = edge->face[1];
+
+    if (nbr)
+    {
+      assert(boundary.find(OEdgeKey(okey[0], okey[1])) == boundary.end());
+      assert(boundary.find(OEdgeKey(okey[1], okey[0])) == boundary.end());
+      // we have a neighbor for this triangle
+      // we'll reset the corresponding neighbor var
+      assert(tri->nbr[i] == nbr);
+      for(int j = 0; j < 3; j++)
+        if (nbr->edge[j] == edge)
+        {
+          nbr->nbr[j] = NULL;
+          break;
+        }
+      // we remove this tri's reference in edge
+      // and move neighbor to face[0] so that edge->face[0] is always not NULL
+      edge->face[0] = nbr;
+      edge->face[1] = NULL;
+
+      // update boundary
+      okey.reverse(); // now it's neighbor's edge
+//      cout << "boundary insertion: " << okey.v[0] << " " << okey.v[1] << endl;
+      boundary.insert(okey);
+    }
+    else
+    {
+      // only this tri shares this edge
+      // we'll delete this edge
+      edges.erase(edgeit);
+      delete edge;
+      // update boundary
+//      cout << "removing boundary edge: " << okey.v[0] << " " << okey.v[1] << endl;
+      assert(boundary.find(okey) != boundary.end());
+      boundary.erase(okey);
+    }
+  }
+  triangles.erase(it);
+  delete tri;
+  return true;
+}
+
+TriMeshManifold::Edge::Edge(const UEdgeKey & key) : UEdgeKey(key)
+{
+  memset(face, 0, sizeof(face));
+}
+
+TriMeshManifold::Triangle::Triangle(const OTriKey & key) : OTriKey(key)
+{
+  memset(nbr, 0, sizeof(nbr));
+  memset(edge, 0, sizeof(edge));
+}
diff --git a/libraries/mesh/triMeshManifold.h b/libraries/mesh/triMeshManifold.h
new file mode 100644
index 0000000000000000000000000000000000000000..c3435f30136fc651a2c7ac5127a7c0c9a64fe8a1
--- /dev/null
+++ b/libraries/mesh/triMeshManifold.h
@@ -0,0 +1,117 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TRIMESHMANIFOLD_H
+#define TRIMESHMANIFOLD_H
+
+#include <map>
+#include <set>
+#include "triKey.h"
+
+// Maintain a triangle mesh to enfore manifoldness on face-edge connections.
+// It can also query neighboring relationship on faces.
+// Note that it only detects non-manifoldness created by one edge shared by more than two faces,
+// Does not consider the case where two faces share no edge but one vertex.
+
+class TriMeshManifold
+{
+public:
+  TriMeshManifold();
+  virtual ~TriMeshManifold();
+
+  // clear internal data
+  void clear();
+
+  class Triangle;
+  class Edge;
+
+  // add a new triangle key or (v0,v1,v2) into the mesh, return the new built Triangle
+  // if the triangle already exists, it just return the triangle
+  // return NULL if the new triangle can cause one edge shared by more than two faces (non-manifoldness)
+  const Triangle * add(int v0, int v1, int v2);
+  const Triangle * add(const OTriKey & key);
+
+  // return a triangle, NULL if not found
+  const Triangle * getTriangle(const OTriKey & key) const;
+
+  // remove a triangle from the mesh, return false if it cannot be found
+  bool remove(int v0, int v1, int v2);
+  bool remove(const OTriKey & key);
+
+  typedef std::map<OTriKey, Triangle *> TriMap;
+  typedef std::map<UEdgeKey, Edge *> EdgeMap;
+
+  // get the connection structure for triangles and edges
+  const TriMap & getTriMap() const { return triangles; }
+  const EdgeMap & getEdgeMap() const { return edges; }
+  // get the boundary edges on the mesh
+  const std::set<OEdgeKey> & getBoundary() const { return boundary; }
+
+  // unoriented edge, storing its neighboring triangles
+  class Edge : UEdgeKey
+  {
+  public:
+    Edge(const UEdgeKey & key);
+    const Triangle * getFace(std::size_t ind) const { return face[ind]; }
+  protected:
+    Triangle * face[2];
+    friend class TriMeshManifold;
+  };
+
+  // oriented triangle, storing triangle indices, neighboring edges and neighboring triangles
+  class Triangle : public OTriKey
+  {
+  public:
+    Triangle(const OTriKey & key);
+    const Triangle * getNeighbor(int ind) const { return nbr[ind]; }
+    int getVtx(std::size_t i) const { return v[i]; }
+  protected:
+    Edge * edge[3];
+    Triangle * nbr[3];
+    friend class TriMeshManifold;
+  };
+
+  typedef TriMap::iterator TriIter;
+  typedef TriMap::const_iterator TriCIter;
+  typedef EdgeMap::iterator EdgeIter;
+  typedef EdgeMap::const_iterator EdgeCIter;
+protected:
+  TriMap triangles;
+  EdgeMap edges;
+  std::set<OEdgeKey> boundary;
+};
+
+
+
+
+
+#endif
diff --git a/libraries/mesh/triMeshNeighbor.cpp b/libraries/mesh/triMeshNeighbor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..896461fd91a2cb34eee20055441666d2b6bbe95f
--- /dev/null
+++ b/libraries/mesh/triMeshNeighbor.cpp
@@ -0,0 +1,670 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "triMeshNeighbor.h"
+#include "basicAlgorithms.h"
+#include "containerHelper.h"
+#include <unordered_map>
+#include <cassert>
+#include <set>
+#include <iostream>
+using namespace std;
+
+namespace
+{
+
+vector<vector<int>> findBoundaryLoops(const std::vector<Vec3i> & triangles, const std::vector<Vec3i> & triNbrs)
+{
+  assert(triNbrs.size() == triangles.size());
+  vector<vector<int>> ret;
+
+  vector<bool> triVisited(triangles.size()*3, false);
+  for(size_t triID = 0; triID < triNbrs.size(); triID++)
+  {
+    const auto & n = triNbrs[triID];
+    const auto & t = triangles[triID];
+//    cout << "visit triID " << triID << endl;
+    for(int i = 0; i < 3; i++)
+    {
+      if (triVisited[triID*3+i]) continue;
+      triVisited[triID*3+i] = true;
+      if (n[i] >= 0) continue;
+      // find a boundary, let's go through the loop and find other edges in this loop
+      int t1 = t[(i+1)%3];
+      vector<int> loop = { t[i], t1 };
+
+      int start = t[i];
+      int end = t1;
+      int curTriID = triID;
+//      cout << "start a loop at " << t[i] << " " << t1 << endl;
+
+      int nextI = (i+1) % 3;
+      while(true)
+      {
+        // end = tri[curTriID][nextI+1]
+        // check whether the edge <tri[curTriID][nextI] = end, tri[curTriID][nextI+1]> is boundary
+        triVisited[curTriID*3+nextI] = true;
+        if (triNbrs[curTriID][nextI] < 0) // the next edge on cur triID is a boundary
+        {
+          end = triangles[curTriID][(nextI+1)%3];
+          if (end == start) { break; } // end this loop
+          loop.push_back(end);
+          nextI = (nextI+1)%3;
+//          cout << "find next vtx on triangle: " << end << endl;
+          continue;
+        }
+
+        curTriID = triNbrs[curTriID][nextI];
+//        assert(curTriID != triID || triVisited[curTriID] == false);
+        // next cutTriID has the edge <tri[old curTriID][nextI+1], tri[old curTriID][nextI] = end>
+        nextI = triangles[curTriID].getInvertedIndex(end);
+        assert(nextI >= 0);
+//        cout << "move to another triangle triID " <<  curTriID << endl;
+      }
+
+      ret.emplace_back(move(loop));
+      break; // because one triangle can only be in one loop
+    } // end for i
+  }
+
+  return ret;
+}
+
+vector<pair<int, OEdgeKey>> findBoundaryTriangles(const std::vector<Vec3i> & triangles, const std::vector<Vec3i> & triNbrs)
+{
+  assert(triNbrs.size() == triangles.size());
+  vector<pair<int, OEdgeKey>> ret;
+  for(size_t triID = 0; triID < triNbrs.size(); triID++)
+  {
+    const auto & n = triNbrs[triID];
+    const auto & t = triangles[triID];
+    for(int i = 0; i < 3; i++)
+    {
+      if (n[i] < 0) // find a boundary
+      {
+        ret.push_back(make_pair(triID, OEdgeKey(t[i], t[(i+1)%3])));
+      }
+    }
+  }
+  return ret;
+}
+
+} // end anonymous namespace
+
+
+
+TriangleNeighbor::TriangleNeighbor(const vector<Vec3i> & triangles) : numTriangles(triangles.size()),
+    triNbrs(triangles.size(), Vec3i(-1))
+{
+  if (getOEdgeTriMap(triangles, oedgeTri) == false) throw 1;
+
+  for(int triID = 0; triID < numTriangles; triID++)
+  {
+    for(int j = 0; j < 3; j++)
+    {
+      if (triNbrs[triID][j] < 0)
+      {
+        int v0 = triangles[triID][j];
+        int v1 = triangles[triID][(j+1)%3];
+        auto iter = oedgeTri.find(OEdgeKey(v1,v0)); // get the revserse key
+        if (iter != oedgeTri.end())
+        {
+          int triID2 = iter->second;
+          triNbrs[triID][j] = triID2;
+          int j2 = triangles[triID2].getInvertedIndex(v1);
+          assert(j2 >= 0);
+          triNbrs[triID2][j2] = triID;
+        }
+      }
+    }
+  }
+}
+
+int TriangleNeighbor::getTriangleAtEdge(const OEdgeKey & edge)
+{
+  auto it = oedgeTri.find(edge);
+  if (it == oedgeTri.end()) return -1;
+  return it->second;
+}
+
+vector<pair<int, OEdgeKey>> TriangleNeighbor::findBoundaryTriangles(const std::vector<Vec3i> & triangles) const
+{
+  return ::findBoundaryTriangles(triangles, triNbrs);
+}
+
+vector<vector<int>> TriangleNeighbor::findBoundaryLoops(const std::vector<Vec3i> & triangles) const
+{
+  return ::findBoundaryLoops(triangles, triNbrs);
+}
+
+vector<int> TriangleNeighbor::findTrianglesArroundBoundaryVertex(int prevVtxID, int vtxID, const vector<Vec3i> & triangles) const
+{
+  assert(triNbrs.size() == triangles.size());
+  vector<int> ret;
+  OEdgeKey prevEdge(prevVtxID, vtxID);
+  auto it = oedgeTri.find(prevEdge);
+  if (it == oedgeTri.end()) return ret;
+  int triID = it->second;
+  do
+  {
+    ret.push_back(triID);
+    int i = triangles[triID].getInvertedIndex(vtxID);
+    assert(i >= 0);
+    triID = triNbrs[triID][i];
+  } while(triID >= 0);
+  return ret;
+}
+
+// ==============================================================================
+//                              TriMeshNeighbor
+// ==============================================================================
+
+TriMeshNeighbor::TriMeshNeighbor(TriMeshRef triMesh) : numVertices(triMesh.numVertices()), numTriangles(triMesh.numTriangles()),
+    vtxNbrTri(triMesh.numVertices()), triNbrs(triMesh.numTriangles(), Vec3i(-1)), vtxEdgeTri(triMesh.numVertices())
+{
+  for(int triID = 0; triID < numTriangles; triID++)
+  {
+    for(int j = 0; j < 3; j++)
+    {
+      int v0 = triMesh.tri(triID)[j];
+      int v1 = triMesh.tri(triID)[(j+1)%3];
+      if (v0 == v1) // error, TriMesh is not valid!
+      {
+        throw 1;
+      }
+      vtxNbrTri[v0].push_back(triID);
+      if (vtxEdgeTri[v0].find(v1) != vtxEdgeTri[v0].end())
+      {
+        throw 1; // error, TriMesh is not manifold!
+      }
+      vtxEdgeTri[v0][v1] = triID;
+    }
+  }
+
+  for(int triID = 0; triID < numTriangles; triID++)
+  {
+    for(int j = 0; j < 3; j++)
+    {
+      if (triNbrs[triID][j] < 0)
+      {
+        int v0 = triMesh.tri(triID)[j];
+        int v1 = triMesh.tri(triID)[(j+1)%3];
+        auto iter = vtxEdgeTri[v1].find(v0);
+        if (iter != vtxEdgeTri[v1].end())
+        {
+          int triID2 = iter->second;
+          triNbrs[triID][j] = triID2;
+          int j2 = triMesh.tri(triID2).getInvertedIndex(v1);
+          assert(j2 >= 0);
+          triNbrs[triID2][j2] = triID;
+        }
+      }
+    }
+  }
+}
+
+vector<pair<int, OEdgeKey>> TriMeshNeighbor::findBoundaryTriangles(const std::vector<Vec3i> & triangles) const
+{
+  return ::findBoundaryTriangles(triangles, triNbrs);
+}
+
+vector<vector<int>> TriMeshNeighbor::findBoundaryLoops(const std::vector<Vec3i> & triangles) const
+{
+  return ::findBoundaryLoops(triangles, triNbrs);
+}
+
+vector<int> TriMeshNeighbor::getVtxNearbyVertices(int vtxID, const TriMeshGeo & mesh) const
+{
+  assert(numVertices == mesh.numVertices() && numTriangles == mesh.numTriangles());
+  vector<int> ret;
+  for(int triID : vtxNbrTri[vtxID])
+  {
+    const auto & t = mesh.tri(triID);
+    ret.insert(ret.end(), t.begin(), t.end());
+  }
+  sortAndDeduplicate(ret);
+  return ret;
+}
+
+bool TriMeshNeighbor::areVerticesNeighbors(int vtxID0, int vtxID1) const
+{
+  return mapFind(vtxEdgeTri[vtxID0], vtxID1) || mapFind(vtxEdgeTri[vtxID1], vtxID0);
+}
+
+// ==============================================================================
+//                          NonManifoldTriangleNeighbor
+// ==============================================================================
+
+NonManifoldTriangleNeighbor::NonManifoldTriangleNeighbor(const vector<Vec3i> & triangles) :
+    numTriangles(triangles.size())
+{
+  if (getNonManifoldOEdgeTrisMap(triangles, oedgeTris) == false) throw 1;
+}
+
+vector<int> NonManifoldTriangleNeighbor::getTriangleAtOEdge(const OEdgeKey & oedge) const
+{
+  auto it = oedgeTris.find(oedge);
+  if (it == oedgeTris.end()) return {};
+  return it->second;
+}
+
+vector<OEdgeKey> NonManifoldTriangleNeighbor::findNonManifoldOEdges() const
+{
+  vector<OEdgeKey> ret;
+  for(const auto & p : oedgeTris)
+  {
+    if (p.second.size() > 1) ret.push_back(p.first);
+  }
+  return ret;
+}
+/////////////////////////////////////////////////////////////////////////
+//                       Other Functions
+/////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+
+using Neighbor = std::function<const std::vector<int> &(int nodeA)>;
+vector<vector<int>> getConnectedComponents(int numNodes, Neighbor getNeighbor)
+{
+  vector<vector<int>> ret;
+  if (numNodes == 0) return ret;
+
+  vector<bool> visited(numNodes, false);
+
+  for(int seedID = 0; seedID < numNodes; seedID++)
+  {
+    if (visited[seedID]) continue;
+
+    vector<int> component;  // record traversed triangles
+
+    visited[seedID] = true;
+    component.push_back(seedID);
+    size_t candidateBegin = 0, candidateEnd = 1;
+
+    while(candidateBegin != candidateEnd)
+    {
+      for(size_t i = candidateBegin; i < candidateEnd; i++)
+      {
+        int node = component[i];
+        const auto & nbrs = getNeighbor(node);
+        for(int nbr : nbrs)
+        {
+          if (nbr < 0) continue;
+          if (visited[nbr]) continue;
+          visited[nbr] = true;
+          component.push_back(nbr);
+        }
+      }
+      candidateBegin = candidateEnd;
+      candidateEnd = component.size();
+    }
+
+    sort(component.begin(), component.end());
+    assert(unique(component.begin(), component.end()) == component.end());
+    ret.emplace_back(move(component));
+  }
+  return ret;
+}
+
+} // anonymous namespace
+
+// build oedgeTri: <v0,v1> -> tri with ordered edge <v0, v1>
+bool getOEdgeTriMap(const std::vector<Vec3i> & triangles, unordered_map<OEdgeKey, int> & oedgeTri)
+{
+  int numTriangles = triangles.size();
+  oedgeTri.clear();
+
+  for(int triID = 0; triID < numTriangles; triID++)
+  {
+    for(int j = 0; j < 3; j++)
+    {
+      int v0 = triangles[triID][j];
+      int v1 = triangles[triID][(j+1)%3];
+      if (v0 == v1 || v0 < 0) // error, TriMesh is not valid!
+      {
+        return false;
+      }
+      OEdgeKey edge(v0, v1);
+      if (oedgeTri.find(edge) != oedgeTri.end())
+      {
+        return false; // this signed edge appears twice, not manifold!
+      }
+      oedgeTri.insert(make_pair(edge, triID));
+    }
+  }
+  return true;
+}
+
+// go through a triangle fan around vtxID on an edge-manifold mesh
+// input includes vtxID, a startingTriID to start the search,
+// and the localVtxID == triangles[startingTriID].getInvertedIndex(vtxID), localVtxID: [0,3)
+// once a new triangle is visited, its triID and the local vtxID [0,3) of the input vtxID is passed to
+// processTriangle for custom processing
+static void visitTriangleFanNeighboringVtx(const std::vector<Vec3i> & triangles,
+    const unordered_map<OEdgeKey, int> & oedgeTri, int vtxID, int startingTriID, int localVtxID,
+    function<void(int newTriID, int newLocalVtxID)> processTriangle)
+{
+  // visit the neighboring triangles to search for the fan shape.
+  // ((lvID + edgeVtxOffset)%3, (lvID + edgeVtxOffset+1)%3) represents an OEdge on triangle triID
+  // return true if we go 360 degrees around the vtx, finishing one full loop.
+  // if it returns false, then we hit a boundary edge on the triangle mesh
+  auto visitNbr = [&](int edgeVtxOffset)
+  {
+    //        cout << "begin search" << endl;
+    // loop over triangles around this vtx, starting at the edge (vEdgeStart, vEdgeEnd)
+    int vEdgeStart = triangles[startingTriID][(localVtxID + edgeVtxOffset)%3];
+    int vEdgeEnd = triangles[startingTriID][(localVtxID + edgeVtxOffset+1)%3];
+    // edge<vEdgeStart, vEdgeEnd> is an OEdge of triID
+    // since we want to find the nbring tri on this edge, we should search the reversed edge in oedgeTri
+    auto it = oedgeTri.find({vEdgeEnd, vEdgeStart});
+    while(it != oedgeTri.end())
+    {
+      int nextTriID = it->second;
+      if(nextTriID == startingTriID) return true; // we finished one full loop around the vtx v
+      int nextlvID = triangles[nextTriID].getInvertedIndex(vtxID);
+      assert(nextlvID >= 0);
+
+      processTriangle(nextTriID, nextlvID);
+
+      vEdgeStart = triangles[nextTriID][(nextlvID + edgeVtxOffset)%3];
+      vEdgeEnd = triangles[nextTriID][(nextlvID + edgeVtxOffset+1)%3];
+      it = oedgeTri.find({vEdgeEnd, vEdgeStart});
+    }
+    return false;
+  };
+
+  if (visitNbr(0) == false) // visitNbr(0) return true only if it finishes one complete loop
+    visitNbr(2);
+}
+
+// The algorithm here is that for each vtx, we visit each neighboring triangles and record this "fan shape" we visited.
+// If the mesh is vtx-manifold, then there will only be one fan shape for each vtx.
+// But on a non-vtx-manifold but edge-manifold mesh, one vtx can have more than one fan shape.
+vector<int> getNonManifoldVerticesOnEdgeManifoldTriangles(const std::vector<Vec3i> & triangles,
+    const unordered_map<OEdgeKey, int> & oedgeTri)
+{
+  vector<int> ret;
+  int numTriangles = triangles.size();
+  set<int> vtxVisited;
+  vector<bool> triVtxVisited(numTriangles * 3, false);
+
+  for(int triID = 0;  triID < numTriangles; triID++)
+  {
+    for(int lvID = 0; lvID < 3; lvID++) // local vtx ID
+    {
+      if (triVtxVisited[triID*3+lvID]) continue;
+      int curVtxID = triangles[triID][lvID];
+      if (vtxVisited.find(curVtxID) != vtxVisited.end()) // if this v has been visited
+      {
+        ret.push_back(curVtxID); // not vtx-manifold
+//        cout << "non-manifold: " << triID << " " << v << endl;
+        continue;
+      }
+      vtxVisited.insert(curVtxID);
+      triVtxVisited[triID*3+lvID] = true;
+
+//      cout << "visit triID " << triID << " v " << v << endl;
+      visitTriangleFanNeighboringVtx(triangles, oedgeTri, curVtxID, triID, lvID, [&](int nextTriID, int nextlvID)
+      {
+        assert(triVtxVisited[nextTriID*3+nextlvID] == false);
+        triVtxVisited[nextTriID*3+nextlvID] = true;
+      });
+    }
+  }
+  sortAndDeduplicate(ret);
+  return ret;
+}
+
+void fixNonManifoldVerticesOnEdgeManifoldTriangles(TriMeshGeo & triMesh, const std::unordered_map<OEdgeKey, int> & oedgeTri,
+    std::map<int, int> * newVtx2OldVtxMap)
+{
+  vector<int> nmVtxIDs = getNonManifoldVerticesOnEdgeManifoldTriangles(triMesh.triangles(), oedgeTri);
+  if (nmVtxIDs.size() == 0) return;
+
+  map<int, vector<int>> nmVtxNbringTris;
+  for(int triID = 0; triID < triMesh.numTriangles(); triID++)
+    for(int i = 0; i < 3; i++)
+    {
+      int vtxID = triMesh.triVtxID(triID, i);
+      if (binarySearchFound(nmVtxIDs, vtxID))
+      {
+        nmVtxNbringTris[vtxID].push_back(triID);
+      }
+    }
+
+  for(int nmVtxID : nmVtxIDs)
+  {
+    const vector<int> & nbringTriIDs = nmVtxNbringTris[nmVtxID];
+    assert(nbringTriIDs.size() > 1);
+    set<int> nbringTriIDVisited;
+    vector<vector<int>> triFans; // stores the triIDs in each triangle fan around nmVtxID
+    for(int i = 0; i < sizei(nbringTriIDs); i++)
+    {
+      int triID = nbringTriIDs[i];
+      if (setFind(nbringTriIDVisited, triID)) continue;
+      nbringTriIDVisited.insert(triID);
+
+      vector<int> newFan = { triID };
+      int lvID = triMesh.tri(triID).getInvertedIndex(nmVtxID); // local vtx ID, [0,3)
+      visitTriangleFanNeighboringVtx(triMesh.triangles(), oedgeTri, nmVtxID, triID, lvID, [&](int newTriID, int newLvID)
+      {
+        assert(binarySearchFound(nbringTriIDs, newTriID));
+        assert(setNotFind(nbringTriIDVisited, newTriID));
+        nbringTriIDVisited.insert(newTriID);
+        newFan.push_back(newTriID);
+      });
+//      cout << "Found a new fan, size " << newFan.size() << endl;
+      triFans.emplace_back(move(newFan));
+    }
+
+    // sanity check
+    size_t sum = 0;
+    for(const auto & fan : triFans)
+      sum += fan.size();
+//    cout << "Fans:  " << triFans.size() << endl;
+//    cout << "sum " << sum << " " << nbringTriIDs.size() << endl;
+    assert(sum == nbringTriIDs.size());
+    assert(triFans.size() > 1);
+
+    for(int i = 1; i < sizei(triFans); i++) // for each addtional fan
+    {
+      int newVtxID = triMesh.numVertices();
+      if (newVtx2OldVtxMap)
+        newVtx2OldVtxMap->emplace(newVtxID, nmVtxID);
+      triMesh.addPos(triMesh.pos(nmVtxID));
+      for(int triID : triFans[i])
+      {
+        int lvID = triMesh.tri(triID).getInvertedIndex(nmVtxID);
+        triMesh.tri(triID)[lvID] = newVtxID;
+      }
+    }
+  } // for each nmVtxID
+}
+
+
+bool areTrianglesEdgeManifold(const std::vector<Vec3i> & triangles)
+{
+  std::unordered_map<OEdgeKey, int> oedgeTri; // <v0,v1> -> tri with ordered edge <v0, v1>
+  return getOEdgeTriMap(triangles, oedgeTri);
+}
+
+bool areTrianglesManifold(const std::vector<Vec3i> & triangles)
+{
+//  int numTriangles = triangles.size();
+  unordered_map<OEdgeKey, int> oedgeTri; // <v0,v1> -> tri with ordered edge <v0, v1>
+
+  if (getOEdgeTriMap(triangles, oedgeTri) == false) return false;
+  // now the mesh is at least edge-manifold
+
+  return (getNonManifoldVerticesOnEdgeManifoldTriangles(triangles, oedgeTri).size() == 0);
+}
+
+std::vector<std::vector<int>> getTriangleNeighborsByEdge(const std::vector<Vec3i> & triangles)
+{
+  vector<vector<int>> nbrs(triangles.size());
+  map<UEdgeKey, vector<int>> uedgeMap;
+  for(size_t triID = 0; triID < triangles.size(); triID++)
+  {
+    const Vec3i & tri = triangles[triID];
+    for(int j = 0; j < 3; j++)
+    {
+      UEdgeKey edge(tri[j], tri[(j+1)%3]);
+      uedgeMap[edge].push_back(triID);
+    }
+  }
+  for(const auto & p : uedgeMap)
+  {
+    const auto & tris = p.second;
+    for(size_t i = 0; i < tris.size(); i++)
+      for(size_t j = i+1; j < tris.size(); j++)
+      {
+        nbrs[tris[i]].push_back(tris[j]);
+        nbrs[tris[j]].push_back(tris[i]);
+      }
+  }
+  for(auto & vec : nbrs) // sort and remove duplicates in each nbr vector
+  {
+    sortAndDeduplicate(vec);
+  }
+
+  return nbrs;
+}
+
+vector<vector<int>> getConnectedComponentsByEdge(const std::vector<Vec3i> & triangles)
+{
+  if (triangles.size() == 0) return {};
+
+  auto triNbrs = getTriangleNeighborsByEdge(triangles);
+
+  auto getNeighbor = [&](int triID) -> const vector<int> & { return triNbrs[triID]; };
+
+  return getConnectedComponents(triangles.size(), getNeighbor);
+}
+
+vector<std::vector<int>> getConnectedComponentsByVertex(const std::vector<Vec3i> & triangles)
+{
+  if (triangles.size() == 0) return {};
+
+
+  map<int, vector<int>> vtxNbringTri;
+  for(size_t i = 0; i < triangles.size(); i++)
+  {
+    for(int j = 0; j < 3; j++)
+      vtxNbringTri[triangles[i][j]].push_back(i);
+  }
+  vector<vector<int>> triNbrTri(triangles.size());
+  for(const auto & p : vtxNbringTri)
+  {
+    const auto & t = p.second; // neighborhood
+    for(size_t i = 0; i < t.size(); i++)
+    {
+      for(size_t j = i+1; j < t.size(); j++)
+      {
+        triNbrTri[t[i]].push_back(t[j]);
+        triNbrTri[t[j]].push_back(t[i]);
+      }
+    }
+  }
+  for(auto & t : triNbrTri)
+  {
+    sortAndDeduplicate(t);
+  }
+
+  auto getNeighbor = [&](int triID) -> const vector<int> & { return triNbrTri[triID]; };
+
+  return getConnectedComponents(triangles.size(), getNeighbor);
+}
+
+bool getNonManifoldOEdgeTrisMap(const vector<Vec3i> & triangles, unordered_map<OEdgeKey, vector<int>> & oedgeTris)
+{
+  int numTriangles = triangles.size();
+  oedgeTris.clear();
+
+  for(int triID = 0; triID < numTriangles; triID++)
+  {
+    for(int j = 0; j < 3; j++)
+    {
+      int v0 = triangles[triID][j];
+      int v1 = triangles[triID][(j+1)%3];
+      if (v0 == v1 || v0 < 0) // error, TriMesh is not valid!
+      {
+        return false;
+      }
+      OEdgeKey edge(v0, v1);
+      oedgeTris[edge].push_back(triID);
+    }
+  }
+  return true;
+}
+
+vector<OEdgeKey> getExteriorEdges(int numTriangles, const Vec3i * triangles)
+{
+  map<OEdgeKey, int> count;
+  for(int triID = 0; triID < numTriangles; triID++)
+  {
+    for(int i = 0; i < 3; i++)
+    {
+      OEdgeKey e(triangles[triID][i], triangles[triID][(i+1)%3]);
+      auto it = count.find(e);
+      if (it != count.end()) { it->second++; }
+      else
+      {
+        it = count.find(e.getReversedEdgeKey());
+        if (it != count.end()) { it->second--; }
+        else
+        {
+          count.emplace(e, 1);
+        }
+      }
+    }
+  }
+  vector<OEdgeKey> ret;
+  for(const auto & p : count)
+  {
+    if (p.second == 0) continue;
+    if (p.second > 0)
+    {
+      for(int i = 0; i < p.second; i++)
+        ret.push_back(p.first);
+    }
+    else
+    {
+      OEdgeKey e = p.first.getReversedEdgeKey();
+      for(int i = 0; i < (-p.second); i++)
+      {
+        ret.push_back(e);
+      }
+    }
+  }
+  return ret;
+}
diff --git a/libraries/mesh/triMeshNeighbor.h b/libraries/mesh/triMeshNeighbor.h
new file mode 100644
index 0000000000000000000000000000000000000000..ffda91b244b29d5800bf9dab1001d1ab14fcb110
--- /dev/null
+++ b/libraries/mesh/triMeshNeighbor.h
@@ -0,0 +1,182 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TRIMESHNEIGHBOR_H
+#define TRIMESHNEIGHBOR_H
+
+#include "triMeshGeo.h"
+#include "edgeKey.h"
+#include <unordered_map>
+#include <map>
+
+// Edge-manifold neighboring structure for vector of triangles.
+// No vtx positions needed.
+class TriangleNeighbor
+{
+public:
+  TriangleNeighbor() {} // empty
+  TriangleNeighbor(const std::vector<Vec3i> & triangles);
+
+  const Vec3i & getTriangleNeighbors(int triID) const { return triNbrs[triID]; }
+
+  // input triangles should be the same one used to construct this neighbor class
+  // return vector of <boundary triID, its boundary OEdgeKey> pairs
+  std::vector<std::pair<int, OEdgeKey>> findBoundaryTriangles(const std::vector<Vec3i> & triangles) const;
+
+  // input triangles should be the same one used to construct this neighbor class
+  // each vector<int> in the returned data contains one loop. The ordering agrees with the boundary edge direction
+  std::vector<std::vector<int>> findBoundaryLoops(const std::vector<Vec3i> & triangles) const;
+
+  // get the triangles arround the boundary vertex vtxID, which starts at a triangle with OEdgeKey(prevVtxID, vtxID)
+  std::vector<int> findTrianglesArroundBoundaryVertex(int prevVtxID, int vtxID,
+      const std::vector<Vec3i> & triangles) const;
+
+  // get the triangle with ordered edge
+  // return -1 if not found
+  int getTriangleAtEdge(const OEdgeKey & edge);
+
+protected:
+  int numTriangles = 0;
+  std::vector<Vec3i> triNbrs;       // triID -> i[0,3) -> nbring triID at edge <tri[i], tri[(i+1)%3]>
+  std::unordered_map<OEdgeKey, int> oedgeTri; // <v0,v1> -> tri with ordered edge <v0, v1>
+};
+
+// Edge-manifold neighboring structure for triMesh
+class TriMeshNeighbor
+{
+public:
+  TriMeshNeighbor() {} // empty
+  TriMeshNeighbor(TriMeshRef triMesh);
+
+  const std::vector<int> & getVtxNearbyTriangles(int vtxID) const { return vtxNbrTri[vtxID]; }
+
+  const Vec3i & getTriangleNeighbors(int triID) const { return triNbrs[triID]; }
+
+  // input triangles should be the same one used to construct this neighbor class
+  // return vector of <boundary triID, its boundary OEdgeKey> pairs
+  std::vector<std::pair<int, OEdgeKey>> findBoundaryTriangles(const std::vector<Vec3i> & triangles) const;
+
+  // input triangles should be the same one used to construct this neighbor class
+  // each vector<int> in the returned data contains one loop. The ordering agrees with the boundary edge direction
+  std::vector<std::vector<int>> findBoundaryLoops(const std::vector<Vec3i> & triangles) const;
+
+  // for vtxID, get a mapping: nbring vtxID -> the triangleID which has the ordered edge of <vtxID, nbring vtxID>
+  const std::map<int, int> & getVertexEdgeTriMap(int vtxID) const { return vtxEdgeTri[vtxID]; }
+
+  // result are sorted
+  std::vector<int> getVtxNearbyVertices(int vtxID, const TriMeshGeo & mesh) const;
+  bool areVerticesNeighbors(int vtxID0, int vtxID1) const;
+
+protected:
+  int numVertices = 0, numTriangles = 0;
+  std::vector<std::vector<int>> vtxNbrTri;    // vtxID -> nearby triIDs
+  std::vector<Vec3i> triNbrs;                 // triID -> i[0,3) -> nbring triID at edge <tri[i], tri[(i+1)%3]>
+  std::vector<std::map<int, int>> vtxEdgeTri; // v0 -> v1 -> tri with ordered edge <v0, v1>
+};
+
+// Possibly non-manifold neighboring structure for vector of triangles
+// No vtx positions needed
+class NonManifoldTriangleNeighbor
+{
+public:
+  NonManifoldTriangleNeighbor() {} // empty
+  NonManifoldTriangleNeighbor(const std::vector<Vec3i> & triangles);
+
+  // return -1 if not found
+  std::vector<int> getTriangleAtOEdge(const OEdgeKey & oedge) const;
+
+  // get ordered edges that are non-manifold (more than one triangles own this oedge)
+  std::vector<OEdgeKey> findNonManifoldOEdges() const;
+
+protected:
+  int numTriangles = 0;
+  std::unordered_map<OEdgeKey, std::vector<int>> oedgeTris;
+};
+
+
+// return if triangles are edge-manifold
+// return false if triangles contain invalid or degenerate triangles
+bool areTrianglesEdgeManifold(const std::vector<Vec3i> & triangles);
+
+// return if triangles are edge-manifold and vtx-manifold
+// return false if triangles contain invalid or degenerate triangles
+bool areTrianglesManifold(const std::vector<Vec3i> & triangles);
+
+// return vector of size #triangles, mapping: triID -> nbring triIDs sorted
+// work on non-manifold meshes
+std::vector<std::vector<int>> getTriangleNeighborsByEdge(const std::vector<Vec3i> & triangles);
+
+// return the triangle indices in each connected component in triangles
+// triIDs in each connected component is sorted
+// the connectivity is defined by sharing (unordered) edges
+// work on non-manifold meshes
+std::vector<std::vector<int>> getConnectedComponentsByEdge(const std::vector<Vec3i> & triangles);
+
+// return the triangle indices in each connected component in triangles
+// triIDs in each connected component is sorted
+// the connectivity is defined by sharing vertices
+// work on non-manifold meshes
+std::vector<std::vector<int>> getConnectedComponentsByVertex(const std::vector<Vec3i> & triangles);
+
+// build oedgeTri: <v0,v1> -> tri with ordered edge <v0, v1>
+// return false if triangles are invalid (degenerate or invalid vtx indices) or not edge-manifold
+bool getOEdgeTriMap(const std::vector<Vec3i> & triangles, std::unordered_map<OEdgeKey, int> & oedgeTri);
+
+// get non-manifold vertices on an edge-manifold mesh
+// need input oedgeTri: <v0,v1> -> tri with ordered edge <v0, v1>
+// return sorted non-manifold vertex IDs
+std::vector<int> getNonManifoldVerticesOnEdgeManifoldTriangles(const std::vector<Vec3i> & triangles,
+    const std::unordered_map<OEdgeKey, int> & oedgeTri);
+// fix those non-manifold vertices by duplicating them to make the entire mesh manifold
+// modify input triMesh to be manifold
+// optionally output newVtx2OldVtxMap: newly generated vtxID -> original vtxID
+void fixNonManifoldVerticesOnEdgeManifoldTriangles(TriMeshGeo & triMesh, const std::unordered_map<OEdgeKey, int> & oedgeTri,
+    std::map<int, int> * newVtx2OldVtxMap = nullptr);
+
+// build oedgeTris: <v0, v1> -> tris with ordered edge <v0, v1>
+// work on non-manifold meshes
+// return false only if triangles are invalid (degenerate or invalid vtx indices)
+bool getNonManifoldOEdgeTrisMap(const std::vector<Vec3i> & triangles, std::unordered_map<OEdgeKey, std::vector<int>> & oedgeTris);
+
+// On a closed manifold mesh, each edge is visited exactly twice: (i, j) and (j, i)
+// Return those edges that are #(i,j) - #(j,i) != 0
+// This includes boundary edges and non-manifold ones
+// If (i, j) is a boundary edge, (i, j) will be returned
+// If k = #(i,j) - #(j,i) > 0, then edge (i, j) will appear k times
+// If k = #(i,j) - #(j,i) < 0, then edge (j, i) will appear -k times
+// Useful when computing winding numbers
+// interface inspired from libigl, which is subject to the terms of the Mozilla Public License, v. 2.0. You can obtain one copy at https://mozilla.org/MPL/2.0/.
+std::vector<OEdgeKey> getExteriorEdges(int numTriangles, const Vec3i * triangles);
+inline std::vector<OEdgeKey> getExteriorEdges(const std::vector<Vec3i> & triangles) { return getExteriorEdges(triangles.size(), triangles.data()); }
+
+#endif
+
diff --git a/libraries/mesh/triMeshPseudoNormal.cpp b/libraries/mesh/triMeshPseudoNormal.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c1a124ebf2bd7dbc1ba398dbd2ff46a1a92a8be2
--- /dev/null
+++ b/libraries/mesh/triMeshPseudoNormal.cpp
@@ -0,0 +1,103 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "triMeshPseudoNormal.h"
+#include "triMeshNeighbor.h"
+#include <cassert>
+using namespace std;
+
+TriMeshPseudoNormal::TriMeshPseudoNormal(TriMeshRef triMesh, const Vec3d * extTriNormals) :
+  vtxNormals(triMesh.numVertices()), edgeNormals(triMesh.numVertices()), triNormals(triMesh.numTriangles())
+{
+  TriMeshNeighbor nbr(triMesh);
+  for(int triID = 0; triID < triMesh.numTriangles(); triID++)
+  {
+    if (extTriNormals)
+      triNormals[triID] = extTriNormals[triID];
+    else
+      triNormals[triID] = triMesh.computeTriangleNormal(triID);
+    assert(triNormals[triID].hasNaN() == false);
+  }
+
+  for(int vtxID = 0; vtxID < triMesh.numVertices(); vtxID++)
+  {
+    auto & vtxNormal = vtxNormals[vtxID];
+    vtxNormal = Vec3d(0.0);
+    for(int triID : nbr.getVtxNearbyTriangles(vtxID))
+    {
+      double angle = triMesh.getTriangleAngleAtVertexRobust(triID, vtxID);
+      vtxNormal += angle * triNormals[triID];
+    }
+    if (len2(vtxNormal) > 0)
+    {
+      vtxNormal.normalize();
+      assert(vtxNormal.hasNaN() == false);
+    }
+  }
+
+  for(int vtxID = 0; vtxID < triMesh.numVertices(); vtxID++)
+  {
+    for(const auto & p : nbr.getVertexEdgeTriMap(vtxID))
+    {
+      int vtxID2 = p.first;
+      int triID = p.second;
+
+      if (vtxID == vtxID2)
+      {
+        throw 1; // error, triMesh is not valid!
+      }
+      int v0 = vtxID, v1 = vtxID2;
+      if (v0 > v1) swap(v0, v1);
+
+      auto iter = edgeNormals[v0].find(v1);
+      if (iter == edgeNormals[v0].end())
+      {
+        edgeNormals[v0][v1] = triNormals[triID];
+      }
+      else
+      {
+        iter->second += triNormals[triID];
+        iter->second.normalize();
+      }
+    }
+  }
+}
+
+const Vec3d & TriMeshPseudoNormal::edgeNormal(int vtxID0, int vtxID1) const
+{
+  if (vtxID0 > vtxID1) { swap(vtxID0, vtxID1); }
+  const auto & edgeMap = edgeNormals[vtxID0];
+  auto iter = edgeMap.find(vtxID1);
+  assert(iter != edgeMap.end());
+//  if (iter == edgeMap.end()) { return Vec3d(0.0); }
+  return iter->second;
+}
diff --git a/libraries/mesh/triMeshPseudoNormal.h b/libraries/mesh/triMeshPseudoNormal.h
new file mode 100644
index 0000000000000000000000000000000000000000..e9d9b7acdf8f3ffa76bbebc5f61c9919ce7f2790
--- /dev/null
+++ b/libraries/mesh/triMeshPseudoNormal.h
@@ -0,0 +1,65 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TRIMESHPSEUDONORMAL_H
+#define TRIMESHPSEUDONORMAL_H
+
+#include "triMeshGeo.h"
+#include <map>
+
+// compute pseudo-normal on a triangle mesh
+// assuming TriMesh is edge-manifold, required by TriMeshNeighbor
+class TriMeshPseudoNormal
+{
+public:
+  TriMeshPseudoNormal() {} // empty
+  TriMeshPseudoNormal(TriMeshRef triMesh, const Vec3d * triangleNormals = nullptr);
+
+  int numVertices() const { return vtxNormals.size(); }
+  int numTriangles() const { return triNormals.size(); }
+
+  // return Vec3d(0.0) if this vtx has no nearby triangles
+  const Vec3d & vtxNormal(int vtxID) const { return vtxNormals[vtxID]; }
+
+  // assert (vtxID0, vtxID1) is a valid edge
+  const Vec3d & edgeNormal(int vtxID0, int vtxID1) const;
+
+  const Vec3d & triNormal(int triID) const { return triNormals[triID]; }
+
+protected:
+  std::vector<Vec3d> vtxNormals;
+  std::vector<std::map<int, Vec3d>> edgeNormals;
+  std::vector<Vec3d> triNormals;
+};
+
+#endif
+
diff --git a/src/libobjMesh/triangle-closestPoint.cpp b/libraries/mesh/triangle-closestPoint.cpp
similarity index 89%
rename from src/libobjMesh/triangle-closestPoint.cpp
rename to libraries/mesh/triangle-closestPoint.cpp
index 3a4a1447a734f011f96aacadb0a6f718b38ee1e6..6967af0dedb946c209167fa0be396f50b7aa441c 100644
--- a/src/libobjMesh/triangle-closestPoint.cpp
+++ b/libraries/mesh/triangle-closestPoint.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,9 +37,9 @@
 */
 #include "triangle.h"
 #ifdef __VERSION_WITH_BARYCENTRIC_COORDS_JNB_CMU__
-double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFeature, double * alpha, double * beta, double * gamma) 
+double TriangleWithCollisionInfo::distanceToPoint2(const Vec3d & point, int * closestFeature, double * alpha, double * beta, double * gamma) const
 #else
-double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFeature)
+double TriangleWithCollisionInfo::distanceToPoint2(const Vec3d & point, int * closestFeature) const
 #endif
 
 {
@@ -60,7 +64,7 @@ double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFea
     |    /  \  F
  N22|   /    \      /
  \  |  /S2  S1\    /N11
-  \ | /   J    \  /   
+  \ | /   J    \  /
  H \|/          \/  D
      ---------------------->
    0|            |1        x
@@ -72,7 +76,8 @@ double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFea
 
   #ifdef __VERSION_WITH_BARYCENTRIC_COORDS_JNB_CMU__
     double * barycentricCoordsRaw[3] = { alpha, beta, gamma};
-    double * barycentricCoords[3] = { 
+    double * barycentricCoords[3] =
+    {
       barycentricCoordsRaw[permutation[0]], barycentricCoordsRaw[permutation[1]], barycentricCoordsRaw[permutation[2]]};
   #endif
 
@@ -84,14 +89,14 @@ double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFea
   double d = P[2] * P[2]; // contribution due to the z-offset
 
   // determine distance of P[0],P[1] to the planar triangle
- 
+
   P[2] = 1; // transformation to homogeneous coordinates
   double x = P[0];
   double y = P[1];
 
   double u,v;
   bool caseECandidate = false;
- 
+
   if (y <= 0)
   {
     if (x < 0)
@@ -128,7 +133,7 @@ double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFea
       return (d + y*y);
     }
   }
-  else 
+  else
   {
     // we have y > 0
 
@@ -167,7 +172,7 @@ double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFea
           //printf("case F\n");
           *closestFeature = permutation[4]; // edge 12
           #ifdef __VERSION_WITH_BARYCENTRIC_COORDS_JNB_CMU__
-            // *alpha = 0; *beta = -v / sideb; *gamma = 1 - *beta; 
+            // *alpha = 0; *beta = -v / sideb; *gamma = 1 - *beta;
             *(barycentricCoords[0]) = 0; *(barycentricCoords[1]) = -v / sideb; *(barycentricCoords[2]) =  1 - *(barycentricCoords[1]);
           #endif
           return (d + u*u);
@@ -204,8 +209,8 @@ double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFea
           #ifdef __VERSION_WITH_BARYCENTRIC_COORDS_JNB_CMU__
             // *beta = 0;
             // *gamma = -v / sidec;
-            // *alpha = 1-*gamma; 
-            *(barycentricCoords[1]) = 0; 
+            // *alpha = 1-*gamma;
+            *(barycentricCoords[1]) = 0;
             *(barycentricCoords[2]) = -v / sidec;
             *(barycentricCoords[0]) = 1- *(barycentricCoords[2]);
           #endif
@@ -242,7 +247,7 @@ double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFea
       else
       {
         // not caseECandidate or else would have exited already
-    
+
         v = dot(N22,P);
         if (v >= 0) // above N22
         {
@@ -259,8 +264,8 @@ double TriangleWithCollisionInfo::distanceToPoint2(Vec3d point, int * closestFea
           printf("case I\n");
           // *beta = 0;
           // *gamma = -v / sidec;
-          // *alpha = 1-*gamma; 
-          *(barycentricCoords[1]) = 0; 
+          // *alpha = 1-*gamma;
+          *(barycentricCoords[1]) = 0;
           *(barycentricCoords[2]) = -v / sidec;
           *(barycentricCoords[0]) = 1- *(barycentricCoords[2]);
           *closestFeature = permutation[5]; // edge 20
diff --git a/src/libobjMesh/triangle.cpp b/libraries/mesh/triangle.cpp
similarity index 64%
rename from src/libobjMesh/triangle.cpp
rename to libraries/mesh/triangle.cpp
index a019890db8851ea6c52574ddc7224ee4bd5ea72b..b33175b5db3510a086ec1827acf807ab768b8ce2 100644
--- a/src/libobjMesh/triangle.cpp
+++ b/libraries/mesh/triangle.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,11 +35,7 @@
   Jernej Barbic, CMU
 */
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #pragma warning(disable : 4996)
   #pragma warning(disable : 4267)
   #pragma warning(disable : 4244)
@@ -43,44 +43,27 @@
 
 #include "openGL-headers.h"
 #include <set>
+#include <algorithm>
 #include "triangle.h"
 #include "tribox3.h"
 using namespace std;
-namespace vega
+
+bool TriangleBasic::doesIntersectBox(const BoundingBox & bbox) const
 {
-bool TriangleBasic::doesIntersectBox(BoundingBox & bbox)
-{   
   // prepare the data and call the "triBoxOverlap" worker routine
+  const Vec3d & center = bbox.center();
+  const Vec3d & halfSides = bbox.halfSides();
+  return triBoxOverlap(&center[0], &halfSides[0], &vertex[0][0], &vertex[1][0], &vertex[2][0]);
+}
 
-  double boxcenter[3];
-  double boxhalfsize[3];
-  double triverts[3][3];
-
-  Vec3d center_ = bbox.center();
-  Vec3d halfSides_ = bbox.halfSides();
-  boxcenter[0] = center_[0];
-  boxcenter[1] = center_[1];
-  boxcenter[2] = center_[2];
-  boxhalfsize[0] = halfSides_[0];
-  boxhalfsize[1] = halfSides_[1];
-  boxhalfsize[2] = halfSides_[2];
-
-  triverts[0][0] = first_[0];
-  triverts[0][1] = first_[1];
-  triverts[0][2] = first_[2];
-
-  triverts[1][0] = second_[0];
-  triverts[1][1] = second_[1];
-  triverts[1][2] = second_[2];
-
-  triverts[2][0] = third_[0];
-  triverts[2][1] = third_[1];
-  triverts[2][2] = third_[2];
-
-  return triBoxOverlap(boxcenter,boxhalfsize,triverts);
+int TriangleBasic::pointSide(const Vec3d & point) const
+{
+  Vec3d normal = cross(vertex[1] - vertex[0], vertex[2] - vertex[0]);
+  double dir = dot(point - vertex[0], normal);
+  return (dir > 0 ? 1 : (dir < 0 ? -1 : 0));
 }
 
-int TriangleBasic::lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd, Vec3d * intersectionPoint)
+int TriangleBasic::lineSegmentIntersection(const Vec3d & segmentStart, const Vec3d & segmentEnd, Vec3d * intersectionPoint, double * intersectionParameter, double barycentricWeight[3]) const
 {
   // Jernej Barbic, CMU, 2005
   // code modified from the following code:
@@ -105,24 +88,24 @@ int TriangleBasic::lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd,
   double r, a, b;             // params to calc ray-plane intersect
 
   // get triangle edge vectors and plane normal
-  u = second_ - first_;
-  v = third_- first_;
+  u = vertex[1] - vertex[0];
+  v = vertex[2]- vertex[0];
   n = cross(u, v);             // cross product
   if ((n[0] == 0) &&         // triangle is degenerate
-      (n[1] == 0) &&         
-      (n[2] == 0) )          
+      (n[1] == 0) &&
+      (n[2] == 0) )
     return -1;                 // do not deal with this case
 
   dir = segmentEnd - segmentStart;             // ray direction vector
-  w0 = segmentStart - first_; 
+  w0 = segmentStart - vertex[0];
   a = -dot(n,w0);
   b = dot(n,dir);
   if (b == 0)
-  {     
+  {
     // ray is parallel to triangle plane
     if (a == 0)                // ray lies in triangle plane
       return 2;
-    else 
+    else
       return 0;             // ray disjoint from plane
   }
 
@@ -133,15 +116,23 @@ int TriangleBasic::lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd,
   // for a segment, also test if (r > 1.0) => no intersect
   if (r > 1.0)                   // ray goes away from triangle
     return 0;                  // => no intersect
+  if(intersectionParameter)
+    *intersectionParameter = r;
 
-  *intersectionPoint = segmentStart + r * dir;           // intersect point of ray and plane
+  if(intersectionPoint)
+  {
+    *intersectionPoint = segmentStart + r * dir;           // intersect point of ray and plane
+    w = *intersectionPoint - vertex[0];
+  }
+  else
+    w = segmentStart + r * dir - vertex[0];
 
   // is intersectionPoint inside T?
   double uu, uv, vv, wu, wv, D;
   uu = dot(u,u);
   uv = dot(u,v);
   vv = dot(v,v);
-  w = *intersectionPoint - first_;
+
   wu = dot(w,u);
   wv = dot(w,v);
   D = uv * uv - uu * vv;
@@ -155,14 +146,20 @@ int TriangleBasic::lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd,
   if (t < 0.0 || (s + t) > 1.0)  // intersectionPoint is outside T
     return 0;
 
+  if(barycentricWeight)
+  {
+    barycentricWeight[0] = 1 - s - t;
+    barycentricWeight[1] = s;
+    barycentricWeight[2] = t;
+  }
   return 1;                      // intersectionPoint is in T
 }
 
-void TriangleBasic::render()
+void TriangleBasic::render() const
 {
-   Vec3d a = first_;
-   Vec3d b = second_;
-   Vec3d c = third_;
+   Vec3d a = vertex[0];
+   Vec3d b = vertex[1];
+   Vec3d c = vertex[2];
 
    glBegin(GL_TRIANGLES);
      glVertex3f(a[0],a[1],a[2]);
@@ -171,11 +168,11 @@ void TriangleBasic::render()
    glEnd();
 }
 
-void TriangleBasic::renderEdges()
+void TriangleBasic::renderEdges() const
 {
-   Vec3d a = first_;
-   Vec3d b = second_;
-   Vec3d c = third_;
+   Vec3d a = vertex[0];
+   Vec3d b = vertex[1];
+   Vec3d c = vertex[2];
 
    glBegin(GL_LINES);
      glVertex3f(a[0],a[1],a[2]);
@@ -188,40 +185,30 @@ void TriangleBasic::renderEdges()
 }
 
 template<class TriangleClass>
-void makeUniqueList(vector<TriangleClass*> &triangleList, vector<TriangleClass*> & uniqueList)
+void makeUniqueList(const vector<TriangleClass*> & triangleList, vector<TriangleClass*> & uniqueList)
 {
-  uniqueList.clear();
-  set<int> indices;
-  unsigned int ssize = triangleList.size();
-  for(unsigned int i=0; i<ssize; i++)
-  {
-    int index = triangleList[i]->index();
-    if (indices.find(index) == indices.end())
-    {
-      indices.insert(index);
-      uniqueList.push_back(triangleList[i]);
-    }
-  }
+  uniqueList = triangleList;
+  makeUniqueList(uniqueList);
 }
 
 template<class TriangleClass>
-void makeUniqueList(vector<TriangleClass*> &triangleList)
+void makeUniqueList(vector<TriangleClass*> & triangleList)
 {
-  vector<TriangleClass*> uniqueList;
-  makeUniqueList(triangleList,uniqueList);
-  triangleList = uniqueList;
+  sort(triangleList.begin(), triangleList.end());
+  auto newEnd = unique(triangleList.begin(), triangleList.end());
+  triangleList.resize(distance(triangleList.begin(), newEnd));
 }
 
 void TriangleWithCollisionInfo::ComputeCollisionInfo()
 {
-  // first, determine the affine transformation that maps the triangle into xy plane, 
+  // first, determine the affine transformation that maps the triangle into xy plane,
   // with first vertex to the origin,
-  // second vertex on the positive x-axis, and 
-  // with a positive second coordinate for the third vertex 
+  // second vertex on the positive x-axis, and
+  // with a positive second coordinate for the third vertex
 
-  Vec3d p0Temp = first_;
-  Vec3d p1Temp = second_;
-  Vec3d p2Temp = third_;
+  Vec3d p0Temp = vertex[0];
+  Vec3d p1Temp = vertex[1];
+  Vec3d p2Temp = vertex[2];
 
   double sideLengths[3] = { len(p1Temp-p0Temp), len(p2Temp-p1Temp), len(p0Temp-p2Temp) };
 
@@ -237,7 +224,7 @@ void TriangleWithCollisionInfo::ComputeCollisionInfo()
     }
   }
 
-  if ((longestIndex < 0) || (longestIndex > 2))
+  if (longestIndex < 0)
   {
     // this should never happen
     printf("Warning: an internal index is negative.\n");
@@ -248,9 +235,9 @@ void TriangleWithCollisionInfo::ComputeCollisionInfo()
 
   if (longestIndex == 0)
   {
-    p0 = p0Temp;    
-    p1 = p1Temp;    
-    p2 = p2Temp;    
+    p0 = p0Temp;
+    p1 = p1Temp;
+    p2 = p2Temp;
 
     permutation[0] = 0;
     permutation[1] = 1;
@@ -262,9 +249,9 @@ void TriangleWithCollisionInfo::ComputeCollisionInfo()
   else
   if (longestIndex == 1)
   {
-    p0 = p1Temp;    
-    p1 = p2Temp;    
-    p2 = p0Temp;    
+    p0 = p1Temp;
+    p1 = p2Temp;
+    p2 = p0Temp;
 
     permutation[0] = 1;
     permutation[1] = 2;
@@ -273,12 +260,11 @@ void TriangleWithCollisionInfo::ComputeCollisionInfo()
     permutation[4] = 5;
     permutation[5] = 3;
   }
-  else
-  //if (longestIndex == 2)
+  else // (longestIndex == 2)
   {
-    p0 = p2Temp;    
-    p1 = p0Temp;    
-    p2 = p1Temp;    
+    p0 = p2Temp;
+    p1 = p0Temp;
+    p2 = p1Temp;
 
     permutation[0] = 2;
     permutation[1] = 0;
@@ -298,9 +284,9 @@ void TriangleWithCollisionInfo::ComputeCollisionInfo()
 
   // now, p0,p1,p2 have been renumbered and permutation has been set
 
-  sidea = len(p1-p0);
-  sideb = len(p2-p1);
-  sidec = len(p0-p2);
+  sidea = sideLengths[permutation[0]]; // p1-p0
+  sideb = sideLengths[permutation[1]]; // p2-p1
+  sidec = sideLengths[permutation[2]]; // p0-p2
 
   Vec3d q1T,q2T,q3T;
 
@@ -311,10 +297,7 @@ void TriangleWithCollisionInfo::ComputeCollisionInfo()
   q2T = cross(q3T,q1T);
 
   // assemble matrix Q
-  Mat3d QT = Mat3d(q1T[0],q2T[0],q3T[0],
-                   q1T[1],q2T[1],q3T[1],
-                   q1T[2],q2T[2],q3T[2]);
-  Q = trans(QT);
+  Q = Mat3d(q1T, q2T, q3T); // three rows
 
   x0 = (-1)*Q*p0;
 
@@ -372,15 +355,15 @@ void TriangleWithCollisionInfo::ComputeCollisionInfo()
   // s1 equals n1 rotate 90 deg clockwise
   Vec2d s1 = Vec2d(n1[1],-n1[0]);
   S1 = Vec3d(s1[0],s1[1],-dot(P1,s1));
-  N11 = Vec3d(n1[0],n1[1],-dot(P1,n1));   
-  N12 = Vec3d(n1[0],n1[1],-dot(P2,n1));   
+  N11 = Vec3d(n1[0],n1[1],-dot(P1,n1));
+  N12 = Vec3d(n1[0],n1[1],-dot(P2,n1));
 
-  Vec2d n2 = norm(P0-P2);   
+  Vec2d n2 = norm(P0-P2);
   // s2 equals n2 rotate 90 deg clockwise
   Vec2d s2 = Vec2d(n2[1],-n2[0]);
   S2 = Vec3d(s2[0],s2[1],-dot(P2,s2));
-  N21 = Vec3d(n2[0],n2[1],-dot(P2,n2));   
-  N22 = Vec3d(n2[0],n2[1],-dot(P0,n2));   
+  N21 = Vec3d(n2[0],n2[1],-dot(P2,n2));
+  N22 = Vec3d(n2[0],n2[1],-dot(P0,n2));
 }
 
 #undef __VERSION_WITH_BARYCENTRIC_COORDS_JNB_CMU__
@@ -389,33 +372,32 @@ void TriangleWithCollisionInfo::ComputeCollisionInfo()
 #include "triangle-closestPoint.cpp"
 
 TriangleWithCollisionInfoAndPseudoNormals::TriangleWithCollisionInfoAndPseudoNormals
-(Vec3d first_g, Vec3d second_g, Vec3d third_g, Vec3d * pseudoNormals): TriangleWithCollisionInfo(first_g,second_g,third_g)
-{ 
-  ComputeCollisionInfo(); 
-  pseudoNormal_[0] = pseudoNormals[0]; 
-  pseudoNormal_[1] = pseudoNormals[1]; 
-  pseudoNormal_[2] = pseudoNormals[2]; 
-  pseudoNormal_[3] = pseudoNormals[3]; 
-  pseudoNormal_[4] = pseudoNormals[4]; 
-  pseudoNormal_[5] = pseudoNormals[5]; 
-  pseudoNormal_[6] = pseudoNormals[6]; 
-
-  pseudoClosestPosition_[0] = first_;
-  pseudoClosestPosition_[1] = second_;
-  pseudoClosestPosition_[2] = third_;
-  pseudoClosestPosition_[3] = 0.5 * (first_ + second_);
-  pseudoClosestPosition_[4] = 0.5 * (second_ + third_);
-  pseudoClosestPosition_[5] = 0.5 * (third_ + first_);
-  pseudoClosestPosition_[6] = 1.0 / 3 * (first_ + second_ + third_);
+(const Vec3d & first_g, const Vec3d & second_g, const Vec3d & third_g, const Vec3d pseudoNormals[7]): TriangleWithCollisionInfo(first_g,second_g,third_g)
+{
+  ComputeCollisionInfo();
+  pseudoNormal_[0] = pseudoNormals[0];
+  pseudoNormal_[1] = pseudoNormals[1];
+  pseudoNormal_[2] = pseudoNormals[2];
+  pseudoNormal_[3] = pseudoNormals[3];
+  pseudoNormal_[4] = pseudoNormals[4];
+  pseudoNormal_[5] = pseudoNormals[5];
+  pseudoNormal_[6] = pseudoNormals[6];
+
+  pseudoClosestPosition_[0] = vertex[0];
+  pseudoClosestPosition_[1] = vertex[1];
+  pseudoClosestPosition_[2] = vertex[2];
+  pseudoClosestPosition_[3] = 0.5 * (vertex[0] + vertex[1]);
+  pseudoClosestPosition_[4] = 0.5 * (vertex[1] + vertex[2]);
+  pseudoClosestPosition_[5] = 0.5 * (vertex[2] + vertex[0]);
+  pseudoClosestPosition_[6] = 1.0 / 3 * (vertex[0] + vertex[1] + vertex[2]);
 }
 
-template void makeUniqueList<TriangleBasic>(vector<TriangleBasic*> &triangleList, vector<TriangleBasic*> & uniqueList);
-template void makeUniqueList<TriangleBasic>(vector<TriangleBasic*> &triangleList);
+template void makeUniqueList<TriangleBasic>(const vector<TriangleBasic*> & triangleList, vector<TriangleBasic*> & uniqueList);
+template void makeUniqueList<TriangleBasic>(vector<TriangleBasic*> & triangleList);
 
-template void makeUniqueList<TriangleWithCollisionInfo>(vector<TriangleWithCollisionInfo*> &triangleList, vector<TriangleWithCollisionInfo*> & uniqueList);
-template void makeUniqueList<TriangleWithCollisionInfo>(vector<TriangleWithCollisionInfo*> &triangleList);
+template void makeUniqueList<TriangleWithCollisionInfo>(const vector<TriangleWithCollisionInfo*> & triangleList, vector<TriangleWithCollisionInfo*> & uniqueList);
+template void makeUniqueList<TriangleWithCollisionInfo>(vector<TriangleWithCollisionInfo*> & triangleList);
 
-template void makeUniqueList<TriangleWithCollisionInfoAndPseudoNormals>(vector<TriangleWithCollisionInfoAndPseudoNormals*> &triangleList, vector<TriangleWithCollisionInfoAndPseudoNormals*> & uniqueList);
-template void makeUniqueList<TriangleWithCollisionInfoAndPseudoNormals>(vector<TriangleWithCollisionInfoAndPseudoNormals*> &triangleList);
+template void makeUniqueList<TriangleWithCollisionInfoAndPseudoNormals>(const vector<TriangleWithCollisionInfoAndPseudoNormals*> & triangleList, vector<TriangleWithCollisionInfoAndPseudoNormals*> & uniqueList);
+template void makeUniqueList<TriangleWithCollisionInfoAndPseudoNormals>(vector<TriangleWithCollisionInfoAndPseudoNormals*> & triangleList);
 
-}
diff --git a/src/libobjMesh/triangle.h b/libraries/mesh/triangle.h
similarity index 54%
rename from src/libobjMesh/triangle.h
rename to libraries/mesh/triangle.h
index ab4775ff2e3b3f74139b57cb2050c37fd0fd2ee8..e1844c5a79b854871a64845269ddf8b95307ad92 100644
--- a/src/libobjMesh/triangle.h
+++ b/libraries/mesh/triangle.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -34,41 +38,45 @@
 #ifndef _TRIANGLE_H_
 #define _TRIANGLE_H_
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 #include <math.h>
 #include <vector>
 #include <iostream>
 #include "boundingBox.h"
 #include "minivector.h"
 
-namespace vega
-{
 class TriangleBasic
 {
 public:
 
-  TriangleBasic(Vec3d first_g, Vec3d second_g, Vec3d third_g):
-    first_(first_g), second_(second_g), third_(third_g), index_(0) {}
+  TriangleBasic(const Vec3d & first, const Vec3d & second, const Vec3d & third): index_(0) { vertex[0] = first; vertex[1] = second; vertex[2] = third; }
 
   // accessors
-  inline Vec3d first() {return first_ ;}
-  inline Vec3d second() {return second_ ;}
-  inline Vec3d third() {return third_ ;}
-  inline int index() { return index_; }
+  inline const Vec3d & first() const {return vertex[0] ;}
+  inline const Vec3d & second() const {return vertex[1] ;}
+  inline const Vec3d & third() const {return vertex[2];}
+  inline const Vec3d & operator[] (size_t i) const { return vertex[i]; }
+  inline int index() const { return index_; }
 
   // index can be used to keep track of what triangle this is
   inline void setIndex(int index_) { this->index_ = index_; }
 
   // squared 3d distance to a point
-  double distanceToPoint2(Vec3d point) { std::cout << "Unimplemented..." << std::endl; return 1;} // unimplemented (it is implemented in class "TriangleWithCollisionInfo" below)
-  double distanceToPoint(Vec3d point) { return sqrt(distanceToPoint2(point));}
+  double distanceToPoint2(const Vec3d & point) const { std::cout << "Unimplemented..." << std::endl; return 1;} // unimplemented (it is implemented in class "TriangleWithCollisionInfo" below)
+  double distanceToPoint(const Vec3d & point) const { return sqrt(distanceToPoint2(point));}
+
+  // return the triangle normal
+  Vec3d triangleNormal() const { return norm(cross(vertex[1] - vertex[0], vertex[2] - vertex[0])); }
 
-  bool doesIntersectBox(BoundingBox & bbox);
+  // whether a point is inside/outside/on the triangle
+  // return: 1 = point is outside the triangle (assuming normal points outward)
+  //        -1 = point is inside
+  //         0 = point is on the triangle
+  int pointSide(const Vec3d & point) const;
 
-  int lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd, Vec3d * intersectionPoint);
+  bool doesIntersectBox(const BoundingBox & bbox) const;
+
+  // int lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd, Vec3d * intersectionPoint);
+  int lineSegmentIntersection(const Vec3d & segmentStart, const Vec3d & segmentEnd, Vec3d * intersectionPoint = NULL, double * t = NULL, double * barycentricWeight = NULL) const;
     //    Output: intersection point (when it exists)
     //    Return: -1 = triangle is degenerate (a segment or point)
     //             0 = disjoint (no intersect)
@@ -76,28 +84,27 @@ public:
     //             2 = are in the same plane
 
 
-  void render();
-  void renderEdges();
+  void render() const;
+  void renderEdges() const;
 
-  Vec3d getBarycentricLocation(double alpha, double beta, double gamma) { return alpha * first_ + beta * second_ + gamma * third_; }
+  Vec3d getBarycentricLocation(double alpha, double beta, double gamma) const { return alpha * vertex[0] + beta * vertex[1] + gamma * vertex[2]; }
 
 protected:
-
-  Vec3d first_, second_, third_;
+  Vec3d vertex[3];
   int index_;
 };
 
 // makes the triangle list unique, using the "index_" field of the <TriangleClass>
 template<class TriangleClass>
-void makeUniqueList(std::vector<TriangleClass*> &triangleList, std::vector<TriangleClass*> & uniqueList);
+void makeUniqueList(const std::vector<TriangleClass*> & triangleList, std::vector<TriangleClass*> & uniqueList);
 
 template<class TriangleClass>
-void makeUniqueList(std::vector<TriangleClass*> &triangleList); // overwrites triangleList with the unique list
+void makeUniqueList(std::vector<TriangleClass*> & triangleList); // overwrites triangleList with the unique list
 
 class TriangleWithCollisionInfo : public TriangleBasic
 {
 public:
-  TriangleWithCollisionInfo(Vec3d first_g, Vec3d second_g, Vec3d third_g):
+  TriangleWithCollisionInfo(const Vec3d & first_g, const Vec3d & second_g, const Vec3d & third_g):
         TriangleBasic(first_g,second_g,third_g) { ComputeCollisionInfo(); }
 
   // squared 3d distance to a point
@@ -109,8 +116,8 @@ public:
   //  4: edge among 12
   //  5: edge among 20
   //  6: the face itself
-  double distanceToPoint2(Vec3d point, int * closestFeature);
-  double distanceToPoint2(Vec3d point, int * closestFeature, double * alpha, double * beta, double * gamma); // also returns the barycentric coordinates of the closest point
+  double distanceToPoint2(const Vec3d & point, int * closestFeature) const;
+  double distanceToPoint2(const Vec3d & point, int * closestFeature, double * alpha, double * beta, double * gamma) const; // also returns the barycentric coordinates of the closest point
 
 protected:
 
@@ -131,21 +138,21 @@ class TriangleWithCollisionInfoAndPseudoNormals : public TriangleWithCollisionIn
 {
 public:
   // pseudoNormals is a caller-provided list of 7 normals, indexed the same as closest features below
-  TriangleWithCollisionInfoAndPseudoNormals(Vec3d first_g, Vec3d second_g, Vec3d third_g, Vec3d * pseudoNormals);
+  TriangleWithCollisionInfoAndPseudoNormals(const Vec3d & first_g, const Vec3d & second_g, const Vec3d & third_g, const Vec3d pseudoNormals[7]);
 
-  inline Vec3d pseudoNormal(int closestFeature) { return pseudoNormal_[closestFeature]; }
-  inline Vec3d interpolatedVertexPseudoNormal(double alpha, double beta, double gamma) { return norm(alpha * pseudoNormal_[0] + beta * pseudoNormal_[1] + gamma * pseudoNormal_[2]); }
+  inline const Vec3d & pseudoNormal(int closestFeature) const { return pseudoNormal_[closestFeature]; }
+  inline Vec3d interpolatedVertexPseudoNormal(double alpha, double beta, double gamma) const { return norm(alpha * pseudoNormal_[0] + beta * pseudoNormal_[1] + gamma * pseudoNormal_[2]); }
 
   // for vertices, returns the vertex itself
   // for edges, returns the midpoint of the edge (for the purposes of distance sign test, this could be any point on the edge)
   // for faces, returns the face centroid
-  inline Vec3d pseudoClosestPosition(int closestFeature) { return pseudoClosestPosition_[closestFeature]; }
+  inline const Vec3d & pseudoClosestPosition(int closestFeature) const { return pseudoClosestPosition_[closestFeature]; }
 
 protected:
   Vec3d pseudoNormal_[7];
   Vec3d pseudoClosestPosition_[7];
 
 };
-}
+
 #endif
 
diff --git a/src/libobjMesh/tribox3.cpp b/libraries/mesh/tribox3.cpp
similarity index 75%
rename from src/libobjMesh/tribox3.cpp
rename to libraries/mesh/tribox3.cpp
index eb5b51410c2ab593225ff9f0fa320ca85d2c57b4..bed092793332daa369c886db6dd2b4f02f266605 100644
--- a/src/libobjMesh/tribox3.cpp
+++ b/libraries/mesh/tribox3.cpp
@@ -1,209 +1,198 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Somya Sharma, Jernej Barbic                             *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <algorithm>
-#include <stdio.h>
-#include <math.h>
-#include <iostream>
-#include "matrixMultiplyMacros.h"
-using namespace std;
-
-namespace vega
-{
-/*
-  Tests if a 3D triangle overlaps with a 3D box.
-
-  INPUT: center of box, box half sizes (in each of the three dimensions), and the three triangle vertices v0, v1, v2.
-  OUTPUT: whether triangle intersects the box or not
-  Note: all entries in boxHalfSize must be >= 0.
-  Note: lower-left-front corner is boxCenter - boxHalfSize, upper-right-back corner is boxCenter + boxHalfSize.
-*/
-
-bool triBoxOverlap(double boxcenter[3], double boxhalfsize[3], double triverts[3][3])
-{
-  bool overlap = true;
-
-  // translate so that box's center coincides with the origin
-  VECTOR_SUBTRACTEQUAL3(triverts[0], boxcenter);
-  VECTOR_SUBTRACTEQUAL3(triverts[1], boxcenter);
-  VECTOR_SUBTRACTEQUAL3(triverts[2], boxcenter);
-
-  // test AABB against minimal AABB around the triangle
-  // 3 tests
-
-  double minx = min( min(triverts[0][0], triverts[1][0]), triverts[2][0] );
-  double miny = min( min(triverts[0][1], triverts[1][1]), triverts[2][1] );
-  double minz = min( min(triverts[0][2], triverts[1][2]), triverts[2][2] );
-  double maxx = max( max(triverts[0][0], triverts[1][0]), triverts[2][0] );
-  double maxy = max( max(triverts[0][1], triverts[1][1]), triverts[2][1] );
-  double maxz = max( max(triverts[0][2], triverts[1][2]), triverts[2][2] );
-
-  if ( ( boxhalfsize[0] < minx ) || ( maxx < -boxhalfsize[0] ) || ( boxhalfsize[1] < miny ) || ( maxy < -boxhalfsize[1] ) || ( boxhalfsize[2] < minz ) || ( maxz < -boxhalfsize[2] ) )
-  {
-    //no overlap
-    overlap = false;
-    return overlap;
-  }
-
-  // normals of AABB at origin
-
-  double e0[3] = { 1, 0, 0 };
-  double e1[3] = { 0, 1, 0 };
-  double e2[3] = { 0, 0, 1 };
-
-  double f0[3];
-  double f1[3];
-  double f2[3];
-
-  VECTOR_SUBTRACT3(triverts[1], triverts[0], f0);
-  VECTOR_SUBTRACT3(triverts[2], triverts[1], f1);
-  VECTOR_SUBTRACT3(triverts[0], triverts[2], f2);
-
-  double * e[3] = { e0, e1, e2 };
-  double * f[3] = { f0, f1, f2 };
-
-  // aij = ej X fj
-  // 9 tests
-  double a[3];
-  double p0, p1, p2;
-  double radius;
-  #define LOOP1(i,j)\
-    {\
-      VECTOR_CROSS_PRODUCT(e[i], f[j], a);\
-      p0 = VECTOR_DOT_PRODUCT3(a, triverts[0]);\
-      p1 = VECTOR_DOT_PRODUCT3(a, triverts[1]);\
-      p2 = VECTOR_DOT_PRODUCT3(a, triverts[2]);\
-\
-      radius = ( boxhalfsize[0] * fabs(a[0]) ) + ( boxhalfsize[1] * fabs(a[1]) ) + ( boxhalfsize[2] * fabs(a[2]) );\
-\
-      if ( (min( min(p0, p1), p2 ) > radius) || (max( max(p0, p1), p2 ) < -radius ) )\
-      {\
-        overlap = false;\
-        return overlap;\
-      }\
-    }\
-
-  LOOP1(0, 0);
-  LOOP1(0, 1);
-  LOOP1(0, 2);
-  LOOP1(1, 0);
-  LOOP1(1, 1);
-  LOOP1(1, 2);
-  LOOP1(2, 0);
-  LOOP1(2, 1);
-  LOOP1(2, 2);
-  #undef LOOP1
-
-/*
-  // aij = ej X fj
-  // 9 tests
-  for ( int i = 0; i <= 2; i ++ )
-  {
-    for ( int j = 0; j <= 2; j ++ )
-    {
-      double a[3];
-      VECTOR_CROSS_PRODUCT(e[i], f[j], a);
-      double p0 = VECTOR_DOT_PRODUCT3(a, triverts[0]);
-      double p1 = VECTOR_DOT_PRODUCT3(a, triverts[1]);
-      double p2 = VECTOR_DOT_PRODUCT3(a, triverts[2]);
-
-      double radius = ( boxhalfsize[0] * fabs(a[0]) ) + ( boxhalfsize[1] * fabs(a[1]) ) + ( boxhalfsize[2] * fabs(a[2]) );
-
-      if ( (min( min(p0, p1), p2 ) > radius) || (max( max(p0, p1), p2 ) < -radius ) )
-      {
-        overlap = false;
-        return overlap;
-      }
-    }
-  }
-*/
-
-  // plane and AABB overlap test
-  // 1 test
-
-  // triangle normal
-  double n[3];
-  VECTOR_CROSS_PRODUCT(f0, f1, n);
-  double len2 = n[0] * n[0] + n[1] * n[1] + n[2] * n[2];
-  double invlen = 1.0 / sqrt(len2);
-  n[0] *= invlen;
-  n[1] *= invlen;
-  n[2] *= invlen;
-
-  // distance of plane from the origin
-  double planeDist = -VECTOR_DOT_PRODUCT3(n, triverts[0]);
-
-  // get nearest and farthest corners
-  double nearestPoint[3];
-  double farthestPoint[3];
-
-  #define LOOP2(i)\
-    if ( n[i] < 0 )\
-    {\
-      nearestPoint[i] = boxhalfsize[i];\
-      farthestPoint[i] = - boxhalfsize[i];\
-    }\
-    else\
-    {\
-      nearestPoint[i] = - boxhalfsize[i];\
-      farthestPoint[i] = boxhalfsize[i];\
-    }\
-
-  LOOP2(0);
-  LOOP2(1);
-  LOOP2(2);
-  #undef LOOP2
-
-/*
-  for ( int i = 0; i <= 2; i++ )
-  {
-    if ( n[i] < 0 )
-    {
-      nearestPoint[i] = boxhalfsize[i];
-      farthestPoint[i] = - boxhalfsize[i];
-    }
-    else
-    {
-      nearestPoint[i] = - boxhalfsize[i];
-      farthestPoint[i] = boxhalfsize[i];
-    }
-  }
-*/
-
-  if ( VECTOR_DOT_PRODUCT3(n, nearestPoint) + planeDist > 0 )
-  {
-    overlap = false;
-    return overlap;
-  }
-
-  overlap = ( VECTOR_DOT_PRODUCT3(n, farthestPoint) + planeDist >= 0 );
-
-  return overlap;
-}
-
-}
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Somya Sharma, Jernej Barbic                             *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <math.h>
+#include <iostream>
+#include <algorithm>
+#include "matrixMultiplyMacros.h"
+using namespace std;
+
+/*
+  Tests if a 3D triangle overlaps with a 3D box.
+
+  INPUT: center of box, box half sizes (in each of the three dimensions), and the three triangle vertices v0, v1, v2.
+  OUTPUT: whether triangle intersects the box or not
+  Note: all entries in boxHalfSize must be >= 0.
+  Note: lower-left-front corner is boxCenter - boxHalfSize, upper-right-back corner is boxCenter + boxHalfSize.
+*/
+
+bool triBoxOverlap(const double boxcenter[3], const double boxhalfsize[3], const double trivert0[3], const double trivert1[3], const double trivert2[3])
+{
+  // translate so that box's center coincides with the origin
+  double triverts[3][3];
+  VECTOR_SUBTRACT3(trivert0, boxcenter, triverts[0]);
+  VECTOR_SUBTRACT3(trivert1, boxcenter, triverts[1]);
+  VECTOR_SUBTRACT3(trivert2, boxcenter, triverts[2]);
+
+  // test AABB against minimal AABB around the triangle
+  // 3 tests
+
+  double minx = min( min(triverts[0][0], triverts[1][0]), triverts[2][0] );
+  double miny = min( min(triverts[0][1], triverts[1][1]), triverts[2][1] );
+  double minz = min( min(triverts[0][2], triverts[1][2]), triverts[2][2] );
+  double maxx = max( max(triverts[0][0], triverts[1][0]), triverts[2][0] );
+  double maxy = max( max(triverts[0][1], triverts[1][1]), triverts[2][1] );
+  double maxz = max( max(triverts[0][2], triverts[1][2]), triverts[2][2] );
+
+  if ( ( boxhalfsize[0] < minx ) || ( maxx < -boxhalfsize[0] ) || ( boxhalfsize[1] < miny ) || ( maxy < -boxhalfsize[1] ) || ( boxhalfsize[2] < minz ) || ( maxz < -boxhalfsize[2] ) )
+    return false; //no overlap
+
+  // normals of AABB at origin
+
+  double e0[3] = { 1, 0, 0 };
+  double e1[3] = { 0, 1, 0 };
+  double e2[3] = { 0, 0, 1 };
+
+  double f0[3];
+  double f1[3];
+  double f2[3];
+
+  VECTOR_SUBTRACT3(triverts[1], triverts[0], f0);
+  VECTOR_SUBTRACT3(triverts[2], triverts[1], f1);
+  VECTOR_SUBTRACT3(triverts[0], triverts[2], f2);
+
+  double * e[3] = { e0, e1, e2 };
+  double * f[3] = { f0, f1, f2 };
+
+  // aij = ej X fj
+  // 9 tests
+  double a[3];
+  double p0, p1, p2;
+  double radius;
+  #define LOOP1(i,j)\
+    {\
+      VECTOR_CROSS_PRODUCT(e[i], f[j], a);\
+      p0 = VECTOR_DOT_PRODUCT3(a, triverts[0]);\
+      p1 = VECTOR_DOT_PRODUCT3(a, triverts[1]);\
+      p2 = VECTOR_DOT_PRODUCT3(a, triverts[2]);\
+      radius = ( boxhalfsize[0] * fabs(a[0]) ) + ( boxhalfsize[1] * fabs(a[1]) ) + ( boxhalfsize[2] * fabs(a[2]) );\
+      if ( (min( min(p0, p1), p2 ) > radius) || (max( max(p0, p1), p2 ) < -radius ) )\
+        return false;\
+    }
+
+  LOOP1(0, 0);
+  LOOP1(0, 1);
+  LOOP1(0, 2);
+  LOOP1(1, 0);
+  LOOP1(1, 1);
+  LOOP1(1, 2);
+  LOOP1(2, 0);
+  LOOP1(2, 1);
+  LOOP1(2, 2);
+  #undef LOOP1
+
+/*
+  // aij = ej X fj
+  // 9 tests
+  for ( int i = 0; i <= 2; i ++ )
+  {
+    for ( int j = 0; j <= 2; j ++ )
+    {
+      double a[3];
+      VECTOR_CROSS_PRODUCT(e[i], f[j], a);
+      double p0 = VECTOR_DOT_PRODUCT3(a, triverts[0]);
+      double p1 = VECTOR_DOT_PRODUCT3(a, triverts[1]);
+      double p2 = VECTOR_DOT_PRODUCT3(a, triverts[2]);
+
+      double radius = ( boxhalfsize[0] * fabs(a[0]) ) + ( boxhalfsize[1] * fabs(a[1]) ) + ( boxhalfsize[2] * fabs(a[2]) );
+
+      if ( (min( min(p0, p1), p2 ) > radius) || (max( max(p0, p1), p2 ) < -radius ) )
+      {
+        overlap = false;
+        return overlap;
+      }
+    }
+  }
+*/
+
+  // plane and AABB overlap test
+  // 1 test
+
+  // triangle normal
+  double normal[3];
+  VECTOR_CROSS_PRODUCT(f0, f1, normal);
+  // no need to normalize the normal as we only check the sign of expressions in the calculations that depend on normal
+  // this also makes the code robust against degenerate triangles with zero-length normals
+  //double len2 = normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2];
+  //double invlen = 1.0 / sqrt(len2);
+  //normal[0] *= invlen;
+  //normal[1] *= invlen;
+  //normal[2] *= invlen;
+
+  // distance of plane from the origin
+  double planeDist = -VECTOR_DOT_PRODUCT3(normal, triverts[0]);
+
+  // get nearest and farthest corners
+  double nearestPoint[3];
+  double farthestPoint[3];
+
+  #define LOOP2(i)\
+    if ( normal[i] < 0 )\
+    {\
+      nearestPoint[i] = boxhalfsize[i];\
+      farthestPoint[i] = - boxhalfsize[i];\
+    }\
+    else\
+    {\
+      nearestPoint[i] = - boxhalfsize[i];\
+      farthestPoint[i] = boxhalfsize[i];\
+    }\
+
+  LOOP2(0);
+  LOOP2(1);
+  LOOP2(2);
+  #undef LOOP2
+
+/*
+  for ( int i = 0; i <= 2; i++ )
+  {
+    if ( n[i] < 0 )
+    {
+      nearestPoint[i] = boxhalfsize[i];
+      farthestPoint[i] = - boxhalfsize[i];
+    }
+    else
+    {
+      nearestPoint[i] = - boxhalfsize[i];
+      farthestPoint[i] = boxhalfsize[i];
+    }
+  }
+*/
+
+  if ( VECTOR_DOT_PRODUCT3(normal, nearestPoint) + planeDist > 0 )
+    return false; // no overlap
+
+  bool overlap = ( VECTOR_DOT_PRODUCT3(normal, farthestPoint) + planeDist >= 0 );
+  return overlap;
+}
+
diff --git a/src/libobjMesh/tribox3.h b/libraries/mesh/tribox3.h
similarity index 73%
rename from src/libobjMesh/tribox3.h
rename to libraries/mesh/tribox3.h
index 738a2996a8f578e34634d7bcb5c118e9de008b97..0ccdbde32521f57cdc0a3c60ec01d286281c7e16 100644
--- a/src/libobjMesh/tribox3.h
+++ b/libraries/mesh/tribox3.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "mesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Somya Sharma, Jernej Barbic                             *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -39,9 +43,7 @@ e.
 #ifndef _TRIBOX3_H_
 #define _TRIBOX3_H_
 
-namespace vega
-{
-bool triBoxOverlap(double boxcenter[3],double boxhalfsize[3],double triverts[3][3]);
-}
+bool triBoxOverlap(const double boxcenter[3], const double boxhalfsize[3], const double trivert0[3], const double trivert1[3], const double trivert2[3] );
+
 #endif
 
diff --git a/libraries/mesh/verticesInfo.cpp b/libraries/mesh/verticesInfo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..41d00c56862fc9dee070a070b9b491042dd01a4b
--- /dev/null
+++ b/libraries/mesh/verticesInfo.cpp
@@ -0,0 +1,180 @@
+/*
+  This code is based on code from the Geometric Tools library,
+  which is licensed under a boost license.
+  Such usage is permitted by the boost license; for details,
+  please see the boost license below.
+*/
+
+// Geometric Tools, LLC
+// Copyright (c) 1998-2014
+// Distributed under the Boost Software License, Version 1.0.
+// http://www.boost.org/LICENSE_1_0.txt
+// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
+
+/*************************************************************************
+ *                                                                       *
+ * We release our improvements to the wildMagic code under our standard  *
+ * Vega FEM license, as follows:                                         *
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "improvements to the wildMagic library" , Copyright (C) 2018 USC      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "verticesInfo.h"
+#include <cassert>
+#include <cstring>
+#include <iostream>
+#include <cmath>
+using namespace std;
+
+VerticesInformation getVerticesInformation(const std::vector<Vec3d> & vertices, double epsilon)
+{
+  VerticesInformation info;
+  assert(epsilon >= 0 && vertices.size() > 0);
+  info.extremeCCW = false;
+
+  // Compute the axis-aligned bounding box for the vertices.  Keep track
+  // of the indices in the vertices for the current min and max.
+  int indexMin[3] = {0,0,0}, indexMax[3] = {0,0,0};
+  vertices[0].convertToArray(info.min);
+  vertices[0].convertToArray(info.max);
+
+  for(size_t i = 1; i < vertices.size(); ++i)
+  {
+    for(int j = 0; j < 3; ++j)
+    {
+      if (vertices[i][j] < info.min[j])
+      {
+        info.min[j] = vertices[i][j];
+        indexMin[j] = i;
+      }
+      else if (vertices[i][j] > info.max[j])
+      {
+        info.max[j] = vertices[i][j];
+        indexMax[j] = i;
+      }
+    }
+  }
+
+  // Determine the maximum range for the bounding box.
+  info.maxRange = info.max[0] - info.min[0];
+  info.extreme[0] = indexMin[0];
+  info.extreme[1] = indexMax[0];
+  double range = info.max[1] - info.min[1];
+  if (range > info.maxRange)
+  {
+    info.maxRange = range;
+    info.extreme[0] = indexMin[1];
+    info.extreme[1] = indexMax[1];
+  }
+  range = info.max[2] - info.min[2];
+  if (range > info.maxRange)
+  {
+    info.maxRange = range;
+    info.extreme[0] = indexMin[2];
+    info.extreme[1] = indexMax[2];
+  }
+
+  // The origin is either the point of minimum x-value, point of
+  // minimum y-value, or point of minimum z-value.
+  info.origin = vertices[info.extreme[0]];
+
+  // Test whether the point set is (nearly) a point.
+  if (info.maxRange < epsilon)
+  {
+    info.dimension = 0;
+    for(int j = 0; j < 3; ++j)
+    {
+      info.extreme[j + 1] = info.extreme[0];
+      info.direction[j] = Vec3d(0.);
+    }
+    return info;
+  }
+
+  // Test whether the point set is (nearly) a line segment.
+  info.direction[0] = vertices[info.extreme[1]] - info.origin;
+  info.direction[0].normalize();
+  double maxDistance = 0.;
+  info.extreme[2] = info.extreme[0];
+  for(size_t i = 0; i < vertices.size(); ++i)
+  {
+    Vec3d diff = vertices[i] - info.origin;
+    double dotProduct = ::dot(info.direction[0], diff);
+    Vec3d proj = diff - dotProduct * info.direction[0];
+    double distance = len(proj);
+    if (distance > maxDistance)
+    {
+      maxDistance = distance;
+      info.extreme[2] = i;
+    }
+  }
+
+  if (maxDistance < epsilon*info.maxRange)
+  {
+    info.dimension = 1;
+    info.extreme[2] = info.extreme[1];
+    info.extreme[3] = info.extreme[1];
+    return info;
+  }
+
+  // Test whether the point set is (nearly) a planar polygon.
+  info.direction[1] = vertices[info.extreme[2]] - info.origin;
+  info.direction[1] -= ::dot(info.direction[0], info.direction[1]) * info.direction[0];
+  info.direction[1].normalize();
+  info.direction[2] = cross(info.direction[0], info.direction[1]);
+  maxDistance = 0;
+  double maxSign = 0;
+  info.extreme[3] = info.extreme[0];
+  for(size_t i = 0; i < vertices.size(); ++i)
+  {
+    Vec3d diff = vertices[i] - info.origin;
+    double distance = ::dot(info.direction[2],diff);
+    int sign = distance >= 0 ? 1 : -1;
+    distance = fabs(distance);
+    if (distance > maxDistance)
+    {
+      maxDistance = distance;
+      maxSign = sign;
+      info.extreme[3] = i;
+    }
+  }
+
+  if (maxDistance < epsilon*info.maxRange)
+  {
+    info.dimension = 2;
+    info.extreme[3] = info.extreme[2];
+    return info;
+  }
+
+  info.dimension = 3;
+  assert(epsilon == 0 || maxSign != 0);
+  info.extremeCCW = (maxSign >= 0);
+
+  return info;
+}
diff --git a/libraries/mesh/verticesInfo.h b/libraries/mesh/verticesInfo.h
new file mode 100644
index 0000000000000000000000000000000000000000..941a42fddad94f84c9789e253a58148bf1365894
--- /dev/null
+++ b/libraries/mesh/verticesInfo.h
@@ -0,0 +1,101 @@
+/*
+  This code is based on code from the Geometric Tools library,
+  which is licensed under a boost license.
+  Such usage is permitted by the boost license; for details,
+  please see the boost license below.
+*/
+
+// Geometric Tools, LLC
+// Copyright (c) 1998-2014
+// Distributed under the Boost Software License, Version 1.0.
+// http://www.boost.org/LICENSE_1_0.txt
+// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
+
+/*************************************************************************
+ *                                                                       *
+ * We release our improvements to the wildMagic code under our standard  *
+ * Vega FEM license, as follows:                                         *
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "improvements to the wildMagic library" , Copyright (C) 2018 USC      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef VERTICESINFO_H
+#define VERTICESINFO_H
+
+// handles floating-point queries among vertices
+#include "minivector.h"
+#include <vector>
+
+
+struct VerticesInformation
+{
+  // The intrinsic dimension of the input set. The parameter 'epsilon'
+  // to the VerticesQuery::getInformation function is used to provide a tolerance when
+  // determining the dimension.
+  // if dimension == 0, all points are effectively the same (or very close based on epsilon)
+  // If dimension == 1, all points effectively lie on a line segment.
+  // If dimension == 2, all points effectively line on a plane.
+  // If dimension == 3, the points are not coplanar.
+  int dimension;
+
+  // Axis-aligned bounding box of the input set.
+  double min[3], max[3];
+  // mMaxRange = max(max[0]-min[0], max[1]-min[1], and max[2]-min[2].)
+  double maxRange;
+
+  // The indices that define the maximum dimensional extents.  The
+  // values mExtreme[0] and mExtreme[1] are the indices for the points
+  // that define the largest extent in one of the coordinate axis
+  // directions.
+  // If the dimension is 2, then mExtreme[2] is the index
+  // for the point that generates the largest extent in the direction
+  // perpendicular to the line through the points corresponding to
+  // mExtreme[0] and mExtreme[1].
+  // Furthermore, if the dimension is 3, then mExtreme[3] is the index
+  // for the point that generates the largest extent in the direction
+  // perpendicular to the triangle defined by the other extreme points.
+  int extreme[4];
+  // If dimenstion == 3, the tetrahedron formed by the
+  // points V[extreme0], V[extreme1], V[extreme2], V[extreme3]> is
+  // counterclockwise (positive) if extremeCCW == true.
+  // tet is positive if its vertices satisfy:  ((v1 - v0) x (v2 - v0)) dot (v3 - v0) >= 0
+  bool extremeCCW;
+
+  // Coordinate system.
+  // The origin == vertices[exterme[0].  The
+  // unit-length direction vector is valid only for 0 <= i < d.
+  Vec3d origin;
+  Vec3d direction[3];
+};
+
+VerticesInformation getVerticesInformation(const std::vector<Vec3d> & vertices, double epsilon);
+
+#endif
diff --git a/libraries/mesh/windingNumberTree.cpp b/libraries/mesh/windingNumberTree.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d49c6f2f3cf2a4a50977183572e55e8c531a9dd
--- /dev/null
+++ b/libraries/mesh/windingNumberTree.cpp
@@ -0,0 +1,190 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "windingNumberTree.h"
+#include "triMeshGeo.h"
+#include "triMeshNeighbor.h"
+#include "basicAlgorithms.h"
+#include "containerHelper.h"
+#include <stack>
+#include <cassert>
+using namespace std;
+
+static vector<Vec3i> buildBoundary(const vector<Vec3i> & triangles)
+{
+  vector<Vec3i> boundaryFaces;
+  auto exEdges = getExteriorEdges(triangles);
+  if (exEdges.size() != 0)
+  {
+    int v = exEdges[0][0];
+    for(size_t edgeID = 0; edgeID < exEdges.size(); edgeID++)
+    {
+      boundaryFaces.emplace_back(v, exEdges[edgeID][0], exEdges[edgeID][1]);
+    }
+  }
+  return boundaryFaces;
+}
+
+WindingNumberTree::Node::Node(int d, const TriMeshRef mesh) : depth(d),
+    // We don't build a bounding box out of all triMesh's positions because some vertices might not be used by triangles
+    triangles(mesh.exportTriangles()), bouFaces(buildBoundary(triangles)), bb(mesh.computeTriangleBoundingBox()) {}
+
+void WindingNumberTree::build(const TriMeshRef mesh, int maxDepth)
+{
+  clear();
+
+  numMeshVertices = mesh.numVertices();
+  numMeshTriangles = mesh.numTriangles();
+  assert(numMeshTriangles > 0);
+
+//  vector<int> rootIndices(triMesh.numTriangles());
+//  iota(rootIndices.begin(), rootIndices.end(), 0);
+
+  nodes.emplace_back(0, mesh);
+
+  stack<int> nodeStack;
+  nodeStack.push(0);
+  while(nodeStack.empty() == false)
+  {
+    int nodeID = nodeStack.top();
+    nodeStack.pop();
+
+    int nodeDepth = nodes[nodeID].depth;
+    if (nodeDepth > depth)
+      depth = nodeDepth;
+
+    const int maxNumTrianglesPerNode = 100;
+
+    // if there are fewer triangles than the threshold value, or cap value is larger than #triangles,
+    // or if max depth has been reached,
+    // this node becomes a leaf
+    size_t numNodeTri = nodes[nodeID].triangles.size();
+    if(numNodeTri <= maxNumTrianglesPerNode || (nodes[nodeID].bouFaces.size() >= numNodeTri) || (nodeDepth >= maxDepth))
+    {
+      continue;
+    }
+
+    // compute the longest side (0,1,2) of the node mesh
+    int dim = nodes[nodeID].bb.longestSide().first;
+
+    vector<double> triCen(numNodeTri);
+    TriMeshRef nodeMesh = nodes[nodeID].meshInNode(mesh);
+    for(int i = 0; i < nodeMesh.numTriangles(); i++)
+      triCen[i] = nodeMesh.computeTriangleCentroid(i)[dim];
+
+    double middle = median(triCen.begin(), triCen.end());
+
+    vector<Vec3i> subTriangles[2];
+    for(size_t i = 0;i<numNodeTri;i++)
+    {
+      if(triCen[i] <= middle) // left side
+      {
+        subTriangles[0].push_back(nodes[nodeID].triangles[i]);
+      }
+      else subTriangles[1].push_back(nodes[nodeID].triangles[i]);
+    }
+
+    if (subTriangles[0].size() == 0 || subTriangles[1].size() == 0) continue;
+
+    for(int i = 0; i < 2; i++)
+    {
+      int childrenID = int(nodes.size());
+      nodes[nodeID].childrenIDs[i] = childrenID;
+      nodes.emplace_back(nodeDepth+1, TriMeshRef(mesh.numVertices(), mesh.positions(), subTriangles[i]));
+      nodeStack.push(childrenID);
+    }
+  }
+}
+
+void WindingNumberTree::clear()
+{
+  nodes.clear();
+  depth = 0;
+  numMeshVertices = numMeshTriangles = 0;
+}
+
+double WindingNumberTree::windingNumber(const TriMeshRef mesh, const Vec3d & p) const
+{
+  assert(mesh.numVertices() == numMeshVertices);
+  assert(mesh.numTriangles() == numMeshTriangles);
+  assert(nodes.size() > 0);
+
+  stack<int> nodeStack;
+  nodeStack.push(0);
+  double sum = 0.0;
+  while(!nodeStack.empty())
+  {
+    int nodeID = nodeStack.top();
+    nodeStack.pop();
+    const auto & node = nodes[nodeID];
+
+    if (node.bb.checkInside(p))
+    {
+      if (node.childrenIDs[0] >= 0)
+      {
+        nodeStack.push(node.childrenIDs[0]);
+        nodeStack.push(node.childrenIDs[1]);
+      }
+      else
+      {
+        sum += node.meshInNode(mesh).computeWindingNumber(p);
+      }
+    } else // p is outside bounding box of the node
+    {
+      if (node.bouFaces.size() < node.triangles.size()) { sum += node.boundaryInNode(mesh).computeWindingNumber(p); }
+      else { sum += node.meshInNode(mesh).computeWindingNumber(p); }
+    }
+  }
+
+  return sum;
+}
+
+void WindingNumberTree::print() const
+{
+  stack<int> nodeStack;
+  nodeStack.push(0);
+  while(!nodeStack.empty())
+  {
+    int nodeID = nodeStack.top();
+    nodeStack.pop();
+    const auto & node = nodes[nodeID];
+    for(int i = 0; i < node.depth; i++)
+      cout << "  ";
+    cout << "nodeID: " << nodeID << " #tri " << node.triangles.size() << " #bou " << node.bouFaces.size() << " ch " << node.childrenIDs[0] << " " << node.childrenIDs[1] << endl;
+    if (node.childrenIDs[0] >= 0)
+    {
+      nodeStack.push(node.childrenIDs[0]);
+      nodeStack.push(node.childrenIDs[1]);
+    }
+  }
+}
+
diff --git a/libraries/mesh/windingNumberTree.h b/libraries/mesh/windingNumberTree.h
new file mode 100644
index 0000000000000000000000000000000000000000..23e2e71cb0309a057d565f9a65f8f1680a21ba94
--- /dev/null
+++ b/libraries/mesh/windingNumberTree.h
@@ -0,0 +1,88 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesh" library , Copyright (C) 2018 USC                               *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef WINDINGNUMBERTREE_H
+#define WINDINGNUMBERTREE_H
+
+// Winding number tree acceleration implementation from the paper:
+// Alec Jacobson, Ladislav Kavan, Olga Sorkine-Hornung:
+// Robust Inside-Outside Segmentation using Generalized Winding Numbers, SIGGRAPH 2013
+// Implementation inspired by libigl.
+
+#include "vec3d.h"
+#include "vec3i.h"
+#include "boundingBox.h"
+#include "triMeshGeo.h"
+#include <map>
+#include <vector>
+#include <list>
+#include <limits>
+#include <climits>
+
+class WindingNumberTree
+{
+public:
+
+  WindingNumberTree() {}
+
+  // build the tree, vertices buffer in mesh should not be changed or deleted when this WindingNumberTree is used
+  void build(const TriMeshRef mesh, int maxDepth = INT_MAX);
+
+  void clear(); // clear internal data
+
+  // compute winding number recursively
+  double windingNumber(const TriMeshRef mesh, const Vec3d & p) const;
+
+  void print() const; // print the tree
+
+protected:
+  int numMeshVertices = 0;
+  int numMeshTriangles = 0;
+
+  struct Node
+  {
+    int depth = 0;
+    std::vector<Vec3i> triangles; // triangles for this node is stored
+    std::vector<Vec3i> bouFaces;  // boundary faces
+    BoundingBox bb;
+    int childrenIDs[2] = {-1,-1};
+    Node(int depth, const TriMeshRef mesh);
+    const TriMeshRef meshInNode(const TriMeshRef fullMesh) const { return TriMeshRef(fullMesh.numVertices(), fullMesh.positions(), triangles); }
+    const TriMeshRef boundaryInNode(const TriMeshRef fullMesh) const { return TriMeshRef(fullMesh.numVertices(), fullMesh.positions(), bouFaces); }
+  };
+
+  int depth = 0;
+  std::vector<Node> nodes;
+};
+
+#endif
+
diff --git a/libraries/mesher/delaunayMesher.cpp b/libraries/mesher/delaunayMesher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1634560546cb50acf94f0b4c73b8046e8db0b5d6
--- /dev/null
+++ b/libraries/mesher/delaunayMesher.cpp
@@ -0,0 +1,1780 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "delaunayMesher.h"
+#include "mat3d.h"
+#include "windingNumber.h"
+#include "verticesInfo.h"
+#include "predicates.h"
+#include <algorithm>
+#include <queue>
+#include <iostream>
+#include <cassert>
+#include <cmath>
+#include <functional>
+using namespace std;
+
+DelaunayMesher::DelaunayMesher()
+{
+  computeVEdgeModification = false;
+  boundaryMesh = NULL;
+  boundaryOctree = NULL;
+  nextBallLabel = 0;
+  epsilon = 0;
+}
+
+DelaunayMesher::~DelaunayMesher()
+{
+  clear();
+}
+
+void DelaunayMesher::clear()
+{
+  for (BallIter itr = ballsToDelete.begin(); itr != ballsToDelete.end(); itr++)
+    delete *itr;
+  ballsToDelete.clear();
+  for (BallIter itr = balls.begin(); itr != balls.end(); itr++)
+    delete *itr;
+  balls.clear();
+  ballsAdded.clear(); // this is a subset of "balls", so no need to do a separate clearing for loop for it
+  epsilon = 0.0;
+  inputVertices.clear();
+  vEdgeDeleted.clear();
+  vEdgeAdded.clear();
+
+  delete boundaryMesh;
+  boundaryMesh = NULL;
+  delete boundaryOctree;
+  boundaryOctree = NULL;
+
+  verticesSet.clear();
+//  boundaryTriangles.clear();
+}
+
+//Warning: the first 4 vertices should not be coplanar to allow successful construction
+bool DelaunayMesher::computeDelaunayTetrahedralization(const std::vector<Vec3d> & vertices, double ep)
+{
+  if (vertices.size() < 4 || ep < 0.0)
+    return false;
+  // clear internal data first
+  clear();
+
+  // remove duplicate vertices
+  vector<Vec3d> uniqueVertices;
+  for (size_t i = 0; i < vertices.size(); i++)
+  {
+    const Vec3d & v = vertices[i];
+    if (verticesSet.find(v) == verticesSet.end())
+    {
+      verticesSet.insert(v);
+      uniqueVertices.push_back(v);
+    }
+  }
+  if (uniqueVertices.size() != vertices.size())
+  {
+    size_t num = vertices.size() - uniqueVertices.size();
+    cout << "Remove " << num << " duplicate " << (num > 1 ? "vertices" : "vertex") << " in DelaunayMesher." << endl;
+  }
+
+  if (uniqueVertices.size() < 4)
+  {
+    cout << "Unique vertices < 4. Cannot generate Delaunay mesh." << endl;
+    return false;
+  }
+
+  int numVertices = uniqueVertices.size();
+  epsilon = ep;
+
+  inputVertices.resize(uniqueVertices.size());
+  vertex2ball.resize(uniqueVertices.size());
+  for (int i = 0; i < numVertices; i++)
+    inputVertices[i] = uniqueVertices[i];
+
+  VerticesInformation info = getVerticesInformation(inputVertices, epsilon);
+  if (info.dimension != 3)
+  {
+    cout << "Delaunay input dimension is not 3 but " << info.dimension << endl;
+    return false;
+  }
+  if (!info.extremeCCW)
+    swap(info.extreme[2], info.extreme[3]);
+
+  // Insert the (nondegenerate) tetrahedron constructed by the call to
+  // GetInformation. This is necessary for the circumsphere-visibility
+  // algorithm to work correctly.
+  if (initialize(info.extreme[0], info.extreme[1], info.extreme[2], info.extreme[3]) == false)
+  {
+    cout << "Failed to initialize Delaunay" << endl;
+    return false;
+  }
+
+  // Incrementally update the tetrahedralization.  The set of processed
+  // points is maintained to eliminate duplicates, either in the original
+  // input points or in the points obtained by snap rounding.
+  vector <bool> processed(numVertices, false);
+  for (int i = 0; i < 4; ++i)
+    processed[info.extreme[i]] = true;
+
+  bool computeVEdge = computeVEdgeModification;
+  computeVEdgeModification = false; // shut down VEdge computation in the loop below
+  for (int i = 0; i < numVertices; ++i)
+  {
+    if (processed[i] == false)
+    {
+      for (BallIter itr = getBallToDeleteSetBegin(); itr != getBallToDeletelSetEnd(); itr++)
+        delete *itr;
+      ballsToDelete.clear();
+      update(i);
+      processed[i] = true;
+    }
+  }
+
+
+  computeVEdgeModification = computeVEdge;
+  if (computeVEdgeModification)
+  {
+    vEdgeAdded.clear();
+    vEdgeDeleted.clear();
+    for (BallIter it = balls.begin(); it != balls.end(); it++)
+    {
+      DelaunayBall * ball = *it;
+      for (int j = 0; j < 4; j++)
+      {
+        UTriKey face = ball->uFaceKey(j);
+        if (DelaunayBall::isFaceRegular(face) == false)
+          continue; // we don't consider infinite face as Voronoi edge
+        VEdgeIter vit = vEdgeAdded.find(face);
+        if (vit == vEdgeAdded.end())
+          vEdgeAdded[face] = ball->getVoronoiEdge(j);
+      }
+    }
+  }
+
+//  cout << balls.size() << " balls inside Delaunay"<< endl;
+
+  return true;
+}
+
+bool DelaunayMesher::addOnePoint(const Vec3d & p)
+{
+
+  if (computeVEdgeModification)
+  {
+    vEdgeAdded.clear();
+    vEdgeDeleted.clear();
+  }
+
+  for (BallIter itr = getBallToDeleteSetBegin(); itr != getBallToDeletelSetEnd(); itr++)
+    delete *itr;
+  ballsToDelete.clear();
+  ballsAdded.clear();
+
+  if (verticesSet.insert(p).second == false) // insertion failure. P is already in verticesSet
+    return false;
+
+  if (boundaryOctree && boundaryMesh)
+  {
+    // for CDT
+    if (fabs(WindingNumber::computeWindingNumber(boundaryMesh, p)) < 0.5)
+      return false;
+  }
+
+  int index = inputVertices.size();
+  inputVertices.push_back(p);
+  vertex2ball.push_back(NULL);
+
+  return update(index);
+}
+
+bool DelaunayMesher::update(int newVtxInd)
+{
+  // find balls containing the newly added point; these balls will be deleted
+  if (ballsToDelete.empty())
+  {
+    if (boundaryOctree == NULL)
+      getBallsContainingPoint(newVtxInd, ballsToDelete);
+    else
+    {
+      Vec3d p = inputVertices[newVtxInd];
+      for (BallIter it = balls.begin(); it != balls.end(); it++)
+      {
+        DelaunayBall * ball = *it;
+        if (ball->isInfinite())
+          continue; // infinite ball will never be erased in CDT
+        if (ball->contain(p) <= 0 && ball->visibleTo(p)) // if the point is visible to the ball
+          ballsToDelete.insert(ball);
+      }
+    }
+  }
+
+  if (ballsToDelete.empty())
+    return false;
+
+  // because some balls (tets) are deleted, some faces of their neighbors are exposed
+  // those exposed faces form a polygon (the boundary of the "cavity")
+  // build the exposed faces
+  //faces are stored by giving the three integer indices of the vertices of the triangle, and the two neighboring balls (one is the deleted one, the other will not be deleted as it is outside of the cavity)
+  vector<pair<OTriKey, pair<DelaunayBall *, DelaunayBall *> > > exposedFaces;
+  // note: there is no particular reason for going backwards here
+  for (BallSet::reverse_iterator iter = ballsToDelete.rbegin(); iter != ballsToDelete.rend(); iter++)
+  {
+    DelaunayBall * ball = *iter;
+    //const int *v = ball.getVertex();
+    //printf("Delete %d %d %d %d\n", v[0] + 1, v[1] + 1, v[2] + 1, v[3] + 1);
+    for (int j = 0; j < 4; j++)
+    {
+      OTriKey face = ball->oFaceKey(j); // this face has orientation pointing outside the tet
+      DelaunayBall * neighborBall = ball->nbr[j];
+      if (ballsToDelete.find(neighborBall) == ballsToDelete.end()) // if neighbor is not in deleted balls
+      {
+        pair<OTriKey, pair<DelaunayBall *, DelaunayBall *> > exposedFace(face, pair<DelaunayBall *, DelaunayBall *>(neighborBall, ball));
+        exposedFaces.push_back(exposedFace);
+      }
+
+      if (computeVEdgeModification && DelaunayBall::isFaceRegular(face))
+      {
+        UTriKey uface = ball->uFaceKey(j);
+        if (vEdgeDeleted.find(uface) == vEdgeDeleted.end())
+          vEdgeDeleted[uface] = ball->getVoronoiEdge(j);
+      }
+    }
+    //ball.faces.clear();
+  }
+
+  //this map is used to assist finding neighbors for the new balls
+  map<UTriKey, DelaunayBall *> neighboringStructure;
+
+  ballsAdded.clear();
+  // add new balls connecting the new point and the exposed faces
+  for (size_t i = 0; i < exposedFaces.size(); i++)
+  {
+    OTriKey & face = exposedFaces[i].first; // this face points outside the deleted polyhedron
+    DelaunayBall * neighborBall = exposedFaces[i].second.first;
+    DelaunayBall * deletedBall = exposedFaces[i].second.second; // the deleted ball
+//    printf("Patching %d %d %d\n", face.v[0], face.v[1], face.v[2]);
+    // create the new ball, this ball has the correct orientation
+    DelaunayBall * ball = createBall(newVtxInd, face[0], face[1], face[2]);
+    
+    if (ball->isRegular() == false) // an infinite ball. This means that face.third < 0, and face.first and face.second give the edge that is on the boundary of the cavity
+    {
+      const int * v = deletedBall->getInfiniteBallTriangle(); //The indices v[0],v[1],v[2] of an infinite ball give the triangle that this ball represents
+      int otherVtx = v[0] ^ v[1] ^ v[2] ^ face[1] ^ face[2]; // xor
+      //printf("Addition %d\n", otherVtx);
+      if ((otherVtx != v[0]) && (otherVtx != v[1]) && (otherVtx != v[2]))
+        // this should never happen
+        return false;
+//      assert (ball->contains(otherVtx) != 1); // a vtx of the deleted ball should not be inside the new ball. This means that the normal of the new ball is incorrect
+    }
+    else
+    {
+      vertex2ball[face[0]] = ball;
+      vertex2ball[face[1]] = ball;
+      vertex2ball[face[2]] = ball;
+    }
+    // build neighboring structure for the new ball
+    UTriKey uface(face[0], face[1], face[2]);
+    int nbrind = ball->getNeighborIndex(uface);
+    assert(nbrind >= 0);
+    ball->nbr[nbrind] = neighborBall;
+    nbrind = neighborBall->getNeighborIndex(uface);
+    assert(nbrind >= 0);
+    neighborBall->nbr[nbrind] = ball;
+    if (computeVEdgeModification && DelaunayBall::isFaceRegular(uface))
+      vEdgeAdded[uface] = neighborBall->getVoronoiEdge(nbrind);
+
+    buildNeighbor(newVtxInd, face[1], face[2], ball, neighboringStructure);
+    buildNeighbor(newVtxInd, face[0], face[2], ball, neighboringStructure);
+    buildNeighbor(newVtxInd, face[1], face[0], ball, neighboringStructure);
+
+    //printf("Inserting ball: %d %d %d %d\n", ball->v[0], ball->v[1], ball->v[2], ball->v[3]);
+//    cout << "inserting " << *ball << endl;
+    balls.insert(ball);
+    ballsAdded.insert(ball);
+  }
+
+  //int tail = balls.size();
+  for (BallSet::iterator itr = ballsToDelete.begin(); itr != ballsToDelete.end(); itr++)
+    balls.erase(*itr);
+
+  vertex2ball[newVtxInd] = *ballsAdded.begin();
+  if (neighboringStructure.size() != 0) //error here
+  {
+    printf("Neighbor structure error\n");
+    return false;
+  }
+  return true;
+}
+
+void DelaunayMesher::getBallsContainingPoint(int vtx, BallSet & containingBalls)
+{
+  containingBalls.clear();
+  // first, get one that contains the given point
+  DelaunayBall * ball = getOneBallContainingPoint(vtx);
+  BallSet unknown;
+  if (ball)
+  {
+    // search for its neighboring tets to go over all the balls that contains the point
+    BallSet candidates;
+    candidates.insert(ball);
+    while (candidates.size() > 0)
+    {
+      DelaunayBall * ball = *candidates.begin();
+      candidates.erase(candidates.begin());
+
+      for (int j = 0; j < 4; j++)
+      {
+        DelaunayBall * adj = ball->nbr[j];
+        if (adj && candidates.find(adj) == candidates.end() && containingBalls.find(adj) == containingBalls.end())
+        {
+          int loc = adj->contains(vtx);
+          if (loc < 0) // adj contains vtx
+            candidates.insert(adj);
+          else if (loc == 0)
+          {
+            assert(adj->isInfinite());
+            unknown.insert(adj);
+          }
+        }
+      }
+      //if (ball->label == 120)
+        //cout << ball->contains(vtx) << ball->getPosition(1) << ball->getPosition(2) << ball->getPosition(3) << endl;
+      containingBalls.insert(ball);
+    }
+  }
+  else
+    cout << "Error: no balls contain the input point: " << vtx << " " << inputVertices[vtx] << endl;
+  for (BallCIter itr = unknown.begin(); itr != unknown.end(); itr++)
+    if (containingBalls.find((*itr)->nbr[0]) != containingBalls.end())
+      containingBalls.insert(*itr);
+  return;
+}
+
+int DelaunayMesher::toPlane(int i, int a, int b, int c) const
+{
+  return toPlane(inputVertices[i], a, b, c);
+}
+
+Vec2d getXY(const Vec3d & v) { return Vec2d(v[0], v[1]); }
+Vec2d getYZ(const Vec3d & v) { return Vec2d(v[1], v[2]); }
+Vec2d getZX(const Vec3d & v) { return Vec2d(v[2], v[0]); }
+int DelaunayMesher::toCircumsphere(int i, int a, int b, int c) const
+{
+  return toCircumsphere(inputVertices[i], a, b, c);
+}
+
+int DelaunayMesher::toCircumsphere(int i, int a, int b, int c, int d) const
+{
+  return toCircumsphere(inputVertices[i], a, b, c, d);
+}
+
+int DelaunayMesher::toPlane(const Vec3d & p, int a, int b, int c) const
+{
+  int q = 0;
+
+  double ret = orient3d(&inputVertices[a][0], &inputVertices[b][0], &inputVertices[c][0], &p[0]);
+  q = ((ret > 0) ? -1 : (ret < 0 ? 1 : 0));
+
+  return q;
+}
+
+int DelaunayMesher::toCircumsphere(const Vec3d & p, int a, int b, int c) const
+{
+  auto pred = [&]() {
+    function<Vec2d(const Vec3d & v)> getComp[3] = { getXY, getYZ, getZX };
+
+    for(int i = 0; i < 3; i++) {
+      auto f = getComp[i];
+      Vec2d i2 = f(p);
+      Vec2d a2 = f(inputVertices[a]);
+      Vec2d b2 = f(inputVertices[b]);
+      Vec2d c2 = f(inputVertices[c]);
+
+      double ret = incircle(&a2[0], &b2[0], &c2[0], &i2[0]);
+      if (ret < 0) return 1;
+      if (ret > 0) return -1;
+    }
+    return 0;
+  };
+
+  int q = 0;
+  q = pred();
+  return q;
+}
+
+int DelaunayMesher::toCircumsphere(const Vec3d & p, int a, int b, int c, int d) const
+{
+  int q = 0;
+  double ret = insphere(&inputVertices[a][0], &inputVertices[b][0], &inputVertices[c][0], &inputVertices[d][0], &p[0]);
+  q = (ret > 0 ? 1 : (ret < 0 ? -1 : 0));
+  return q;
+}
+
+DelaunayMesher::DelaunayBall * DelaunayMesher::getOneBallContainingPoint(int vtx)
+{
+  // find a regular ball to begin searching
+  DelaunayBall * ball = *balls.begin();
+  if (ball->isInfinite())
+  {
+    // infinite ball must have a regular neighbor
+    for (int i = 0; i < 4; i++)
+      if (ball->nbr[i]->isRegular())
+      {
+        ball = ball->nbr[i];
+        break;
+      }
+  }
+
+  size_t numTet = balls.size();
+  for (size_t i = 0; i < numTet; i++)
+  {
+    assert(ball->isRegular());
+    int j = 0;
+    for (j = 0; j < 4; j++)
+    {
+      OTriKey face = ball->oFaceKey(j);
+      if (toPlane(vtx, face[0], face[1], face[2]) > 0)
+      {
+        // vtx sees face <v0,v1,v2> from outside the tetrahedron.
+        DelaunayBall * nbrBall = ball->nbr[j];
+        ball = nbrBall;
+        if (nbrBall->isRegular()) // tetra has adjacent tet on that face
+          break;                  // Traverse to the tetrahedron sharing the face.
+        else 
+          return ball;            // We reached a surface face, so the point is outside the hull
+      }
+    }
+
+    if (j == 4)
+      return ball;   // The point is inside all four faces, so the point is inside a tetrahedron.
+  }
+  // this should never be reached
+  return NULL;
+}
+
+// use neighboringStructure to connect ball with its neighbors
+int DelaunayMesher::buildNeighbor(const UTriKey & face, DelaunayBall * ball, map<UTriKey, DelaunayBall *> & neighboringStructure)
+{
+  map<UTriKey, DelaunayBall *>::iterator itr = neighboringStructure.find(face);
+  if (itr == neighboringStructure.end())
+  {
+    neighboringStructure[face] = ball;
+    return 1;
+  }
+  else
+  {
+    DelaunayBall * nbrBall = itr->second;
+    int nbrind = nbrBall->getNeighborIndex(face);
+    assert(nbrind >= 0);
+    nbrBall->nbr[nbrind] = ball;
+    nbrind = ball->getNeighborIndex(face);
+    ball->nbr[nbrind] = nbrBall;
+    neighboringStructure.erase(itr);
+    if (computeVEdgeModification && DelaunayBall::isFaceRegular(face))
+      vEdgeAdded[face] = ball->getVoronoiEdge(nbrind);
+    return 0;
+  }
+}
+
+bool DelaunayMesher::initialize(int v0, int v1, int v2, int v3)
+{
+  Vec3d a = inputVertices[v0];
+  Vec3d b = inputVertices[v1];
+  Vec3d c = inputVertices[v2];
+  Vec3d d = inputVertices[v3];
+  double volume = dot(b - a, cross(c - a, d - a)); //compute volume
+  if (volume <= 0)
+    return false;
+  
+  DelaunayBall * ball0 = NULL, *ball1 = NULL, *ball2 = NULL, *ball3 = NULL, *ball4 = NULL;
+
+  ball0 = createBall(v0, v1, v2, v3); // form a regular ball
+  ball1 = createBall(-1, v0, v1, v2);
+  ball2 = createBall(-1, v0, v3, v1);
+  ball3 = createBall(-1, v0, v2, v3);
+  ball4 = createBall(-1, v1, v3, v2);
+
+  balls.insert(ball0);
+  balls.insert(ball1);
+  balls.insert(ball2);
+  balls.insert(ball3);
+  balls.insert(ball4);
+
+  assert(ball0->getNeighborIndex(UTriKey(v0, v1, v2)) >= 0);
+
+  // build neighboring structure
+  #define ASSIGN_NEIGHBOR(a, b, v0, v1, v2) do { \
+    a->nbr[a->getNeighborIndex(UTriKey(v0,v1,v2))] = b; \
+    b->nbr[b->getNeighborIndex(UTriKey(v0,v1,v2))] = a; } while(0)
+
+  ASSIGN_NEIGHBOR(ball0, ball1, v0, v1, v2);
+//  ball0->nbr[ball0->getNeighborIndex(UTriKey(v0,v1,v2))] = ball1;
+  ASSIGN_NEIGHBOR(ball0, ball2, v0, v3, v1);
+  ASSIGN_NEIGHBOR(ball0, ball3, v0, v2, v3);
+  ASSIGN_NEIGHBOR(ball0, ball4, v1, v3, v2);
+  ASSIGN_NEIGHBOR(ball1, ball2, -1, v0, v1);
+  ASSIGN_NEIGHBOR(ball1, ball3, -1, v0, v2);
+  ASSIGN_NEIGHBOR(ball1, ball4, -1, v1, v2);
+  ASSIGN_NEIGHBOR(ball2, ball3, -1, v0, v3);
+  ASSIGN_NEIGHBOR(ball2, ball4, -1, v1, v3);
+  ASSIGN_NEIGHBOR(ball3, ball4, -1, v2, v3);
+  assert(ball0->nbr[0] != NULL);
+  vertex2ball[v0] = ball0;
+  vertex2ball[v1] = ball0;
+  vertex2ball[v2] = ball0;
+  vertex2ball[v3] = ball0;
+  return true;
+}
+
+TetMesh * DelaunayMesher::getMesh() const
+{
+//  return NULL;
+  vector<int> ele;
+//  cout << "Building mesh: " << endl;
+  for (set<DelaunayBall *, DelaunayBallCompare>::const_iterator i = balls.begin(); i != balls.end(); i++)
+  {
+    DelaunayBall * ball = *i;
+    if (ball->isRegular())
+    {
+      const int * vtx = ball->getVertices();
+      for (int k = 0; k < 4; k++)
+        ele.push_back(vtx[k]);
+//      cout << *ball << endl;
+    }
+  }
+  vector<double> vertices(3 * inputVertices.size());
+  for (unsigned int i = 0; i < inputVertices.size(); i++)
+    inputVertices[i].convertToArray(&vertices[i * 3]);
+  //printf("%d\n", ele.size());
+  return new TetMesh(inputVertices.size(), &vertices[0], ele.size() / 4, &ele[0]);
+}
+
+// Constructor assumes that the incoming vtx has correct orientation
+// Regular ball: v3 is on the positive side of plane(v0, v1, v2)
+// Infinite ball: Other vertices on the positive side of (v1, v2, v3)
+DelaunayMesher::DelaunayBall::DelaunayBall(int v0, int v1, int v2, int v3, DelaunayMesher * p, label_t l)
+    : OTetKey(v0, v1, v2, v3), parent(*p), label(l)
+{
+  // const double DEGENERATE_RADIUS = 1e10;
+
+  memset(nbr, 0, sizeof(nbr));
+
+  Vec3d p1 = parent.inputVertices[v[1]];
+  Vec3d p2 = parent.inputVertices[v[2]];
+  Vec3d p3 = parent.inputVertices[v[3]];
+
+  if (v[0] >= 0)  //regular ball
+  {
+    Vec3d p0 = parent.inputVertices[v[0]];
+    // double vol = TetMesh::getTetVolume(&p0,&p1,&p2,&p3);
+    // if (vol < 1e-10) 
+    //   cout << "problem: tet volume too small: " << vol << endl;
+
+    center = parent.circumcenter(p0, p1, p2, p3);
+  }
+  else
+  {
+  }
+}
+
+int DelaunayMesher::DelaunayBall::getNeighborIndex(const UTriKey & key) const
+{
+  for (int i = 0; i < 4; i++)
+  {
+    UTriKey uface = uFaceKey(i);
+    if (uface == key)
+      return i;
+  }
+  return -1;
+}
+
+int DelaunayMesher::DelaunayBall::getNeighborIndex(const OTriKey & key) const
+{
+  for (int i = 0; i < 4; i++)
+  {
+    OTriKey oface = oFaceKey(i);
+    if (oface == key)
+      return i;
+  }
+  return -1;
+}
+
+void DelaunayMesher::DelaunayBall::setNeighbor(const UTriKey & key, DelaunayBall * nbrBall)
+{
+  int ind = getNeighborIndex(key);
+  if (ind >= 0)
+    nbr[ind] = nbrBall;
+}
+
+//return value: 
+//1 means that the point is outside the ball
+//0 means that the point is on the ball
+//-1 means that the point is inside the ball
+int DelaunayMesher::DelaunayBall::contains(int newVtx) const
+{
+  int result = 0;
+  if (isRegular())
+  {
+    int query = parent.toCircumsphere(newVtx, v[0], v[1], v[2], v[3]);
+    if (query)
+      return query;
+    int tmpVtx[5];
+    memcpy(tmpVtx, &v[0], sizeof(int) * 4);
+    tmpVtx[4] = newVtx;
+
+    //Using buble sort
+    int numSwaps = 0; // Record the total number of swaps.
+
+    for (int i = 0; i < 5; i++)
+      for (int j = 4; j > i; j--)
+        if (tmpVtx[j] < tmpVtx[j - 1])
+        {
+          numSwaps++;
+          swap(tmpVtx[j], tmpVtx[j - 1]);
+        }
+
+    int oriA = parent.toPlane(tmpVtx[4], tmpVtx[1], tmpVtx[2], tmpVtx[3]);
+    if (oriA != 0)
+      return (numSwaps & 1? oriA: -oriA);
+
+    int oriB = -parent.toPlane(tmpVtx[4], tmpVtx[0], tmpVtx[2], tmpVtx[3]);
+    // Flip the sign if there are odd number of swaps.
+    if (!oriB)
+    {
+      cout << *this << newVtx << endl;
+      cout << parent.inputVertices[v[0]] << parent.inputVertices[v[1]] << parent.inputVertices[v[2]] << parent.inputVertices[v[3]] << parent.inputVertices[newVtx] << endl;
+      cout << query << " " << oriA << " " << oriB << endl;
+      exit(-9);
+    }
+    return (numSwaps & 1? oriB: -oriB);
+  }
+  else
+  {
+    const int * vtx = getInfiniteBallTriangle();
+    // ret = +1: point outside infinite ball
+    // ret = -1: point inside infinite ball
+    result = parent.toPlane(newVtx, vtx[0], vtx[1], vtx[2]);
+    return result;
+    if (result == 0)
+    {
+      // For infinite ball, the matters are more complicated when the point is on the plane of the boundary triangle
+      // In this case, we further test whether the point is inside the circumcircle of the triangle on that plane
+      result = parent.toCircumsphere(newVtx, vtx[0], vtx[1], vtx[2]);
+      return result;
+    }
+  }
+  return result;
+}
+
+int DelaunayMesher::DelaunayBall::contain(const Vec3d & p) const
+{
+  int result = 0;
+  if (isRegular())
+    return parent.toCircumsphere(p, v[0], v[1], v[2], v[3]);
+  else
+  {
+    const int * vtx = getInfiniteBallTriangle();
+    result = parent.toPlane(p, vtx[0], vtx[1], vtx[2]);
+    if (!result)
+      return result;  // Not on the plane
+    // If on the plane, check whether it is inside the circumcircle
+    return parent.toCircumsphere(p, vtx[0], vtx[1], vtx[2]);
+  }
+}
+
+bool DelaunayMesher::DelaunayBall::visibleTo(const Vec3d & p) const
+{
+  if (parent.boundaryOctree == NULL)
+    return true;
+  vector<TriangleBasic*> triangles;
+  if (isRegular())
+  {
+    for (unsigned int j = 0; j < 4; j++)
+    {
+      Vec3d vtx = parent.inputVertices[v[j]];
+      parent.boundaryOctree->lineSegmentIntersection(triangles, p, vtx + (p - vtx) * 1e-10);
+      if (triangles.empty() == false)
+        return false;
+    }
+  }
+  else // infinity ball case:
+    return false;  //in CDT, the boundary can't be removed
+  return true;
+}
+
+// vertices a, b, c, d have the same distance to circumcenter
+// so the circumcenter lies on the perpendicular plane for each edge of the tet
+// for edge (ab), the equation for this perpendicular plane is: dot((b - a), (x,y,z)) = dot((b - a), (a+b)/2)
+// Form a 3x3 linear systemn with three equations for three edges (ab, ac, ad)
+Vec3d DelaunayMesher::circumcenter(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d)
+{
+  Vec3d n1 = b - a;
+  Vec3d n2 = c - a;
+  Vec3d n3 = d - a;
+
+  #ifdef USE_MAT3D
+    Mat3d mat(n1,n2,n3); //system matrix
+    Vec3d rhs = Vec3d(dot(n1, (a + b)), dot(n2, (a + c)), dot(n3, (a + d))) / 2.;
+    return inv(mat) * rhs;
+  #else
+  // Cramer rule for solving linear equations
+    Vec3d rhs = Vec3d(dot(n1, (a + b)), dot(n2, (a + c)), dot(n3, (a + d)));
+    Vec3d _a = Vec3d(n1[0], n2[0], n3[0]);
+    Vec3d _b = Vec3d(n1[1], n2[1], n3[1]);
+    Vec3d _c = Vec3d(n1[2], n2[2], n3[2]);
+
+    double _w = det(_a, _b, _c) * 2;
+    Vec3d ret = Vec3d(det(rhs, _b, _c), det(_a, rhs, _c), det(_a, _b, rhs)) / _w;
+    return ret;
+  #endif
+}
+
+bool DelaunayMesher::checkDelaunay() const
+{
+  cout << "Checking delaunay " << endl;
+  for (BallIter it = balls.begin(); it != balls.end(); it++)
+  {
+//    cout << "check ball " << count << endl;
+    DelaunayBall * ball = *it;
+    for (size_t i = 0; i < inputVertices.size(); i++)
+    {
+      int j = i;
+      const int * v = ball->getVertices();
+      if (j == v[0] || j == v[1] || j == v[2] || j == v[3])
+        continue;
+
+      if (ball->contains(j) == -1) //point is inside the ball
+      {
+        cout << "point " << j << " " << inputVertices[j] << " is inside ball: ";
+        if (ball->isRegular())
+        {
+          cout << " center: " << ball->center << ", r=" << len(ball->getPosition(v[0]) - ball->center) << " length from point to center: " << len(inputVertices[j] - ball->center) << endl;
+          cout << "regular ball " << *ball << endl;
+        }
+        else
+        {
+          cout << "Infinite " << ball->v[1] << inputVertices[ball->v[1]] << " " << ball->v[2] << inputVertices[ball->v[2]] << " " << ball->v[3] << inputVertices[ball->v[3]] << endl;
+        }
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+Vec3d DelaunayMesher::getFaceNormal(const OTriKey & face) const
+{
+  const Vec3d & v0 = inputVertices[face[0]];
+  const Vec3d & v1 = inputVertices[face[1]];
+  const Vec3d v2 = inputVertices[face[2]];
+  return norm(cross(v1 - v0, v2 - v0));
+}
+
+DelaunayMesher::VoronoiEdge::VoronoiEdge(bool isFinite, const Vec3d & s, const Vec3d & other) : finite(isFinite), start(s) 
+{
+  if (isFinite)
+  {
+    end = other;
+    direction = Vec3d(0.0);
+  }
+  else
+  {
+    direction = other;
+    end = Vec3d(0.0);
+  }
+}
+
+DelaunayMesher::VoronoiEdge DelaunayMesher::DelaunayBall::getVoronoiEdge(int faceIndex) const
+{
+  VoronoiEdge vedge;
+  UTriKey uface = uFaceKey(faceIndex);
+  assert(isFaceRegular(uface));
+  const DelaunayBall * nbrBall = nbr[faceIndex];
+  assert(nbrBall);
+  if (isRegular() && nbrBall->isRegular())
+  {
+    vedge.finite = true;
+    vedge.start = center;
+    vedge.end = nbrBall->center;
+  }
+  else
+  {
+    vedge.finite = false;
+    OTriKey oface = oFaceKey(faceIndex);
+    if (isRegular() == false)
+    {
+      assert(nbrBall->isRegular());
+      vedge.start = nbrBall->center;
+      vedge.direction = (-1) * parent.getFaceNormal(oface);
+    }
+    else
+    {
+      vedge.start = center;
+      vedge.direction = parent.getFaceNormal(oface);
+    }
+  }
+  //assert(vedge.end.hasNaN() == false);
+  //assert(vedge.direction.hasNaN() == false);
+  return vedge;
+}
+
+bool DelaunayMesher::initializeCDT(TetMesh * inputMesh, double ep)
+{
+  // clean previous data
+  clear();
+  TetMesh tetMesh(*inputMesh);
+  tetMesh.orient();
+  epsilon = ep;
+  if (tetMesh.getNumVertices() < 4)
+    return false;
+
+  // check whether tetMesh is face-manifold
+  map<UTriKey, int> faceCount;
+  for (int i = 0; i < tetMesh.getNumElements(); i++)
+  {
+    OTetKey tet(tetMesh.getVertexIndex(i, 0), tetMesh.getVertexIndex(i, 1), tetMesh.getVertexIndex(i, 2), tetMesh.getVertexIndex(i, 3));
+    for (int j = 0; j < 4; j++)
+    {
+      UTriKey face = tet.uFaceKey(j);
+      faceCount[face]++;
+    }
+  }
+  for (map<UTriKey, int>::iterator it = faceCount.begin(); it != faceCount.end(); it++)
+  {
+    if (it->second > 2)
+    {
+      // non-manifold faces found
+      return false;
+    }
+  }
+
+  // add all points
+  inputVertices.resize(tetMesh.getNumVertices());
+  for (int i = 0; i < tetMesh.getNumVertices(); i++)
+    inputVertices[i] = tetMesh.getVertex(i);
+
+#ifdef USE_QUERY_IN_DELAUNAY
+  buildQuery();
+#endif
+
+  for (int i = 0; i < tetMesh.getNumElements(); i++)
+    balls.insert(createBall(tetMesh.getVertexIndex(i, 0), tetMesh.getVertexIndex(i, 1), tetMesh.getVertexIndex(i, 2), tetMesh.getVertexIndex(i, 3)));
+
+  //this map is used to assist in finding neighbors for the new regular balls
+  map<UTriKey, DelaunayBall *> neighboringStructure;
+  for (BallIter it = balls.begin(); it != balls.end(); it++)
+  {
+    DelaunayBall * ball = *it;
+    for (int j = 0; j < 4; j++)
+    {
+      UTriKey face = ball->uFaceKey(j);
+      buildNeighbor(face, ball, neighboringStructure);
+    }
+  }
+
+  int * edge = new int[neighboringStructure.size() * 3];
+  int k = 0;
+  map <UTriKey, DelaunayBall *> infiniteNeighboring; //assist finding neighbors for the new infinite balls
+
+  // for all the surface triangles, we'll consturct its neighboring infinite balls
+  for (map<UTriKey, DelaunayBall *>::iterator itr = neighboringStructure.begin(); itr != neighboringStructure.end(); itr++)
+  {
+    const UTriKey & face = itr->first;
+    DelaunayBall * ball = itr->second;
+
+    int faceind = ball->getNeighborIndex(face);
+    OTriKey oface = ball->oFaceKey(faceind);
+
+    // record the surface face of the tetMesh
+    edge[k++] = oface[0];
+    edge[k++] = oface[1];
+    edge[k++] = oface[2];
+
+    oface.reverse(); // reverse the oriented face to let it become the neighboring infinity ball's face
+
+    DelaunayBall * newBall = createBall(-1, oface[0], oface[1], oface[2]);
+    ball->setNeighbor(face, newBall);
+    newBall->setNeighbor(face, ball);
+
+    balls.insert(newBall);
+
+    //build all the neighboring structure for new infinite balls
+    const int * v = oface.indices();
+    buildNeighbor(-1, v[0], v[1], newBall, infiniteNeighboring);
+    buildNeighbor(-1, v[1], v[2], newBall, infiniteNeighboring);
+    buildNeighbor(-1, v[0], v[2], newBall, infiniteNeighboring);
+  }
+
+  vector<double> vertices(3 * inputVertices.size());
+  for (size_t i = 0; i < inputVertices.size(); i++)
+    inputVertices[i].convertToArray(&vertices[i * 3]);
+
+  boundaryMesh = new ObjMesh(inputVertices.size(), &vertices[0], neighboringStructure.size(), edge);
+  //  boundaryMesh->save("boundary.obj");
+  boundaryOctree = new ObjMeshOctree<TriangleBasic>(boundaryMesh, 5, 10, 0);
+  printf("Octree built\n");
+  delete [] edge;
+
+  return true;
+}
+
+std::ostream & operator <<(std::ostream & o, const DelaunayMesher::DelaunayBall & ball)
+{
+  o << "(" << ball.v[0] << "," << ball.v[1] << "," << ball.v[2] << "," << ball.v[3] << ")";
+  return o;
+}
+
+DelaunayMesher::DelaunayBall * DelaunayMesher::createBall(int v0, int v1, int v2, int v3)
+{
+  return new DelaunayBall(v0, v1, v2, v3, this, nextBallLabel++);
+}
+
+bool DelaunayMesher::isTetMeshFaceManifold(const TetMesh * tetMesh)
+{
+  map<UTriKey, int> faceCount;
+  for(int i = 0; i < tetMesh->getNumElements(); i++) 
+  {
+    OTetKey tet(tetMesh->getVertexIndices(i));
+    for(int j = 0; j < 4; j++) 
+    {
+      UTriKey face = tet.uFaceKey(j);
+      faceCount[face]++;
+    }
+  }
+  for(map<UTriKey, int>::iterator it = faceCount.begin(); it != faceCount.end(); it++) 
+  {
+    if(it->second > 2) 
+      return false; // non-manifold faces found
+  }
+  return true;
+}
+
+// Remove a ball from the delaunay mesh
+void DelaunayMesher::removeBall(DelaunayBall * ball)
+{
+  if (balls.find(ball) == balls.end())
+    return;
+  for (int i = 0; i < 4; i++)
+  {
+    UTriKey key = ball->uFaceKey(i);
+    map<UTriKey, DelaunayBall *>::iterator itr = neighboringStructure.find(key);
+    if (itr == neighboringStructure.end())	
+    {
+      neighboringStructure.insert(make_pair(key, ball->nbr[i]));        // A face loses its one neighbor, save it as a hanging face
+      ball->nbr[i]->setNeighbor(key, NULL);
+    }
+    else
+    {
+      assert(itr->second == ball);
+      neighboringStructure.erase(itr);        // A face has no neighbor, remove it
+    }
+  }
+  balls.erase(ball);
+  delete ball;
+}
+
+DelaunayMesher::DelaunayBall * DelaunayMesher::addBall(const int v0, const int v1, const int v2, const int v3)
+{
+  DelaunayBall * ball = createBall(v0, v1, v2, v3);
+  if (v0 >= 0) 
+    vertex2ball[v0] = ball;
+  if (v1 >= 0) 
+    vertex2ball[v1] = ball;
+  if (v2 >= 0) 
+    vertex2ball[v2] = ball;
+  if (v3 >= 0) 
+    vertex2ball[v3] = ball;
+  if (balls.find(ball) != balls.end())
+    return ball;
+  balls.insert(ball);
+  for (int i = 0; i < 4; i++)
+  {
+    UTriKey key = ball->uFaceKey(i);
+    map<UTriKey, DelaunayBall *>::iterator itr = neighboringStructure.find(key);
+    if (itr == neighboringStructure.end())
+      neighboringStructure.insert(make_pair(key, ball)); // A face has only one neighbor, save it as a hanging face
+    else // Build neighbor relationship
+    {
+      ball->nbr[i] = itr->second;
+      itr->second->setNeighbor(key, ball);
+      neighboringStructure.erase(itr);
+    }
+  }
+  return ball;
+}
+
+DelaunayMesher::DelaunayBall * DelaunayMesher::addBall(const int* v)
+{
+  return addBall(v[0], v[1], v[2], v[3]);
+}
+
+int DelaunayMesher::buildCDT()
+{
+  map<UTriKey, DelaunayBall *>::iterator faceKey = neighboringStructure.begin();
+
+  int * edge = (int*)malloc(neighboringStructure.size() * 3 * sizeof(int));
+
+  int edgeSize = 0;
+  // Building infinite ball and neighboring information
+  while (faceKey != neighboringStructure.end())
+  {
+    // Create an infinite ball
+    DelaunayBall * regularBall = faceKey->second;
+    int faceIndex = regularBall->getNeighborIndex(faceKey->first);
+
+    OTriKey okey = regularBall->oFaceKey(faceIndex);
+    edge[edgeSize++] = okey[0];
+    edge[edgeSize++] = okey[1];
+    edge[edgeSize++] = okey[2];
+    DelaunayBall * infiniteBall = createBall(-1, faceKey->first[0], faceKey->first[1], faceKey->first[2]);
+    faceKey++;
+    balls.insert(infiniteBall);
+    for (int i = 0; i < 4; i++)
+    {
+      UTriKey key = infiniteBall->uFaceKey(i);
+      map<UTriKey, DelaunayBall *>::iterator itr = neighboringStructure.find(key);
+      if (itr == neighboringStructure.end())
+        neighboringStructure.insert(make_pair(key, infiniteBall));
+      else
+      {
+        infiniteBall->nbr[i] = itr->second;
+        itr->second->setNeighbor(key, infiniteBall);
+        neighboringStructure.erase(itr);
+      }
+    }
+  }
+
+  vector<double> vertices(3 * inputVertices.size());
+  for (size_t i = 0; i < inputVertices.size(); i++)
+    inputVertices[i].convertToArray(&vertices[i * 3]);
+
+  boundaryMesh = new ObjMesh(inputVertices.size(), vertices.data(), edgeSize / 3, edge);
+
+  boundaryOctree = new ObjMeshOctree<TriangleBasic>(boundaryMesh, 5, 10, 0);
+  free(edge);
+  return 0;
+}
+
+static const int SECT_FACE = 0;
+static const int ON_FACE = 1;
+static const int SECT_EDGE = 2;
+static const int ON_EDGE = 3;
+static const int SECT_VERTEX = 4;
+static const int ON_VERTEX = 5;
+static const int IN_TET = 6;
+
+/*
+ * Test whether seg(v0,v1) intersect face(f0,f1,f2), Assume that Volume(f0,f1,f2,v0) must be non-negative
+ * 0: seg (f1, f2) causes unflippable
+ * 1: seg (f2, f0) causes unflippable
+ * 2: seg (f0, f1) causes unflippable
+ * 3: flippable
+ */
+
+//Permute origin vtx table, first column of each row is the index of the origin
+static const int permute[4][4] = { {0, 1, 2, 3}, {1, 2, 0, 3}, {2, 0, 1, 3}, {3, 1, 0, 2} };
+
+static const int FLIPPABLE = 8;
+static const int SEMI_FLIPPABLE = 4;
+
+// test wheter seg(v0,v1) intersects face
+// if intersect of the boundary of the face, return SEMI_FLIPPABLE + which edge of the face the segement intersects
+int DelaunayMesher::flippable(const OTriKey &face, const int v0, const int v1)
+{
+  int onFace = FLIPPABLE;
+  for (int i = 0; i < 3; i++)
+  {
+    OEdgeKey edge = face.oEdgeKey(i);
+    int result = toPlane(v1, edge[0], edge[1], v0);
+    if (result == 1)
+      return i;
+    else if (result == 0)
+      onFace = i;
+  }
+  return (onFace == FLIPPABLE? FLIPPABLE: SEMI_FLIPPABLE + onFace);
+}
+
+// face is on ball1, the opposite face is on ball0
+int DelaunayMesher::flip23(const OTriKey& face, DelaunayBall * ball0, DelaunayBall * ball1, int requestNewBallIdx, DelaunayBall ** newBallAroundEdge, int allowFlat)
+{
+  if (ball0->isInfinite() != ball1->isInfinite())
+    return 1; // Can't flip boundary face
+  UTriKey uface = UTriKey(face.indices());
+  int n0 = ball0->getNeighborIndex(uface);
+  int v0 = ball0->getVtx(n0);
+  int n1 = ball1->getNeighborIndex(uface);
+  int v1 = ball1->getVtx(n1);
+  // v0 must on the positive side of face
+  if (face[0] >= 0)
+  { // a triangular face
+    if (toPlane(v0, face[0], face[1], face[2]) == -1)
+    {
+      swap(v0, v1);
+    }
+    // test whether the face is flippable
+    int flippableResult = flippable(face, v0, v1);
+    if (flippableResult < SEMI_FLIPPABLE)
+      return flippableResult;
+    if (flippableResult != SEMI_FLIPPABLE + allowFlat && flippableResult != FLIPPABLE)
+      return flippableResult;
+    for (int i = 0; i < 4; i++)
+      if (i != n0)
+      {
+        OTriKey f = ball0->oFaceKey(i);
+        if (toPlane(v1, f[0], f[1], f[2]) == 1) // not flippable
+          return i;
+      }
+  }
+  else // a boundary edge
+  {
+  }
+
+  // if it is flippable, remove 2 original tets and add 3 new tets
+  removeBall(ball0);
+  removeBall(ball1);
+  DelaunayBall * newBall[3];
+  for (int i = 0; i < 3; i++)
+  {
+    newBall[i] = addBall(face[OTriKey::triEdgeIndex[i][0]], face[OTriKey::triEdgeIndex[i][1]], v1, v0);
+  }
+  if (newBallAroundEdge)
+    *newBallAroundEdge = newBall[requestNewBallIdx];
+
+  return FLIPPABLE;
+}
+
+static void getOtherTwoIndices(const int a, const int b, int & c, int & d)
+{
+  if (a + b == 3)
+  { //segment is (0,3) or (1,2)
+    c = (a + 2) & 3;
+    d = (b + 2) & 3;
+  }
+  else
+  { // segment isn't (0,3) or (1,2)
+    c = 3 - a;
+    d = 3 - b;
+  }
+}
+
+//Get a face number which contains the two vertices, the other vertex is accessed via transpose
+static const int faceLookUpTableByTwoVertices[4][4] = { {-1, 2, 3, 1}, {3, -1, 0, 2}, {1, 3, -1, 0}, {2, 0, 1, -1} };
+
+static map<int, int> eleCounter;
+
+int DelaunayMesher::clearCounter(const DelaunayMesher::TetAroundEdge & tetsAroundEdge)
+{
+  for (DelaunayMesher::TetAroundEdge::const_iterator itr = tetsAroundEdge.begin(); itr != tetsAroundEdge.end(); itr++)
+  {
+    if (balls.find(itr->third) == balls.end())
+      cout << "Missing " << *(itr->third) << endl;
+    eleCounter[itr->third->label]--;
+  }
+  return 0;
+}
+
+int DelaunayMesher::getTetsAroundEdge(const OEdgeKey& edge, TetAroundEdge & tetsAroundEdge)
+{
+  if ((getOneBallBySegment(edge[0], edge[1]) & 255) != ON_VERTEX)
+  {
+    return 0;
+  }
+
+  // Store all Balls with this edge
+  DelaunayBall *& ball = vertex2ball[edge[0]];
+
+  // The initial ball, keep its neighbors
+  int v0 = ball->getInvertedIndex(edge[0]);
+  int v1 = ball->getInvertedIndex(edge[1]);
+  int currentFaceNumber = faceLookUpTableByTwoVertices[v0][v1];
+  int nextFaceNumber = faceLookUpTableByTwoVertices[v1][v0];
+  tetsAroundEdge.push_back(make_triple(ball->getVtx(currentFaceNumber), ball->oFaceKey(nextFaceNumber), ball));
+  //eleCounter[ball->label]++;
+  DelaunayBall * firstBall = ball;
+  while (true)
+  {
+    //cout << *ball << nextFaceNumber << endl;
+    //DelaunayBall * pre = ball;
+    ball = ball->nbr[nextFaceNumber];
+    if (ball == firstBall)
+      break; // All balls visited
+
+    v0 = ball->getInvertedIndex(edge[0]);
+    v1 = ball->getInvertedIndex(edge[1]);
+    currentFaceNumber = faceLookUpTableByTwoVertices[v0][v1];
+    nextFaceNumber = faceLookUpTableByTwoVertices[v1][v0];
+    tetsAroundEdge.push_back(make_triple(ball->getVtx(currentFaceNumber), ball->oFaceKey(nextFaceNumber), ball));
+    //eleCounter[ball->label]++;
+  }
+  return tetsAroundEdge.size();
+}
+
+int DelaunayMesher::flip32(const OEdgeKey& edge, const TetAroundEdge & tetsAroundEdge)
+{
+  vector <int> f;
+  TetAroundEdge::const_iterator itr = tetsAroundEdge.begin();
+  DelaunayBall * ball0 = itr->third;
+  f.push_back((itr++)->first);
+  DelaunayBall * ball1 = itr->third;
+  f.push_back((itr++)->first);
+  DelaunayBall * ball2 = itr->third;
+  f.push_back((itr++)->first);
+
+  int v0, v1;
+  int flippableResult;
+
+  v0 = edge[1]; v1 = edge[0];
+  if (f[0] >= 0 && f[1] >= 0 && f[1] >= 0) // Regular face
+  {
+    swap(v0, v1);
+  }
+
+  flippableResult = flippable(OTriKey(f.data()), v0, v1);
+  if (flippableResult != FLIPPABLE && flippableResult != SEMI_FLIPPABLE && flippableResult != SEMI_FLIPPABLE+1 && flippableResult != SEMI_FLIPPABLE+2)
+    return flippableResult;
+
+  // Remove 3 original tets and add 2 new tets
+  removeBall(ball0);
+  removeBall(ball1);
+  removeBall(ball2);
+  addBall(f[0], f[1], f[2], v0);
+  addBall(f[0], f[2], f[1], v1);
+  return FLIPPABLE;
+}
+
+int DelaunayMesher::getTwoBallsByFace(const OTriKey& face, std::pair<DelaunayBall*, DelaunayBall*> & twoBalls)
+{
+  DelaunayBall * ball = getOneBallByFace(face);
+  if (!ball)
+    return -1;
+  int ind = ball->getNeighborIndex(face);
+  if (ind == -1)
+    return -2;
+  twoBalls = make_pair(ball, ball->nbr[ind]);
+  return 0;
+}
+
+DelaunayMesher::DelaunayBall* DelaunayMesher::getOneBallByFace(const OTriKey& face)
+{
+  // Find the segment
+  int result = getOneBallBySegment(face[0], face[1]);
+  DelaunayBall * & ball = vertex2ball[face[0]];
+
+  if ((result & 255) != ON_VERTEX) // the end point must be on the ball
+  {
+    printf("Failed to find the segment (%d, %d) in tetmesh\n", face[0], face[1]);
+    cout << *ball << endl;
+    return NULL;
+  }
+  else
+  {
+    while (true)
+    {
+      // get the two indices of vertices in the ball
+      int v0 = ball->getInvertedIndex(face[0]);
+      int v1 = ball->getInvertedIndex(face[1]);
+      int f0, f1;
+      getOtherTwoIndices(v0, v1, f0, f1);
+      OTriKey f = ball->oFaceKey(f0);
+      if (toPlane(face[2], f[0], f[1], f[2]) > 0)
+        ball = ball->nbr[f0];
+      else
+      {
+        f = ball->oFaceKey(f1);
+        if (toPlane(face[2], f[0], f[1], f[2]) > 0)
+          ball = ball->nbr[f1];
+        else
+          return ball;
+      }
+    }
+  }
+}
+
+/*
+ 0: intersect with face
+ 1: on a face
+ 2: intersect with edge
+ 3: on an edge
+ 4: intersect with vertex
+ 5: on a vertex
+ 6: in the tet
+ */
+
+int DelaunayMesher::getOneBallBySegment(const int start, const int end)
+{
+  if (start == end) // it's not a segment
+    return -6;
+
+  DelaunayBall *  & ball = vertex2ball[start];
+  if (ball->isInfinite())
+  {
+    ball = ball->nbr[0];
+  }
+  int i;
+  while (true)
+  {
+    if (!ball)
+      cout << "Null pointer" << endl;
+    int originInd = ball->getInvertedIndex(start);
+      if (ball->isInfinite())
+      {
+        printf("Infinite ball\n");
+        return -2;
+      }
+      if (originInd == -1)
+        return -1;
+
+    //OFaceKey
+    int sign[4];
+    for (i = 1; i < 4; i++)
+    {
+      OTriKey face = ball->oFaceKey(permute[originInd][i]);
+      if ((sign[i] = toPlane(end, face[0], face[1], face[2])) == 1)
+        break;
+    }
+    if (i < 4)
+    {
+      ball = ball->nbr[permute[originInd][i]]; // go to its neighbor ball
+    }
+    else
+    {
+      OTriKey face = ball->oFaceKey(permute[originInd][0]);
+      sign[0] = toPlane(end, face[0], face[1], face[2]);
+      int sumSign = -(sign[1] + sign[2] + sign[3]);
+      if (sign[0] == 1)
+      {
+        switch (sumSign)
+        {
+          case 1:
+            return SECT_VERTEX;
+          case 2:
+            if (sign[1] == 0)
+            {
+              return (permute[originInd][2] << 8) | (permute[originInd][3] << 12) | SECT_EDGE;
+            }
+            else if (sign[2] == 0)
+              return (permute[originInd][3] << 8) | (permute[originInd][1] << 12) | SECT_EDGE;
+            else //if (sign[3] == 0)
+              return (permute[originInd][1] << 8) | (permute[originInd][2] << 12) | SECT_EDGE;
+          case 3:
+            return SECT_FACE | (permute[originInd][0] << 8);
+          default:
+            return -3;
+        }
+      }
+      else
+      {
+        sumSign -= sign[0];
+        switch (sumSign)
+        {
+          //TODO: consider a degenerated tet
+          case 1:
+            return ON_VERTEX;
+          case 2:
+            return ON_EDGE;
+          case 3:
+            return ON_FACE;
+          case 4:
+            return IN_TET;
+          default:
+            return -4;
+        }
+      }
+    }
+  }
+  return -5;
+}
+
+int DelaunayMesher::segmentRecoveryUsingFlip(const OEdgeKey & lineSegment, int depth)
+{
+  const int maxIterations = 100; // needed to avoid an infinite loop
+  int iter = -1;
+  while (true)
+  {
+    iter++;
+    if (iter >= maxIterations)
+      return -3;
+
+    //printf("*"); fflush(NULL);
+    OEdgeKey edgeToBeRemoved;
+    // find a tet such that the vertex lineSegment[0] is a tet vertex, and the line segment goes through the tet
+    // result stores how the line segment goes through the tet
+    //    ON_VERTEX: the line segment goes down a tet edge and ends at another tet vertex
+    //    SECT_EDGE: the line segment goes down an interior of a tet face, and exits through an edge
+    //    SECT_FACE: the line segment goes through the interior of the tet, and exits through the opposite tet interior face
+    int result = getOneBallBySegment(lineSegment[0], lineSegment[1]); 
+    int locationType = result & 255;
+    if (locationType == ON_VERTEX)
+    {
+      recoveredEdge.insert(UEdgeKey(lineSegment[0], lineSegment[1]));
+      return 0; // this edge is already recovered; no more work needed
+    }
+    else if (locationType == SECT_FACE) 
+    { 
+      // line segment intersects the interior of the face, try flip23
+      int originIdx = vertex2ball[lineSegment[0]]->getInvertedIndex(lineSegment[0]);
+      OTriKey face = vertex2ball[lineSegment[0]]->oFaceKey(originIdx);
+      int flipResult = flip23(face, vertex2ball[lineSegment[0]]->nbr[originIdx], vertex2ball[lineSegment[0]]);
+      if (flipResult == FLIPPABLE)
+        continue; // successfully flipped, search for the next face
+      else
+      {
+        // flip23 did not succeed
+        if (flipResult >= SEMI_FLIPPABLE)
+          flipResult -= SEMI_FLIPPABLE;
+        edgeToBeRemoved = face.oEdgeKey(flipResult);
+      }
+    }
+    else if (locationType == SECT_EDGE)
+    {
+      int v0 = vertex2ball[lineSegment[0]]->getVtx((result >> 8) & 15);
+      int v1 = vertex2ball[lineSegment[0]]->getVtx((result >> 12) & 15);
+      edgeToBeRemoved = OEdgeKey(v0, v1);
+    }
+
+    if (segmentRemovalUsingFlip(edgeToBeRemoved, depth) != 0)
+    {
+      // removal failed
+      return -2;
+    }
+  }
+  return -1;
+}
+
+int DelaunayMesher::segmentRemovalUsingFlip(const OEdgeKey & edge, int depth)
+{
+  if (recoveredEdge.find(UEdgeKey(edge[0], edge[1])) != recoveredEdge.end())
+    return -8; // a recovered edge can't be removed
+  if (--depth < 0)
+    return -9;
+
+  TetAroundEdge tetsAroundEdge;
+  if (getTetsAroundEdge(edge, tetsAroundEdge) < 3)
+  {
+    //assert(0);
+    return -1;
+  }
+  for (TetAroundEdge::iterator itr = tetsAroundEdge.begin(); itr != tetsAroundEdge.end(); itr++)
+    ++eleCounter[itr->third->label]; // add one reference of the tet
+
+  size_t k = tetsAroundEdge.size() + 1;
+  while (tetsAroundEdge.size() > 3)
+  {
+    if (tetsAroundEdge.size() >= k)
+      return 5;
+    k = tetsAroundEdge.size();
+    TetAroundEdge::iterator itr;
+    for (itr = tetsAroundEdge.begin(); itr != tetsAroundEdge.end(); itr++)
+    {
+      DelaunayBall * ball = itr->third;
+      TetAroundEdge::iterator nextItr = itr;
+      if (++nextItr == tetsAroundEdge.end())
+        nextItr = tetsAroundEdge.begin();
+      DelaunayBall * nextBall = nextItr->third;
+      if (ball->isInfinite() != nextBall->isInfinite() || (tetsAroundEdge.size() > 4 && ball->isInfinite()))
+        continue;
+      if (eleCounter[ball->label] > 1 || eleCounter[nextBall->label] > 1)
+        continue;
+      OTriKey f = itr->second;
+
+      int vIdx = f.getInvertedIndex(itr->first);
+      DelaunayBall * newBallAroundEdge;
+      int allowFlat = -1;
+      if (tetsAroundEdge.size() == 4)
+      {
+        allowFlat = vIdx;
+      }
+      if (flip23(f, nextBall, ball, vIdx, &newBallAroundEdge, allowFlat) == FLIPPABLE)
+      {
+        // Insert the new tet
+        vIdx = 3 - nextItr->second.getInvertedIndex(edge[0]) - nextItr->second.getInvertedIndex(edge[1]);
+        tetsAroundEdge.insert(itr, make_triple(nextItr->second[vIdx], nextItr->second, newBallAroundEdge));
+        eleCounter[newBallAroundEdge->label]++;
+        //cout << *newBallAroundEdge << endl;
+        // Remove the old two tets
+        tetsAroundEdge.erase(itr);
+        tetsAroundEdge.erase(nextItr);
+
+        break;
+      }
+    }
+    if (itr == tetsAroundEdge.end())
+    {
+	// No face is flippable
+      if (depth > 0)
+      {
+        // Try to remove an edge
+        for (TetAroundEdge::iterator itr = tetsAroundEdge.begin(); itr != tetsAroundEdge.end(); itr++)
+        {
+          DelaunayBall * ball = itr->third;
+          TetAroundEdge::iterator nextItr = itr;
+          if (++nextItr == tetsAroundEdge.end())
+            nextItr = tetsAroundEdge.begin();
+          DelaunayBall * nextBall = nextItr->third;
+          if (ball->isInfinite() || nextBall->isInfinite())
+            continue;
+          if (eleCounter[ball->label] > 1 || eleCounter[nextBall->label] > 1)
+            continue;
+          OTriKey f = itr->second;
+          int flipResult = flip23(f, nextBall, ball);
+          if (flipResult >= 4 && flipResult < 8)
+            flipResult -= 4;
+          if (flipResult >= 0 && flipResult < 3)
+          {
+            OEdgeKey e = f.oEdgeKey(flipResult);
+            if (segmentRemovalUsingFlip(e, depth) == 0)
+            {
+
+              tetsAroundEdge.erase(itr);
+              tetsAroundEdge.erase(nextItr);
+              break;
+            }
+          }
+        }
+      }
+      if (itr == tetsAroundEdge.end())
+      {
+        clearCounter(tetsAroundEdge);
+        return -4;
+      }
+      else
+      {
+        return -5;
+      }
+    }
+  }
+  if ((flip32(edge, tetsAroundEdge)) == FLIPPABLE)
+  {
+    return 0;
+  }
+  else
+  {
+    clearCounter(tetsAroundEdge);
+    cout << "FLIP32 fail" << endl;
+    return -1;
+  }
+}
+
+int DelaunayMesher::segmentRecoveryUsingSteinerPoint(const OEdgeKey& edge)
+{
+  int result = getOneBallBySegment(edge[0], edge[1]);
+  int locationType = result & 255;
+
+  OEdgeKey removalEdge;
+  if (locationType != SECT_EDGE)
+    return 200;
+  if (locationType == SECT_EDGE)
+  {
+    int v0 = vertex2ball[edge[0]]->getVtx((result >> 8) & 15);
+    int v1 = vertex2ball[edge[0]]->getVtx((result >> 12) & 15);
+    removalEdge = OEdgeKey(v0, v1);
+  }
+  TetAroundEdge tetAroundEdge;
+  vertex2ball[removalEdge[0]] = vertex2ball[edge[0]];
+  DelaunayBall *& ball = vertex2ball[removalEdge[0]];
+  if (ball->isInfinite())
+    ball = ball->nbr[0];
+
+  int v0 = ball->getInvertedIndex(removalEdge[0]);
+  int v1 = ball->getInvertedIndex(removalEdge[1]);
+  int currentFaceNumber = faceLookUpTableByTwoVertices[v0][v1];
+  if (ball->getVtx(currentFaceNumber) != edge[0])
+  {
+    ball = ball->nbr[currentFaceNumber];
+  }
+  v0 = ball->getInvertedIndex(removalEdge[0]);
+  v1 = ball->getInvertedIndex(removalEdge[1]);
+  currentFaceNumber = faceLookUpTableByTwoVertices[v0][v1];
+  assert( ball->getVtx(currentFaceNumber) == edge[0]);
+  vertex2ball[removalEdge[0]] = ball;
+  getTetsAroundEdge(removalEdge, tetAroundEdge);
+
+  assert(tetAroundEdge.begin()->first == edge[0]);
+
+  int idx = 0;
+  int e = -1;
+  int reverse = 0;
+  assert(tetAroundEdge.size() >= 5);
+  for (TetAroundEdge::const_iterator itr = tetAroundEdge.begin(); itr != tetAroundEdge.end(); itr++)
+  {
+    TetAroundEdge::const_iterator nextItr = itr;
+    nextItr++;
+    if (nextItr == tetAroundEdge.end())
+      nextItr = tetAroundEdge.begin();
+    if (itr->first == edge[1])
+      e = idx;
+    if (itr->first == -1 || nextItr->first == -1)
+    {
+      if (e != -1)
+        reverse = 1;
+      else
+        reverse = -1;
+      idx++;
+      continue;
+    }
+    idx++;
+  }
+  if (reverse == 0)
+  {
+    if (e <= 2)
+    {
+      reverse = -1;
+    }
+    else
+      reverse = 1;
+  }
+
+  TetAroundEdge::const_iterator itr = tetAroundEdge.begin();
+  if (reverse == -1)
+  {
+    tetAroundEdge.push_back(*tetAroundEdge.begin());
+    while (tetAroundEdge.begin()->first != edge[1])
+      tetAroundEdge.pop_front();
+  }
+  else
+  {
+    while ((int)tetAroundEdge.size() != e + 1)
+      tetAroundEdge.pop_back();
+  }
+
+  assert(tetAroundEdge.size() >= 4);
+
+  itr = tetAroundEdge.begin();
+  map<OTriKey, DelaunayBall *> cavityNeighbors;
+  for (itr++ ; itr != tetAroundEdge.end(); itr++)
+  {
+    OTriKey face(itr->first, itr->third->getVtxOpposeFace(itr->second), removalEdge[1]);
+    cavityNeighbors.insert(make_pair(face, itr->third->getNeighborByFace(face)));
+    face = OTriKey(itr->first, removalEdge[0], itr->third->getVtxOpposeFace(itr->second));
+    cavityNeighbors.insert(make_pair(face, itr->third->getNeighborByFace(face)));
+  }
+
+  tetAroundEdge.pop_front();
+  if (reverse == 1)
+  {
+    OTriKey face(removalEdge[0], removalEdge[1], edge[0]);
+    DelaunayBall * ball = (tetAroundEdge.begin())->third;
+    assert(ball->getNeighborIndex(face) != -1);
+    cavityNeighbors.insert(make_pair(face, ball->getNeighborByFace(face)));
+    face = OTriKey(removalEdge[1], removalEdge[0], edge[1]);
+    ball = (--tetAroundEdge.end())->third;
+    assert(ball->getNeighborIndex(face) != -1);
+    cavityNeighbors.insert(make_pair(face, ball->getNeighborByFace(face)));
+  }
+  else
+  {
+    OTriKey face(removalEdge[0], removalEdge[1], edge[1]);
+    DelaunayBall * ball = (tetAroundEdge.begin())->third;
+    assert(ball->getNeighborIndex(face) != -1);
+    cavityNeighbors.insert(make_pair(face, ball->getNeighborByFace(face)));
+    face = OTriKey(removalEdge[1], removalEdge[0], edge[0]);
+    ball = (--tetAroundEdge.end())->third;
+    assert(ball->getNeighborIndex(face) != -1);
+    cavityNeighbors.insert(make_pair(face, ball->getNeighborByFace(face)));
+  }
+
+  //Calculate the gravity center
+
+  Vec3d center(0, 0, 0);
+  double volume = 0;
+  for (itr = tetAroundEdge.begin(); itr != tetAroundEdge.end(); itr++)
+  {
+    DelaunayBall * ball = itr->third;
+    Vec3d c = (ball->getPosition(0) + ball->getPosition(1) + ball->getPosition(2) + ball->getPosition(3)) / 4;
+    double v = TetMesh::getTetVolume(ball->getPosition(0), ball->getPosition(1), ball->getPosition(2), ball->getPosition(3));
+    assert(v > 0);
+    center += c * v;
+    volume += v;
+  }
+  center /= volume;
+
+  //Check all volumes
+  for (map<OTriKey, DelaunayBall *>::iterator itr = cavityNeighbors.begin(); itr != cavityNeighbors.end(); itr++)
+  {
+    OTriKey face = itr->first;
+    assert (toPlane(center, face[0], face[1], face[2]) == -1);
+  }
+
+  // Add the Steiner point
+  int newVtx = inputVertices.size();
+  inputVertices.push_back(center);
+
+#ifdef USE_QUERY_IN_DELAUNAY
+  query->addVertex(center);
+#endif
+
+  vertex2ball.push_back(NULL);
+
+  // Remove old balls
+
+  for (itr = tetAroundEdge.begin(); itr != tetAroundEdge.end(); itr++)
+  {
+    DelaunayBall * ball = itr->third;
+    balls.erase(ball);
+    delete ball;
+  }
+
+  // Add new balls
+  map<UTriKey, DelaunayBall *> hangingFaces;
+  for (map<OTriKey, DelaunayBall *>::iterator itr = cavityNeighbors.begin(); itr != cavityNeighbors.end(); itr++)
+  {
+
+    OTriKey oface = itr->first;
+    DelaunayBall * ball = createBall(oface[0], oface[2], oface[1], newVtx);
+    balls.insert(ball);
+    vertex2ball[oface[0]] = vertex2ball[oface[1]] = vertex2ball[oface[2]] = vertex2ball[newVtx] = ball;
+    //set boundary neighbor
+    UTriKey uface(oface[0], oface[1], oface[2]);
+    assert(ball->getNeighborIndex(uface) != -1);
+    ball->setNeighbor(uface, itr->second);
+    itr->second->setNeighbor(uface, ball);
+    assert(itr->second->getNeighborIndex(uface) != -1);
+
+    //set inter neighbor
+    for (int i = 0; i < 4; i++)
+      if (ball->nbr[i] == NULL)
+      {
+        UTriKey uface = ball->uFaceKey(i);
+        map<UTriKey, DelaunayBall *>::iterator iter = hangingFaces.find(uface);
+        if (iter == hangingFaces.end())
+          hangingFaces.insert(make_pair(uface, ball));
+        else
+        {
+          ball->nbr[i] = iter->second;
+          iter->second->setNeighbor(uface, ball);
+          hangingFaces.erase(iter);
+        }
+      }
+  }
+ // assert(hangingFaces.empty());
+  return 100;
+}
+
diff --git a/libraries/mesher/delaunayMesher.h b/libraries/mesher/delaunayMesher.h
new file mode 100644
index 0000000000000000000000000000000000000000..511629eceaf96126ea41f33bf7cdd45811e3ec29
--- /dev/null
+++ b/libraries/mesher/delaunayMesher.h
@@ -0,0 +1,399 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Compute 3D Delaunay tetrahedralization of a set of input points in 3D.
+  The tetrahedralization can be updated incrementally by adding new points.
+*/
+
+#ifndef _DELAUNAYMESHER_H_
+#define _DELAUNAYMESHER_H_
+
+#include <stddef.h>
+#include <cassert>
+#include <iostream>
+#include <list>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+#include "objMeshOctree.h"
+#include "vec3d.h"
+#include "vegalong.h"
+#include "triangle.h"
+#include "triple.h"
+#include "tetMesh.h"
+#include "tetKey.h"
+#include "triKey.h"
+
+class TetMesh;
+
+class DelaunayMesher
+{
+public:
+  DelaunayMesher();
+  virtual ~DelaunayMesher();
+
+  /***************************************************************************
+   *              Basic Functions for Delaunay Tetrahedralization            *
+   ***************************************************************************/
+  // compute Delaunay tetrahedralization of a set of input points
+  // must have points.size() >= 4
+  // epsilon: threshold used to check whether input vertices are degenerate
+  //          the larger the epsilon, the more input will be treated as degenerate by DelaunayMesher
+  // returns true upon success, and false upon failure (degeneracy)
+  // use getMesh() to get the result
+  bool computeDelaunayTetrahedralization(const std::vector<Vec3d> & points,
+    double epsilon = 1e-9);
+
+  // return the current Delaunay mesh
+  TetMesh * getMesh() const;
+
+  // after computeDelaunayTetrahedralization, insert one additional point into the tetrahedralization
+  bool addOnePoint(const Vec3d & p);
+  size_t getNumVertices() const { return inputVertices.size(); } // get the total number of input vertices 
+  const Vec3d & getVertex(int index) const { return inputVertices[index]; } // get the position of an input vertex
+
+  // clear the mesh to empty (one can then call computeDelaunayTetrahedralization on a new input)
+  void clear();
+
+  // **********************************************************************************
+  // All functionality below here mostly exists to support our isomesher and tetMesher
+  // We do not expect functions below here to be of general-purpose use
+  // **********************************************************************************
+
+  /***************************************************************************
+   *  Functions for CDT (Constrained Delaunay Tetrahedralization)            *
+   *                                                                         *
+   ***************************************************************************/
+
+  // initialize the Constrained Delaunay Tetrahedralization algorithm; to conform to the surface faces of "mesh"
+  // the tet mesh "mesh" must be manifold and free of self-intersections
+  // further calls to this->addOnePoint() will incrementally rebuild the tetrahedralization while preserving the constrained Delaunay property
+  bool initializeCDT(TetMesh * mesh, double epsilon = 1e-6);
+
+  // Must do remove outside and all infite balls first
+  int buildCDT();
+  // get the surface mesh of the tet mesh "mesh" passed to initializeCDT (used in the Constrained Delaunay Tetrahedralization)
+  inline const ObjMesh * getCDTBoundaryMesh() const { return boundaryMesh; }
+
+  /***************************************************************************
+   *                      Voronoi Edge Query Functions                       *
+   ***************************************************************************/
+
+  // if input is true, DelaunayMesher will record Voronoi Edge update information for each subsequent operation (computeDelaunayTetrahedralization or addOnePoint)
+  // get last Voronoi Edge update by calling getDeletedVoronoiEdges() and getAddedVoronoiEdges
+  void computeVoronoiEdgeModification(bool compute = true) { computeVEdgeModification = compute; }
+
+  // A struct for Voronoi Edge
+  // A Voronoi Edge is the line connecting two adjacent tet centers in Delaunay, or an infinite line starting at one border tet center,
+  // and pointing outside perpendicular to a border face on this tet
+  struct VoronoiEdge
+  {
+    bool finite; //whether this edge has finite length
+    Vec3d start; // start point of the edge
+    Vec3d end;   // end point if it's not infinite, otherwise undefined
+    Vec3d direction; // direction of the edge if it's infinite, otherwise undefined
+    VoronoiEdge() : finite(true), start(0.0), end(0.0), direction(0.0) {}
+    VoronoiEdge(bool isFinite, const Vec3d & start, const Vec3d & other);
+    bool isFinite() const { return finite; }
+  };
+
+  typedef std::map<UTriKey, VoronoiEdge> VoronoiEdgeMap;
+  typedef VoronoiEdgeMap::iterator VEdgeIter;
+  typedef VoronoiEdgeMap::const_iterator VEdgeCIter;
+
+  // vEdgeDeleted and vEdgeAdded will hold Voronoi edges only for last operation on Delaunay
+  // e.g. when addOnePoint() is called, vEdgeDeleted is reset and only stores the edges deleted by the last addOnePoint() call
+  const VoronoiEdgeMap & getDeletedVoronoiEdges() const { return vEdgeDeleted; }
+  const VoronoiEdgeMap & getAddedVoronoiEdges() const { return vEdgeAdded; }
+
+  /***************************************************************************
+   *                     Delaunay Ball Query Functions                       *
+   ***************************************************************************/
+
+  // Delaunay Balls are used for representing Delaunay
+  typedef vegaunsignedlong label_t;
+  struct DelaunayBall;
+  struct DelaunayBallCompare
+  {
+    bool operator() (const DelaunayBall * const & p1, const DelaunayBall * const & p2) const { return p1->label < p2->label; }
+  };
+
+  typedef std::set<DelaunayBall *, DelaunayBallCompare> BallSet;
+  typedef BallSet::iterator BallIter;
+  typedef BallSet::const_iterator BallCIter;
+
+  // used to traverse all the balls
+  inline BallCIter getBallSetBegin() const { return balls.begin(); }
+  inline BallCIter getBallSetEnd() const { return balls.end(); }
+
+  // used to traverse all the ballsToDelete
+  inline BallCIter getBallToDeleteSetBegin() const { return ballsToDelete.begin(); }
+  inline BallCIter getBallToDeletelSetEnd() const { return ballsToDelete.end(); }
+
+  // used to traverse all the ballsAdded
+  inline BallCIter getBallAddedSetBegin() const { return ballsAdded.begin(); }
+  inline BallCIter getBallAddedSetEnd() const { return ballsAdded.end(); }
+
+  // There're two kinds of Delaunay Balls: regular and infinite
+  // Regular ball (constructor input parameter v0 >= 0) is built by four vertices and represent a tetrahedron.
+  // Infinite ball (constructor input parameter v0 < 0) is a ball with infinite radius representing the half space separated by a border face of the Delaunay
+  // the other three vtx indices of one infinite ball give the border face triangle on the convex hull, their orientation points inside the convex hull
+  // Infinite ball's center is at infinity, on a line perpendicular to the border face and intersecting the face at its circumcenter
+  // In short, regular ball represents the space inside one tetrahedron, infinite ball represents the empty space outside one border face on the convex hull
+  struct DelaunayBall : public OTetKey
+  {
+    // get vertex of this ball
+    // if it's infinite ball, getVtx(0) returns -1
+    inline int getVtx(int ind) const { return v[ind]; }
+    // get the array stores the 4 vtx indices
+    inline const int * getVertices() const {return &v[0];}
+    // get the position of vertices
+    inline Vec3d getPosition(int ind) const { return parent.getVertex(v[ind]); }
+
+    inline bool isRegular() const { return v[0] >= 0; }
+    inline bool isInfinite() const { return v[0] < 0; }
+
+    // get ball center if it's regular ball
+    inline const Vec3d & getCenter() const { return center; }
+
+    // for regular ball, return >0 for outside, ==0 for on the ball, <0 for inside
+    // for infinite ball, return >0 for on the inner side of the border face (or the plane the face lies on), ==0 for on the plane, <0 for on the outside of the border face
+    int contain(const Vec3d & p) const;
+    // Used only in CDT. Always return false for infinite ball
+    // For regular ball, if atleast one of the four ball vtx cannot be seen from p because the CDT boundary mesh obscures the sight, return false. Otherwise return true
+    // This can be tested by creating a line segment from p to one ball vtx and test intersection of this line segment with boundary mesh
+    // Notice that if the ball vtx is a mesh vtx of the boundary mesh, this does not counted as one valid intersection in this case
+    bool visibleTo(const Vec3d & p) const;
+    inline DelaunayBall * getNeighbor(const int ind) const { return nbr[ind]; }
+
+  protected:
+
+    // ==================== Member Vars =================================
+
+    const DelaunayMesher & parent; // pointer to DelaunayMesher
+
+    Vec3d center; //for regular ball, the center
+
+    // neighboring balls to this ball; there are always four neighbors
+    // regular ball: for every face of this tet, there is a neighboring tet across that face (may be an infinite ball)
+    // infinite ball: the border face (triangle) has three neighbors, one for each face edge; neighbors of the ball are the regular ball across the triangle, and the three infintie balls corresponding to the convex hull neighboring triangles
+    DelaunayBall * nbr[4];
+
+    label_t label; //label unique to every Delaunay Ball
+
+    // ==================== End Member Vars =============================
+
+    DelaunayBall(int v0, int v1, int v2, int v3, DelaunayMesher * parent, label_t label);
+
+    //return value:
+    //1 means point is outside the ball
+    //0 means point is on the ball
+    //-1 means point is inside the ball
+    int contains(int vtx) const;
+
+    // A regular face is one face in Delaunay. Two Delaunay Balls (regular vs. regular, or regular vs. infinite) share one regular face
+    // An irregular face is one line segment (f.v[1], f.v[2]) on a face.
+    // Two infinite balls share an irregular face. This is actually two border faces share one triangle edge.
+    inline static bool isFaceRegular(const UTriKey & f) { return f[0] >= 0; }
+    inline static bool isFaceRegular(const OTriKey & f) { return f[0] >= 0; }
+
+    // get the indices forming the infinite ball border face triangle
+    const int * getInfiniteBallTriangle() const { assert(!isRegular()); return &v[1]; }
+
+    // if uface[i] == key, return i, otherwise return -1
+    int getNeighborIndex(const UTriKey & key) const;
+    // if oface[i] == key, return i, otherwise return -1
+    int getNeighborIndex(const OTriKey & key) const;
+
+    inline int getVtxOpposeFace(const OTriKey & key) const { return v[getNeighborIndex(key)]; }
+    DelaunayBall * getNeighborByFace(const OTriKey & key) const { return nbr[getNeighborIndex(key)]; }
+    // if uface[i] == key, set DelaunayBall::nbr[i] to be nbr
+    void setNeighbor(const UTriKey & key, DelaunayBall * nbr);
+
+    // get the Voronoi Edge connecting this Delaunay ball and one neighbor ball at nbr[nbrIndex]
+    VoronoiEdge getVoronoiEdge(int nbrIndex) const;
+
+    friend class DelaunayMesher;
+
+    friend std::ostream & operator << (std::ostream & o, const DelaunayBall & ball);
+
+  };
+
+  // recover an edge by flipping, level means the maximal levels of recursion the recover process can have
+  int segmentRecoveryUsingFlip(const OEdgeKey & edge, int level);
+  // recover an edge by adding steiner points
+  int segmentRecoveryUsingSteinerPoint(const OEdgeKey & edge);
+
+  // simply add a ball for the delaunayMesher, we won't check whether it is still a delaunay mesh
+  // v is an array of the four indices of the vertices in the delaunay mesh
+  DelaunayMesher::DelaunayBall * addBall(const int * v);
+  // simply remove a ball for the delaunayMesher, a hole may exist
+  void removeBall(DelaunayBall * ball);
+
+  /***************************************************************************
+   *                          Debugging Functions                            *
+   ***************************************************************************/
+
+  // check whether each tet satisfyies the Delaunay criterion by looping over every tet-vtx pair
+  // It's slow. For debugging purpose.
+  bool checkDelaunay() const;
+
+protected:
+  // First, the apex of the tet, second, the shared face of the tet, third, the pointer to the tet
+  typedef std::list<triple <int, OTriKey, DelaunayBall*> > TetAroundEdge;
+
+  // Fliping a face, the face is shared by ball0 and ball1
+  // ball0 and ball1 will be removed and three new balls will be added to the delaunay mesh
+  // It can return one of the three added balls by setting requestNewBallIdx and newBallAroundEdge
+  // By setting allowFlat, sometimes the three added balls may be flat and then removed by the next flip32.
+  int flip23(const OTriKey & face, DelaunayBall * ball0, DelaunayBall * ball1, int requestNewBallIdx = -1, DelaunayBall ** newBallAroundEdge = NULL, int allowFlat = -1);
+  // Fliping a edge, there must be 3 tets around the edge
+  int flip32(const OEdgeKey & edge, const TetAroundEdge & tetsAroundEdge);
+
+  // remove an edge by flipping, level means the maximal levels of recursion
+  int segmentRemovalUsingFlip(const OEdgeKey & edge, int level = 1);
+
+
+  // Find two delaunay balls that have the face
+  int getTwoBallsByFace(const OTriKey & face, std::pair<DelaunayBall*, DelaunayBall *> & twoBalls);
+
+
+  // Find one delaunay ball that has the face
+  DelaunayBall * getOneBallByFace(const OTriKey & face);
+
+  // Find tets that have the edge
+  int getTetsAroundEdge(const OEdgeKey & edge, TetAroundEdge & tetsAroundEdge);
+
+  // Clear the counter of some tets
+  int clearCounter(const DelaunayMesher::TetAroundEdge & tetsAroundEdge);
+
+  // The origin of the return ball should be start
+  int getOneBallBySegment(const int start, const int end);
+
+  //void debug() const;
+
+//  double getCheckContainingTime() const { return checkContainingWatch.getElapsedTime(); }
+//  double getUpdateTime() const { return updateWatch.getElapsedTime(); }
+
+  DelaunayMesher::DelaunayBall * addBall(const int v0, const int v1, const int v2, const int v3);
+
+  // compute circumcenter of a tet
+  static Vec3d circumcenter(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+
+  // get all the Delaunay balls containing the point getDelaunayVtx(i)
+  void getBallsContainingPoint(int i, BallSet & balls);
+  
+  // used to construct neighboring information for new Delaunay balls
+  // search face in neighboringStructure. If face is found, ball is neighbor to neighboringStructure[face], build related neighboring data
+  int buildNeighbor(const UTriKey & face, DelaunayBall * ball, std::map<UTriKey, DelaunayBall *> & neighboringStructure);
+  inline int buildNeighbor(int v0, int v1, int v2, DelaunayBall * ball, std::map<UTriKey, DelaunayBall *> & neighboringStructure)  {return buildNeighbor(UTriKey(v0, v1, v2), ball, neighboringStructure);}
+
+  // get only one ball containing the point getDelaunayVtx(i)
+  DelaunayBall * getOneBallContainingPoint(int i);
+
+  DelaunayBall * createBall(int v0, int v1, int v2, int v3);
+
+  //DelaunayMesher(const DelaunayMesher &);
+
+  // initialize the first four vertices for Delaunay 
+  // return false if failed 
+  bool initialize(int a, int b, int c, int d);
+
+  bool update(int i); // update Delaunay for the new point at index i
+
+  Vec3d getFaceNormal(const OTriKey & face) const;
+
+  static inline double det(const Vec3d & a, const Vec3d & b, const Vec3d & c) { return dot(a, cross(b, c)); }
+
+  static bool isTetMeshFaceManifold(const TetMesh * tetMesh); // check whether any tet mesh face is at most shared by two tets
+
+  int toPlane(int i, int a, int b, int c) const;
+
+  int toCircumsphere(int i, int a, int b, int c) const;
+
+  int toCircumsphere(int i, int a, int b, int c, int d) const;
+
+  int toPlane(const Vec3d & p, int a, int b, int c) const;
+
+  int toCircumsphere(const Vec3d & p, int a, int b, int c) const;
+
+  int toCircumsphere(const Vec3d & p, int a, int b, int c, int d) const;
+
+  int flippable(const OTriKey &face, const int v0, const int v1);
+
+  double epsilon;
+  std::vector<Vec3d> inputVertices;
+  std::set<Vec3d> verticesSet;
+
+  BallSet balls;
+  BallSet ballsToDelete;
+  BallSet ballsAdded;
+
+  std::map<UTriKey, DelaunayBall *> neighboringStructure; // Used for modifying balls
+
+  // Look up by the index of the vertex, only return one of the balls that contains the vertex
+  std::vector <DelaunayBall*> vertex2ball;
+
+  std::set <UEdgeKey> recoveredEdge;
+
+  // ============ DEBUG CODE ====================
+//  typedef std::map<label_t, DelaunayBall *> BallMap;
+//  typedef BallMap::iterator BallMapIter;
+//  typedef BallMap::const_iterator BallMapCIter;
+//  BallMap ballMap;
+////  std::map<label_t, label_t> ballConnections;
+//
+//  struct Operation {
+//    std::vector<label_t> deletedBalls;
+//    std::vector<label_t> addedBalls;
+//  };
+//
+//  std::vector<Operation> opStack;
+//  TetMeshManifold tetManifold;
+  // ============ END DEBUG CODE ==================
+
+  bool computeVEdgeModification;
+  VoronoiEdgeMap vEdgeDeleted;
+  VoronoiEdgeMap vEdgeAdded;
+
+  // data for CDT
+  ObjMesh * boundaryMesh;
+  //std::vector<UTriKey> boundaryTriangles;
+  ObjMeshOctree<TriangleBasic> * boundaryOctree;
+
+  label_t nextBallLabel;
+};
+
+#endif
+
diff --git a/libraries/mesher/isosurfaceMesher.cpp b/libraries/mesher/isosurfaceMesher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f5b8fd4c9b62f862e5c93b1101a9fb5317bf124
--- /dev/null
+++ b/libraries/mesher/isosurfaceMesher.cpp
@@ -0,0 +1,701 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <set>
+#include <cfloat>
+#include <limits.h>
+#include <algorithm>
+#include <fstream>
+
+using namespace std;
+
+#include "matrixIO.h"
+#include "isosurfaceMesher.h"
+#include "marchingCubes.h"
+#include "delaunayMesher.h"
+#include "objMeshOrientable.h"
+#include "performanceCounter.h"
+
+#ifndef M_PI
+  #define M_PI 3.1415926525897932384
+#endif
+
+IsosurfaceMesher::IsosurfaceMesher(DistanceFieldBase * f) : field(f)
+{
+  delaunay.computeVoronoiEdgeModification(true);
+}
+
+IsosurfaceMesher::IsosurfaceMesher(ObjMesh * d) : detailedSurfaceMesh(d)
+{
+  delaunay.computeVoronoiEdgeModification(true);
+}
+
+IsosurfaceMesher::~IsosurfaceMesher()
+{
+  delete isoMesh;
+  delete marchingCubesMesh;
+#ifdef USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+  delete objMeshOctree;
+#endif
+}
+
+void IsosurfaceMesher::setStepping(double iso, double a, double r, size_t nis, double ep)
+{
+  epsilon = ep;
+  assert(epsilon >= 0 && epsilon <= 1);
+  isovalue = iso;
+  angularBound = a;
+  radialBound = r;
+  numInitialSamples = nis;
+
+  //clamp angle bound
+  cosAngularBound = cos(angularBound * (M_PI / 180.0));
+  if (cosAngularBound < sqrt(3.) / 2.)
+    cosAngularBound = sqrt(3.) / 2.;
+
+  if (detailedSurfaceMesh == NULL)
+  {
+    PerformanceCounter pc;
+    pc.StartCounter();
+    marchingCubesMesh = MarchingCubes::compute(field, isovalue);
+
+    assert(marchingCubesMesh);
+    pc.StopCounter();
+    cout << "Finished marching cubes: " << pc.GetElapsedTime() << endl;
+    //regroup the marching cubes mesh according to connected components
+    splitIsosurfaceMesh = marchingCubesMesh->splitIntoConnectedComponents(0, 0);
+
+    if (saveMarchingCubesObj == false)
+    {
+      delete marchingCubesMesh;
+      marchingCubesMesh = NULL;
+    }
+  }
+  else
+    splitIsosurfaceMesh = detailedSurfaceMesh->splitIntoConnectedComponents(0, 0);
+  assert(splitIsosurfaceMesh);
+
+  delete isoMesh;
+  isoMesh = new ObjMesh;
+
+  if (field)
+    fieldDiagonal = field->diagonal();
+  else
+  {
+    Vec3d bmin, bmax;
+    detailedSurfaceMesh->getBoundingBox(1.0, &bmin, &bmax);
+    fieldDiagonal = len(bmax - bmin);
+  }
+
+  splitComponent.resize(splitIsosurfaceMesh->getNumGroups(), NULL);
+  for (unsigned int i = 0; i < splitIsosurfaceMesh->getNumGroups(); i++)
+  {
+    // get one connected component and extract an isosurface from it
+    ObjMesh * singleMesh = splitIsosurfaceMesh->extractGroup(i);
+    splitComponent[i] = singleMesh;
+    assert(singleMesh);
+  }
+
+  splitComponentIndex = 0;
+}
+
+bool IsosurfaceMesher::endStepping() const
+{
+  return splitComponentIndex == splitComponent.size();
+}
+
+void IsosurfaceMesher::doOneStep()
+{
+  if (splitComponentIndex == splitComponent.size())
+    return;
+  ObjMesh * objMesh = splitComponent[splitComponentIndex];
+  if (oneMeshLoopIndex == 0)
+  {
+    initializeOneMesh(objMesh);
+  }
+
+  if (addOneTriangle() == false) // return false if it stops
+  {
+    ObjMesh * mesh = buildCurrentSurfaceMesh();
+    if (mesh != NULL)
+      isoMesh->appendMesh(mesh);
+    delete mesh;
+    splitComponentIndex++;
+    oneMeshLoopIndex = 0;
+  }
+  else
+    oneMeshLoopIndex++;
+}
+
+bool IsosurfaceMesher::initializeOneMesh(ObjMesh * objMesh)
+{
+  // create initial samples (run marching cubes, then do low-discrepancy sampling)
+  delaunay.clear();
+
+  int maxNumTrianglesInLeafNode = 5;
+  int maxTreeDepth = 10;
+
+  //octreeCounter.start();
+#ifdef USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+  delete objMeshOctree;
+  objMeshOctree = new ObjMeshOctree<TriangleBasic>(objMesh, maxNumTrianglesInLeafNode, maxTreeDepth);
+#else
+  inputMeshTriangles.clear();
+  objMesh->exportTriangles(inputMeshTriangles);
+  inputMeshRef = TriMeshRef(objMesh->getNumVertices(), &objMesh->getPosition(0), inputMeshTriangles);
+  octree.build(inputMeshRef, maxTreeDepth, maxNumTrianglesInLeafNode);
+#endif
+
+  //octreeCounter.stop();
+  vector<Vec3d> points;
+  generateInitialPointSetByDiscrepancy(objMesh, points, numInitialSamples);
+
+  bool success = delaunay.computeDelaunayTetrahedralization(points, epsilon);
+
+  if (success == false)
+  {
+    return false;
+  }
+
+  // initialize containers to store and query IsoFaces
+  radiusSet.clear();
+  angleSet.clear();
+  faceIters.clear();
+  return true;
+}
+
+bool IsosurfaceMesher::compute(double o, double a, double r, int nis, double ep, int maxNumberOfIterations, double maxTimeSeconds)
+{
+  cout << "Running isosurfaceMesher" << endl;
+  setStepping(o, a, r, nis, ep);
+
+//  marchingCubesMesh->save("MarchingCubes.obj");
+//  delaunayUpdateCounter.reset();
+//  octreeCounter.reset();
+//  intersectionCounter.reset();
+
+  bool timeout = false;
+  if (maxTimeSeconds < 0)
+    maxTimeSeconds = DBL_MAX;
+
+  if (maxNumberOfIterations < 0)
+    maxNumberOfIterations = INT_MAX;
+  for (size_t i = 0; i < splitComponent.size(); i++)
+  {
+    // get one connected component and extract an isosurface from it
+    ObjMesh * singleMesh = splitComponent[i];
+    if (singleMesh->getNumVertices() < splitIsosurfaceMesh->getNumVertices() * ignoredSplitComponentVtxRatio)
+      continue;
+
+    ObjMesh * mesh = computeOneMesh(singleMesh, maxNumberOfIterations, maxTimeSeconds, timeout);
+
+    if (mesh)
+    {
+      isoMesh->appendMesh(mesh);
+      delete mesh;
+    }
+
+    if (timeout || maxNumberOfIterations <= 0 || maxTimeSeconds <= 0.0)
+      break;
+  }
+
+  // clean intermediate vars
+  delete splitIsosurfaceMesh;
+  splitIsosurfaceMesh = NULL;
+  for (size_t i = 0; i < splitComponent.size(); i++)
+    delete splitComponent[i];
+  splitComponent.clear();
+
+  return timeout;
+}
+
+bool IsosurfaceMesher::addOneTriangle(double * targetRadius)
+{
+  // remove faces deleted in delaunay mesh from radiusSet, angleSet and faceIters
+  const VoronoiEdgeMap & deletedVEdges = delaunay.getDeletedVoronoiEdges();
+  for (VEdgeCIter it = deletedVEdges.begin(); it != deletedVEdges.end(); it++)
+  {
+    const UTriKey & tri = it->first;
+    IterPairMap::iterator it2 = faceIters.find(tri);
+    if (it2 != faceIters.end())
+    { // a deleted face is inside face
+      // cout << "erase isosurface: " << it2->first.v[0] << " " << it2->first.v[1] << " " << it2->first.v[2] << endl;
+      RadiusIter rit = it2->second.first;
+      AngleIter ait = it2->second.second;
+      radiusSet.erase(rit);
+      angleSet.erase(ait);
+      faceIters.erase(it2);
+    }
+  }
+
+  // test whether new faces in delaunay should be added to radiusSet, angleSet and faceIters
+  const VoronoiEdgeMap & addedVEdges = delaunay.getAddedVoronoiEdges();
+
+  // cout << numIter << ": added VE Edges: " << addedVEdges.size() << ": ";
+  for (VEdgeCIter it = addedVEdges.begin(); it != addedVEdges.end(); it++)
+  {
+    const UTriKey & tri = it->first;
+    const VoronoiEdge & vedge = it->second;
+    // build a Voronoi edge from the center of one ball to the center of the other ball, or to infinity
+    // test intersection of octree with this Voronoi edge. If yes, the tet face of this Voronoi edge should be one of the IsoFaces
+    Vec3d start, dir, isopoint(0.0); // isopoint is the intersection point
+    bool isRay = false;
+    if (vedge.isFinite())
+    {
+      start = vedge.start;
+      dir = vedge.end - start;
+      isRay = false;
+    }
+    else
+    {
+      isRay = true;
+      start = vedge.start;
+      dir = vedge.direction;
+    }
+    // intersectionCounter.start();
+
+    bool onIsosurface = false;
+    {
+      Vec3d segmentEnd;
+      Vec3d segmentStart = start;
+
+      Vec3d direction = dir;
+      if (isRay)
+      {
+        direction *= 1.1 * fieldDiagonal * 100.0;
+        segmentEnd = segmentStart + direction; // this line will be long enough to determine intersection
+      }
+      else
+        segmentEnd = segmentStart + direction;
+
+      // Too large value at segmentStart may cause precison problems in the octree. So we swap them if needed.
+      if (len2(segmentStart) > len2(segmentEnd))
+      {
+        Vec3d tmp = segmentStart;
+        segmentStart = segmentEnd;
+        segmentEnd = tmp;
+      }
+
+      std::vector<int> triangleList;
+      std::vector<Vec3d> intersectionList;
+
+      std::vector<TriangleBasic*> tlist;
+#ifdef USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+      double length2 = len2(segmentEnd - segmentStart);
+      objMeshOctree->lineSegmentIntersection(tlist, segmentStart, segmentEnd, &intersectionList);
+      for (size_t i = 0; i < tlist.size(); i++)
+        triangleList.push_back(tlist[i]->index());
+      double minT = DBL_MAX;
+      for (unsigned i = 0; i < triangleList.size(); i++)
+      {
+        double t2 = len2(intersectionList[i] - segmentStart) / length2;
+        if (t2 < minT)
+        {
+          minT = t2;
+          isopoint = intersectionList[i];
+          onIsosurface = true;
+        }
+      }
+#else
+      double segWeight[2];
+      onIsosurface = octree.lineSegmentFirstIntersectionPoint(inputMeshRef, segmentStart, segmentEnd, segWeight);
+      if (onIsosurface) {
+        isopoint = segmentStart * segWeight[0] + segmentEnd * segWeight[1];
+        if (segWeight[0] == 0.0) {
+          cout << "one added face has intersection at weight 0" << endl;
+        }
+      }
+#endif
+    }
+
+    if (onIsosurface)
+    {
+      IsoFace isoface(tri, delaunay.getVertex(tri[0]), delaunay.getVertex(tri[1]), delaunay.getVertex(tri[2]), isopoint);
+      RadiusIter rit = (radiusSet.insert(isoface)).first;
+      AngleIter ait = (angleSet.insert(isoface)).first;
+      faceIters[tri] = IterPair(rit, ait);
+    }
+  }
+
+  // compute maximum radius among the surface faces
+  if (radiusSet.size() == 0 || angleSet.size() == 0)
+  {
+    // strange, no iso faces now
+    if (targetRadius)
+      *targetRadius = 0.;
+    return false;
+  }
+
+  //cout << radiusSet.size() << endl;
+
+  for (IsoFaceRadiusSet::iterator itr = radiusSet.begin(); itr != radiusSet.end(); itr++)
+  {
+    IsoFaceRadiusSet::iterator::value_type targetRadiusFace = *itr;
+    if (targetRadius)
+      *targetRadius = targetRadiusFace.radius;
+    if (targetRadiusFace.radius <= radialBound) // requirement fulfilled
+    {
+      //printf("Fulfilled\n");
+      break;
+    }
+
+    if (delaunay.addOnePoint(targetRadiusFace.isopoint))
+      return true;
+    //else
+      //cout << targetRadiusFace.isopoint  << endl;
+  }
+
+  for (IsoFaceRadiusSet::iterator itr = angleSet.begin(); itr != angleSet.end(); itr++)
+  {
+    IsoFaceRadiusSet::iterator::value_type targetAngleFace = *itr;
+    if (targetRadius)
+      *targetRadius = targetAngleFace.radius;
+    if (targetAngleFace.maxCosAngle <= cosAngularBound) // requirement fulfilled
+    {
+      //printf("Fulfilled\n");
+      break;
+    }
+
+    if (delaunay.addOnePoint(targetAngleFace.isopoint))
+      return true;
+    //else
+      //cout << targetRadiusFace.isopoint  << endl;
+  }
+
+  return false;
+}
+
+ObjMesh * IsosurfaceMesher::computeOneMesh(ObjMesh * objMesh, int & maxNumberOfIterations, double & maxTimeSeconds, bool & timeout)
+{
+  // repeat:
+  // build Delaunay mesh
+  // determine faces intersected by the Voronoi diagram edges
+  // check all the faces for the splitting condition (angularBound, radialBound)
+  // split the worst face
+  timeout = false;
+  if (initializeOneMesh(objMesh) == false)
+    return NULL;
+
+  oneMeshLoopIndex = 0;
+  bool done = false;
+  PerformanceCounter loopTimeCounter; // measure elapsed time in the following loop
+  loopTimeCounter.StartCounter();
+  while (!done)
+  {
+    // return true if it won't stop
+    double targetRadius = 0;
+    if (addOneTriangle(&targetRadius) == false)
+      break;
+
+    oneMeshLoopIndex++;
+    if (oneMeshLoopIndex >= maxNumberOfIterations)
+      break;
+    loopTimeCounter.StopCounter();
+    double timeElapsed = loopTimeCounter.GetElapsedTime();
+    if (timeElapsed >= maxTimeSeconds)
+    {
+      timeout = true;
+      break;
+    }
+
+    if (oneMeshLoopIndex % 1000 == 0) {
+      printf("Itr %d: radius=%.5f\n", oneMeshLoopIndex, targetRadius);
+      fflush(stdout);
+    }
+  }
+
+  maxNumberOfIterations -= oneMeshLoopIndex;
+  loopTimeCounter.StopCounter();
+  maxTimeSeconds -= loopTimeCounter.GetElapsedTime();
+  cout << "Time spent in refining isosurface: " << loopTimeCounter.GetElapsedTime() << endl;
+
+  if (checkDelaunay)
+  {
+    cout << "Begin Delaunay check in IsosurfaceMesher: " << endl;
+    PerformanceCounter pc;
+    bool ret = delaunay.checkDelaunay();
+    pc.StopCounter();
+    cout << "Time in check: " << pc.GetElapsedTime() << ",  ";
+    cout << "Check: " << ret << endl;
+  }
+  return buildCurrentSurfaceMesh();
+}
+
+ObjMesh * IsosurfaceMesher::buildCurrentSurfaceMesh(bool keepAllDelaunayVtx) const
+{
+  vector<double> vertices;
+  vector<int> faces;
+  if (keepAllDelaunayVtx)
+  {
+    vertices.resize(3 * delaunay.getNumVertices());
+    for (size_t i = 0; i < delaunay.getNumVertices(); i++)
+    {
+      Vec3d pos = delaunay.getVertex(i);
+      pos.convertToArray(&vertices[3 * i]);
+    }
+    faces.resize(faceIters.size() * 3);
+    int k = 0;
+    for (IterPairMap::const_iterator it = faceIters.begin(); it != faceIters.end(); it++, k++)
+      memcpy(&faces[3 * k], it->first.indices(), sizeof(int) * 3);
+  }
+  else
+  {
+    map<int, int> vertexMap; // vertex delaunay ID -> new surface mesh vtx ID
+    // go through all the triangles
+    for (IterPairMap::const_iterator it = faceIters.begin(); it != faceIters.end(); it++)
+      for (int j = 0; j < 3; j++)
+      {
+        int vtx = it->first[j]; // one vtx on one triangle
+        map<int, int>::iterator itr = vertexMap.find(vtx);
+        if (itr == vertexMap.end())
+        { // this is a new vtx in vertexMap
+          vertexMap[vtx] = vertices.size() / 3;
+          Vec3d pos = delaunay.getVertex(vtx);
+          for (int i = 0; i < 3; i++)
+            vertices.push_back(pos[i]);
+        }
+        faces.push_back(vertexMap[vtx]);
+      }
+  }
+  return new ObjMesh(vertices.size() / 3, &vertices[0], faces.size() / 3, &faces[0]);
+}
+
+// choose some points from objMesh into points. These points should be as far away from each other as possible.
+// The number of the points required is numInitialSamples
+void IsosurfaceMesher::generateInitialPointSetByDiscrepancy(const ObjMesh * objMesh, std::vector<Vec3d> & points, unsigned int numInitialSamples)
+{
+  srand(6);
+  int numVertices = 0;
+  double * vertices = NULL;
+  objMesh->exportGeometry(&numVertices, &vertices);
+  vector<bool> selected(numVertices, false);
+
+  // select the first vertex
+  int p = rand() % numVertices;
+
+  points.clear();
+  // put first vertex into points
+  points.push_back(vertices + 3 * p);
+  selected[p] = true;
+  vector<int> selectedInd;
+  selectedInd.push_back(p);
+
+  while (points.size() < numInitialSamples)
+  {
+    double max_distance = 0;
+    p = -1;
+    for (int i = 0; i < numVertices; i++)
+    {
+      if (selected[i] == false)
+      {
+        double cur_distance = DBL_MAX;
+        for (size_t j = 0; j < selectedInd.size(); j++)
+        {
+          Vec3d d;
+          for (int k = 0; k < 3; k++)
+            d[k] = vertices[3 * selectedInd[j] + k] - vertices[3 * i + k];
+          double distance = len2(d);
+          if (distance < cur_distance)
+            cur_distance = distance;
+        }
+        if (cur_distance > max_distance)
+        {
+          max_distance = cur_distance;
+          p = i;
+        }
+      }
+    }
+
+    if (p >= 0)
+    {
+      //new point is found
+      points.push_back(vertices + 3 * p);
+      selected[p] = true;
+      selectedInd.push_back(p);
+    }
+    else
+    {
+      numInitialSamples = points.size();
+      break;
+    }
+  }
+
+  // points.push_back(Vec3d(0.0));
+  free(vertices);
+}
+
+bool IsosurfaceMesher::enforceManifoldnessAndOrientNormals(ObjMesh* &objMesh)
+{
+  bool flag1 = false, flag2 = false;
+  //remove non-manifold faces and edges
+  do
+  {
+    flag1 = false;
+    int removeHangingFaceNum = 0;
+    do
+    {
+      removeHangingFaceNum = objMesh->removeHangingFaces();
+      if (removeHangingFaceNum > 0)
+        flag1 = true;
+    }
+    while (removeHangingFaceNum > 0);
+
+    int removeEdge = 0;
+    flag2 = false;
+    do
+    {
+      removeEdge = objMesh->removeNonManifoldEdges();
+      if (removeEdge > 0)
+        flag2 = true;
+    }
+    while (removeEdge > 0);
+  }
+  while (flag1 || flag2);
+
+  // process each connected component
+  ObjMesh * connectedComponentMesh = objMesh->splitIntoConnectedComponents();
+  delete objMesh;
+  objMesh = new ObjMesh;
+
+  // for each connected component
+  for (unsigned int i = 0; i < connectedComponentMesh->getNumGroups(); i++)
+  {
+    ObjMesh * component = connectedComponentMesh->extractGroup(i);
+
+    // convert to oriented mesh
+    ObjMeshOrientable objMeshOrientable(component, 1, NULL, 0);
+    ObjMesh * singleMesh = objMeshOrientable.GenerateOrientedMesh();
+    delete component;
+
+    vector<int> group;
+    for (unsigned int i = 0; i < singleMesh->getNumGroups(); i++)
+      group.push_back(i);
+    singleMesh->mergeGroups(group);
+
+    // flip all face normals if orientation is incorrect
+    if (singleMesh->computeVolume() < 0)
+      for (unsigned int i = 0; i < singleMesh->getNumGroups(); i++)
+      {
+        ObjMesh::Group * group = (ObjMesh::Group*)singleMesh->getGroupHandle(i);
+        for (unsigned int j = 0; j < group->getNumFaces(); j++)
+        {
+          ObjMesh::Face * face = (ObjMesh::Face*)group->getFaceHandle(j);
+          face->reverseVertices();
+        }
+      }
+
+    objMesh->appendMesh(singleMesh);
+    delete singleMesh;
+  }
+
+  delete connectedComponentMesh;
+  return true;
+}
+
+IsosurfaceMesher::IsoFace::IsoFace(const UTriKey & t, const Vec3d & v0, const Vec3d & v1, const Vec3d & v2, const Vec3d & c) : tri(t)
+{
+  isopoint = c;
+  radius = (len(v0 - c) + len(v1 - c) + len(v2 - c)) / 3;
+  Vec3d base1 = v1 - v0, base2 = v2 - v0;
+  Vec3d base3 = base2 - base1; // = v2 - v1
+  base1.normalize();
+  base2.normalize();
+  base3.normalize();
+  double cosAngle[3] = { dot(base1, base2), dot(base1, base3), dot(base2, base3) };
+  maxCosAngle = max(cosAngle[0], max(cosAngle[1], cosAngle[2]));
+}
+
+bool IsosurfaceMesher::IsoFaceRadiusCompare::operator() (const IsoFace & a, const IsoFace & b) const
+{
+  if (a.radius > b.radius)
+    return true;
+  if (a.radius < b.radius)
+    return false;
+  return a.tri < b.tri;
+}
+
+bool IsosurfaceMesher::IsoFaceAngleCompare::operator() (const IsoFace & a, const IsoFace & b) const
+{
+  if (a.maxCosAngle > b.maxCosAngle)
+    return true;
+  if (a.maxCosAngle < b.maxCosAngle)
+    return false;
+  return a.tri < b.tri;
+}
+
+ObjMesh * IsosurfaceMesher::getMesh(bool enforceManifold)
+{
+//  cout << "delaunay update time: " << delaunayUpdateCounter.getElapsedTime() << endl;
+//  cout << "octree time: " << octreeCounter.getElapsedTime() << endl;
+//  cout << "intersection time: " << intersectionCounter.getElapsedTime() << endl;
+
+  if (enforceManifold)
+  {
+    // outputMesh->save("non-manifold.obj");
+    enforceManifoldnessAndOrientNormals(isoMesh);
+  }
+  else if (field)
+  {
+    // orient faces consistently with the distance field gradient
+    // iterate over all groups and faces
+    for (unsigned int groupIndex = 0; groupIndex < isoMesh->getNumGroups(); groupIndex++)
+    {
+      const ObjMesh::Group * groupHandle = isoMesh->getGroupHandle(groupIndex);
+      for (unsigned int faceIndex = 0; faceIndex < groupHandle->getNumFaces(); faceIndex++)
+      {
+        ObjMesh::Face * faceHandle = (ObjMesh::Face*) groupHandle->getFaceHandle(faceIndex);
+        if (faceHandle->getNumVertices() < 3)
+          cout << "Warning: encountered a face (group=" << groupIndex << ",face=" << faceIndex << ") with fewer than 3 vertices." << endl;
+
+        // compute face centroid
+        Vec3d centroid(0, 0, 0);
+        for (unsigned int vertexIndex = 0; vertexIndex < faceHandle->getNumVertices(); vertexIndex++)
+        {
+          const ObjMesh::Vertex * vertexHandle = faceHandle->getVertexHandle(vertexIndex);
+          Vec3d vertexPos = isoMesh->getPosition(vertexHandle->getPositionIndex());
+          centroid += vertexPos;
+
+        }
+        if (faceHandle->getNumVertices() > 0)
+          centroid *= 1.0 / faceHandle->getNumVertices();
+
+        // reverse face if normal is in the opposite direction to the distance field gradient
+        Vec3d normal = isoMesh->computeFaceNormal(*faceHandle);
+        Vec3d gradient = field->gradient(centroid);
+        if (dot(normal, gradient) < 0)
+          faceHandle->reverseVertices();
+      }
+    }
+  }
+  return new ObjMesh(*isoMesh);
+}
+
diff --git a/libraries/mesher/isosurfaceMesher.h b/libraries/mesher/isosurfaceMesher.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1aea55a866abd112c433e7e91e55d9156321629
--- /dev/null
+++ b/libraries/mesher/isosurfaceMesher.h
@@ -0,0 +1,209 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Compute a quality triangular mesh of a distance field isosurface (level set),
+  using the following paper:
+
+  Steve Oudot, Laurent Rineau, Mariette Yvinec:
+  Meshing Volumes Bounded by Smooth Surfaces
+  Proceedings of the 14th International Meshing Roundtable 2005, pp 203-219
+*/
+
+#ifndef _ISOSURFACEMESHER_H_
+#define _ISOSURFACEMESHER_H_
+
+#include <time.h>
+#include <vector>
+#include <map>
+#include <set>
+
+#include "distanceFieldBase.h"
+#include "objMesh.h"
+#include "tetMesh.h"
+#include "triple.h"
+#include "triangle.h"
+#include "delaunayMesher.h"
+
+#define USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+#ifdef USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+#include "objMeshOctree.h"
+#else
+#include "exactOctree.h"
+#endif
+
+class IsosurfaceMesher
+{
+public:
+
+  // initialize the mesher
+  // the mesher will mesh an isosurface of the distance field "field"
+  IsosurfaceMesher(DistanceFieldBase * distanceField);
+  virtual ~IsosurfaceMesher();
+
+  // calculate the isosurface
+  // isovalue: the isovalue of the distance field to be meshed
+  // angularBound: the angles of the triangle on the isosurface mesh should not be larger than angularBound
+  //               the angular bound is passed in radians
+  // radialBound: the largest circumcircle of the triangle on the isosurface mesh should not be larger than "radialBound"
+  // numInitialSamples: number of points generated in the first step of the algorithm (internal initial mesh)
+  // epsilon: threshold used to check whether input vertices are degenerate
+  //          the larger the epsilon, the more input will be treated as degenerate by DelaunayMesher
+  // maxNumberOfIterations: the routine will terminate if this number of iterations is exceeded. < 0 means no limitation
+  // maxTimeSeconds: the routine will terminate if this computation time (in seconds) is exceeded. < 0 means no limitation
+  // return value: true if timeout occurred, false otherwise
+  bool compute(double isovalue, double angularBound, double radialBound,
+               int numInitialSamples = 20, double epsilon = 1e-6,
+               int maxNumberOfIterations = -1, double maxTimeSeconds = -1.0);
+
+  // get the result of compute()
+  ObjMesh * getMesh(bool enforceManifold = true);
+
+  // =========== advanced routines ===================
+
+  // pass a mesh representing the zero isosurface, to be remeshed via compute()
+  // only isovalue=0 is supported in this mode
+  IsosurfaceMesher(ObjMesh * detailedSurfaceMesh);
+
+  // =========== debugging =================
+
+  void saveMarchingCubesMesh(bool save = true) { saveMarchingCubesObj = save; }
+  void checkDelaunayAfterComputation(bool check = true) { checkDelaunay = check; }
+  const ObjMesh * getMarchingCubesMesh() const { return marchingCubesMesh; }
+
+  // stepping functions
+  void setStepping(double isovalue, double angularBound, double radialBound, size_t numInitialSamples = 20, double epsilon = 1e-6);
+  void doOneStep();
+  bool endStepping() const;
+
+  const DelaunayMesher & getCurrentDelaunay() const { return delaunay; }
+
+  // if keepAllDelaunayVtx is false, only vertices that belong to at least one triangle are kept
+  // otherwise, all vertices in the Delaunay mesh are added to the resulting obj mesh
+  ObjMesh * buildCurrentSurfaceMesh(bool keepAllDelaunayVtx = false) const;
+
+  // remove non-manifold faces and edges, orient faces
+  static bool enforceManifoldnessAndOrientNormals(ObjMesh * &objMesh);
+protected:
+  IsosurfaceMesher(const IsosurfaceMesher &);
+
+  // represent a face on the isosurface mesh
+  struct IsoFace
+  {
+    Vec3d isopoint{0.0};   // a point on the isosurface near this face. This point is also the center of a circumsphere of the triangle face
+    double radius = 0.0;   // radius of the circumsphere centered at isopoint
+    double maxCosAngle = 0.0; // max cosine of the angles of this triangle
+    UTriKey tri;     // the triangle vtx index of this face
+    IsoFace() {}
+    IsoFace(const UTriKey & tri, const Vec3d & v0, const Vec3d & v1, const Vec3d & v2, const Vec3d & isopoint);
+  };
+  // compare the radius of two IsoFace, used in a set to find the face with largest circumcircle radius
+  struct IsoFaceRadiusCompare
+  {
+    bool operator() (const IsoFace & a, const IsoFace & b) const;
+  };
+  // compare the radius of two IsoFace, used in a set to find the face with largest maxCosAngle
+  struct IsoFaceAngleCompare
+  {
+    bool operator() (const IsoFace & a, const IsoFace & b) const;
+  };
+
+  // the set that compares IsoFace circumcircle radius, used to find the face with largest circumcircle radius
+  typedef std::set<IsoFace, IsoFaceRadiusCompare> IsoFaceRadiusSet;
+  // the set that compares IsoFace maxCosAngle, used to find the face with largest maxCosAngle
+  typedef std::set<IsoFace, IsoFaceAngleCompare> IsoFaceAngleSet;
+
+  typedef IsoFaceRadiusSet::iterator RadiusIter;
+  typedef IsoFaceAngleSet::iterator AngleIter;
+  typedef std::pair<RadiusIter, AngleIter> IterPair;
+  typedef std::map<UTriKey, IterPair> IterPairMap;
+
+  // DelaunayMesher related typedefs for easy interaction with DelaunayMesher
+  typedef DelaunayMesher::VoronoiEdge VoronoiEdge;
+  typedef DelaunayMesher::VoronoiEdgeMap VoronoiEdgeMap;
+  typedef DelaunayMesher::VEdgeIter VEdgeIter;
+  typedef DelaunayMesher::VEdgeCIter VEdgeCIter;
+
+  // void intersection(const std::vector<TriangleBasic*> & triangles, const Vec3d & v, const Vec3d & direction, bool isRay, std::vector<double> & t);
+
+  static void generateInitialPointSetByDiscrepancy(const ObjMesh * objMesh, std::vector<Vec3d> & point, const unsigned int numInitialSamples);
+
+  ObjMesh * computeOneMesh(ObjMesh * objMesh, int & maxNumberOfIterations, double & maxTimeSeconds, bool & timeout);
+  bool initializeOneMesh(ObjMesh * objMesh);
+  bool addOneTriangle(double * targetRadius = NULL); // return true if it won't stop
+
+  DistanceFieldBase * field = nullptr;
+  ObjMesh * detailedSurfaceMesh = nullptr;
+  double isovalue = 0.0;
+  double angularBound = 30.0, cosAngularBound = 0.5, radialBound = 1.0;
+  size_t numInitialSamples = 200;
+  DelaunayMesher delaunay;
+  double epsilon = 0.0;
+
+  ObjMesh * splitIsosurfaceMesh = nullptr;
+  std::vector<ObjMesh *> splitComponent;
+  double fieldDiagonal = 0.0;
+
+#ifdef USE_OBJMESH_OCTREE_IN_ISOSURFACE_MESHER
+  ObjMeshOctree<TriangleBasic> * objMeshOctree = nullptr;
+#else
+  // data for exact octree query
+  ExactTriMeshOctree octree;
+  std::vector<Vec3i> inputMeshTriangles;
+  TriMeshRef inputMeshRef;
+#endif
+
+  // index for stepping
+  size_t splitComponentIndex = 0;
+  int oneMeshLoopIndex = 0;
+
+  IsoFaceRadiusSet radiusSet;  // a set stores the faces for the isosurface to be generated and order according to its circumcircle radius
+  IsoFaceAngleSet angleSet;    // a set stores the faces and order according to its max cosine angle
+  IterPairMap faceIters;       // map of UTriKey -> two iterators pointing to the face locations in radiusSet and angleSet
+
+  ObjMesh * isoMesh = nullptr;
+
+  // StopWatch delaunayUpdateCounter;
+  // StopWatch octreeCounter;
+  // StopWatch intersectionCounter;
+  bool checkDelaunay = false;
+  bool saveMarchingCubesObj = false;
+
+  ObjMesh * marchingCubesMesh = nullptr;
+
+  double ignoredSplitComponentVtxRatio = 0.01;
+
+  int globalLoopIndex = 0;
+};
+
+#endif
+
diff --git a/libraries/mesher/tetMesher.cpp b/libraries/mesher/tetMesher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bf0e86207240d22eb619a70a7257761f2344e1ee
--- /dev/null
+++ b/libraries/mesher/tetMesher.cpp
@@ -0,0 +1,1361 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <limits.h>
+#include <vector>
+#include <cfloat>
+#include <stack>
+
+using namespace std;
+
+#include "tetMesher.h"
+#include "triangleTetIntersection.h"
+#include "objMeshOrientable.h"
+#include "windingNumber.h"
+#include "performanceCounter.h"
+
+#ifndef M_PI
+  #define M_PI 3.1415926525897932384
+#endif
+
+DelaunayMesher::DelaunayBallCompare TetMesher::TetMeshWithRefineInfo::defaultComparor;
+
+TetMesher::TetMesher() : resultTetMesh(delaunay)
+{
+  objMesh = NULL;
+  numSteinerVertices = 0;
+}
+
+TetMesh * TetMesher::compute(ObjMesh * inputMesh, double refinementQuality, double alpha, double minDihedral, int maxSteinerVertices, double maxTimeSeconds)
+{
+  // clean previous data
+  delete objMesh;
+  objMesh = NULL;
+  faceRecoveryDepth = 0;
+  //hasMissing = false;
+  numSteinerVertices = 0;
+
+  // Deep copy the input objMesh
+  objMesh = new ObjMesh(*inputMesh);
+  objMesh->removeIsolatedVertices();
+
+  // Merge all groups of the objMesh
+  vector <int> gIdx;
+  for (size_t i = 0; i < objMesh->getNumGroups(); i++)
+    gIdx.push_back(i);
+  objMesh->mergeGroups(gIdx);
+
+  // make sure the first four vertices of the triangle mesh are not in the same plane
+  if (renumberInitialVertices(objMesh) == false)
+  {
+    delete objMesh;
+    objMesh = NULL;
+    return NULL;
+  }
+
+  PerformanceCounter counter;
+  counter.StartCounter();
+  initializeCDT();
+  counter.StopCounter();
+  double cdtTime = counter.GetElapsedTime();
+
+  if (maxSteinerVertices == -1)
+    maxSteinerVertices = INT_MAX;
+
+  if ((maxTimeSeconds >= 0) && (cdtTime >= maxTimeSeconds))
+    return delaunay.getMesh();
+
+  double maxRefinementTime = -1.0;
+  if (maxTimeSeconds >= 0)
+    maxRefinementTime = maxTimeSeconds - cdtTime;
+  printf("Starting tet mesh refinement.\n");
+
+  delaunay.buildCDT();
+
+  // Enable the minimal dihedral angle refinement mechanism. If the minimal dihedral angle of a tet is smaller than a threshold, it needs refining.
+  if (minDihedral > 0)
+    resultTetMesh.enableAngleRefine();
+
+  // Insert all tets (regular balls in the delaunay)
+  for (BallCIter it = delaunay.getBallSetBegin(), end = delaunay.getBallSetEnd(); it != end; it++)
+  {
+    const DelaunayBall * ball = *it;
+
+    if (ball->isInfinite())
+      continue;
+    resultTetMesh.insert(ball);
+  }
+
+  double averageEdgeLength = objMesh->computeAverageEdgeLength();
+
+  //cout << "Average length: " << averageEdgeLength << endl;
+
+  numSteinerVertices = 0;
+  counter.StartCounter();
+  //First we use edge refine rule (radius/minEdge)
+  while ((refineEdge(refinementQuality, alpha * averageEdgeLength) == 0) && (numSteinerVertices < maxSteinerVertices))
+  {
+    printf("#"); fflush(NULL);
+    counter.StopCounter();
+    double elapsedTime = counter.GetElapsedTime();
+    if ((maxRefinementTime >= 0) && (elapsedTime > maxRefinementTime))
+      break;
+  }
+  printf("\n");
+
+  //Use minimal dihedral angle refinement
+  if (minDihedral > 0)
+  {
+    printf("Enforcing the minimum dihedral angle condition...\n");
+    while (refineAngle(minDihedral, alpha * averageEdgeLength) == 0 && numSteinerVertices < maxSteinerVertices)
+    {
+      printf("#"); fflush(NULL);
+      counter.StopCounter();
+      double elapsedTime = counter.GetElapsedTime();
+      if ((maxRefinementTime >= 0) && (elapsedTime > maxRefinementTime))
+        break;
+    }
+  }
+
+  printf("\n");
+  cout << numSteinerVertices << " steiner points inserted." << endl;
+  TetMesh * ret = resultTetMesh.getTetMesh();
+  //ret->orient();
+  return ret;
+}
+
+// Make sure the first four vertices of the triangle mesh are not on the same plane
+bool TetMesher::renumberInitialVertices(ObjMesh * surfaceMesh)
+{
+  Vec3d p0 = surfaceMesh->getPosition(0);
+  Vec3d p1 = Vec3d(0, 0, 0), p2 = Vec3d(0, 0, 0), p3 = Vec3d(0, 0, 0);
+  int v1 = -1, v2 = -1, v3 = -1;
+  double max_dis = 1e-10;
+  /*The second vertex is different from the first vertex*/
+  for (size_t i = 1; i < surfaceMesh->getNumVertices(); i++)
+  {
+    Vec3d d10 = surfaceMesh->getPosition(i) - p0;
+    double dis = dot(d10, d10);
+    if (dis > max_dis)
+    {
+      max_dis = dot(d10, d10);
+      p1 = surfaceMesh->getPosition(i);
+      v1 = i;
+    }
+  }
+  if (v1 == -1)
+    return false; //new TetMesh(0, (double*)NULL, 0, (int*)NULL);     /* The triangle mesh has only one position*/
+
+  p1 = surfaceMesh->getPosition(v1);
+  /*The first three vertices are not on the same straight line*/
+  Vec3d p10 = p1 - p0;
+  p10.normalize();
+  max_dis = 0;
+  for (size_t i = 1; i < surfaceMesh->getNumVertices(); i++)
+  {
+    if(i == (size_t)v1)
+      continue;
+    Vec3d normal = cross(p10, surfaceMesh->getPosition(i) - p0);
+    if (dot(normal, normal) > 1e-10)
+    {
+      p2 = surfaceMesh->getPosition(i);
+      double dis = min(len2(p2 - p0), len2(p2 - p1));
+      if (dis > max_dis)
+      {
+        max_dis = dis;
+        v2 = i;
+      }
+    }
+  }
+  if (v2 == -1)
+    return false; // new TetMesh(0, (double*)NULL, 0, (int*)NULL);     /* The triangle mesh has only one line*/
+
+  /*The first four vertices are not on the same plane*/
+  p2 = surfaceMesh->getPosition(v2);
+  Vec3d p20 = p2 - p0;
+  p20.normalize();
+  max_dis = 0;
+  for (size_t i = 3; i < surfaceMesh->getNumVertices(); i++)
+  {
+    if (fabs(dot(p20, cross(p10, surfaceMesh->getPosition(i) - p0))) > 0)
+    {
+      p3 = surfaceMesh->getPosition(i);
+      double dis = (std::min)(len2(p3 - p0), (std::min)(len2(p2 - p0), len2(p1 - p0)));
+      if (dis > max_dis)
+      {
+        max_dis = dis;
+        v3 = i;
+      }
+    }
+  }
+  if (v3 == -1)
+    return false; // new TetMesh(0, (double*)NULL, 0, (int*)NULL);     /* The triangle mesh has only one plane*/
+
+  // Sort the first four vertices so that the volume is positive
+  p3 = surfaceMesh->getPosition(v3);
+  if (dot(p20, cross(p10, p3 - p0)) < 0)
+    swap(v2, v3);
+
+  // Permute the objMesh
+  vector<int> renumber;
+  renumber.resize(surfaceMesh->getNumVertices());
+
+  for (size_t i = 0; i < renumber.size(); i++)
+    renumber[i] = i;
+  swap(renumber[1], renumber[v1]);
+
+  int overwrite = renumber[v2];
+  renumber[v2] = 2;
+  for (size_t i = 0; i < renumber.size(); i++)
+    if (renumber[i] == 2)
+    {
+      renumber[i] = overwrite;
+      break;
+    }
+  overwrite = renumber[v3];
+  renumber[v3] = 3;
+  for (size_t i = 0; i < renumber.size(); i++)
+    if (renumber[i] == 3)
+    {
+      renumber[i] = overwrite;
+      break;
+    }
+  surfaceMesh->renumberVertices(renumber);
+
+  return true;
+}
+
+TetMesher::~TetMesher()
+{
+  delete objMesh;
+}
+
+int TetMesher::initializeCDT(bool recovery)
+{
+  //printf("Entering initializeCDT:\n"); fflush(NULL);
+  unsigned nv;
+  double *v = NULL;
+  const ObjMesh::Group *group = objMesh->getGroupHandle(0);
+  if (recovery)
+  {
+    //get all faces and build neighbors in the triangular mesh
+    map<pair<int, int>, vector<int> > neighborBuffer;
+    for (unsigned i = 0; i < group->getNumFaces(); i++)
+    {
+      const ObjMesh::Face face = group->getFace(i);
+      int a = face.getVertex(0).getPositionIndex();
+      int b = face.getVertex(1).getPositionIndex();
+      int c = face.getVertex(2).getPositionIndex();
+      pair<int, int> edge[3] = { (a < b ? make_pair(a, b) : make_pair(b, a)), (a < c ? make_pair(a, c) : make_pair(c, a)), (c < b ? make_pair(c, b) : make_pair(b, c)) };
+      for (int j = 0; j < 3; j++)
+        neighborBuffer[edge[j]].push_back(i);
+    }
+
+    neighborSurface.resize(group->getNumFaces());
+    for (map<pair<int, int>, vector<int> >::const_iterator itr = neighborBuffer.begin(); itr != neighborBuffer.end(); itr++)
+    {
+
+      if (itr->second.size() != 2)
+      {
+        printf("The input mesh must be a 2-manifold mesh\n");
+        exit(3);
+      }
+
+      for (size_t i = 0; i < itr->second.size(); i++)
+        for (size_t j = i + 1; j < itr->second.size(); j++)
+        {
+          int t0 = itr->second.at(i);
+          int t1 = itr->second.at(j);
+          neighborSurface[t0].push_back(t1);
+          neighborSurface[t1].push_back(t0);
+        }
+    }
+
+    for (size_t i = 0; i < neighborSurface.size(); i++)
+      if (neighborSurface[i].size() != 3)
+      {
+        printf("The input mesh must be a triagular mesh\n");
+        exit(4);
+      }
+  }
+  objMesh->exportGeometry((int *) &nv, &v);
+  vector<Vec3d> objVertices;
+  for(size_t i = 0; i < nv; i++) 
+    objVertices.push_back(&v[3*i]);
+  //printf("Before computeDelaunayTetrahedralization:\n"); fflush(NULL);
+  delaunay.computeDelaunayTetrahedralization(objVertices);
+  //printf("After computeDelaunayTetrahedralization:\n"); fflush(NULL);
+  free(v);
+  trianglesInTet.clear();
+
+  for (DelaunayMesher::BallCIter itr = delaunay.getBallSetBegin(); itr != delaunay.getBallSetEnd(); itr++)
+  {
+    const DelaunayMesher::DelaunayBall *ball = *itr;
+    if (ball->isRegular())
+      insertTet(ball->getVertices());
+  }
+
+  //printf("Before segmentRecovery:\n"); fflush(NULL);
+  segmentRecovery();
+  //printf("After segmentRecovery:\n"); fflush(NULL);
+  flipSurface();
+
+  lost.clear();
+  for (unsigned i = 0; i < group->getNumFaces(); i++)
+  {
+    const ObjMesh::Face face = group->getFace(i);
+    int a = face.getVertex(0).getPositionIndex();
+    int b = face.getVertex(1).getPositionIndex();
+    int c = face.getVertex(2).getPositionIndex();
+    UTriKey key = UTriKey(a, b, c);
+    if (trianglesInTet.find(key) == trianglesInTet.end())
+    {
+      lost.push_back(key);
+      //printf("Lost face (%d, %d %d)\n", key[0], key[1], key[2]);
+    }
+  }
+
+  //cout << "Lost size: " << lost.size() << endl;
+
+  //printf("\n");
+  if (!lost.empty() && recovery)
+    faceRecovery();
+  removeOutside();
+  return lost.size();
+}
+
+
+int TetMesher::refineAngle(const double angleBound, const double minmimalDist)
+{
+  TetMeshWithRefineInfo::AngleRefineIterator itr;
+  for ( itr = resultTetMesh.getAngleRefineBegin(); itr != resultTetMesh.getAngleRefineEnd(); itr++)
+  {
+    const DelaunayBallWithRefineInfo  * delaunayBallWithRefineInfo = *itr;
+    if (delaunayBallWithRefineInfo->minDihedral > angleBound)
+      return 2; // no bad tet to refine
+
+    Vec3d r = delaunayBallWithRefineInfo->getCenter();
+    if (delaunayBallWithRefineInfo->visibleTo(r) == false)
+    {
+      continue; //invisible, find a new bad tet to refine
+    }
+
+    if (isTooCloseToOtherVertices(r, minmimalDist))
+    {
+      //cout << "Too close vertices" << endl;
+      continue; //too close to the other vertices, find a new bad tet to refine
+    }
+
+    int added = delaunay.addOnePoint(r);
+    if (added) // one point added
+    {
+      for (DelaunayMesher::BallIter itr = delaunay.getBallToDeleteSetBegin(); itr != delaunay.getBallToDeletelSetEnd(); itr++)
+        resultTetMesh.remove(*itr);
+      for (DelaunayMesher::BallIter itr = delaunay.getBallAddedSetBegin(); itr != delaunay.getBallAddedSetEnd(); itr++)
+        resultTetMesh.insert(*itr);
+      numSteinerVertices++;
+      return 0;
+    }
+  }
+
+  //No tet to refine due to too sparse steiner points
+  return 1;
+}
+
+/*
+  Ret value
+  0: refined
+  1: No tet to refine due to enough steiner points
+  2: No bad tet to refine
+*/
+int TetMesher::refineEdge(double refinementQuality, double minmimalDist)
+{
+//  maxSteinerVertices = 1000;
+
+  TetMeshWithRefineInfo::EdgeRefineIterator itr;
+  for ( itr = resultTetMesh.getEdgeRefineBegin(); itr != resultTetMesh.getEdgeRefineEnd(); itr++)
+  {
+    const DelaunayBallWithRefineInfo  * delaunayBallWithRefineInfo = *itr;
+    if (delaunayBallWithRefineInfo->edgeQuality < refinementQuality)
+    {
+      cout << "All tets are good quality after refining." << endl;
+      return 2;            //No bad tet to refine
+    }
+
+    Vec3d r = delaunayBallWithRefineInfo->getCenter();
+    if (delaunayBallWithRefineInfo->contain(r) >= 0 || delaunayBallWithRefineInfo->visibleTo(r) == false)
+      continue;     //invisible, find a new bad tet to refine
+
+    if (isTooCloseToOtherVertices(r, minmimalDist))
+      continue;     //too close to other vertices, find a new bad tet to refine
+
+    int added = delaunay.addOnePoint(r);
+    if (added)         //One point added
+    {
+      for (DelaunayMesher::BallIter itr = delaunay.getBallToDeleteSetBegin(); itr != delaunay.getBallToDeletelSetEnd(); itr++)
+        resultTetMesh.remove(*itr);
+      for (DelaunayMesher::BallIter itr = delaunay.getBallAddedSetBegin(); itr != delaunay.getBallAddedSetEnd(); itr++)
+        resultTetMesh.insert(*itr);
+      numSteinerVertices++;
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int TetMesher::removeOutside()
+{
+  const ObjMesh::Group *group = objMesh->getGroupHandle(0);
+
+  set <UTriKey> surfaceTri;
+  for (unsigned i = 0; i < group->getNumFaces(); i++)
+  {
+    const ObjMesh::Face face = group->getFace(i);
+    int a = face.getVertex(0).getPositionIndex();
+    int b = face.getVertex(1).getPositionIndex();
+    int c = face.getVertex(2).getPositionIndex();
+    surfaceTri.insert(UTriKey(a, b, c));
+  }
+/*
+  for (DelaunayMesher::BallCIter itr = delaunay.getBallSetBegin(); itr != delaunay.getBallSetEnd(); itr++)
+  {
+    if ((*itr)->isInfinite())
+      continue;
+    for (int j = 0; j < 4; j++)
+      insertToFaceElementMap((*itr)->uFaceKey(j), *itr);
+  }
+  */
+  set <DelaunayMesher::DelaunayBall*, DelaunayMesher::DelaunayBallCompare> removeSetCandidate;
+  set <DelaunayMesher::DelaunayBall*, DelaunayMesher::DelaunayBallCompare> removeSet;
+
+  for (DelaunayMesher::BallCIter itr = delaunay.getBallSetBegin(); itr != delaunay.getBallSetEnd(); itr++)
+  {
+    if ((*itr)->isInfinite())
+    {
+       removeSetCandidate.insert(*itr);
+    }
+  }
+
+  while (!removeSetCandidate.empty())
+  {
+    DelaunayMesher::DelaunayBall * ball = *removeSetCandidate.begin();
+    removeSetCandidate.erase(removeSetCandidate.begin());
+    removeSet.insert(ball);
+    if (!ball)
+      continue;
+
+    for (int i = 0; i < 4; i++)
+    {
+      DelaunayMesher::DelaunayBall * neighbor = ball->getNeighbor(i);
+      if (surfaceTri.find(ball->uFaceKey(i)) == surfaceTri.end() && removeSet.find(neighbor) == removeSet.end() && removeSetCandidate.find(neighbor) == removeSetCandidate.end())
+      {
+        Vec3d center = (neighbor->getPosition(0) + neighbor->getPosition(1) + neighbor->getPosition(2) + neighbor->getPosition(3)) / 4;
+        double windingNumber = fabs(WindingNumber::computeWindingNumber(objMesh, center));
+        if (windingNumber > 0.5)
+            continue;
+
+        removeSetCandidate.insert(ball->getNeighbor(i));
+      }
+    }
+  }
+
+  for (DelaunayMesher::BallCIter itr = removeSet.begin(); itr != removeSet.end(); itr++)
+    delaunay.removeBall(*itr);
+
+  return removeSet.size();
+}
+
+
+void TetMesher::insertFace(int f1, int f2, int f3)
+{
+  UTriKey key = UTriKey(f1, f2, f3);
+  map<UTriKey, unsigned>::iterator itr = trianglesInTet.find(key);
+  if (itr == trianglesInTet.end())
+  {
+    trianglesInTet[key] = 1;
+  }
+  else
+    itr->second++;
+  insertEdge(f1, f2);
+  insertEdge(f1, f3);
+  insertEdge(f3, f2);
+}
+
+void TetMesher::removeFace(int f1, int f2, int f3)
+{
+  UTriKey key = UTriKey(f1, f2, f3);
+  map<UTriKey, unsigned>::iterator itr = trianglesInTet.find(key);
+  assert(itr != trianglesInTet.end());
+  if (itr->second == 1)
+    trianglesInTet.erase(itr);
+  else
+    itr->second--;
+  removeEdge(f1, f2);
+  removeEdge(f1, f3);
+  removeEdge(f3, f2);
+}
+
+void TetMesher::insertEdge(int e1, int e2)
+{
+  UEdgeKey key(e1, e2);
+  map<UEdgeKey, unsigned>::iterator itr = edgesInTet.find(key);
+  if (itr == edgesInTet.end())
+  {
+    edgesInTet[key] = 1;
+  }
+  else
+    itr->second++;
+}
+
+void TetMesher::removeEdge(int e1, int e2)
+{
+  UEdgeKey key(e1, e2);
+  map<UEdgeKey, unsigned>::iterator itr = edgesInTet.find(key);
+  if (itr->second == 1)
+    edgesInTet.erase(itr);
+  else
+    itr->second--;
+}
+
+UEdgeKey find_intersect(UTriKey t1, UTriKey t2)
+{
+  bool found = false;
+  pair<int, int> ret;
+  for (int i = 0; i < 3; i++)
+    for (int j = 0; j < 3; j++)
+      if (t1[i] == t2[j])
+      {
+        if (!found)
+        {
+          found = true;
+          ret.first = t1[i];
+        }
+        else
+        {
+          ret.second = t1[i];
+          break;
+        }
+      }
+  return UEdgeKey(ret.first, ret.second);
+}
+void TetMesher::formRegion(int face, set<int> &region)
+{
+  if (region.find(face) != region.end())      // The triangle is already in the region
+    return;
+  UTriKey vertexIndices = getFace(face);
+  if (trianglesInTet.find(vertexIndices) != trianglesInTet.end())
+    return;    // The triangle is not missing
+  region.insert(face);
+  for (unsigned i = 0; i < neighborSurface[face].size(); i++)
+  {
+    UTriKey neighborVertexIndices = getFace(neighborSurface[face][i]);
+    UEdgeKey commonEdge = find_intersect(vertexIndices, neighborVertexIndices);
+    if (edgesInTet.find(commonEdge) == edgesInTet.end()) // The common edge is not in the tet
+      formRegion(neighborSurface[face][i], region);
+  }
+}
+
+UTriKey TetMesher::getFace(int index)
+{
+  const ObjMesh::Face *face = objMesh->getGroupHandle(0)->getFaceHandle(index);
+  return UTriKey(face->getVertexHandle(0)->getPositionIndex(), face->getVertexHandle(1)->getPositionIndex(), face->getVertexHandle(2)->getPositionIndex());
+}
+
+void TetMesher::faceRecovery()
+{
+  //printf("Patching\n");
+  //tet->save("delaunay.veg");
+  for (unsigned int face = 0; face < objMesh->getNumFaces(); face++)
+  {
+    if (trianglesInTet.find(getFace(face)) != trianglesInTet.end()) // This face is not missing
+      continue;
+    //printf("Missing\n");
+    region.clear();
+    formRegion(face, region); // Form a region that is connected missing triangles
+    vector<vector<Vec3d> > regionVertices; // n*3 Vec3d array
+    regionVertices.resize(region.size());
+    int tail = 0;
+    for (set<int>::const_iterator i = region.begin(); i != region.end(); i++)
+    {
+      regionVertices[tail].push_back(objMesh->getPosition(objMesh->getGroupHandle(0)->getFaceHandle(*i)->getVertex(0).getPositionIndex()));
+      regionVertices[tail].push_back(objMesh->getPosition(objMesh->getGroupHandle(0)->getFaceHandle(*i)->getVertex(1).getPositionIndex()));
+      regionVertices[tail].push_back(objMesh->getPosition(objMesh->getGroupHandle(0)->getFaceHandle(*i)->getVertex(2).getPositionIndex()));
+      tail++;
+    }
+
+    vector <DelaunayMesher::DelaunayBall*> intersectTet;
+    intersectTet.clear();
+
+    // Find all tets that intersect this region
+    for (DelaunayMesher::BallCIter itr = delaunay.getBallSetBegin(); itr != delaunay.getBallSetEnd(); itr++)
+    {
+      DelaunayMesher::DelaunayBall * ball = *itr;
+      if (ball->isInfinite())
+        continue; // No check for infinite ball
+      Vec3d v0 = ball->getPosition(0);
+      Vec3d v1 = ball->getPosition(1);
+      Vec3d v2 = ball->getPosition(2);
+      Vec3d v3 = ball->getPosition(3);
+      for (unsigned j = 0; j < region.size(); j++)
+      {
+        if (TriangleTetIntersection::tetrahedronIntersectTriangle(v0, v1, v2, v3, regionVertices[j][0], regionVertices[j][1], regionVertices[j][2]))
+        {
+          intersectTet.push_back(ball);
+          break;
+        }
+      }
+    }
+    //printf("\n");
+    vector<UTriKey> boundaryFace;
+    boundaryFace.clear();
+    map<pair<int, int>, pair<int, int> > neighbor;
+    set<pair<int, int> > missingBoundary;
+    calculateTetBoundary(intersectTet, boundaryFace);
+    calculateTriangleBoundary(region, missingBoundary);
+    buildTriangleNeighbor(boundaryFace, neighbor);
+    //bool patched = false;
+    if (intersectTet.empty()) // Impossible
+      continue;
+    formTwoCavities(boundaryFace, missingBoundary, neighbor);
+    for (unsigned i = 0; i < intersectTet.size(); i++)
+    {
+      const int * v = intersectTet[i]->getVertices();
+      removeTet(v);
+      delaunay.removeBall(intersectTet[i]);
+    }
+    for (unsigned i = 0; i < toAdd.size(); i++)
+    {
+      insertTet(toAdd[i].data());
+      //tet->addElement(toAdd[i][0], toAdd[i][1], toAdd[i][2], toAdd[i][3]);
+      delaunay.addBall(toAdd[i].data());
+    }
+  }
+}
+
+void TetMesher::calculateTetBoundary(std::vector<DelaunayMesher::DelaunayBall*>& intersectTet, std::vector<UTriKey>& faceVector)
+{
+  std::set<UTriKey> faceSet;
+  faceSet.clear();
+  for (unsigned i = 0; i < intersectTet.size(); i++)
+  {
+    const int * v = intersectTet[i]->getVertices();
+    UTriKey face(v[0], v[1], v[2]);
+    set<UTriKey>::iterator itr;
+    itr = faceSet.find(face);
+    if (itr == faceSet.end())
+      faceSet.insert(face);
+    else
+      faceSet.erase(itr);
+    face = UTriKey(v[3], v[1], v[2]);
+    itr = faceSet.find(face);
+    if (itr == faceSet.end())
+      faceSet.insert(face);
+    else
+      faceSet.erase(itr);
+    face = UTriKey(v[0], v[3], v[2]);
+    itr = faceSet.find(face);
+    if (itr == faceSet.end())
+      faceSet.insert(face);
+    else
+      faceSet.erase(itr);
+    face = UTriKey(v[0], v[1], v[3]);
+    itr = faceSet.find(face);
+    if (itr == faceSet.end())
+      faceSet.insert(face);
+    else
+      faceSet.erase(itr);
+  }
+  faceVector.clear();
+  for (std::set<UTriKey>::iterator i = faceSet.begin(); i != faceSet.end(); i++)
+    faceVector.push_back(*i);
+}
+
+void TetMesher::calculateTriangleBoundary(set<int>& missingRegion, std::set<std::pair<int, int> >& boundary)
+{
+  boundary.clear();
+  for (set<int>::iterator i = missingRegion.begin(); i != missingRegion.end(); i++)
+  {
+    int v[3];
+    const ObjMesh::Face *face = objMesh->getGroupHandle(0)->getFaceHandle(*i);
+    for (unsigned j = 0; j < 3; j++)
+      v[j] = face->getVertexHandle(j)->getPositionIndex();
+    std::pair<int, int> edge;
+    set<std::pair<int, int> >::iterator itr;
+    edge = make_pair(v[0], v[1]);
+    if (edge.first > edge.second)
+      std::swap(edge.first, edge.second);
+    itr = boundary.find(edge);
+    if (itr == boundary.end())
+      boundary.insert(edge);
+    else
+      boundary.erase(itr);
+    edge = make_pair(v[2], v[1]);
+    if (edge.first > edge.second)
+      std::swap(edge.first, edge.second);
+    itr = boundary.find(edge);
+    if (itr == boundary.end())
+      boundary.insert(edge);
+    else
+      boundary.erase(itr);
+    edge = make_pair(v[0], v[2]);
+    if (edge.first > edge.second)
+      std::swap(edge.first, edge.second);
+    itr = boundary.find(edge);
+    if (itr == boundary.end())
+      boundary.insert(edge);
+    else
+      boundary.erase(itr);
+  }
+}
+
+void TetMesher::buildTriangleNeighbor(std::vector<UTriKey>& mesh, std::map<std::pair<int, int>, std::pair<int, int> >& neighbor)
+{
+  neighbor.clear();
+  for (unsigned i = 0; i < mesh.size(); i++)
+  {
+    int v[3];
+    v[0] = mesh[i][0];
+    v[1] = mesh[i][1];
+    v[2] = mesh[i][2];
+    std::sort(v, v + 3);
+    pair<int, int> edge;
+    map<pair<int, int>, pair<int, int> >::iterator itr;
+    edge = make_pair(v[0], v[1]);
+    itr = neighbor.find(edge);
+    if (itr == neighbor.end())
+      neighbor[edge].first = i;
+    else
+      neighbor[edge].second = i;
+    edge = make_pair(v[1], v[2]);
+    itr = neighbor.find(edge);
+    if (itr == neighbor.end())
+      neighbor[edge].first = i;
+    else
+      neighbor[edge].second = i;
+    edge = make_pair(v[0], v[2]);
+    itr = neighbor.find(edge);
+    if (itr == neighbor.end())
+      neighbor[edge].first = i;
+    else
+      neighbor[edge].second = i;
+  }
+}
+
+bool TetMesher::formTwoCavities(std::vector<UTriKey>& faceSet, std::set<std::pair<int, int> >& boundary, std::map<std::pair<int, int>, std::pair<int, int> >& neighborMap)
+{
+  std::set<int> cavity1;
+  std::set<int> cavity2;
+  cavity1.clear();
+  cavity2.clear();
+  cavity1.insert(neighborMap[*boundary.begin()].first);
+  toAdd.clear();
+  queue<int> q;
+  while (!q.empty())
+    q.pop();       //q.clear()
+  q.push(neighborMap[*boundary.begin()].first);
+  while (!q.empty())
+  {
+    int i = q.front();
+    q.pop();
+    int v[3];
+    v[0] = faceSet[i][0];
+    v[1] = faceSet[i][1];
+    v[2] = faceSet[i][2];
+    std::sort(v, v + 3);
+    pair<int, int> edge;
+    pair<int, int> twoNeighbors;
+    int neighbor;
+    map<pair<int, int>, pair<int, int> >::iterator itr;
+    edge = make_pair(v[0], v[1]);
+    if (boundary.find(edge) == boundary.end())
+    {
+      twoNeighbors = neighborMap[edge];
+      neighbor = (twoNeighbors.first == i ? twoNeighbors.second : twoNeighbors.first);
+      if (cavity1.find(neighbor) == cavity1.end())
+      {
+        cavity1.insert(neighbor);
+        q.push(neighbor);
+      }
+    }
+    edge = make_pair(v[1], v[2]);
+    if (boundary.find(edge) == boundary.end())
+    {
+      twoNeighbors = neighborMap[edge];
+      neighbor = (twoNeighbors.first == i ? twoNeighbors.second : twoNeighbors.first);
+      if (cavity1.find(neighbor) == cavity1.end())
+      {
+        cavity1.insert(neighbor);
+        q.push(neighbor);
+      }
+    }
+    edge = make_pair(v[0], v[2]);
+    if (boundary.find(edge) == boundary.end())
+    {
+      twoNeighbors = neighborMap[edge];
+      neighbor = (twoNeighbors.first == i ? twoNeighbors.second : twoNeighbors.first);
+      if (cavity1.find(neighbor) == cavity1.end())
+      {
+        cavity1.insert(neighbor);
+        q.push(neighbor);
+      }
+    }
+  }
+  cavity2.insert(neighborMap[*boundary.begin()].second);
+  while (!q.empty())
+    q.pop();       //q.clear()
+  q.push(neighborMap[*boundary.begin()].second);
+  while (!q.empty())
+  {
+    int i = q.front();
+    q.pop();
+    int v[3];
+    v[0] = faceSet[i][0];
+    v[1] = faceSet[i][1];
+    v[2] = faceSet[i][2];
+    std::sort(v, v + 3);
+    pair<int, int> edge;
+    pair<int, int> twoNeighbors;
+    int neighbor;
+    map<pair<int, int>, pair<int, int> >::iterator itr;
+    edge = make_pair(v[0], v[1]);
+    if (boundary.find(edge) == boundary.end())
+    {
+      twoNeighbors = neighborMap[edge];
+      neighbor = (twoNeighbors.first == i ? twoNeighbors.second : twoNeighbors.first);
+      if (cavity2.find(neighbor) == cavity2.end())
+      {
+        cavity2.insert(neighbor);
+        q.push(neighbor);
+      }
+    }
+    edge = make_pair(v[1], v[2]);
+    if (boundary.find(edge) == boundary.end())
+    {
+      twoNeighbors = neighborMap[edge];
+      neighbor = (twoNeighbors.first == i ? twoNeighbors.second : twoNeighbors.first);
+      if (cavity2.find(neighbor) == cavity2.end())
+      {
+        cavity2.insert(neighbor);
+        q.push(neighbor);
+      }
+    }
+    edge = make_pair(v[0], v[2]);
+    if (boundary.find(edge) == boundary.end())
+    {
+      twoNeighbors = neighborMap[edge];
+      neighbor = (twoNeighbors.first == i ? twoNeighbors.second : twoNeighbors.first);
+      if (cavity2.find(neighbor) == cavity2.end())
+      {
+        cavity2.insert(neighbor);
+        q.push(neighbor);
+      }
+    }
+  }
+  vector<UTriKey> holeBoundary;
+  holeBoundary.clear();
+  for (set<int>::iterator i = cavity1.begin(); i != cavity1.end(); i++)
+  {
+    holeBoundary.push_back(faceSet[*i]);
+  }
+  bool ret = fillHole(holeBoundary);
+  holeBoundary.clear();
+  for (set<int>::iterator i = cavity2.begin(); i != cavity2.end(); i++)
+  {
+    holeBoundary.push_back(faceSet[*i]);
+  }
+  ret = ret && fillHole(holeBoundary);
+  return ret;
+}
+
+bool TetMesher::fillHole(std::vector<UTriKey>& holeBoundary)
+{
+  vector<UTriKey> holeAndBottom;
+  holeAndBottom.clear();
+  for (set<int>::iterator i = region.begin(); i != region.end(); i++)
+  {
+    const ObjMesh::Face *face = objMesh->getGroupHandle(0)->getFaceHandle(*i);
+    holeAndBottom.push_back(UTriKey(face->getVertexHandle(0)->getPositionIndex(), face->getVertexHandle(1)->getPositionIndex(), face->getVertexHandle(2)->getPositionIndex()));
+  }
+  for (vector<UTriKey>::iterator i = holeBoundary.begin(); i != holeBoundary.end(); i++)
+  {
+    holeAndBottom.push_back(UTriKey((*i)[0], (*i)[1], (*i)[2]));
+  }
+
+  map<int, int> vertexMap;
+  vertexMap.clear();
+  vector<int> vertex_reverse;
+  vertex_reverse.clear();
+  ObjMesh * mesh = new ObjMesh;
+  mesh->addGroup("bottom");
+  mesh->addGroup("hole");
+  for (unsigned i = 0; i < holeAndBottom.size(); i++)
+  {
+    int v[3] = { holeAndBottom[i][0], holeAndBottom[i][1], holeAndBottom[i][2] };
+    for (int j = 0; j < 3; j++)
+    {
+      if (vertexMap.find(v[j]) == vertexMap.end())
+      {
+        mesh->addVertexPosition(objMesh->getPosition(v[j]));
+        vertexMap.insert(make_pair(v[j], vertexMap.size()));
+        vertex_reverse.push_back(v[j]);
+      }
+      v[j] = vertexMap[v[j]];
+    }
+  }
+  ObjMesh::Group * group = (ObjMesh::Group*) mesh->getGroupHandle(1);
+  for (unsigned i = 0; i < holeBoundary.size(); i++)
+  {
+    int v[3] = { vertexMap[holeBoundary[i][0]], vertexMap[holeBoundary[i][1]], vertexMap[holeBoundary[i][2]] };
+    ObjMesh::Face face;
+    face.addVertex(ObjMesh::Vertex(v[0]));
+    face.addVertex(ObjMesh::Vertex(v[1]));
+    face.addVertex(ObjMesh::Vertex(v[2]));
+    group->addFace(face);
+  }
+  group = (ObjMesh::Group*) mesh->getGroupHandle(0);
+  for (set<int>::iterator i = region.begin(); i != region.end(); i++)
+  {
+    const ObjMesh::Face *face = objMesh->getGroupHandle(0)->getFaceHandle(*i);
+    int v[3] = { vertexMap[face->getVertexHandle(0)->getPositionIndex()], vertexMap[face->getVertexHandle(1)->getPositionIndex()], vertexMap[face->getVertexHandle(2)->getPositionIndex()] };
+    ObjMesh::Face _face;
+    _face.addVertex(ObjMesh::Vertex(v[0]));
+    _face.addVertex(ObjMesh::Vertex(v[1]));
+    _face.addVertex(ObjMesh::Vertex(v[2]));
+    group->addFace(_face);
+  }
+  vector<int> groupIndices;
+  groupIndices.push_back(0), groupIndices.push_back(1);
+  mesh->mergeGroups(groupIndices);
+
+  ObjMeshOrientable * mesh_orient = NULL;
+  try 
+  {
+    mesh_orient = new ObjMeshOrientable(mesh, 1, NULL, 0);
+  }
+  catch (int) 
+  {
+    delete mesh;
+    delete mesh_orient;
+    throw 1;
+  }
+
+  ObjMesh * meshToDelete = mesh;
+  mesh = mesh_orient->GenerateOrientedMesh();
+
+  delete mesh_orient;
+  delete meshToDelete;
+
+  group = (ObjMesh::Group*) mesh->getGroupHandle(0);
+  if (mesh->computeVolume() < 0)
+  {
+    ObjMesh::Group group_2("hole");
+    for (unsigned i = 0; i < group->getNumFaces(); i++)
+    {
+      const ObjMesh::Face *face = group->getFaceHandle(i);
+      ObjMesh::Face face_2;
+      face_2.addVertex(face->getVertex(0));
+      face_2.addVertex(face->getVertex(2));
+      face_2.addVertex(face->getVertex(1));
+      group_2.addFace(face_2);
+    }
+    mesh->removeAllGroups();
+    mesh->addGroup(group_2);
+  }
+
+  const int maxFaceRecoveryDepth = 10;
+  if (faceRecoveryDepth < maxFaceRecoveryDepth)
+  {
+    TetMesher mesher;
+    mesher.faceRecoveryDepth = faceRecoveryDepth + 1;
+    mesher.objMesh = mesh;
+    mesher.initializeCDT(false);
+    for (DelaunayMesher::BallCIter itr = mesher.delaunay.getBallSetBegin(); itr != mesher.delaunay.getBallSetEnd(); itr++)
+    {
+      if ((*itr)->isInfinite())
+        continue;
+      vector<int> tetIndex;
+      tetIndex.resize(4);
+      for (int j = 0; j < 4; j++)
+      {
+        tetIndex[j] = vertex_reverse[(*itr)->getVtx(j)];
+      }
+      toAdd.push_back(tetIndex);
+    }
+    return true;
+  }
+  else
+    return false;
+}
+
+TetMesher::DelaunayBallWithRefineInfo::DelaunayBallWithRefineInfo(const DelaunayMesher::DelaunayBall& parent)
+: DelaunayMesher::DelaunayBall(parent)
+{
+  // compute minimal dihedral
+  if (isRegular())
+  {
+    Vec3d v0 = this->parent.getVertex(v[0]);
+    Vec3d v1 = this->parent.getVertex(v[1]);
+    Vec3d v2 = this->parent.getVertex(v[2]);
+    Vec3d v3 = this->parent.getVertex(v[3]);
+    Vec3d p0 = v1 - v0;
+    Vec3d p1 = v2 - v0;
+    Vec3d p2 = v3 - v0;
+    if (dot(cross(p0, p1), p2) < 0)
+    {
+      swap(v2, v3);
+      swap(p1, p2);
+    }
+    Vec3d n0 = cross(p0, p1);
+    Vec3d n1 = cross(p1, p2);
+    Vec3d n2 = cross(p2, p0);
+
+    Vec3d p3 = v3 - v1;
+    Vec3d p4 = v2 - v1;
+    Vec3d n3 = cross(p3, p4);
+    n0.normalize();
+    n1.normalize();
+    n2.normalize();
+    n3.normalize();
+    double m = dot(n0, n1);
+    m = min(m, dot(n0, n2));
+    m = min(m, dot(n0, n3));
+    m = min(m, dot(n1, n2));
+    m = min(m, dot(n1, n3));
+    m = min(m, dot(n2, n3));
+    minDihedral = acos(-m) / M_PI * 180;
+
+    double minEdge2 = (std::min)((std::min)(len2(p0), len2(p1)), len2(p2));
+    minEdge2 = (std::min)((std::min)(len2(p3), len2(p4)), minEdge2);
+    minEdge2 = (std::min)(len2(v3 - v2), minEdge2);
+    edgeQuality = sqrt(len2(v0 - center) / minEdge2);
+  }
+  else
+  {
+    minDihedral = NAN;
+    edgeQuality = NAN;
+  }
+}
+
+bool TetMesher::isTooCloseToOtherVertices(const Vec3d& r, const double minimalDist) const
+{
+double minimalDist2 = minimalDist * minimalDist;
+  for (unsigned int i = 0; i < delaunay.getNumVertices(); i++)
+    if (len2(r - delaunay.getVertex(i)) < minimalDist2)
+      return true;
+  return false;
+}
+
+
+TetMesh * TetMesher::TetMeshWithRefineInfo::getTetMesh()
+{
+  TetMesh * tetMesh;        // The return tet mesh
+  if (edgeRefineBalls.empty())      // No tets, almost impossible to trigger
+    tetMesh = new TetMesh(0, (double*)NULL, 0, (int*)NULL);
+  else
+  {
+    //const DelaunayMesher * delaunayMesher = &(*this->begin())->parent;
+    int numVertices = delaunayMesher.getNumVertices();
+    double * vertex = (double *)malloc(3 * sizeof(double) * numVertices);
+    for (int i = 0; i < numVertices; i++)
+      delaunayMesher.getVertex(i).convertToArray(vertex + 3 * i);
+    int numElements = edgeRefineBalls.size();
+    int * elements = (int *)malloc(4 * sizeof(int) * numElements);
+    int i = 0;
+    for (EdgeRefineIterator itr = edgeRefineBalls.begin(); itr != edgeRefineBalls.end(); itr++)
+      memcpy(elements + 4 * (i++), (*itr)->getVertices(), 4 * sizeof(int));
+    tetMesh = new TetMesh(numVertices, vertex, numElements, elements);
+    free(vertex);
+    free(elements);
+  }
+  return tetMesh;
+}
+
+TetMesher::TetMeshWithRefineInfo::TetMeshWithRefineInfo(const DelaunayMesher & delaunayMesher)
+  : enabledAngleRefine(false), delaunayMesher(delaunayMesher) {}
+
+TetMesher::TetMeshWithRefineInfo::~TetMeshWithRefineInfo()
+{
+  for (EdgeRefineIterator itr = edgeRefineBalls.begin(); itr != edgeRefineBalls.end(); itr++)
+    delete *itr;
+}
+
+// Insert a tet during the refine process
+void TetMesher::TetMeshWithRefineInfo::insert(const DelaunayMesher::DelaunayBall * delaunayBall)
+{
+  DelaunayBallWithRefineInfo * delaunayBallWithRefineInfo = new DelaunayBallWithRefineInfo(*delaunayBall);
+  edgeRefineMap.insert(make_pair(delaunayBall, edgeRefineBalls.insert(delaunayBallWithRefineInfo).first));
+  if (enabledAngleRefine) angleRefineMap.insert(make_pair(delaunayBall, angleRefineBalls.insert(delaunayBallWithRefineInfo).first));
+}
+
+// remove a tet during the refine process
+void TetMesher::TetMeshWithRefineInfo::remove(const DelaunayMesher::DelaunayBall * delaunayBall)
+{
+  map<const DelaunayMesher::DelaunayBall *, EdgeRefineIterator, DelaunayMesher::DelaunayBallCompare>::iterator itr =  edgeRefineMap.find(delaunayBall);
+  delete *(itr->second);
+  edgeRefineBalls.erase(itr->second);
+  edgeRefineMap.erase(itr);
+  if (enabledAngleRefine)
+  {
+    itr = angleRefineMap.find(delaunayBall);
+    angleRefineBalls.erase(itr->second);
+    angleRefineMap.erase(itr);
+  }
+}
+
+// Inser a tet
+void TetMesher::insertTet(const int* v)
+{
+  insertFace(v[0], v[1], v[2]);
+  insertFace(v[0], v[1], v[3]);
+  insertFace(v[0], v[3], v[2]);
+  insertFace(v[3], v[1], v[2]);
+}
+
+// Remove a tet
+void TetMesher::removeTet(const int* v)
+{
+  removeFace(v[0], v[1], v[2]);
+  removeFace(v[0], v[1], v[3]);
+  removeFace(v[0], v[3], v[2]);
+  removeFace(v[3], v[1], v[2]);
+}
+
+int TetMesher::segmentRecovery()
+{
+  vector <int> groupID;
+  for (unsigned i = 0; i < objMesh->getNumGroups(); i++)
+    groupID.push_back(i);
+  objMesh->mergeGroups(groupID);
+
+  // Insert all edges of the triangular surface to a queue;
+  set <UEdgeKey> edgeSet;
+  list <OEdgeKey> edgeStack;
+  ObjMesh::Group & group = *objMesh->getGroupHandle(0);
+  for (unsigned int i = 0; i < group.getNumFaces(); i++)
+  {
+    const ObjMesh::Face & face = group.getFace(i);
+    int v0 = face.getVertex(0).getPositionIndex();
+    int v1 = face.getVertex(1).getPositionIndex();
+    int v2 = face.getVertex(2).getPositionIndex();
+
+    // Insert three edges into the queu, no orientation
+    assert(v0 != v1);
+    assert(v2 != v1);
+    assert(v0 != v2);
+    if (edgeSet.find(UEdgeKey(v0, v1)) == edgeSet.end())
+    {
+      edgeSet.insert(UEdgeKey(v0, v1));
+      edgeStack.push_front(OEdgeKey(v0, v1));
+    }
+    if (edgeSet.find(UEdgeKey(v1, v2)) == edgeSet.end())
+    {
+      edgeSet.insert(UEdgeKey(v1, v2));
+      edgeStack.push_front(OEdgeKey(v1, v2));
+    }
+    if (edgeSet.find(UEdgeKey(v2, v0)) == edgeSet.end())
+    {
+      edgeSet.insert(UEdgeKey(v2, v0));
+      edgeStack.push_front(OEdgeKey(v2, v0));
+    }
+  }
+
+  int level = 2;
+
+  while (true)
+  {
+    int sum = 0;
+    int flipped = 0;
+    list <OEdgeKey>::iterator itr = edgeStack.begin();
+    while (itr != edgeStack.end())
+    {
+      OEdgeKey edge = *itr;
+      int result;
+      if (level < 1000)
+      {
+        result = delaunay.segmentRecoveryUsingFlip(OEdgeKey(edge[0], edge[1]), level);
+        if (result != 0)
+        {
+          fflush(NULL);
+          result = delaunay.segmentRecoveryUsingFlip(OEdgeKey(edge[1], edge[0]), level);
+        }
+      }
+      else
+      {
+        result = delaunay.segmentRecoveryUsingSteinerPoint(OEdgeKey(edge[0], edge[1]));
+        result = delaunay.segmentRecoveryUsingFlip(OEdgeKey(edge[0], edge[1]), level);
+      }
+      if (result )
+      {
+      }
+      if (!result)
+        flipped++;
+      sum++;
+      //cout << "Next edge" << " " << result << endl;
+      //if (!result)
+      if (!result)
+        edgeStack.erase(itr++);
+      else
+        itr++;
+      continue;
+    }
+    // No missing edges
+    if (edgeStack.empty())
+      break;
+
+    // Increase the level of flipping edges
+    if (level < 4)
+      level++;
+    // Using steiner points to recover the edges
+    else if (level < 10000)
+      level = 10000;
+    // Fail to recover
+    else
+      break;
+  }
+  return 0;
+}
+
+bool TetMesher::TetMeshWithRefineInfo::DelaunayBallEdgeRefineCMP
+::operator ()(const DelaunayBallWithRefineInfo* const & p1, const DelaunayBallWithRefineInfo* const & p2)
+{
+  if (p1->edgeQuality > p2->edgeQuality)
+    return true;
+  if (p1->edgeQuality < p2->edgeQuality)
+    return false;
+  return defaultComparor(p1, p2);
+}
+
+bool TetMesher::TetMeshWithRefineInfo::DelaunayBallAngleRefineCMP
+::operator ()(const DelaunayBallWithRefineInfo* const & p1, const DelaunayBallWithRefineInfo* const & p2)
+{
+  if (p1->minDihedral < p2->minDihedral)
+    return true;
+  if (p1->minDihedral > p2->minDihedral)
+    return false;
+  return defaultComparor(p1, p2);
+}
+
+int TetMesher::flipSurface()
+{
+  //delaunay.getMesh()->save("beforeflipsurface.veg");
+  edgesInTet.clear();
+  trianglesInTet.clear();
+  for (DelaunayMesher::BallCIter itr = delaunay.getBallSetBegin(); itr != delaunay.getBallSetEnd(); itr++)
+    if ((*itr)->isRegular())
+      insertTet((*itr)->getVertices());
+
+  set <UEdgeKey> edgeQueue;
+  ObjMesh::Group & group = *objMesh->getGroupHandle(0);
+  for (unsigned int i = 0; i < group.getNumFaces(); i++)
+  {
+    const ObjMesh::Face & face = group.getFace(i);
+    int v0 = face.getVertex(0).getPositionIndex();
+    int v1 = face.getVertex(1).getPositionIndex();
+    int v2 = face.getVertex(2).getPositionIndex();
+
+    // Insert three edges into the queue, no orientation
+    assert(v0 != v1);
+    assert(v2 != v1);
+    assert(v0 != v2);
+    if (edgesInTet.find(UEdgeKey(v0, v1)) == edgesInTet.end())
+    {
+      edgeQueue.insert(UEdgeKey(v0, v1));
+    }
+    if (edgesInTet.find(UEdgeKey(v1, v2)) == edgesInTet.end())
+      edgeQueue.insert(UEdgeKey(v1, v2));
+    if (edgesInTet.find(UEdgeKey(v2, v0)) == edgesInTet.end())
+      edgeQueue.insert(UEdgeKey(v2, v0));
+  }
+  while (true)
+  {
+    bool recover = false;
+    set <UEdgeKey>::iterator itr = edgeQueue.begin();
+    while (itr != edgeQueue.end())
+    {
+      UEdgeKey edge = *itr;
+      int f0 = -1, f1 = -1;
+      int v0 = -1, v1 = -1;
+      for (int i = group.getNumFaces() - 1; i >= 0; i--)
+      {
+        const ObjMesh::Face & face = group.getFace(i);
+        int p0 = face.getVertex(0).getPositionIndex(), p1 = face.getVertex(1).getPositionIndex(), p2 = face.getVertex(2).getPositionIndex();
+        if (p0 == edge[0] && p1 == edge[1])
+        {
+          f0 = i; v0 = p2;
+        }
+        else if (p1 == edge[0] && p0 == edge[1])
+        {
+          f1 = i; v1 = p2;
+        }
+        else if (p1 == edge[0] && p2 == edge[1])
+        {
+          f0 = i; v0 = p0;
+        }
+        else if (p2 == edge[0] && p1 == edge[1])
+        {
+          f1 = i; v1 = p0;
+        }
+        else if (p2 == edge[0] && p0 == edge[1])
+        {
+          f0 = i; v0 = p1;
+        }
+        else if (p0 == edge[0] && p2 == edge[1])
+        {
+          f1 = i; v1 = p1;
+        }
+      }
+      assert(f0 != -1 && f1 != -1);
+      if (edgesInTet.find(UEdgeKey(v0, v1)) != edgesInTet.end())
+      {
+        //cout << "Flipping surface " << edge[0] << " " << edge[1] << endl;
+        group.removeFace((std::max)(f0, f1));
+        group.removeFace((std::min)(f0, f1));
+        group.addFace(ObjMesh::Face(ObjMesh::Vertex(edge[0]), ObjMesh::Vertex(v1), ObjMesh::Vertex(v0)));
+        group.addFace(ObjMesh::Face(ObjMesh::Vertex(edge[1]), ObjMesh::Vertex(v0), ObjMesh::Vertex(v1)));
+        recover = true;
+        edgeQueue.erase(itr++);
+      }
+      else
+        itr++;
+
+    }
+    if (!recover)
+    {
+      return edgeQueue.size();
+    }
+  }
+  return 0;
+
+  //objMesh->removeAllGroups();
+  //objMesh->addGroup(group);
+  //objMesh->save("flipped.obj");
+}
diff --git a/libraries/mesher/tetMesher.h b/libraries/mesher/tetMesher.h
new file mode 100644
index 0000000000000000000000000000000000000000..50df5b875bdb6315a19c17de7538cf58d101cab1
--- /dev/null
+++ b/libraries/mesher/tetMesher.h
@@ -0,0 +1,253 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Implements a constrained 3D Delaunay tet mesher with refinement,
+  using the methods from:
+
+  Hang Si. Adaptive tetrahedral mesh generation by constrained Delaunay refinement. 
+  International Journal for Numerical Methods in Engineering, 75:856–880, 2008.
+
+  Hang Si and Klaus Gartner. 3d boundary recovery by constrained delaunay tetrahedralization. 
+  International Journal for Numerical Methods in Engineering, 85(11):1341–1364, 2011.
+
+  The input is a manifold and self-intersection-free triangle mesh,
+  and the output is a quality tet mesh of the volume enclosed by the input mesh
+  that conforms to the input mesh.
+  This mesh is generated using constrained Delaunay tetrahedralization,
+  and by inserting new vertices (``Steiner vertices'') into the mesh as needed
+  to satisy the quality conditions.
+
+  Note: The tet mesher is experimental. It may generate sliver tets with some inputs.
+*/
+
+#ifndef _TETMESHER_H_
+#define _TETMESHER_H_
+
+#include <vector>
+#include <set>
+#include <queue>
+
+#include "vec3d.h"
+#include "objMesh.h"
+#include "tetMesh.h"
+#include "delaunayMesher.h"
+
+class TetMesher
+{
+public:
+
+  TetMesher();
+  virtual ~TetMesher();
+
+  // compute the tet mesh, using constrained 3D Delaunay tetrahedralization, with refinement
+  // refinementQuality is a scalar; one must use refinementQuality >= 1
+  // the smaller the value, the more the mesh will be refined
+  // minDihedralAngle: the minimal dihedral angle in a tet on the result tet mesh should not be larger than "minDihedralAngle"
+  // maxSteinerVertices controls how many vertices are added in the refinement process. < 0 means no limitation.
+  // alpha: the smallest distance between a new Steiner vertex and the existing vertices should be larger than alpha times 
+  // the average edge length of the input mesh
+  // maxTimeSeconds: the routine will terminate if this computation time (in seconds) is exceeded. < 0 means no limitation.
+  TetMesh * compute(ObjMesh * surfaceMesh, double refinementQuality = 1.1, double alpha = 2.0, double minDihedralAngle = 0.0, 
+      int maxSteinerVertices = -1, double maxTimeSeconds = -1.0);
+
+  inline int getNumAddedSteinerVertices() const { return numSteinerVertices; }
+
+protected:
+
+  typedef DelaunayMesher::DelaunayBall DelaunayBall;
+  typedef DelaunayMesher::BallCIter BallCIter;
+
+
+  // Delaunay ball with information of radius/minEdge and minimal dihedral angle
+  struct DelaunayBallWithRefineInfo : public DelaunayBall
+  {
+    double minDihedral;       // Not valid for infinite ball. The minimal dihedral angel of the tetrahedral. Lazy compute
+    double edgeQuality;       // Not valid for infinite ball. Square of the minimal edge of the tetrahedral. Lazy compute
+    DelaunayBallWithRefineInfo(const DelaunayBall & parent);
+  protected:
+    DelaunayBallWithRefineInfo(const DelaunayBallWithRefineInfo &);
+  };
+
+  // A set of delaunay balls with information of radius/minEdge and minimal dihedral angle
+  struct TetMeshWithRefineInfo
+  {
+    static DelaunayMesher::DelaunayBallCompare defaultComparor;
+    TetMeshWithRefineInfo(const DelaunayMesher & delaunayMesher);
+    ~TetMeshWithRefineInfo();
+
+    struct DelaunayBallEdgeRefineCMP           // sort radius/minEdge descend
+    {
+      bool operator () (const DelaunayBallWithRefineInfo * const & p1, const DelaunayBallWithRefineInfo * const & p2);
+    };
+
+
+    struct DelaunayBallAngleRefineCMP         // sort dihedral angel ascend
+    {
+      bool operator () (const DelaunayBallWithRefineInfo * const & p1, const DelaunayBallWithRefineInfo * const & p2);
+    };
+
+    typedef std::set<const DelaunayBallWithRefineInfo *, DelaunayBallEdgeRefineCMP>::const_iterator EdgeRefineIterator;
+    typedef std::set<const DelaunayBallWithRefineInfo *, DelaunayBallAngleRefineCMP>::const_iterator AngleRefineIterator;
+    inline EdgeRefineIterator getEdgeRefineBegin() { return edgeRefineBalls.begin(); }
+    inline EdgeRefineIterator getEdgeRefineEnd() { return edgeRefineBalls.end(); }
+    inline EdgeRefineIterator getAngleRefineBegin() { return angleRefineBalls.begin(); }
+    inline EdgeRefineIterator getAngleRefineEnd() { return angleRefineBalls.end(); }
+
+    // Get Tetmesh from the set of delaunayBall.
+    // Callee will alloc the memory and caller is responsible to free the memory
+    TetMesh * getTetMesh();
+
+    // Compute the refine information such as edge quality and dihedral angle
+    // Alloc memory and deep copy inside, free memory in deconstruc function and remove function
+    void insert(const DelaunayMesher::DelaunayBall * delaunayBall);
+
+    // Remove the delaunay ball that is removed in the last refinement. Free memory inside
+    void remove(const DelaunayMesher::DelaunayBall * delaunayBall);
+
+    // Enable the minimal dihedral angle refinement
+    inline void enableAngleRefine() { enabledAngleRefine = true; }
+    //friend class TetMesher;
+  protected:
+
+    bool enabledAngleRefine; // Whether the angle refinement is enabled
+    const DelaunayMesher & delaunayMesher; // Reference to the delaunayMesher, used to get the positions of vertices
+    std::set<const DelaunayBallWithRefineInfo *, DelaunayBallEdgeRefineCMP> edgeRefineBalls; // Sort the current delaunay ball by radius/minEdge descendingly
+    std::set<const DelaunayBallWithRefineInfo *, DelaunayBallAngleRefineCMP> angleRefineBalls; // Sort the current delaunay ball by diheral angle  aescendingly
+    std::map<const DelaunayMesher::DelaunayBall *, EdgeRefineIterator, DelaunayMesher::DelaunayBallCompare> edgeRefineMap; // Look up the iterator in edgeRefineSet by pointer
+    std::map<const DelaunayMesher::DelaunayBall *, AngleRefineIterator, DelaunayMesher::DelaunayBallCompare> angleRefineMap;     // Look up the iterator in angleRefineSet by pointer
+  } resultTetMesh;
+
+  // construct a constrained delaunay mesh, if recovery is true, the face recovery process will be done
+  int initializeCDT(bool recovery = true);
+  // refine a constrained delaunay mesh
+
+  /*
+    Refine one tet with bad edge quality, radius/minEdge
+    Ret value
+    0: refined
+    1: No tet to refine due to enough steiner points
+    2: No bad tet to refine
+  */
+  int refineEdge(double refinementQuality, const double minmimalDist);
+
+  /*
+    Refine one tet with bad dihedral angle, too small dihedral angle
+    Ret value
+    0: refined
+    1: No tet to refine due to enough steiner points
+    2: No bad tet to refine
+  */
+  int refineAngle(const double angleBound, const double minmimalDist);
+
+  // Make sure the first four vertices of the triangle mesh are not on the same plane
+  static bool renumberInitialVertices(ObjMesh * surfaceMesh);
+
+  //remove all tetrahedrons that are outside the constrained delaunay mesh
+  int removeOutside();
+
+  // insert a tet into the tetmesh
+  void insertTet(const int * v);
+  // remove a tet from the tetmesh
+  void removeTet(const int * v);
+
+  // insert a face into the tetmesh
+  void insertFace(int f1, int f2, int f3);
+  // remove a face from the tetmesh
+  void removeFace(int f1, int f2, int f3);
+  // insert an edge into the tetmesh
+  void insertEdge(int e1, int e2);
+  // remove an edge from the tetmesh
+  void removeEdge(int e1, int e2);
+
+  // get a face from the input triangular mesh
+  UTriKey getFace(int index);
+
+  // Do face recovery process
+  void faceRecovery();
+
+  // form a missing region by one face, the boundary of the missing face is on the current tet, the triangles in the region are all missing
+  void formRegion(int face, std::set<int> &region);
+  // calculate the boundary of a tet mesh, triangles that has only one neighbor tetrahedron
+  void calculateTetBoundary(std::vector<DelaunayMesher::DelaunayBall*> &tet, std::vector<UTriKey> &boundary);
+  // calculate the boundary of a missing region, the region consists of connected triangles, boundary is a set of edges that have only one neighbor triangle
+  void calculateTriangleBoundary(std::set<int> &missingRegion, std::set<std::pair<int, int> > &boundary);
+  // build a map for triangular mesh, which two triangles are the neigbhor of an edge
+  void buildTriangleNeighbor(std::vector<UTriKey> &mesh, std::map<std::pair<int, int>, std::pair<int, int> > &neighbor);
+  // insert the missing region into the cavity formed by removing all tetrahedrons that intersect the missing region, and then form two cavities
+  bool formTwoCavities(std::vector<UTriKey> &missingRegion, std::set<std::pair<int, int> >& boundary, std::map<std::pair<int, int>, std::pair<int, int> > &neighbor);
+  // fill a hole defined by a set of boundary triangles, compute what tetrahedrons need adding
+  bool fillHole(std::vector<UTriKey> &holeBoundary);
+
+  // determine whether the point is too close to any existent vertices in the current mesh
+  bool isTooCloseToOtherVertices(const Vec3d & r, const double minimalDist) const;
+
+  // Get the steiner point in segement recovery.
+  Vec3d getSteinerPoint(const UEdgeKey & edge, const bool isAcute1, const bool isAcute2, const double lfs1, const double lfs2, const double c = 2) const;
+
+  // segment recovery
+  int segmentRecovery();
+
+  // Used to compute delaunay mesh
+  DelaunayMesher delaunay;
+  // the input triangular mesh
+  ObjMesh * objMesh;
+
+  // the set of triangles that are in the input triangular mesh but not in the volumetric mesh
+  std::vector<UTriKey> lost;
+  // the indices of triangles that are in a missing region
+  std::set<int> region;
+
+  // for the input triangular mesh, which three triangles are the neigbhors of a triangle. All triangles are described by their indices
+  std::vector<std::vector<int> > neighborSurface;
+
+  // Tetradedrons added into the tet mesh to fill a hole
+  std::vector <std::vector<int> > toAdd;
+
+  // Triangles in the volumetric mesh, key is the triangle and value is how many tets are associated with the triangle
+  std::map<UTriKey, unsigned> trianglesInTet;
+
+  // flip the input objmesh to fit the delaunay mesh in case failure in the segment recovery
+  int flipSurface();
+
+  // edges in the volumetric mesh, key is the edge and value is how many tets are associated with the edge
+  std::map<UEdgeKey, unsigned> edgesInTet;
+
+  // the number of steiner points inserted (refinement)
+  int numSteinerVertices;
+
+  // recursion depth for face recovery;
+  int faceRecoveryDepth;
+};
+
+#endif /* TETMESHER_H_ */
+
diff --git a/libraries/mesher/triangleTetIntersection.cpp b/libraries/mesher/triangleTetIntersection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ffceed54030e6c74fb5305f0c3ba480970d51b9f
--- /dev/null
+++ b/libraries/mesher/triangleTetIntersection.cpp
@@ -0,0 +1,146 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "triangleTetIntersection.h"
+
+bool TriangleTetIntersection::tetrahedronIntersectTriangle(const Vec3d & v1, const Vec3d & v2, const Vec3d & v3, const Vec3d & v4, const Vec3d & t1, const Vec3d & t2, const Vec3d & t3)
+{
+  Tetrahedron tet(v1, v2, v3, v4);
+
+  // If any vertex of a triangle is in the tetrahedron, intersect.
+  if (tet.contains(t1) || tet.contains(t2) || tet.contains(t3))
+    return true;
+  Triangle triangle(t1, t2, t3);
+
+  // If any edge of a tetrahedron intersects the triangle, intersect
+  bool intersect = triangle.intersect(v2, v1) || triangle.intersect(v1, v3) || triangle.intersect(v1, v4) || triangle.intersect(v2, v3) || triangle.intersect(v2, v4) || triangle.intersect(v3, v4);
+  if (intersect)
+    return true;
+
+  // If any edge of a triangle intersects any face of the tetrahedron, intersect
+  Triangle triangle1(v1, v2, v3), triangle2(v1, v2, v4), triangle3(v1, v3, v4), triangle4(v2, v3, v4);
+  if (triangle1.intersect(t1, t2) || triangle1.intersect(t1, t3) || triangle1.intersect(t2, t3))
+    return true;
+  if (triangle2.intersect(t1, t2) || triangle2.intersect(t1, t3) || triangle2.intersect(t2, t3))
+    return true;
+  if (triangle3.intersect(t1, t2) || triangle3.intersect(t1, t3) || triangle3.intersect(t2, t3))
+    return true;
+  if (triangle4.intersect(t1, t2) || triangle4.intersect(t1, t3) || triangle4.intersect(t2, t3))
+    return true;
+  return false;
+}
+
+TriangleTetIntersection::Tetrahedron::Tetrahedron(const Vec3d & v0_, const Vec3d & v1_, const Vec3d & v2_, const Vec3d & v3_)
+    : v0(v0_), v1(v1_), v2(v2_), v3(v3_)
+{
+  static const Vec3d ZERO = Vec3d(0, 0, 0);
+  n0 = cross(v2 - v1, v3 - v1);
+  d0 = dot(n0, v1);
+  n1 = cross(v2 - v0, v3 - v0);
+  d1 = dot(n1, v0);
+  n2 = cross(v1 - v0, v3 - v0);
+  d2 = dot(n2, v0);
+  n3 = cross(v2 - v0, v1 - v0);
+  d3 = dot(n3, v0);
+  if (dot(n0, v0) < d0)
+  {
+    n0 = ZERO - n0;
+    d0 = -d0;
+  }
+  if (dot(n1, v1) < d1)
+  {
+    n1 = ZERO - n1;
+    d1 = -d1;
+  }
+  if (dot(n2, v2) < d2)
+  {
+    n2 = ZERO - n2;
+    d2 = -d2;
+  }
+  if (dot(n3, v3) < d3)
+  {
+    n3 = ZERO - n3;
+    d3 = -d3;
+  }
+}
+
+bool TriangleTetIntersection::Tetrahedron::contains(const Vec3d & p)
+{
+//  cout << dot(p, n0) << " " << dot(p, n1) << " " << dot(p, n2) << " " << dot(p, n3) << "\n";
+  bool ret = dot(p, n0) > d0 + 1e-10 && dot(p, n1) > d1 + 1e-10 && dot(p, n2) > d2 + 1e-10 && dot(p, n3) > d3 + 1e-10;
+  //printf("%d\n", ret);
+  return ret;
+}
+
+TriangleTetIntersection::Triangle::Triangle(const Vec3d & v0_, const Vec3d & v1_, const Vec3d & v2_)
+    : v0(v0_), v1(v1_), v2(v2_)
+{
+  base1 = v1 - v0;
+  base2 = v2 - v0;
+  n = cross(base1, base2);
+  n.normalize();
+  d = dot(n, v0);
+}
+
+bool TriangleTetIntersection::Triangle::contains(const Vec3d & p)
+{
+  double d0 = (base1[1] * base2[2] - base1[2] * base2[1]);
+  double d1 = (base1[0] * base2[2] - base1[2] * base2[0]);
+  double d2 = (base1[0] * base2[1] - base1[1] * base2[0]);
+  Vec3d v = p - v0;
+  double t1, t2;
+  if (fabs(d2) > fabs(d1) && fabs(d2) > fabs(d0))
+  {
+    t1 = (v[0] * base2[1] - v[1] * base2[0]) / d2;
+    t2 = (v[1] * base1[0] - v[0] * base1[1]) / d2;
+  }
+  else if (fabs(d1) > fabs(d0))
+  {
+    t1 = (v[0] * base2[2] - v[2] * base2[0]) / d1;
+    t2 = (v[2] * base1[0] - v[0] * base1[2]) / d1;
+  }
+  else
+  {
+    t1 = (v[1] * base2[2] - v[2] * base2[1]) / d0;
+    t2 = (v[2] * base1[1] - v[1] * base1[2]) / d0;
+  }
+  return t1 >= 1e-10 && t2 >= 1e-10 && t1 + t2 <= 1 - 1e-10;
+}
+
+bool TriangleTetIntersection::Triangle::intersect(const Vec3d & l0, const Vec3d & l1)
+{
+  double t = (d - dot(n, l0)) / dot(n, l1 - l0);
+  if (!(t > 1e-10 && t < 1 - 1e-10))
+    return false;
+  return contains(l0 * (1 - t) + l1 * t);
+}
+
diff --git a/libraries/mesher/triangleTetIntersection.h b/libraries/mesher/triangleTetIntersection.h
new file mode 100644
index 0000000000000000000000000000000000000000..d1ad7b4ec47d56f33cc57294209ab3b11b599453
--- /dev/null
+++ b/libraries/mesher/triangleTetIntersection.h
@@ -0,0 +1,94 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "mesher" library , Copyright (C) 2018 USC                             *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Intersection between a tet and a triangle.
+*/
+
+#ifndef _TRIANGLETETINTERSECTION_H_
+#define _TRIANGLETETINTERSECTION_H_
+
+#include "vec3d.h"
+
+// This class computes whether a tetrahedron intersects a triangle, excluding just touching.
+class TriangleTetIntersection
+{
+public:
+
+  // does the tetrahedron intersects the triangle, excluding just touching
+  static bool tetrahedronIntersectTriangle(const Vec3d & tet1, const Vec3d & tet2, const Vec3d & tet3, const Vec3d & tet4, const Vec3d & t1, const Vec3d & t2, const Vec3d & t3);
+
+protected:
+  class Tetrahedron
+  {
+  protected:
+    // The positions of the four vertices
+    const Vec3d v0, v1, v2, v3;
+
+    // The normals of the four faces
+    Vec3d n0, n1, n2, n3;
+    // The positions of the four faces
+    double d0, d1, d2, d3;
+  public:
+    Tetrahedron(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2, const Vec3d & v3);
+    // Whether a point is inside the tetrahedron, excluding on the tetrahedron
+    bool contains(const Vec3d & p);
+  };
+
+  class Triangle 
+  {
+  protected:
+    // The positions of the three vertices
+    const Vec3d v0, v1, v2;
+    // The normal
+    Vec3d n;
+    // The position of the face
+    double d;
+    // The two base vectors of the face
+    Vec3d base1, base2;
+  public:
+    Triangle(const Vec3d & v0, const Vec3d & v1, const Vec3d & v2);
+    // Whether a point is inside the triangle, excluding on the triangle
+    bool contains(const Vec3d & p);
+    // Whether the segment starting from l0 and ending at l1 intersects the triangle, excluding just touching
+    bool intersect(const Vec3d & l0, const Vec3d & l1);
+  };
+
+  //compute the determinant of a 3x3 matrix defined by three vectors whose dimensions are all three
+  static inline double det(const Vec3d & a, const Vec3d & b, const Vec3d & c) { return dot(a, cross(b, c)); }
+
+private:
+  TriangleTetIntersection();
+};
+
+#endif /* INTERSECTION_H_ */
+
diff --git a/src/libminivector/eig3.cpp b/libraries/minivector/eig3.cpp
similarity index 99%
rename from src/libminivector/eig3.cpp
rename to libraries/minivector/eig3.cpp
index 7cbb3a51856e6737eec2bd75e9f56eca86c16655..53ec4b194b3e1a134100cdc1b8dbd84f6d4ebd9f 100644
--- a/src/libminivector/eig3.cpp
+++ b/libraries/minivector/eig3.cpp
@@ -3,8 +3,6 @@
 
 #include <math.h>
 
-namespace vega
-{
 #ifdef MAX
 #undef MAX
 #endif
@@ -264,4 +262,3 @@ void eigen_decomposition(double A[n][n], double V[n][n], double d[n]) {
   tred2(V, d, e);
   tql2(V, d, e);
 }
-}
diff --git a/libraries/minivector/eig3.h b/libraries/minivector/eig3.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b36472adf59a252347728c31f5c7d61a8cde8de
--- /dev/null
+++ b/libraries/minivector/eig3.h
@@ -0,0 +1,10 @@
+/* Eigen-decomposition for symmetric 3x3 real matrices.
+   Public domain, copied from the public domain Java library JAMA. */
+
+#ifndef _eig_h
+
+/* Symmetric matrix A => eigenvectors in columns of V, corresponding
+   eigenvalues in d. */
+void eigen_decomposition(double A[3][3], double V[3][3], double d[3]);
+
+#endif
diff --git a/libraries/minivector/mat3d.cpp b/libraries/minivector/mat3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4534de69907ebe5b4c53fa4a483f5ca47dfcba39
--- /dev/null
+++ b/libraries/minivector/mat3d.cpp
@@ -0,0 +1,277 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "mat3d.h"
+#include "eig3.h"
+
+const Mat3d Mat3d::Identity(1.0);
+const Mat3d Mat3d::Zero(0.0);
+
+static_assert(sizeof(Mat3d) == sizeof(double) * 9, "Size of Mat3d should be sizeof(double) * 9");
+
+// This routine calls a public domain routine (eigen_decomposition), which was 
+// downloaded from:
+// http://barnesc.blogspot.com/2007/02/eigenvectors-of-3x3-symmetric-matrix.html
+// According to the header file of eig3.h, the C version in eig3.{h,cpp} 
+// was obtained by copying code from the public domain Java library JAMA, 
+// and declaring the resulting C code to also be public domain.
+// The eig3.{h,cpp} files are included in this package, intact as they were
+// downloaded from the Internet.
+
+// This routine was written by Jernej Barbic.
+void eigen_sym(Mat3d & a, Vec3d & eig_val, Vec3d eig_vec[3])
+{
+  double A[3][3] = { {a[0][0], a[0][1], a[0][2]},
+                     {a[1][0], a[1][1], a[1][2]},
+                     {a[2][0], a[2][1], a[2][2]} };
+  double V[3][3];
+  double d[3];
+  eigen_decomposition(A, V, d);
+
+  eig_val = Vec3d(d[2],d[1],d[0]);
+  if(eig_vec)
+  {
+    eig_vec[0] = Vec3d(V[0][2], V[1][2], V[2][2]);
+    eig_vec[1] = Vec3d(V[0][1], V[1][1], V[2][1]);
+    eig_vec[2] = Vec3d(V[0][0], V[1][0], V[2][0]);
+  }
+}
+
+/*
+    The implementation follows section 5 of 
+    G. Irving, J. Teran, and R. Fedkiw. Invertible Finite Elements for Robust Simulation of Large Deformation. 
+    In Symp. on Computer Animation (SCA), pages 131–140, 2004.
+    It computes a^T a, and computes its eigenvectors, a^T a = V Sigma^2 V^T . 
+    Sigma is then recovered using sqrt from Sigma^2.
+    To recover U, compute U = F * V * diag(Sigma^{-1}).
+    Care must be taken when singular values of Sigma are small (this is 
+    handled in the code).
+
+    We have also added the ability to compute standard SVD (modifiedSVD = 0).
+*/
+
+int SVD(Mat3d & F, Mat3d & U, Vec3d & Sigma, Mat3d & V, double singularValue_eps, int modifiedSVD)
+{
+  // The code handles the following special situations:
+
+  //---------------------------------------------------------
+  // 1. det(V) == -1
+  //    - multiply the first column of V by -1
+  //---------------------------------------------------------
+  // 2. An entry of Sigma is near zero
+  //---------------------------------------------------------
+  // (if modifiedSVD == 1) :
+  // 3. negative determinant (Tet is inverted in solid mechanics).
+  //    - check if det(U) == -1
+  //    - If yes, then negate the minimal element of Sigma
+  //      and the corresponding column of U
+  //---------------------------------------------------------
+
+  // form F^T F and do eigendecomposition
+  Mat3d normalEq = trans(F) * F;
+  Vec3d eigenValues;
+  Vec3d eigenVectors[3];
+
+  eigen_sym(normalEq, eigenValues, eigenVectors);
+
+  V.set(eigenVectors[0][0], eigenVectors[1][0], eigenVectors[2][0],
+    eigenVectors[0][1], eigenVectors[1][1], eigenVectors[2][1],
+    eigenVectors[0][2], eigenVectors[1][2], eigenVectors[2][2]);
+  /*
+    printf("--- original V ---\n");
+    V.print();
+    printf("--- eigenValues ---\n");
+    printf("%G %G %G\n", eigenValues[0], eigenValues[1], eigenValues[2]);
+  */
+
+  // Handle situation:
+  // 1. det(V) == -1
+  //    - multiply the first column of V by -1
+  if (det(V) < 0.0)
+  {
+    // convert V into a rotation (multiply column 1 by -1)
+    V[0][0] *= -1.0;
+    V[1][0] *= -1.0;
+    V[2][0] *= -1.0;
+  }
+
+  Sigma[0] = (eigenValues[0] > 0.0) ? sqrt(eigenValues[0]) : 0.0;
+  Sigma[1] = (eigenValues[1] > 0.0) ? sqrt(eigenValues[1]) : 0.0;
+  Sigma[2] = (eigenValues[2] > 0.0) ? sqrt(eigenValues[2]) : 0.0;
+
+  //printf("--- Sigma ---\n");
+  //printf("%G %G %G\n", Sigma[0][0], Sigma[1][1], Sigma[2][2]);
+
+  // compute inverse of singular values
+  // also check if singular values are close to zero
+  Vec3d SigmaInverse;
+  SigmaInverse[0] = (Sigma[0] > singularValue_eps) ? (1.0 / Sigma[0]) : 0.0;
+  SigmaInverse[1] = (Sigma[1] > singularValue_eps) ? (1.0 / Sigma[1]) : 0.0;
+  SigmaInverse[2] = (Sigma[2] > singularValue_eps) ? (1.0 / Sigma[2]) : 0.0;
+  
+  // compute U using the formula:
+  // U = F * V * diag(SigmaInverse)
+  U = F * V;
+  U.multiplyDiagRight(SigmaInverse);
+
+  // In theory, U is now orthonormal, U^T U = U U^T = I .. it may be a rotation or a reflection, depending on F.
+  // But in practice, if singular values are small or zero, it may not be orthonormal, so we need to fix it.
+  // Handle situation:
+  // 2. An entry of Sigma is near zero
+  // ---------------------------------------------------------
+
+  /*
+    printf("--- SigmaInverse ---\n");
+    SigmaInverse.print();
+    printf(" --- U ---\n");
+    U.print();
+  */
+  
+  if ((Sigma[0] < singularValue_eps) && (Sigma[1] < singularValue_eps) && (Sigma[2] < singularValue_eps))
+  {
+    // extreme case, all singular values are small, material has collapsed almost to a point
+    // see [Irving 04], p. 4
+    U.set(1.0, 0.0, 0.0,
+          0.0, 1.0, 0.0,
+          0.0, 0.0, 1.0);
+  }
+  else 
+  {
+    // handle the case where two singular values are small, but the third one is not
+    // handle it by computing two (arbitrary) vectors orthogonal to the eigenvector for the large singular value
+    int done = 0;
+    for(int dim=0; dim<3; dim++)
+    {
+      int dimA = dim;
+      int dimB = (dim + 1) % 3;
+      int dimC = (dim + 2) % 3;
+      if ((Sigma[dimB] < singularValue_eps) && (Sigma[dimC] < singularValue_eps))
+      {
+        // only the column dimA can be trusted, columns dimB and dimC correspond to tiny singular values
+        Vec3d tmpVec1(U[0][dimA], U[1][dimA], U[2][dimA]); // column dimA
+        Vec3d tmpVec2;
+        tmpVec2 = tmpVec1.findOrthonormalVector();
+        Vec3d tmpVec3 = norm(cross(tmpVec1, tmpVec2));
+        U[0][dimB] = tmpVec2[0];
+        U[1][dimB] = tmpVec2[1];
+        U[2][dimB] = tmpVec2[2];
+        U[0][dimC] = tmpVec3[0];
+        U[1][dimC] = tmpVec3[1];
+        U[2][dimC] = tmpVec3[2];
+        if (det(U) < 0.0)
+        {
+          U[0][dimB] *= -1.0;
+          U[1][dimB] *= -1.0;
+          U[2][dimB] *= -1.0;
+        }
+        done = 1;
+        break; // out of for
+      }
+    }
+
+    // handle the case where one singular value is small, but the other two are not
+    // handle it by computing the cross product of the two eigenvectors for the two large singular values
+    if (!done) 
+    {
+      for(int dim=0; dim<3; dim++)
+      {
+        int dimA = dim;
+        int dimB = (dim + 1) % 3;
+        int dimC = (dim + 2) % 3;
+
+        if (Sigma[dimA] < singularValue_eps)
+        {
+          // columns dimB and dimC are both good, but column dimA corresponds to a tiny singular value
+          Vec3d tmpVec1(U[0][dimB], U[1][dimB], U[2][dimB]); // column dimB
+          Vec3d tmpVec2(U[0][dimC], U[1][dimC], U[2][dimC]); // column dimC
+          Vec3d tmpVec3 = norm(cross(tmpVec1, tmpVec2));
+          U[0][dimA] = tmpVec3[0];
+          U[1][dimA] = tmpVec3[1];
+          U[2][dimA] = tmpVec3[2];
+          if (det(U) < 0.0)
+          {
+            U[0][dimA] *= -1.0;
+            U[1][dimA] *= -1.0;
+            U[2][dimA] *= -1.0;
+          }
+          done = 1;
+          break; // out of for
+        }
+      }
+    }
+
+    if ((!done) && (modifiedSVD == 1))
+    {
+      // Handle situation:
+      // 3. negative determinant (Tet is inverted in solid mechanics)
+      //    - check if det(U) == -1
+      //    - If yes, then negate the minimal element of Sigma
+      //      and the corresponding column of U
+
+      double detU = det(U);
+      if (detU < 0.0)
+      {
+        // negative determinant
+        // find the smallest singular value (they are all non-negative)
+        int smallestSingularValueIndex = 0;
+        for(int dim=1; dim<3; dim++)
+          if (Sigma[dim] < Sigma[smallestSingularValueIndex])
+            smallestSingularValueIndex = dim;
+
+        // negate the smallest singular value
+        Sigma[smallestSingularValueIndex] *= -1.0;
+        U[0][smallestSingularValueIndex] *= -1.0;
+        U[1][smallestSingularValueIndex] *= -1.0;
+        U[2][smallestSingularValueIndex] *= -1.0;
+      }
+    }
+  }
+
+  /*
+    printf("U = \n");
+    U.print();
+    printf("Sigma = \n");
+    Sigma.print();
+    printf("V = \n");
+    V.print();
+  */
+
+  return 0;
+}
+
+  // NOTE: This particular routine is not publicly released, as its implementation
+  // was taken from Numerical Recipes, which is not free software.
+  // (you can use the eigen_sym routine above which is public domain)
+bool eigen_sym_NR(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3], int maxIterations, double epsilon)
+{
+  return false;
+}
+
diff --git a/libraries/minivector/mat3d.h b/libraries/minivector/mat3d.h
new file mode 100644
index 0000000000000000000000000000000000000000..feca236c902cd90a8f2d41465c15b6c045be6630
--- /dev/null
+++ b/libraries/minivector/mat3d.h
@@ -0,0 +1,520 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  This class implements a 3x3 matrix, with elementary algebra,
+  including the computation of eigenvalues and eigenvectors of a *symmetric* 3x3 matrix.
+  (using a public domain external routine; see mat3d.cpp and eig3.h)
+*/
+
+
+
+#ifndef _MINIMAT3D_H_
+#define _MINIMAT3D_H_
+
+#include "vec3d.h"
+
+class Mat3d {
+public:
+  
+  inline Mat3d() {}
+
+  /*
+          [ x0  x1  x2 ]
+    M  =  [ x3  x4  x5 ]
+          [ x6  x7  x8 ]
+  */
+  inline Mat3d(double x0, double x1, double x2,
+               double x3, double x4, double x5,
+               double x6, double x7, double x8);
+  inline Mat3d(const double mat[9]); // "mat" must be given in row-major order
+  inline Mat3d(const Vec3d rows[3]); 
+  inline Mat3d(const Vec3d & row0, const Vec3d & row1, const Vec3d & row2);
+  inline Mat3d(double diag); // create a diagonal matrix with all entries "diag" (can create zero matrix by passing 0.0)
+
+  inline void set(double x0, double x1, double x2,
+		  double x3, double x4, double x5,
+		  double x6, double x7, double x8);
+  inline void set(double value); // set all elements to value
+
+  inline Mat3d & operator=(const Mat3d & source); 
+
+  inline Mat3d operator+ (const Mat3d & ) const;
+  inline Mat3d & operator+= (const Mat3d & );
+
+  inline Mat3d operator- (const Mat3d & ) const;
+  inline Mat3d & operator-= (const Mat3d & );
+
+  inline Mat3d & operator*= (double scalar);
+  inline Mat3d & operator/= (double scalar);
+
+  inline bool operator== (const Mat3d &) const;
+  inline bool operator!= (const Mat3d &) const;
+
+  friend inline Mat3d operator* (double scalar, const Mat3d & mat2);
+  friend inline Mat3d operator/ (const Mat3d & mat2, double scalar);
+
+  inline Mat3d & multiplyDiagRight(Vec3d & v); // M = M * diag(v)
+  inline Mat3d & multiplyDiagLeft(Vec3d & v); // M = diag(v) * M
+
+  friend inline Mat3d tensorProduct(const Vec3d & vec1, const Vec3d & vec2);
+
+  friend inline Mat3d inv(const Mat3d &); // inverse matrix
+  friend inline double det(const Mat3d & mat); // determinant
+  friend inline Mat3d trans(const Mat3d & mat); // transpose
+
+  inline double maxAbsEntry() const;
+  inline bool hasNaN() const;
+  friend inline std::ostream &operator << (std::ostream &s, const Mat3d &v);
+  inline void print() const;
+
+  inline Vec3d & operator[] (int index); // M[i] returns i-th row
+  inline const Vec3d & operator[] (int index) const; // M[i] returns i-th row
+
+  // converts the matrix into a C array of length 9 in row-major order
+  inline void convertToArray(double array[9]) const;
+  inline void addToArray(double array[9]) const;
+
+  const double * data() const { return &elt[0][0]; }
+  double * data() { return &elt[0][0]; }
+
+  // matrix-vector multiply
+  inline const Vec3d operator* (const Vec3d & vec) const;
+
+  // matrix-matrix multiply
+  inline const Mat3d operator* (const Mat3d & mat2) const;
+
+  // Computes eigenvalues and eigenvectors of a 3x3 matrix M
+  // Assumes symmetric matrix; contents of matrix "M" are not modified by the routine
+  // Eigenvalues are sorted in decreasing order (not decreasing absolute-value order)
+  // Returned eigenvectors are unit length
+  friend void eigen_sym(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3]);
+
+  // NOTE: This particular routine is not publicly released, as its implementation
+  // was taken from Numerical Recipes, which is not free software.
+  // (you can use the eigen_sym routine above which is public domain)
+  // Computes eigenvalues and eigenvectors of a 3x3 matrix, using Jacobi Iteration
+  // Assumes symmetric matrix; contents of matrix "M" are overwritten (destroyed)
+  // Returns true if iteration succeeded in making the sum of abs values of non-diagonal values below epsilon, and false otherwise
+  // Default epsilon is machine precision (which always converged with our matrices)
+  // Eigenvalues are sorted in decreasing absolute order
+  // Returned eigenvectors are unit length
+  friend bool eigen_sym_NR(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3], int maxIterations, double epsilon);
+
+  /*
+    Standard SVD (modifiedSVD == 0):
+
+    Given a 3x3 matrix M, decomposes it using SVD so
+    that M = U Sigma V^T where U is a 3x3 rotation, 
+    V is 3x3 orthonormal (V^T V = V V^T = I), and
+    Sigma is a diagonal matrix with non-negative entries,
+    in descending order, Sigma[0] >= Sigma[1] >= Sigma[2] >= 0.
+    Note that the matrix V may have a determinant equaling 
+    to -1 (i.e., reflection).
+
+    singularValue_eps is a threshold to determine
+    when a singular value is deemed zero, and special handling is then invoked
+    to improve numerical robustness.
+
+    Modified SVD (modifiedSVD == 1):
+
+    The SVD is modified so that it gives the 
+    following properties (useful in solid mechanics applications) :
+
+    1) Not just the determinant of U, but also the determinant of V is 1 
+      (i.e., both U and V are rotations, not reflections). 
+
+    2) The smallest diagonal value Sigma[2] may be negative. We have:
+       Sigma[0] >= Sigma[1] >= abs(Sigma[2]) >= 0 .
+  */
+  friend int SVD(Mat3d & M, Mat3d & U, Vec3d & Sigma, Mat3d & V, double singularValue_eps, int modifiedSVD);
+
+  const static Mat3d Identity;
+  const static Mat3d Zero;
+protected:
+  Vec3d elt[3]; // the three rows of the matrix
+};
+
+// Computes eigenvalues and eigenvectors of a 3x3 matrix M
+// Assumes symmetric matrix; contents of matrix "M" are not modified by the routine
+// Eigenvalues are sorted in decreasing order (not decreasing absolute-value order)
+// Returned eigenvectors are unit length
+void eigen_sym(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3]=NULL);
+
+// inverse of a 3x3 matrix (suitable when A is given by a pointer to the 9 entries)
+// input: A (row-major)
+// output: AInv
+void inverse3x3(const double * A, double * AInv);
+
+// these two declarations are repeated here, with default arguments, to conform to the C++ standard; this makes it possible to compile the code on Apple's clang compiler
+bool eigen_sym_NR(Mat3d & M, Vec3d & eig_val, Vec3d eig_vec[3], int maxIterations=50, double epsilon=0.0);
+int SVD(Mat3d & M, Mat3d & U, Vec3d & Sigma, Mat3d & V, double singularValue_eps=1e-8, int modifiedSVD=0);
+
+// ===== below is the implementation =====
+
+inline Mat3d::Mat3d(double x0_g, double x1_g, double x2_g,
+                    double x3_g, double x4_g, double x5_g,
+                    double x6_g, double x7_g, double x8_g)
+{
+  elt[0] = Vec3d(x0_g,x1_g,x2_g);
+  elt[1] = Vec3d(x3_g,x4_g,x5_g);
+  elt[2] = Vec3d(x6_g,x7_g,x8_g);
+}
+
+inline Mat3d::Mat3d(const double mat[9])
+{
+  elt[0] = Vec3d(mat[0],mat[1],mat[2]);
+  elt[1] = Vec3d(mat[3],mat[4],mat[5]);
+  elt[2] = Vec3d(mat[6],mat[7],mat[8]);
+}
+
+inline Mat3d::Mat3d(const Vec3d rows[3]) 
+{
+  elt[0] = rows[0];
+  elt[1] = rows[1];
+  elt[2] = rows[2];
+}
+
+inline Mat3d::Mat3d(const Vec3d & row0, const Vec3d & row1, const Vec3d & row2)
+{
+  elt[0] = row0;
+  elt[1] = row1;
+  elt[2] = row2;
+}
+
+inline Mat3d::Mat3d(double diag)
+{
+  elt[0] = Vec3d(diag,0,0);
+  elt[1] = Vec3d(0,diag,0);
+  elt[2] = Vec3d(0,0,diag);
+}
+
+inline void Mat3d::set(double x0, double x1, double x2,
+		       double x3, double x4, double x5,
+		       double x6, double x7, double x8)
+{
+  elt[0][0] = x0;
+  elt[0][1] = x1;
+  elt[0][2] = x2;
+
+  elt[1][0] = x3;
+  elt[1][1] = x4;
+  elt[1][2] = x5;
+
+  elt[2][0] = x6;
+  elt[2][1] = x7;
+  elt[2][2] = x8;
+}
+
+inline void Mat3d::set(double value)
+{
+  elt[0][0] = elt[0][1] = elt[0][2] = 
+  elt[1][0] = elt[1][1] = elt[1][2] =
+  elt[2][0] = elt[2][1] = elt[2][2] = value;
+}
+
+inline Mat3d & Mat3d::operator=(const Mat3d & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+  elt[2] = source.elt[2];
+
+  return *this;
+}
+
+inline Mat3d Mat3d::operator+ (const Mat3d & mat2) const
+{
+  Mat3d sum = *this;
+  sum.elt[0] += mat2.elt[0];
+  sum.elt[1] += mat2.elt[1];
+  sum.elt[2] += mat2.elt[2];
+
+  return sum;
+}
+
+inline Mat3d & Mat3d::operator+= (const Mat3d & mat2)
+{
+  elt[0] += mat2.elt[0];
+  elt[1] += mat2.elt[1];
+  elt[2] += mat2.elt[2];
+  return *this;
+}
+
+inline Mat3d Mat3d::operator- (const Mat3d & mat2) const
+{
+  Mat3d sum = *this;
+  sum.elt[0] -= mat2.elt[0];
+  sum.elt[1] -= mat2.elt[1];
+  sum.elt[2] -= mat2.elt[2];
+
+  return sum;
+}
+
+inline Mat3d & Mat3d::operator-= (const Mat3d & mat2)
+{
+  elt[0] -= mat2.elt[0];
+  elt[1] -= mat2.elt[1];
+  elt[2] -= mat2.elt[2];
+
+  return *this;
+}
+
+inline Vec3d & Mat3d::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const Vec3d & Mat3d::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline Mat3d & Mat3d::operator*= (double scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  elt[2] *= scalar;
+
+  return *this;
+}
+
+inline bool Mat3d::operator== (const Mat3d & mat2) const
+{
+  return elt[0] == mat2.elt[0] && elt[1] == mat2.elt[1] && elt[2] == mat2.elt[2]; 
+}
+
+inline bool Mat3d::operator!= (const Mat3d & mat2) const
+{
+  return elt[0] != mat2.elt[0] || elt[1] != mat2.elt[1] || elt[2] != mat2.elt[2];
+}
+
+inline Mat3d & Mat3d::multiplyDiagRight(Vec3d & v) // M = M * diag(v)
+{
+  elt[0][0] *= v[0];
+  elt[1][0] *= v[0];
+  elt[2][0] *= v[0];
+
+  elt[0][1] *= v[1];
+  elt[1][1] *= v[1];
+  elt[2][1] *= v[1];
+
+  elt[0][2] *= v[2];
+  elt[1][2] *= v[2];
+  elt[2][2] *= v[2];
+
+  return *this;
+}
+
+inline Mat3d & Mat3d::multiplyDiagLeft(Vec3d & v) // M = diag(v) * M
+{
+  elt[0] *= v[0];
+  elt[1] *= v[1];
+  elt[2] *= v[2];
+
+  return *this;
+}
+
+inline Mat3d operator* (double scalar, const Mat3d & mat2)
+{
+  Mat3d result = mat2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+
+  return result;
+}
+
+inline Mat3d operator/ (const Mat3d & mat2, double scalar)
+{
+  Mat3d result = mat2;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+  result.elt[2] /= scalar;
+
+  return result;
+}
+ 
+inline Mat3d tensorProduct(const Vec3d & vecA, const Vec3d & vecB)
+{
+  Mat3d result(vecA[0]*vecB[0],vecA[0]*vecB[1],vecA[0]*vecB[2],
+	       vecA[1]*vecB[0],vecA[1]*vecB[1],vecA[1]*vecB[2],
+	       vecA[2]*vecB[0],vecA[2]*vecB[1],vecA[2]*vecB[2]);
+
+  return result;
+}
+
+inline Mat3d & Mat3d::operator/= (double scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  elt[2] /= scalar;
+
+  return *this;
+}
+
+inline const Vec3d Mat3d::operator* (const Vec3d & vec) const
+{
+  return(Vec3d(
+    dot(elt[0],vec),
+    dot(elt[1],vec),
+    dot(elt[2],vec)));
+}
+
+inline const Mat3d Mat3d::operator* (const Mat3d & mat2) const
+{
+  return(Mat3d(
+    dot(elt[0],Vec3d(mat2.elt[0][0],mat2.elt[1][0],mat2.elt[2][0])),
+    dot(elt[0],Vec3d(mat2.elt[0][1],mat2.elt[1][1],mat2.elt[2][1])),
+    dot(elt[0],Vec3d(mat2.elt[0][2],mat2.elt[1][2],mat2.elt[2][2])),
+
+    dot(elt[1],Vec3d(mat2.elt[0][0],mat2.elt[1][0],mat2.elt[2][0])),
+    dot(elt[1],Vec3d(mat2.elt[0][1],mat2.elt[1][1],mat2.elt[2][1])),
+    dot(elt[1],Vec3d(mat2.elt[0][2],mat2.elt[1][2],mat2.elt[2][2])),
+
+    dot(elt[2],Vec3d(mat2.elt[0][0],mat2.elt[1][0],mat2.elt[2][0])),
+    dot(elt[2],Vec3d(mat2.elt[0][1],mat2.elt[1][1],mat2.elt[2][1])),
+    dot(elt[2],Vec3d(mat2.elt[0][2],mat2.elt[1][2],mat2.elt[2][2])) ));
+}
+
+inline Mat3d inv(const Mat3d & mat)
+{
+  double A[9];
+  mat.convertToArray(A);
+  double AInv[9];
+  inverse3x3(A, AInv);
+
+  return Mat3d(AInv);
+}
+
+// inverse of a 3x3 matrix
+// row-major format
+inline void inverse3x3(const double * A, double * AInv)
+{
+  // converted to C from Mathematica output   
+  AInv[0] = -A[5] * A[7] + A[4] * A[8];
+  AInv[1] = A[2] * A[7] - A[1] * A[8];
+  AInv[2] = -A[2] * A[4] + A[1] * A[5];
+  AInv[3] = A[5] * A[6] - A[3] * A[8];
+  AInv[4] = -A[2] * A[6] + A[0] * A[8];
+  AInv[5] = A[2] * A[3] - A[0] * A[5];
+  AInv[6] = -A[4] * A[6] + A[3] * A[7];
+  AInv[7] = A[1] * A[6] - A[0] * A[7];
+  AInv[8] = -A[1] * A[3] + A[0] * A[4];
+
+  double invDet = 1.0 / (A[0] * AInv[0] + A[1] * AInv[3] + A[2] * AInv[6]);
+
+  for(int i=0; i<9; i++)
+    AInv[i] *= invDet;
+}
+
+inline double det(const Mat3d & mat) 
+{
+  return
+   (-mat[0][2] * mat[1][1] * mat[2][0] + 
+     mat[0][1] * mat[1][2] * mat[2][0] + 
+     mat[0][2] * mat[1][0] * mat[2][1] - 
+     mat[0][0] * mat[1][2] * mat[2][1] - 
+     mat[0][1] * mat[1][0] * mat[2][2] + 
+     mat[0][0] * mat[1][1] * mat[2][2] );
+}
+
+inline Mat3d trans(const Mat3d & mat)
+{
+  return
+       Mat3d( mat[0][0], mat[1][0], mat[2][0],
+              mat[0][1], mat[1][1], mat[2][1],
+              mat[0][2], mat[1][2], mat[2][2] );
+}
+
+// compute [  0  -v2  v1 ]
+//         [  v2  0  -v0 ]
+//         [ -v1  v0  0  ]
+inline Mat3d skewSymmetricMatrix(const Vec3d & vec)
+{
+  return Mat3d(0, -vec[2], vec[1], 
+               vec[2], 0, -vec[0],
+               -vec[1], vec[0], 0);
+}
+
+inline void Mat3d::convertToArray(double array[9]) const // in row-major order
+{
+  array[0] = elt[0][0]; array[1] = elt[0][1]; array[2] = elt[0][2];
+  array[3] = elt[1][0]; array[4] = elt[1][1]; array[5] = elt[1][2];
+  array[6] = elt[2][0]; array[7] = elt[2][1]; array[8] = elt[2][2];
+}
+
+inline void Mat3d::addToArray(double array[9]) const // in row-major order
+{
+  array[0] += elt[0][0]; array[1] += elt[0][1]; array[2] += elt[0][2];
+  array[3] += elt[1][0]; array[4] += elt[1][1]; array[5] += elt[1][2];
+  array[6] += elt[2][0]; array[7] += elt[2][1]; array[8] += elt[2][2];
+}
+
+inline std::ostream &operator << (std::ostream &s, const Mat3d &v)
+{
+  double a00 = v.elt[0][0]; double a01 = v.elt[0][1]; double a02 = v.elt[0][2];
+  double a10 = v.elt[1][0]; double a11 = v.elt[1][1]; double a12 = v.elt[1][2];
+  double a20 = v.elt[2][0]; double a21 = v.elt[2][1]; double a22 = v.elt[2][2];
+
+  return(
+    s << '[' << a00 << ' ' << a01 << ' ' << a02 << ']' << '\n'
+      << '[' << a10 << ' ' << a11 << ' ' << a12 << ']' << '\n'
+      << '[' << a20 << ' ' << a21 << ' ' << a22 << ']'
+  );
+}
+
+inline void Mat3d::print() const
+{
+  double a00 = elt[0][0]; double a01 = elt[0][1]; double a02 = elt[0][2];
+  double a10 = elt[1][0]; double a11 = elt[1][1]; double a12 = elt[1][2];
+  double a20 = elt[2][0]; double a21 = elt[2][1]; double a22 = elt[2][2];
+  
+  printf("[%G %G %G;\n", a00, a01, a02);
+  printf(" %G %G %G;\n", a10, a11, a12);
+  printf(" %G %G %G]\n", a20, a21, a22);
+}
+
+inline double Mat3d::maxAbsEntry() const
+{
+  double maxAbsEntry = 0.0;
+  for(int i = 0; i < 3; i++)
+    for(int j = 0; j < 3; j++)
+    {
+       if (fabs(elt[i][j]) > maxAbsEntry)
+         maxAbsEntry = fabs(elt[i][j]);
+    }
+  return maxAbsEntry;
+}
+
+inline bool Mat3d::hasNaN() const
+{
+  return elt[0].hasNaN() || elt[1].hasNaN() || elt[2].hasNaN();
+}
+
+#endif
+
diff --git a/src/libminivector/mat3d.cpp b/libraries/minivector/minivector.h
similarity index 61%
rename from src/libminivector/mat3d.cpp
rename to libraries/minivector/minivector.h
index 7daa106031531fb1981cbf1edce06760df4f1cb3..11208a2c14a095dcb7630dce922fe7e7999537b5 100644
--- a/src/libminivector/mat3d.cpp
+++ b/libraries/minivector/minivector.h
@@ -27,38 +27,17 @@
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-  Version: 1.2
 */
 
+#ifndef _MINIVECTOR_H_
+#define _MINIVECTOR_H_
 
+#include "vec2d.h"
+#include "vec3d.h"
+#include "vec4d.h"
 #include "mat3d.h"
-#include "eig3.h"
-
-namespace vega
-{
-// This routine calls a public domain routine (eigen_decomposition), which was 
-// downloaded from:
-// http://barnesc.blogspot.com/2007/02/eigenvectors-of-3x3-symmetric-matrix.html
-// According to the header file of eig3.h, the C version in eig3.{h,cpp} 
-// was obtained by copying code from the public domain Java library JAMA, 
-// and declaring the resulting C code to also be public domain.
-// The eig3.{h,cpp} files are included in this package, intact as they were
-// downloaded from the Internet.
+#include "vec3i.h"
+#include "vec4i.h"
 
-// This routine written by Jernej Barbic
-void eigen_sym(Mat3d & a, Vec3d & eig_val, Vec3d eig_vec[3])
-{
-  double A[3][3] = { {a[0][0], a[0][1], a[0][2]},
-                     {a[1][0], a[1][1], a[1][2]},
-                     {a[2][0], a[2][1], a[2][2]} };
-  double V[3][3];
-  double d[3];
-  eigen_decomposition(A, V, d);
+#endif
 
-  eig_val = Vec3d(d[2],d[1],d[0]);
-  eig_vec[0] = Vec3d(V[0][2], V[1][2], V[2][2]);
-  eig_vec[1] = Vec3d(V[0][1], V[1][1], V[2][1]);
-  eig_vec[2] = Vec3d(V[0][0], V[1][0], V[2][0]);
-}
-}
diff --git a/src/libminivector/vec2d.cpp b/libraries/minivector/vec2d.cpp
similarity index 97%
rename from src/libminivector/vec2d.cpp
rename to libraries/minivector/vec2d.cpp
index b21cecbc82be5e59f521abaca3cdb34fe0f78912..8a7d9d3206a291eafba9c79e63aa6a43c7771cd3 100644
--- a/src/libminivector/vec2d.cpp
+++ b/libraries/minivector/vec2d.cpp
@@ -27,11 +27,6 @@
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-  Version: 1.2
 */
 
-
 #include "vec2d.h"
-
-namespace vega {}
diff --git a/libraries/minivector/vec2d.h b/libraries/minivector/vec2d.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ce76c62c8c24be779f68535a10f86ee64329854
--- /dev/null
+++ b/libraries/minivector/vec2d.h
@@ -0,0 +1,239 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  A simple class for vector algebra on 2D vectors 
+  (summation, normalization, dot product, etc.).
+*/
+
+#ifndef _MINIVEC2D_H_
+#define _MINIVEC2D_H_
+
+#include <math.h>
+#include <ostream>
+
+class Vec2d {
+public:
+
+  inline Vec2d() {}
+  inline Vec2d(double x, double y) { elt[0]=x; elt[1]=y; }
+  inline Vec2d(const double v[2]) { elt[0]=v[0]; elt[1]=v[1]; }
+  inline explicit Vec2d(double entry); // // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+
+  inline Vec2d & operator=(const Vec2d & source);
+  inline bool operator==(const Vec2d & vec2) const;
+  inline bool operator!=(const Vec2d & vec2) const;
+
+  inline Vec2d operator+ (const Vec2d & vec2);
+  inline Vec2d & operator+= (const Vec2d & vec2);
+
+  inline Vec2d operator- (const Vec2d & vec2);
+  inline Vec2d & operator-= (const Vec2d & vec2);
+
+  inline Vec2d operator* (double scalar) const;
+  inline Vec2d & operator*= (double scalar);
+
+  inline Vec2d operator/ (double scalar) const;
+  inline Vec2d & operator/= (double scalar);
+
+  friend inline Vec2d operator* (double scalar, const Vec2d & vec2);
+  friend inline Vec2d operator/ (double scalar, const Vec2d & vec2);
+  friend inline Vec2d operator- (const Vec2d & vec1);
+
+  friend inline double dot(const Vec2d & vec1, const Vec2d & vec2); // dot product
+
+  friend inline Vec2d norm(const Vec2d & vec1); // returns normalized vector (unit length)
+  inline void normalize(); // normalize itself without returning anything
+  friend inline std::ostream &operator << (std::ostream &s, const Vec2d &v);
+
+  friend class Mat3d;
+
+  inline double & operator[] (int index); // v[i] returns i-th vector component
+  inline const double & operator[] (int index) const;
+
+protected:
+  double elt[2];
+};
+
+// === below is the implementation ===
+
+inline Vec2d::Vec2d(double entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+}
+
+inline Vec2d & Vec2d::operator=(const Vec2d & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+
+  return *this;
+}
+
+inline bool Vec2d::operator==(const Vec2d & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]));
+}
+
+inline bool Vec2d::operator!=(const Vec2d & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]));
+}
+
+
+inline Vec2d operator* (double scalar, const Vec2d & vec2)
+{
+  Vec2d result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+
+  return result;
+}
+
+inline Vec2d operator/ (double scalar, const Vec2d & vec2)
+{
+  Vec2d result = vec2;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+
+  return result;
+}
+
+inline Vec2d operator- (const Vec2d & vec1)
+{
+  return vec1 * (-1);
+}
+
+inline Vec2d Vec2d::operator+ (const Vec2d & vec2)
+{
+  Vec2d sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+
+  return sum;
+}
+
+inline Vec2d & Vec2d::operator+= (const Vec2d & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+
+  return *this;
+}
+
+inline Vec2d Vec2d::operator- (const Vec2d & vec2)
+{
+  Vec2d sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+
+  return sum;
+}
+
+inline Vec2d & Vec2d::operator-= (const Vec2d & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+
+  return *this;
+}
+
+inline double & Vec2d::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const double & Vec2d::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline double dot(const Vec2d & vec1, const Vec2d & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1]);
+}
+
+inline Vec2d norm(const Vec2d & vec1)
+{
+  double norm2 = dot(vec1,vec1);
+  Vec2d result = vec1;
+  result *= 1.0 / sqrt(norm2);
+  
+  return result;
+}
+
+inline Vec2d & Vec2d::operator*= (double scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  return *this;
+}
+
+inline Vec2d Vec2d::operator* (double scalar) const
+{
+  return (Vec2d(elt[0]*scalar,elt[1]*scalar));
+}
+
+inline Vec2d Vec2d::operator/ (double scalar) const
+{
+  return (Vec2d(elt[0]/scalar,elt[1]/scalar));
+}
+
+inline Vec2d & Vec2d::operator/= (double scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  return *this;
+}
+
+inline double len(const Vec2d & vec1)
+{
+  return(sqrt(dot(vec1,vec1)));
+}
+
+inline void Vec2d::normalize()
+{
+  double invMag = 1.0 / len(*this);
+  (*this) *= invMag;
+}
+
+
+inline std::ostream &operator << (std::ostream &s, const Vec2d &v)
+{
+  double a = v[0];
+  double b = v[1];
+  
+  return(s << '[' << a << ' ' << b << ']');
+}
+
+#endif
+
diff --git a/libraries/minivector/vec2i.h b/libraries/minivector/vec2i.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b2842a0d150d8fb5c328fbcc52ec5aed1bbb0dc
--- /dev/null
+++ b/libraries/minivector/vec2i.h
@@ -0,0 +1,307 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "minivector" library , Copyright (C) 2018 USC                         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Funding: National Science Foundation                                  *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef VEC2I_H
+#define VEC2I_H
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+
+class Vec2i
+{
+public:
+  inline Vec2i() {}
+  inline Vec2i(int x, int y) {elt[0]=x; elt[1]=y;}
+  inline Vec2i(int entry); // create a vector with all entries "entry" (can create zero vector for entry=0)
+  inline Vec2i(const int vec[2]); // create a vector from the array of three ints pointed to by "vec"
+  inline Vec2i(const Vec2i & vec);
+
+  inline void set(int x0, int x1); // assign vector [x0, x1]
+  inline void set(int value); // set all elements to value
+
+  inline Vec2i & operator=(const Vec2i & source);
+  inline bool operator==(const Vec2i & vec2) const;
+  inline bool operator!=(const Vec2i & vec2) const;
+
+  inline const Vec2i operator+ (const Vec2i & vec2) const;
+  inline Vec2i & operator+= (const Vec2i & vec2);
+
+  inline const Vec2i operator- (const Vec2i & vec2) const;
+  inline Vec2i & operator-= (const Vec2i & vec2);
+
+  inline const Vec2i operator* (int scalar) const;
+  inline Vec2i & operator*= (int scalar);
+
+  inline Vec2i operator/ (int scalar) const;
+  inline Vec2i & operator/= (int scalar);
+
+  // operator for Vec2i to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec2i & vec2) const;
+
+  friend inline Vec2i operator* (int scalar, const Vec2i & vec2);
+  friend inline Vec2i operator/ (int scalar, const Vec2i & vec2);
+  friend inline Vec2i operator- (const Vec2i & vec1);
+
+  friend inline int dot(const Vec2i & vec1, const Vec2i & vec2); // dot product
+
+  friend inline Vec2i cross(const Vec2i & vec1, const Vec2i & vec2); // cross product
+
+  friend inline std::ostream &operator << (std::ostream & s, const Vec2i & v);
+  void print() const;
+
+  inline int & operator[] (int index); // v[i] returns i-th entry of v
+  inline const int & operator[] (int index) const;
+
+  // copy the vector into an array of length 2
+  inline void convertToArray(int vecArray[2]) const;
+  // add the vector into an array of length 2
+  inline void addToArray(int vecArray[2]) const;
+
+  // find the first index in elt which equals to value; return -1 if not found
+  inline int getInvertedIndex(int value) const;
+ 
+  // rotate elt[0] to elt[1] so that elt[newStartIndex] is moved to elt[0]
+  inline void rotate(int newStartIndex);
+
+  // do set intersection; return whether this and vec2 share at least one element
+  inline bool intersect(const Vec2i & vec2) const;
+
+  const int * begin() const { return elt; }
+  int * begin() { return elt; }
+  const int * end() const { return elt + 2; }
+  int * end() { return elt + 2; }
+
+protected:
+  int elt[2];
+};
+
+// === below is the implementation ===
+
+inline Vec2i::Vec2i(int entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+}
+
+inline Vec2i::Vec2i(const int vec[2])
+{
+  elt[0] = vec[0];
+  elt[1] = vec[1];
+}
+
+inline Vec2i::Vec2i(const Vec2i & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+}
+
+inline Vec2i & Vec2i::operator=(const Vec2i & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+
+  return *this;
+}
+
+inline bool Vec2i::operator==(const Vec2i & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]));
+}
+
+inline bool Vec2i::operator!=(const Vec2i & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]));
+}
+
+inline bool Vec2i::operator<(const Vec2i & vec2) const
+{
+  if(elt[0] < vec2[0])
+    return true;
+  if(elt[0] > vec2[0])
+    return false;
+  return elt[1] < vec2[1];
+}
+
+inline Vec2i operator* (int scalar, const Vec2i & vec2)
+{
+  Vec2i result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+
+  return result;
+}
+
+inline Vec2i operator/ (int scalar, const Vec2i & vec2)
+{
+  Vec2i result = vec2;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+
+  return result;
+}
+
+inline Vec2i operator- (const Vec2i & vec1)
+{
+  Vec2i result = vec1;
+  result.elt[0] *= -1;
+  result.elt[1] *= -1;
+
+  return result;
+}
+
+inline const Vec2i Vec2i::operator+ (const Vec2i & vec2) const
+{
+  Vec2i sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+
+  return sum;
+}
+
+inline Vec2i & Vec2i::operator+= (const Vec2i & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+
+  return *this;
+}
+
+inline const Vec2i Vec2i::operator- (const Vec2i & vec2) const
+{
+  Vec2i sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+
+  return sum;
+}
+
+inline Vec2i & Vec2i::operator-= (const Vec2i & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+
+  return *this;
+}
+
+inline int & Vec2i::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const int & Vec2i::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline int dot(const Vec2i & vec1, const Vec2i & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1]);
+}
+
+inline Vec2i & Vec2i::operator*= (int scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  return *this;
+}
+
+inline const Vec2i Vec2i::operator* (int scalar) const
+{
+  return (Vec2i(elt[0] * scalar, elt[1] * scalar));
+}
+
+inline Vec2i Vec2i::operator/ (int scalar) const
+{
+  return (Vec2i(elt[0] / scalar, elt[1] / scalar));
+}
+
+inline Vec2i & Vec2i::operator/= (int scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  return *this;
+}
+
+inline std::ostream &operator << (std::ostream &s, const Vec2i &v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ']');
+}
+
+inline void Vec2i::convertToArray(int vecArray[2]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+}
+
+inline void Vec2i::addToArray(int vecArray[2]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+}
+
+inline void Vec2i::print() const
+{
+  printf("[%d %d]\n", elt[0], elt[1]);
+}
+
+inline void Vec2i::set(int x0, int x1) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+}
+
+inline void Vec2i::set(int value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+}
+
+inline int Vec2i::getInvertedIndex(int value) const
+{
+  if (value == elt[0]) return 0;
+  if (value == elt[1]) return 1;
+  return -1;
+}
+
+inline void Vec2i::rotate(int newStartIndex)
+{
+  if (newStartIndex == 1) // rotate left one time
+  {
+    std::swap(elt[0], elt[1]);
+  }
+}
+
+inline bool Vec2i::intersect(const Vec2i & vec2) const
+{
+  for(int i = 0; i < 2; i++)
+    for(int j = 0; j < 2; j++)
+      if (elt[i] == vec2.elt[j])
+        return true;
+  return false;
+}
+
+#endif
diff --git a/src/libminivector/vec3d.cpp b/libraries/minivector/vec3.cpp
similarity index 96%
rename from src/libminivector/vec3d.cpp
rename to libraries/minivector/vec3.cpp
index 3a792037fa82cbe9777199362e64f04ba2cce502..577765a077ade59034752ef044b5e6993c347f1c 100644
--- a/src/libminivector/vec3d.cpp
+++ b/libraries/minivector/vec3.cpp
@@ -27,10 +27,7 @@
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-  Version: 1.2
 */
 
-#include "vec3d.h"
+#include "vec3.h"
 
-namespace vega {}
diff --git a/libraries/minivector/vec3.h b/libraries/minivector/vec3.h
new file mode 100644
index 0000000000000000000000000000000000000000..7c2c661869867befc6e7698f46a18c21cc5b0bf4
--- /dev/null
+++ b/libraries/minivector/vec3.h
@@ -0,0 +1,360 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  A simple class for vector algebra on 3D vectors 
+  (summation, normalization, dot product, cross product, etc.).
+*/
+
+#ifndef _MINIVEC3_H_
+#define _MINIVEC3_H_
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+
+// forward declarations for external helper functions
+template <typename real> class Vec3;
+template <typename real> inline Vec3<real> operator* (real alpha, Vec3<real> vec);
+template <typename real> inline Vec3<real> operator/ (real alpha, Vec3<real> vec);
+template <typename real> inline real dot(const Vec3<real> & vec1, const Vec3<real> & vec2); 
+template <typename real> inline Vec3<real> cross(const Vec3<real> & vec1, const Vec3<real> & vec2); // cross product
+template <typename real> inline real len(const Vec3<real> & vec); // length of vector
+template <typename real> inline real len2(const Vec3<real> & vec); // length^2 of vector
+template <typename real> inline Vec3<real> norm(const Vec3<real> & vec); // returns normalized vector (unit length)
+template <typename real> inline std::ostream & operator << (std::ostream & s, const Vec3<real> & v);
+
+typedef Vec3<double> Vec3d;
+typedef Vec3<float> Vec3f;
+
+template <class real>
+class Vec3
+{
+public:
+  inline Vec3() {}
+  inline Vec3(real x, real y, real z) { elt[0]=x; elt[1]=y; elt[2]=z; }
+  inline Vec3(real entry); // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+  inline Vec3(const real * vec); // create a vector from the array of three reals pointed to by "vec"
+  inline Vec3(const Vec3 & vec);
+
+  inline void set(real x0, real x1, real x2); // assign vector [x0, x1, x2]
+  inline void set(real value); // set all elements to value
+
+  inline Vec3 & operator=(const Vec3 & source);
+  inline bool operator==(const Vec3 & vec) const;
+
+  inline const Vec3 operator+ (const Vec3 & vec) const;
+  inline Vec3 & operator+= (const Vec3 & vec);
+
+  inline const Vec3 operator- (const Vec3 & vec) const;
+  inline Vec3 & operator-= (const Vec3 & vec);
+
+  inline const Vec3 operator* (real scalar) const;
+  inline Vec3 & operator*= (real scalar);
+
+  inline Vec3 operator/ (real scalar) const;
+  inline Vec3 & operator/= (real scalar);
+
+  inline void normalize(); // normalize itself 
+
+  void print() const; // print the vector out
+
+  inline real & operator[] (int index); // v[i] returns i-th entry of v
+  inline const real & operator[] (int index) const;
+
+  // finds a unit vector orthogonal to this vector
+  Vec3 findOrthonormalVector() const;
+
+  // copies the vector into an array of length 3
+  inline void convertToArray(real * vecArray) const;
+  
+protected:
+  real elt[3];
+};
+
+// === below is the implementation ===
+
+template <class real>
+inline Vec3<real>::Vec3(real entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+  elt[2] = entry;
+}
+
+template <class real>
+inline Vec3<real>::Vec3(const real * vec)
+{
+  elt[0] = vec[0];
+  elt[1] = vec[1];
+  elt[2] = vec[2];
+}
+
+template <class real>
+inline Vec3<real>::Vec3(const Vec3<real> & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+  elt[2] = vec.elt[2];
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator=(const Vec3<real> & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+  elt[2] = source.elt[2];
+
+  return *this;
+}
+
+template <class real>
+inline bool Vec3<real>::operator==(const Vec3<real> & vec) const
+{
+  return ((elt[0] == vec[0]) &&
+          (elt[1] == vec[1]) &&
+          (elt[2] == vec[2]));
+}
+
+template <class real>
+inline Vec3<real> operator* (real scalar, const Vec3<real> & vec)
+{
+  Vec3<real> result = vec;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+
+  return result;
+}
+
+template <class real>
+inline Vec3<real> operator/ (real scalar, const Vec3<real> & vec)
+{
+  Vec3<real> result = vec;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+  result.elt[2] /= scalar;
+
+  return result;
+}
+
+template <class real>
+inline const Vec3<real> Vec3<real>::operator+ (const Vec3<real> & vec) const
+{
+  Vec3<real> sum = *this;
+  sum.elt[0] += vec.elt[0];
+  sum.elt[1] += vec.elt[1];
+  sum.elt[2] += vec.elt[2];
+
+  return sum;
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator+= (const Vec3<real> & vec)
+{
+  elt[0] += vec.elt[0];
+  elt[1] += vec.elt[1];
+  elt[2] += vec.elt[2];
+
+  return *this;
+}
+
+template <class real>
+inline const Vec3<real> Vec3<real>::operator- (const Vec3<real> & vec) const
+{
+  Vec3<real> sum = *this;
+  sum.elt[0] -= vec.elt[0];
+  sum.elt[1] -= vec.elt[1];
+  sum.elt[2] -= vec.elt[2];
+
+  return sum;
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator-= (const Vec3<real> & vec)
+{
+  elt[0] -= vec.elt[0];
+  elt[1] -= vec.elt[1];
+  elt[2] -= vec.elt[2];
+
+  return *this;
+}
+
+template <class real>
+inline real & Vec3<real>::operator[] (int index)
+{
+  return elt[index];
+}
+
+template <class real>
+inline const real & Vec3<real>::operator[] (int index) const
+{
+  return elt[index];
+}
+
+template <class real>
+inline real dot(const Vec3<real> & vec1, const Vec3<real> & vec2)
+{
+  return (vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2]);
+}
+
+template <class real>
+inline Vec3<real> cross(const Vec3<real> & vec1, const Vec3<real> & vec2)
+{
+  Vec3<real> result(vec1[1] * vec2[2] - vec2[1] * vec1[2],
+                   -vec1[0] * vec2[2] + vec2[0] * vec1[2],
+                    vec1[0] * vec2[1] - vec2[0] * vec1[1]);
+
+  return result;
+}
+
+template <class real>
+inline Vec3<real> norm(const Vec3<real> & vec)
+{
+  real norm2 = dot(vec,vec);
+  Vec3<real> result = vec;
+  result *= 1.0 / sqrt(norm2);
+  
+  return result;
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator*= (real scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  elt[2] *= scalar;
+  return *this;
+}
+
+template <class real>
+inline const Vec3<real> Vec3<real>::operator* (real scalar) const
+{
+  return (Vec3<real>(elt[0]*scalar, elt[1]*scalar, elt[2]*scalar));
+}
+
+template <class real>
+inline Vec3<real> Vec3<real>::operator/ (real scalar) const
+{
+  return (Vec3<real>(elt[0]/scalar, elt[1]/scalar, elt[2]/scalar));
+}
+
+template <class real>
+inline Vec3<real> & Vec3<real>::operator/= (real scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  elt[2] /= scalar;
+  return *this;
+}
+
+template <class real>
+inline real len(const Vec3<real> & vec)
+{
+  return(sqrt(dot(vec,vec)));
+}
+
+template <class real>
+inline real len2(const Vec3<real> & vec)
+{
+  return(dot(vec,vec));
+}
+
+template <class real>
+inline std::ostream & operator << (std::ostream & s, const Vec3<real> & v)
+{
+  real a = v[0];
+  real b = v[1];
+  real c = v[2];
+  
+  return(s << '[' << a << ' ' << b << ' ' << c << ']');
+}
+
+template <class real>
+inline void Vec3<real>::convertToArray(real * vecArray) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+}
+
+template <class real>
+inline void Vec3<real>::normalize()
+{
+  real invMag = 1.0 / sqrt(elt[0]*elt[0] + elt[1]*elt[1] + elt[2]*elt[2]);
+  elt[0] *= invMag;
+  elt[1] *= invMag;
+  elt[2] *= invMag;
+}
+
+template <class real>
+inline void Vec3<real>::print() const
+{
+  real a = elt[0];
+  real b = elt[1];
+  real c = elt[2];
+  
+  printf("[%G %G %G]\n", a, b, c);
+}
+
+template <class real>
+inline void Vec3<real>::set(real x0, real x1, real x2) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+}
+
+template <class real>
+inline void Vec3<real>::set(real value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+  elt[2] = value;
+}
+
+// Given an input vector v, find a unit vector that is orthogonal to it 
+template <class real>
+Vec3<real> Vec3<real>::findOrthonormalVector() const
+{
+  // find smallest abs component of v
+  int smallestIndex = 0;
+  for(int dim=1; dim<3; dim++)
+    if (fabs(elt[dim]) < fabs(elt[smallestIndex]))
+      smallestIndex = dim;
+
+  Vec3<real> axis(0.0, 0.0, 0.0);
+  axis[smallestIndex] = 1.0;
+
+  // this cross-product will be non-zero (as long as v is not zero)
+  Vec3<real> result = norm(cross(elt, axis));
+  return result;
+}
+
+#endif
+
diff --git a/libraries/minivector/vec3d.cpp b/libraries/minivector/vec3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e306aea047a9883b0806e8e786d16e67a4d1ed6
--- /dev/null
+++ b/libraries/minivector/vec3d.cpp
@@ -0,0 +1,54 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "vec3d.h"
+
+static_assert(sizeof(Vec3d) == sizeof(double) * 3, "Size of Vec3d should be sizeof(double) * 3");
+
+/*
+  Given an input vector v, find a unit vector that is orthogonal to it 
+*/
+Vec3d Vec3d::findOrthonormalVector() const
+{
+  // find smallest abs component of v
+  int smallestIndex = 0;
+  for(int dim=1; dim<3; dim++)
+    if (fabs(elt[dim]) < fabs(elt[smallestIndex]))
+      smallestIndex = dim;
+
+  Vec3d axis(0.0, 0.0, 0.0);
+  axis[smallestIndex] = 1.0;
+
+  // this cross-product will be non-zero (as long as v is not zero)
+  Vec3d result = norm(cross(elt, axis));
+  return result;
+}
+
diff --git a/libraries/minivector/vec3d.h b/libraries/minivector/vec3d.h
new file mode 100644
index 0000000000000000000000000000000000000000..74f63d3091dac8460cc3d77a0e65535bcdec6b75
--- /dev/null
+++ b/libraries/minivector/vec3d.h
@@ -0,0 +1,332 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  A simple class for vector algebra on 3D vectors 
+  (summation, normalization, dot product, cross product, etc.).
+*/
+
+#ifndef _MINIVEC3D_H_
+#define _MINIVEC3D_H_
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+#include <cstring>
+
+class Vec3d
+{
+public:
+  inline Vec3d() {}
+  inline Vec3d(double x, double y, double z) {elt[0]=x; elt[1]=y; elt[2]=z;}
+  inline explicit Vec3d(double entry); // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+  inline Vec3d(const double vec[3]); // create a vector from the array of three doubles pointed to by "vec"
+  inline Vec3d(const Vec3d & vec);
+
+  inline void set(double x0, double x1, double x2); // assign vector [x0, x1, x2]
+  inline void set(double value); // set all elements to value
+
+  inline Vec3d & operator=(const Vec3d & source);
+  inline bool operator==(const Vec3d & vec2) const;
+  inline bool operator!=(const Vec3d & vec2) const;
+
+  inline const Vec3d operator+ (const Vec3d & vec2) const;
+  inline Vec3d & operator+= (const Vec3d & vec2);
+
+  inline const Vec3d operator- (const Vec3d & vec2) const;
+  inline Vec3d & operator-= (const Vec3d & vec2);
+
+  inline const Vec3d operator* (double scalar) const;
+  inline Vec3d & operator*= (double scalar);
+
+  inline Vec3d operator/ (double scalar) const;
+  inline Vec3d & operator/= (double scalar);
+
+  // operator for Vec3d to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec3d & vec2) const;
+
+  friend inline Vec3d operator* (double scalar, const Vec3d & vec2);
+  friend inline Vec3d operator- (const Vec3d & vec1);
+
+  friend inline double dot(const Vec3d & vec1, const Vec3d & vec2); // dot product
+
+  friend inline Vec3d cross(const Vec3d & vec1, const Vec3d & vec2); // cross product
+
+  friend inline Vec3d norm(const Vec3d & vec1); // returns normalized vector (unit length)
+  inline void normalize(); // normalize itself without returning anything
+
+  friend inline std::ostream &operator << (std::ostream &s, const Vec3d &v);
+  void print() const;
+
+  friend class Mat3d;
+
+  inline double & operator[] (int i) { return elt[i]; } // v[i] returns i-th entry of v
+  inline const double & operator[] (int i) const { return elt[i]; }
+
+  // allow implicit conversion from Vec3d to const double *
+  operator const double * () const { return &elt[0]; }
+
+  const double * data() const { return &elt[0]; }
+  double * data() { return &elt[0]; }
+
+  // finds a unit vector orthogonal to this vector
+  Vec3d findOrthonormalVector() const;
+
+  // copies the vector into an array of length 3
+  inline void convertToArray(double vecArray[3]) const;
+  // adds the vector into an array of length 3
+  inline void addToArray(double vecArray[3]) const;
+
+  inline bool hasNaN() const;
+
+  inline static bool isNaN(double x);
+  
+protected:
+  double elt[3];
+};
+
+// === below is the implementation ===
+
+inline Vec3d::Vec3d(double entry)
+{
+  set(entry);
+}
+
+inline Vec3d::Vec3d(const double vec[3])
+{
+  memcpy(elt, vec, sizeof(double) * 3);
+}
+
+inline Vec3d::Vec3d(const Vec3d & vec)
+{
+  memcpy(elt, vec.elt, sizeof(double) * 3);
+}
+
+inline Vec3d & Vec3d::operator=(const Vec3d & source)
+{
+  memcpy(elt, source.elt, sizeof(double) * 3);
+  return *this;
+}
+
+inline bool Vec3d::operator==(const Vec3d & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]) &&
+          (elt[2] == vec2[2]));
+}
+
+inline bool Vec3d::operator!=(const Vec3d & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]));
+}
+
+inline bool Vec3d::operator<(const Vec3d & vec2) const
+{
+  if(elt[0] < vec2[0]) 
+    return true;
+  if(elt[0] > vec2[0]) 
+    return false;
+  if(elt[1] < vec2[1]) 
+    return true;
+  if(elt[1] > vec2[1]) 
+    return false;
+  return elt[2] < vec2[2];
+}
+
+inline Vec3d operator* (double scalar, const Vec3d & vec2)
+{
+  Vec3d result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+
+  return result;
+}
+
+inline Vec3d operator- (const Vec3d & vec1)
+{
+  return vec1 * (-1);
+}
+
+inline const Vec3d Vec3d::operator+ (const Vec3d & vec2) const
+{
+  Vec3d sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+  sum.elt[2] += vec2.elt[2];
+
+  return sum;
+}
+
+inline Vec3d & Vec3d::operator+= (const Vec3d & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+  elt[2] += vec2.elt[2];
+
+  return *this;
+}
+
+inline const Vec3d Vec3d::operator- (const Vec3d & vec2) const
+{
+  Vec3d sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+  sum.elt[2] -= vec2.elt[2];
+
+  return sum;
+}
+
+inline Vec3d & Vec3d::operator-= (const Vec3d & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+  elt[2] -= vec2.elt[2];
+
+  return *this;
+}
+
+inline double dot(const Vec3d & vec1, const Vec3d & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1] + vec1.elt[2] * vec2.elt[2]);
+}
+
+inline Vec3d cross(const Vec3d & vec1, const Vec3d & vec2)
+{
+  Vec3d result(vec1.elt[1] * vec2.elt[2] - vec2.elt[1] * vec1.elt[2],
+              -vec1.elt[0] * vec2.elt[2] + vec2.elt[0] * vec1.elt[2],
+               vec1.elt[0] * vec2.elt[1] - vec2.elt[0] * vec1.elt[1]);
+
+  return result;
+}
+
+inline const Vec3d Vec3d::operator* (double scalar) const
+{
+  return (Vec3d(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar));
+}
+
+inline Vec3d Vec3d::operator/ (double scalar) const
+{
+  return (Vec3d(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar));
+}
+
+inline Vec3d & Vec3d::operator*= (double scalar)
+{
+  *this = (*this) * scalar;
+  return *this;
+}
+
+inline Vec3d & Vec3d::operator/= (double scalar)
+{
+  *this = (*this) / scalar;
+  return *this;
+}
+
+inline double len(const Vec3d & vec1)
+{
+  return(sqrt(dot(vec1,vec1)));
+}
+
+inline Vec3d norm(const Vec3d & vec1)
+{
+  // return vec1 / len(vec1); 
+
+  double norm2 = dot(vec1,vec1);
+  Vec3d result = vec1;
+  result *= 1.0 / sqrt(norm2);
+  
+  return result;
+}
+
+inline double len2(const Vec3d & vec1)
+{
+  return(dot(vec1,vec1));
+}
+
+inline std::ostream &operator << (std::ostream & s, const Vec3d & v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ' ' << v[2] << ']');
+}
+
+inline void Vec3d::convertToArray(double vecArray[3]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+}
+
+inline void Vec3d::addToArray(double vecArray[3]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+}
+
+inline void Vec3d::normalize()
+{
+  double invMag = 1.0 / len(*this);
+  (*this) *= invMag;
+}
+
+inline void Vec3d::print() const
+{
+  printf("[%G %G %G]\n", elt[0], elt[1], elt[2]);
+}
+
+inline void Vec3d::set(double x0, double x1, double x2) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+}
+
+inline void Vec3d::set(double value) // set all elements to value
+{
+  elt[0] = elt[1] = elt[2] = value;
+}
+
+inline bool Vec3d::hasNaN() const
+{
+  return (isNaN(elt[0]) || isNaN(elt[1]) || isNaN(elt[2]));
+}
+
+inline bool Vec3d::isNaN(double x) 
+{ 
+  #ifdef isnan
+    return (isnan(x) != 0);
+  #elif defined(_WIN32)
+    return (_isnan(x) != 0);
+  #else
+    return (x != x); 
+  #endif
+}
+
+#endif
+
diff --git a/src/libvolumetricMesh/volumetricMeshExtensions.cpp b/libraries/minivector/vec3i.cpp
similarity index 69%
rename from src/libvolumetricMesh/volumetricMeshExtensions.cpp
rename to libraries/minivector/vec3i.cpp
index 6936f52967b9310c653d6924cab7bbc49571e590..e2bd400e2765058d74ce8355808dcc0a6976e578 100644
--- a/src/libvolumetricMesh/volumetricMeshExtensions.cpp
+++ b/libraries/minivector/vec3i.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "minivector" library , Copyright (C) 2018 USC                         *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,8 +30,5 @@
  *                                                                       *
  *************************************************************************/
 
-#include "volumetricMeshExtensions.h"
 
-namespace vega
-{
-}
+#include "vec3i.h"
diff --git a/libraries/minivector/vec3i.h b/libraries/minivector/vec3i.h
new file mode 100644
index 0000000000000000000000000000000000000000..9f3d806a6cae4b0fef54a5364e3e89511eda4ac7
--- /dev/null
+++ b/libraries/minivector/vec3i.h
@@ -0,0 +1,363 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "minivector" library , Copyright (C) 2018 USC                         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef VEC3I_H
+#define VEC3I_H
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+#include "vec3d.h"
+
+class Vec3i
+{
+public:
+  inline Vec3i() {}
+  inline Vec3i(int x, int y, int z) {elt[0]=x; elt[1]=y; elt[2]=z;}
+  inline Vec3i(int entry); // create a vector with all entries "entry" (can create zero vector for entry=0)
+  inline Vec3i(const int vec[3]); // create a vector from the array of three ints pointed to by "vec"
+  inline Vec3i(const Vec3i & vec);
+
+  inline void set(int x0, int x1, int x2); // assign vector [x0, x1, x2]
+  inline void set(int value); // set all elements to value
+
+  inline Vec3i & operator=(const Vec3i & source);
+  inline bool operator==(const Vec3i & vec2) const;
+  inline bool operator!=(const Vec3i & vec2) const;
+
+  inline const Vec3i operator+ (const Vec3i & vec2) const;
+  inline Vec3i & operator+= (const Vec3i & vec2);
+
+  inline const Vec3i operator- (const Vec3i & vec2) const;
+  inline Vec3i & operator-= (const Vec3i & vec2);
+
+  inline const Vec3i operator* (int scalar) const;
+  inline Vec3i & operator*= (int scalar);
+
+  inline const Vec3i operator/ (int scalar) const;
+  inline Vec3i & operator/= (int scalar);
+
+  inline const Vec3d operator* (double scalar) const;
+  inline const Vec3d operator/ (double scalar) const;
+
+  // operator for Vec3i to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec3i & vec2) const;
+
+  friend inline Vec3i operator* (int scalar, const Vec3i & vec2);
+  friend inline Vec3i operator- (const Vec3i & vec1);
+
+  friend inline int dot(const Vec3i & vec1, const Vec3i & vec2); // dot product
+
+  friend inline Vec3i cross(const Vec3i & vec1, const Vec3i & vec2); // cross product
+
+  friend inline std::ostream &operator << (std::ostream & s, const Vec3i & v);
+  void print() const;
+
+  inline int & operator[] (int index); // v[i] returns i-th entry of v
+  inline const int & operator[] (int index) const;
+
+  // copy the vector into an array of length 3
+  inline void convertToArray(int vecArray[3]) const;
+  // add the vector into an array of length 3
+  inline void addToArray(int vecArray[3]) const;
+
+  // find the first index in elt which equals to value; return -1 if not found
+  inline int getInvertedIndex(int value) const;
+ 
+  // rotate elt[0] to elt[3] so that elt[newStartIndex] is moved to elt[0]
+  inline void rotate(int newStartIndex);
+
+  // do set intersection; return whether this and vec2 share at least one element
+  inline bool intersect(const Vec3i & vec2) const;
+
+  const int * begin() const { return elt; }
+  int * begin() { return elt; }
+  const int * end() const { return elt + 3; }
+  int * end() { return elt + 3; }
+
+protected:
+  int elt[3];
+};
+
+// === below is the implementation ===
+
+inline Vec3i::Vec3i(int entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+  elt[2] = entry;
+}
+
+inline Vec3i::Vec3i(const int vec[3])
+{
+  elt[0] = vec[0];
+  elt[1] = vec[1];
+  elt[2] = vec[2];
+}
+
+inline Vec3i::Vec3i(const Vec3i & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+  elt[2] = vec.elt[2];
+}
+
+inline Vec3i & Vec3i::operator=(const Vec3i & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+  elt[2] = source.elt[2];
+
+  return *this;
+}
+
+inline bool Vec3i::operator==(const Vec3i & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]) &&
+          (elt[2] == vec2[2]));
+}
+
+inline bool Vec3i::operator!=(const Vec3i & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]));
+}
+
+inline bool Vec3i::operator<(const Vec3i & vec2) const
+{
+  if(elt[0] < vec2[0])
+    return true;
+  if(elt[0] > vec2[0])
+    return false;
+  if(elt[1] < vec2[1])
+    return true;
+  if(elt[1] > vec2[1])
+    return false;
+  return elt[2] < vec2[2];
+}
+
+inline Vec3i operator* (int scalar, const Vec3i & vec2)
+{
+  Vec3i result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+
+  return result;
+}
+
+inline Vec3i operator- (const Vec3i & vec1)
+{
+  Vec3i result = vec1;
+  result.elt[0] *= -1;
+  result.elt[1] *= -1;
+  result.elt[2] *= -1;
+
+  return result;
+}
+
+inline const Vec3i Vec3i::operator+ (const Vec3i & vec2) const
+{
+  Vec3i sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+  sum.elt[2] += vec2.elt[2];
+
+  return sum;
+}
+
+inline Vec3i & Vec3i::operator+= (const Vec3i & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+  elt[2] += vec2.elt[2];
+
+  return *this;
+}
+
+inline const Vec3i Vec3i::operator- (const Vec3i & vec2) const
+{
+  Vec3i sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+  sum.elt[2] -= vec2.elt[2];
+
+  return sum;
+}
+
+inline Vec3i & Vec3i::operator-= (const Vec3i & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+  elt[2] -= vec2.elt[2];
+
+  return *this;
+}
+
+inline int & Vec3i::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const int & Vec3i::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline int dot(const Vec3i & vec1, const Vec3i & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1] + vec1.elt[2] * vec2.elt[2]);
+}
+
+inline Vec3i cross(const Vec3i & vec1, const Vec3i & vec2)
+{
+  Vec3i result(vec1.elt[1] * vec2.elt[2] - vec2.elt[1] * vec1.elt[2],
+              -vec1.elt[0] * vec2.elt[2] + vec2.elt[0] * vec1.elt[2],
+               vec1.elt[0] * vec2.elt[1] - vec2.elt[0] * vec1.elt[1]);
+
+  return result;
+}
+
+inline Vec3i & Vec3i::operator*= (int scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  elt[2] *= scalar;
+  return *this;
+}
+
+inline const Vec3i Vec3i::operator* (int scalar) const
+{
+  return (Vec3i(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar));
+}
+
+inline const Vec3i Vec3i::operator/ (int scalar) const
+{
+  return (Vec3i(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar));
+}
+
+inline const Vec3d Vec3i::operator* (double scalar) const
+{
+  return (Vec3d(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar));
+}
+
+inline const Vec3d Vec3i::operator/ (double scalar) const
+{
+  return (Vec3d(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar));
+}
+
+
+inline Vec3i & Vec3i::operator/= (int scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  elt[2] /= scalar;
+  return *this;
+}
+
+inline std::ostream &operator << (std::ostream &s, const Vec3i &v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ' ' << v[2] << ']');
+}
+
+inline void Vec3i::convertToArray(int vecArray[3]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+}
+
+inline void Vec3i::addToArray(int vecArray[3]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+}
+
+inline void Vec3i::print() const
+{
+  printf("[%d %d %d]\n", elt[0], elt[1], elt[2]);
+}
+
+inline void Vec3i::set(int x0, int x1, int x2) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+}
+
+inline void Vec3i::set(int value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+  elt[2] = value;
+}
+
+inline int Vec3i::getInvertedIndex(int value) const
+{
+  if (value == elt[0]) return 0;
+  if (value == elt[1]) return 1;
+  if (value == elt[2]) return 2;
+  return -1;
+}
+
+inline void Vec3i::rotate(int newStartIndex)
+{
+  if (newStartIndex == 1) // rotate left one time
+  {
+    int tmp = elt[0];
+    elt[0] = elt[1];
+    elt[1] = elt[2];
+    elt[2] = tmp;
+  }
+  else if (newStartIndex == 2) // rotate right one time
+  {
+    int tmp = elt[2];
+    elt[2] = elt[1];
+    elt[1] = elt[0];
+    elt[0] = tmp;
+  }
+}
+
+inline bool Vec3i::intersect(const Vec3i & vec2) const
+{
+  for(int i = 0; i < 3; i++)
+    for(int j = 0; j < 3; j++)
+      if (elt[i] == vec2.elt[j])
+        return true;
+  return false;
+}
+
+#endif
diff --git a/libraries/minivector/vec4d.h b/libraries/minivector/vec4d.h
new file mode 100644
index 0000000000000000000000000000000000000000..accea7dc8ef301e6cef106b55f82c42455d9f819
--- /dev/null
+++ b/libraries/minivector/vec4d.h
@@ -0,0 +1,308 @@
+/*
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  A simple class for vector algebra on 4D vectors 
+  (summation, normalization, dot product, cross product, etc.).
+*/
+
+#ifndef _MINIVEC4D_H_
+#define _MINIVEC4D_H_
+
+#include <stdio.h>
+#include <cmath>
+#include <ostream>
+#include <cstring>
+
+class Vec4d
+{
+public:
+  inline Vec4d() {}
+  inline Vec4d(double x, double y, double z, double w) {elt[0]=x; elt[1]=y; elt[2]=z; elt[3]=w;}
+  inline explicit Vec4d(double entry); // create a vector with all entries "entry" (can create zero vector for entry=0.0)
+  inline Vec4d(const double vec[4]); // create a vector from the array of four doubles pointed to by "vec"
+  inline Vec4d(const Vec4d & vec);
+
+  inline void set(double x0, double x1, double x2, double x3); // assign vector [x0, x1, x2, x3]
+  inline void set(double value); // set all elements to value
+
+  inline Vec4d & operator=(const Vec4d & source);
+  inline bool operator==(const Vec4d & vec2) const;
+  inline bool operator!=(const Vec4d & vec2) const;
+
+  inline const Vec4d operator+ (const Vec4d & vec2) const;
+  inline Vec4d & operator+= (const Vec4d & vec2);
+
+  inline const Vec4d operator- (const Vec4d & vec2) const;
+  inline Vec4d & operator-= (const Vec4d & vec2);
+
+  inline const Vec4d operator* (double scalar) const;
+  inline Vec4d & operator*= (double scalar);
+
+  inline Vec4d operator/ (double scalar) const;
+  inline Vec4d & operator/= (double scalar);
+
+  // operator for Vec4d to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec4d & vec2) const;
+
+  friend inline Vec4d operator* (double scalar, const Vec4d & vec2);
+  friend inline Vec4d operator/ (double scalar, const Vec4d & vec2);
+  friend inline Vec4d operator- (const Vec4d & vec1);
+
+  friend inline double dot(const Vec4d & vec1, const Vec4d & vec2); // dot product
+
+  friend inline Vec4d norm(const Vec4d & vec1); // returns normalized vector (unit length)
+  inline void normalize(); // normalize itself without returning anything
+
+  friend inline std::ostream &operator << (std::ostream &s, const Vec4d &v);
+  void print() const;
+
+  inline double & operator[] (int i) { return elt[i]; } // v[i] returns i-th entry of v
+  inline const double & operator[] (int i) const { return elt[i]; }
+
+  operator const double * () const { return &elt[0]; }
+
+  // copies the vector into an array of length 4
+  inline void convertToArray(double vecArray[4]) const;
+  // adds the vector into an array of length 4
+  inline void addToArray(double vecArray[4]) const;
+
+  inline bool hasNaN() const;
+  
+protected:
+  double elt[4];
+};
+
+// === below is the implementation ===
+
+inline Vec4d::Vec4d(double entry)
+{
+  set(entry);
+}
+
+inline Vec4d::Vec4d(const double vec[4])
+{
+  memcpy(elt, vec, sizeof(double) * 4);
+}
+
+inline Vec4d::Vec4d(const Vec4d & vec)
+{
+  memcpy(elt, vec.elt, sizeof(double) * 4);
+}
+
+inline Vec4d & Vec4d::operator=(const Vec4d & source)
+{
+  memcpy(elt, source.elt, sizeof(double) * 4);
+  return *this;
+}
+
+inline bool Vec4d::operator==(const Vec4d & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]) &&
+          (elt[2] == vec2[2]) &&
+          (elt[3] == vec2[3]));
+}
+
+inline bool Vec4d::operator!=(const Vec4d & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]) ||
+          (elt[3] != vec2[3]));
+}
+
+inline bool Vec4d::operator<(const Vec4d & vec2) const
+{
+  if(elt[0] < vec2[0]) 
+    return true;
+  if(elt[0] > vec2[0]) 
+    return false;
+  if(elt[1] < vec2[1]) 
+    return true;
+  if(elt[1] > vec2[1]) 
+    return false;
+  if(elt[2] < vec2[2]) 
+    return true;
+  if(elt[2] > vec2[2]) 
+    return false;
+  return elt[3] < vec2[3];
+}
+
+inline Vec4d operator* (double scalar, const Vec4d & vec2)
+{
+  Vec4d result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+  result.elt[3] *= scalar;
+
+  return result;
+}
+
+inline Vec4d operator- (const Vec4d & vec1)
+{
+  return vec1 * (-1);
+}
+
+inline const Vec4d Vec4d::operator+ (const Vec4d & vec2) const
+{
+  Vec4d sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+  sum.elt[2] += vec2.elt[2];
+  sum.elt[3] += vec2.elt[3];
+
+  return sum;
+}
+
+inline Vec4d & Vec4d::operator+= (const Vec4d & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+  elt[2] += vec2.elt[2];
+  elt[3] += vec2.elt[3];
+
+  return *this;
+}
+
+inline const Vec4d Vec4d::operator- (const Vec4d & vec2) const
+{
+  Vec4d sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+  sum.elt[2] -= vec2.elt[2];
+  sum.elt[3] -= vec2.elt[3];
+
+  return sum;
+}
+
+inline Vec4d & Vec4d::operator-= (const Vec4d & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+  elt[2] -= vec2.elt[2];
+  elt[3] -= vec2.elt[3];
+
+  return *this;
+}
+
+inline double dot(const Vec4d & vec1, const Vec4d & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1] + vec1.elt[2] * vec2.elt[2] + vec1.elt[3] * vec2.elt[3]);
+}
+
+inline const Vec4d Vec4d::operator* (double scalar) const
+{
+  return (Vec4d(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar, elt[3] * scalar));
+}
+
+inline Vec4d Vec4d::operator/ (double scalar) const
+{
+  return (Vec4d(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar, elt[3] / scalar));
+}
+
+inline Vec4d & Vec4d::operator*= (double scalar)
+{
+  *this = (*this) * scalar;
+  return *this;
+}
+
+inline Vec4d & Vec4d::operator/= (double scalar)
+{
+  *this = (*this) / scalar;
+  return *this;
+}
+
+inline double len(const Vec4d & vec1)
+{
+  return(sqrt(dot(vec1,vec1)));
+}
+
+inline Vec4d norm(const Vec4d & vec1)
+{
+  return vec1 / len(vec1); 
+}
+
+inline double len2(const Vec4d & vec1)
+{
+  return(dot(vec1,vec1));
+}
+
+inline std::ostream &operator << (std::ostream & s, const Vec4d & v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ' ' << v[2] << ' ' << v[3] << ']');
+}
+
+inline void Vec4d::convertToArray(double vecArray[4]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+  vecArray[3] = elt[3];
+}
+
+inline void Vec4d::addToArray(double vecArray[4]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+  vecArray[3] += elt[3];
+}
+
+inline void Vec4d::normalize()
+{
+  double invMag = 1.0 / len(*this);
+  (*this) *= invMag;
+}
+
+inline void Vec4d::print() const
+{
+  printf("[%G %G %G %G]\n", elt[0], elt[1], elt[2], elt[3]);
+}
+
+inline void Vec4d::set(double x0, double x1, double x2, double x3)
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+  elt[3] = x3;
+}
+
+inline void Vec4d::set(double value)
+{
+  elt[0] = elt[1] = elt[2] = elt[3] = value;
+}
+
+inline bool Vec4d::hasNaN() const
+{
+  return (std::isnan(elt[0]) || std::isnan(elt[1]) || std::isnan(elt[2]) || std::isnan(elt[3]));
+}
+
+#endif
+
diff --git a/libraries/minivector/vec4i.cpp b/libraries/minivector/vec4i.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..702bfa768c4613fde5c7075b6df73992b2c4513b
--- /dev/null
+++ b/libraries/minivector/vec4i.cpp
@@ -0,0 +1,34 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "minivector" library , Copyright (C) 2018 USC                         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#include "vec4i.h"
diff --git a/libraries/minivector/vec4i.h b/libraries/minivector/vec4i.h
new file mode 100644
index 0000000000000000000000000000000000000000..8c206a402077d0786d1fc7c1180cf8ea92fbabd3
--- /dev/null
+++ b/libraries/minivector/vec4i.h
@@ -0,0 +1,388 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "minivector" library , Copyright (C) 2018 USC                         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef VEC4I_H
+#define VEC4I_H
+
+
+#include <stdio.h>
+#include <math.h>
+#include <ostream>
+#include <algorithm>
+
+class Vec4i
+{
+public:
+  inline Vec4i() {}
+  inline Vec4i(int x, int y, int z, int w) {elt[0]=x; elt[1]=y; elt[2]=z; elt[3]=w; }
+  inline Vec4i(int entry); // create a vector with all entries "entry" (can create zero vector for entry=0)
+  inline Vec4i(const int vec[4]); // create a vector from the array of four ints pointed to by "vec"
+  inline Vec4i(const Vec4i & vec);
+
+  inline void set(int x0, int x1, int x2, int x3); // assign vector [x0, x1, x2, x3]
+  inline void set(int value); // set all elements to value
+
+  inline Vec4i & operator=(const Vec4i & source);
+  inline bool operator==(const Vec4i & vec2) const;
+  inline bool operator!=(const Vec4i & vec2) const;
+
+  inline const Vec4i operator+ (const Vec4i & vec2) const;
+  inline Vec4i & operator+= (const Vec4i & vec2);
+
+  inline const Vec4i operator- (const Vec4i & vec2) const;
+  inline Vec4i & operator-= (const Vec4i & vec2);
+
+  inline const Vec4i operator* (int scalar) const;
+  inline Vec4i & operator*= (int scalar);
+
+  inline Vec4i operator/ (int scalar) const;
+  inline Vec4i & operator/= (int scalar);
+
+  // operator for Vec4i to be used as a key in std::set, std::map, etc.
+  inline bool operator < (const Vec4i & vec2) const;
+
+  friend inline Vec4i operator* (int scalar, const Vec4i & vec2);
+  friend inline Vec4i operator/ (int scalar, const Vec4i & vec2);
+  friend inline Vec4i operator- (const Vec4i & vec1);
+
+  friend inline int dot(const Vec4i & vec1, const Vec4i & vec2); // dot product
+
+  friend inline std::ostream &operator << (std::ostream & s, const Vec4i & v);
+  void print() const;
+
+  inline int & operator[] (int index); // v[i] returns i-th entry of v
+  inline const int & operator[] (int index) const;
+
+  const int * data() const { return &elt[0]; }
+  int * data() { return &elt[0]; }
+
+  // copy the vector into an array of length 4
+  inline void convertToArray(int vecArray[4]) const;
+  // add the vector into an array of length 4
+  inline void addToArray(int vecArray[4]) const;
+
+  // find the first index in elt which equals to value; return -1 if not found
+  inline int getInvertedIndex(int value) const;
+  // rotate elt[0] to elt[4] so that elt[newStartIndex] is moved to elt[0]
+  inline void rotate(int newStartIndex);
+
+  // do set intersection; return whether this and vec2 share at least one element
+  inline bool intersect(const Vec4i & vec2) const;
+
+  const int * begin() const { return elt; }
+  int * begin() { return elt; }
+  const int * end() const { return elt + 4; }
+  int * end() { return elt + 4; }
+
+protected:
+  int elt[4];
+};
+
+// === below is the implementation ===
+
+inline Vec4i::Vec4i(int entry)
+{
+  elt[0] = entry;
+  elt[1] = entry;
+  elt[2] = entry;
+  elt[3] = entry;
+}
+
+inline Vec4i::Vec4i(const int vec[4])
+{
+  elt[0] = vec[0];
+  elt[1] = vec[1];
+  elt[2] = vec[2];
+  elt[3] = vec[3];
+}
+
+inline Vec4i::Vec4i(const Vec4i & vec)
+{
+  elt[0] = vec.elt[0];
+  elt[1] = vec.elt[1];
+  elt[2] = vec.elt[2];
+  elt[3] = vec.elt[3];
+}
+
+inline Vec4i & Vec4i::operator=(const Vec4i & source)
+{
+  elt[0] = source.elt[0];
+  elt[1] = source.elt[1];
+  elt[2] = source.elt[2];
+  elt[3] = source.elt[3];
+
+  return *this;
+}
+
+inline bool Vec4i::operator==(const Vec4i & vec2) const
+{
+  return ((elt[0] == vec2[0]) &&
+          (elt[1] == vec2[1]) &&
+          (elt[2] == vec2[2]) &&
+          (elt[3] == vec2[3]));
+}
+
+inline bool Vec4i::operator!=(const Vec4i & vec2) const
+{
+  return ((elt[0] != vec2[0]) ||
+          (elt[1] != vec2[1]) ||
+          (elt[2] != vec2[2]) ||
+          (elt[3] != vec2[3]));
+}
+
+inline bool Vec4i::operator<(const Vec4i & vec2) const
+{
+  if(elt[0] < vec2[0])
+    return true;
+  if(elt[0] > vec2[0])
+    return false;
+  if(elt[1] < vec2[1])
+    return true;
+  if(elt[1] > vec2[1])
+    return false;
+  if(elt[2] < vec2[2])
+      return true;
+  if(elt[2] > vec2[2])
+      return false;
+  return elt[3] < vec2[3];
+}
+
+inline Vec4i operator* (int scalar, const Vec4i & vec2)
+{
+  Vec4i result = vec2;
+  result.elt[0] *= scalar;
+  result.elt[1] *= scalar;
+  result.elt[2] *= scalar;
+  result.elt[3] *= scalar;
+
+  return result;
+}
+
+inline Vec4i operator/ (int scalar, const Vec4i & vec2)
+{
+  Vec4i result = vec2;
+  result.elt[0] /= scalar;
+  result.elt[1] /= scalar;
+  result.elt[2] /= scalar;
+  result.elt[3] /= scalar;
+
+  return result;
+}
+
+inline Vec4i operator- (const Vec4i & vec1)
+{
+  Vec4i result = vec1;
+  result.elt[0] *= -1;
+  result.elt[1] *= -1;
+  result.elt[2] *= -1;
+  result.elt[3] *= -1;
+
+  return result;
+}
+
+inline const Vec4i Vec4i::operator+ (const Vec4i & vec2) const
+{
+  Vec4i sum = *this;
+  sum.elt[0] += vec2.elt[0];
+  sum.elt[1] += vec2.elt[1];
+  sum.elt[2] += vec2.elt[2];
+  sum.elt[3] += vec2.elt[3];
+
+  return sum;
+}
+
+inline Vec4i & Vec4i::operator+= (const Vec4i & vec2)
+{
+  elt[0] += vec2.elt[0];
+  elt[1] += vec2.elt[1];
+  elt[2] += vec2.elt[2];
+  elt[3] += vec2.elt[3];
+
+  return *this;
+}
+
+inline const Vec4i Vec4i::operator- (const Vec4i & vec2) const
+{
+  Vec4i sum = *this;
+  sum.elt[0] -= vec2.elt[0];
+  sum.elt[1] -= vec2.elt[1];
+  sum.elt[2] -= vec2.elt[2];
+  sum.elt[3] -= vec2.elt[3];
+
+  return sum;
+}
+
+inline Vec4i & Vec4i::operator-= (const Vec4i & vec2)
+{
+  elt[0] -= vec2.elt[0];
+  elt[1] -= vec2.elt[1];
+  elt[2] -= vec2.elt[2];
+  elt[3] -= vec2.elt[3];
+
+  return *this;
+}
+
+inline int & Vec4i::operator[] (int index)
+{
+  return elt[index];
+}
+
+inline const int & Vec4i::operator[] (int index) const
+{
+  return elt[index];
+}
+
+inline int dot(const Vec4i & vec1, const Vec4i & vec2)
+{
+  return (vec1.elt[0] * vec2.elt[0] + vec1.elt[1] * vec2.elt[1] + vec1.elt[2] * vec2.elt[2] + vec1.elt[3] * vec2.elt[3]);
+}
+
+inline Vec4i & Vec4i::operator*= (int scalar)
+{
+  elt[0] *= scalar;
+  elt[1] *= scalar;
+  elt[2] *= scalar;
+  elt[3] *= scalar;
+  return *this;
+}
+
+inline const Vec4i Vec4i::operator* (int scalar) const
+{
+  return (Vec4i(elt[0] * scalar, elt[1] * scalar, elt[2] * scalar, elt[3] * scalar));
+}
+
+inline Vec4i Vec4i::operator/ (int scalar) const
+{
+  return (Vec4i(elt[0] / scalar, elt[1] / scalar, elt[2] / scalar, elt[3] / scalar));
+}
+
+inline Vec4i & Vec4i::operator/= (int scalar)
+{
+  elt[0] /= scalar;
+  elt[1] /= scalar;
+  elt[2] /= scalar;
+  elt[3] /= scalar;
+  return *this;
+}
+
+inline std::ostream &operator << (std::ostream &s, const Vec4i &v)
+{
+  return(s << '[' << v[0] << ' ' << v[1] << ' ' << v[2] << ' ' << v[3] << ']');
+}
+
+inline void Vec4i::convertToArray(int vecArray[4]) const
+{
+  vecArray[0] = elt[0];
+  vecArray[1] = elt[1];
+  vecArray[2] = elt[2];
+  vecArray[3] = elt[3];
+}
+
+inline void Vec4i::addToArray(int vecArray[4]) const
+{
+  vecArray[0] += elt[0];
+  vecArray[1] += elt[1];
+  vecArray[2] += elt[2];
+  vecArray[3] += elt[3];
+}
+
+inline void Vec4i::print() const
+{
+  int a = elt[0];
+  int b = elt[1];
+  int c = elt[2];
+  int d = elt[3];
+
+  printf("[%d %d %d %d]\n", a, b, c, d);
+}
+
+inline void Vec4i::set(int x0, int x1, int x2, int x3) // assign vector [x0, x1, x2]
+{
+  elt[0] = x0;
+  elt[1] = x1;
+  elt[2] = x2;
+  elt[3] = x3;
+}
+
+inline void Vec4i::set(int value) // set all elements to value
+{
+  elt[0] = value;
+  elt[1] = value;
+  elt[2] = value;
+  elt[3] = value;
+}
+
+inline int Vec4i::getInvertedIndex(int value) const
+{
+  if (value == elt[0]) return 0;
+  if (value == elt[1]) return 1;
+  if (value == elt[2]) return 2;
+  if (value == elt[3]) return 3;
+  return -1;
+}
+
+inline void Vec4i::rotate(int newStartIndex)
+{
+  if (newStartIndex == 1) // rotate left one time, (1, 2, 3, 0)
+  {
+    int tmp = elt[0];
+    elt[0] = elt[1];
+    elt[1] = elt[2];
+    elt[2] = elt[3];
+    elt[3] = tmp;
+  }
+  else if (newStartIndex == 2) // rotate left two times (2, 3, 0, 1)
+  {
+    std::swap(elt[0], elt[2]);
+    std::swap(elt[1], elt[3]);
+  }
+  else if (newStartIndex == 3) // rotate right one time (3, 0, 1, 2)
+  {
+    int tmp = elt[3];
+    elt[3] = elt[2];
+    elt[2] = elt[1];
+    elt[1] = elt[0];
+    elt[0] = tmp;
+  }
+}
+
+inline bool Vec4i::intersect(const Vec4i & vec2) const
+{
+  for(int i = 0; i < 4; i++)
+    for(int j = 0; j < 4; j++)
+      if (elt[i] == vec2.elt[j])
+        return true;
+  return false;
+}
+
+#endif
diff --git a/src/libmodalMatrix/modalMatrix.cpp b/libraries/modalMatrix/modalMatrix.cpp
similarity index 91%
rename from src/libmodalMatrix/modalMatrix.cpp
rename to libraries/modalMatrix/modalMatrix.cpp
index 286c5963d6f33503b95716274aa115729a2d5330..714670188ab7881b44680f96b2f1000b259d9bd6 100644
--- a/src/libmodalMatrix/modalMatrix.cpp
+++ b/libraries/modalMatrix/modalMatrix.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "modalMatrix" library , Copyright (C) 2007 CMU                        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libmodalMatrix/modalMatrix.h b/libraries/modalMatrix/modalMatrix.h
similarity index 89%
rename from src/libmodalMatrix/modalMatrix.h
rename to libraries/modalMatrix/modalMatrix.h
index ad12fa08ad25bf1845c0591c7e4fcb517ddcb052..8db15957d7f3491d2ae5d4aa32c6ddfe81fd9d85 100644
--- a/src/libmodalMatrix/modalMatrix.h
+++ b/libraries/modalMatrix/modalMatrix.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "modalMatrix" library , Copyright (C) 2007 CMU                        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/libraries/modalMatrix/modalMatrixTransposed.cpp b/libraries/modalMatrix/modalMatrixTransposed.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7331b4678562978f12163b58dc15afdaa9e60a93
--- /dev/null
+++ b/libraries/modalMatrix/modalMatrixTransposed.cpp
@@ -0,0 +1,464 @@
+/*
+* Copyright (c) 2007, Carnegie Mellon University
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Code author: Jernej Barbic
+  Research: Jernej Barbic, Doug L. James
+  Funding: NSF, Link Foundation
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "lapack-headers.h"
+#ifdef __APPLE__
+  #include "TargetConditionals.h"
+#endif
+#include "modalMatrixTransposed.h"
+
+template<bool C>
+class _cblas_xgemv {};
+
+template<>
+class _cblas_xgemv<true> {
+public:
+    inline static void f (const  CBLAS_ORDER order,
+                 const  CBLAS_TRANSPOSE trans, const int m, const int n,
+                 const float alpha, const float * a, const int lda,
+                 const float * x, const int incx, float beta,
+                 float * y, const int incy)
+     { cblas_sgemv(order, trans, m, n, alpha, a, lda, x, incx, beta, y, incy);}
+};
+
+template<>
+class _cblas_xgemv<false> {
+public:
+    inline static void f (const  CBLAS_ORDER order,
+                 const  CBLAS_TRANSPOSE trans, const int m, const int n,
+                 const double alpha, const double * a, const int lda,
+                 const double * x, const int incx, double beta,
+                 double * y, const int incy)
+     { cblas_dgemv(order, trans, m, n, alpha, a, lda, x, incx, beta, y, incy);}
+};
+
+template <class real>
+ModalMatrixTransposed<real>::ModalMatrixTransposed(int n, int r, real * U, int align)
+{
+  this->n = n;
+
+  if (align)
+  {
+    this->r = 4 * (((r - 1) / 4) + 1); 
+  }
+  else
+    this->r = r;
+
+  unsigned int blockSize = sizeof(real) * 3 * n * this->r;
+  if (align)
+    blockSize += 16;
+
+  UTBuffer = (real*) malloc (blockSize);
+
+  if (align)
+  {
+    // align UT to 16 bytes
+    /*char * memPos = (char *) UTBuffer;
+    unsigned int memPosValue = *(unsigned int*)(&memPos);
+    memPosValue = (memPosValue & 0xFFFFFFF0) + 16;
+    UT = *(real**)(&memPosValue);*/
+    size_t alignedAddress = ((size_t)((unsigned char*)UTBuffer + sizeof(void*) - 1)) &
+	    (~(16 - 1));
+    alignedAddress += 16;
+    UT = (real*) alignedAddress;
+  }
+  else
+    UT = UTBuffer;
+
+  for(int i=0; i<3*n; i++)
+  {
+    for(int j=0; j<r; j++)
+    {
+      UT[ELT(this->r,j,i)] = U[ELT(3*n,i,j)];
+    }
+    for(int j=r; j<this->r; j++)
+      UT[ELT(this->r,j,i)] = 0;
+  }
+}
+
+template <class real>
+ModalMatrixTransposed<real>::~ModalMatrixTransposed()
+{
+  free(UTBuffer);
+}
+
+template <class real>
+void ModalMatrixTransposed<real>::ProjectSingleVertex
+  (int vertex, real vx, real vy, real vz, real * vreduced) 
+{
+  int j;
+  for (j=0; j<r; j++) // over all columns of U
+  {
+    vreduced[j] = UT[ELT(r,j,3*vertex+0)] * vx +
+           UT[ELT(r,j,3*vertex+1)] * vy +
+           UT[ELT(r,j,3*vertex+2)] * vz;
+  }
+}
+
+template <class real>
+void ModalMatrixTransposed<real>::AddProjectSingleVertex
+  (int vertex, real vx, real vy, real vz, real * vreduced) 
+{
+  int j;
+  for (j=0; j<r; j++) // over all columns of U
+  {
+  	vreduced[j] += UT[ELT(r,j,3*vertex+0)] * vx +
+             UT[ELT(r,j,3*vertex+1)] * vy +
+             UT[ELT(r,j,3*vertex+2)] * vz;
+  }
+}
+
+template <class real>
+void ModalMatrixTransposed<real>::ProjectVector(real * v, real * vreduced) 
+{
+  // has to make inner product of vector f will all the columns of U
+  // i.e. multiply U^T * f = q
+  CBLAS_ORDER     order= CblasColMajor;
+  CBLAS_TRANSPOSE trans= CblasNoTrans;
+  int M = r;
+  int N = 3*n;
+  real alpha = 1;
+  real * a = UT;
+  int lda = r;
+  real * x = v;
+  int incx = 1;
+  real beta = 0;
+  real * y = vreduced; 
+  int incy = 1;
+
+  _cblas_xgemv<sizeof(real)==sizeof(float)>::f(order, trans, M, N, alpha, a, lda, x, incx, beta, y, incy);
+  //cblas_dgemv(order, trans, M, N, alpha, a, lda, x, incx, beta, y, incy);
+}
+
+template <class real>
+void ModalMatrixTransposed<real>::AddProjectVector(real * v, real * vreduced) 
+{
+  // has to make inner product of vector f will all the columns of U
+  // i.e. multiply U^T * f = q
+  CBLAS_ORDER     order= CblasColMajor;
+  CBLAS_TRANSPOSE trans= CblasNoTrans;
+  int M = r;
+  int N = 3*n;
+  real alpha = 1;
+  real * a = UT;
+  int lda = r;
+  real * x = v;
+  int incx = 1;
+  real beta = 1;
+  real * y = vreduced; 
+  int incy = 1;
+
+  _cblas_xgemv<sizeof(real)==sizeof(float)>::f(order, trans, M, N, alpha, a, lda, x, incx, beta, y, incy);
+  //cblas_dgemv(order, trans, M, N, alpha, a, lda, x, incx, beta, y, incy);
+}
+
+template <class real>
+void ModalMatrixTransposed<real>::ProjectSparseVector(int numSparseEntries, real * sparseVector, int * sparseVectorIndices, real * vreduced)
+{
+  // has to make inner product of vector f will all the columns of U
+  int i,j;
+  for (j=0; j<r; j++) // over all columns of U
+  {
+    // dot product of column j of U with vector f
+    vreduced[j] = 0;
+    for (i=0; i<numSparseEntries; i++)
+      vreduced[j] += UT[ELT(r,j,sparseVectorIndices[i])] * sparseVector[i];
+  }
+}
+
+template <class real>
+void ModalMatrixTransposed<real>::AddProjectSparseVector(int numSparseEntries, real * sparseVector, int * sparseVectorIndices, real * vreduced)
+{
+  // has to make inner product of vector f will all the columns of U
+  int i,j;
+  for (j=0; j<r; j++) // over all columns of U
+  {
+    // dot product of column j of U with vector f
+    for (i=0; i<numSparseEntries; i++)
+      vreduced[j] += UT[ELT(r,j,sparseVectorIndices[i])] * sparseVector[i];
+  }
+}
+
+template <class real>
+void ModalMatrixTransposed<real>::AssembleVector(real * q, real * u) //u = U * q;
+{
+  CBLAS_ORDER     order= CblasColMajor;
+  CBLAS_TRANSPOSE trans= CblasTrans;
+  int M = r;
+  int N = 3*n;
+  real alpha = 1;
+  real * a = UT;
+  int lda = r;
+  real * x = q;
+  int incx = 1;
+  real beta = 0;
+  real * y = u; 
+  int incy = 1;
+
+  _cblas_xgemv<sizeof(real)==sizeof(float)>::f(order, trans, M, N, alpha, a, lda, x, incx, beta, y, incy);
+  //cblas_dgemv(order, trans, M, N, alpha, a, lda, x, incx, beta, y, incy);
+}
+
+template <class real>
+void ModalMatrixTransposed<real>::AddAssembleVector(real * q, real * u) //u = U * q;
+{
+  CBLAS_ORDER     order= CblasColMajor;
+  CBLAS_TRANSPOSE trans= CblasTrans;
+  int M = r;
+  int N = 3*n;
+  real alpha = 1;
+  real * a = UT;
+  int lda = r;
+  real * x = q;
+  int incx = 1;
+  real beta = 1.0;
+  real * y = u; 
+  int incy = 1;
+
+  _cblas_xgemv<sizeof(real)==sizeof(float)>::f(order, trans, M, N, alpha, a, lda, x, incx, beta, y, incy);
+  //cblas_dgemv(order, trans, M, N, alpha, a, lda, x, incx, beta, y, incy);
+}
+
+#ifdef WIN32
+template <>
+void ModalMatrixTransposed<float>::AssembleSingleVertex_SSE
+  (int vertex, float * q, float * outputDefo) // q and outputDefo must be 16-byte aligned
+{
+  long int rr = this->r;
+
+  float * posx = &UT[ELT(rr,0,3*vertex)];
+  float * posy = posx + rr;
+  float * posz = posy + rr;
+/*
+  q[0] = 1.55;
+  q[1] = -2.34;
+  q[2] = 3.45;
+  q[3] = 4.56;
+  q[4] = 0.45;
+  q[5] = 1.34;
+  q[9] = 12.11;
+  q[12] = 2.4;
+  q[13] = 4.5;
+
+  float bla;
+*/
+
+  #ifdef _M_IX86
+  __asm{
+    XOR ebx, ebx
+    XORPS xmm0, xmm0
+    XORPS xmm1, xmm1
+    XORPS xmm2, xmm2
+
+   loopStart: 
+ 
+    MOV edx, ebx
+    ADD edx, 4
+    CMP edx, rr
+    JG  outOfLoop
+
+    // load q into xmm7
+    MOV    edx, [q]
+    MOVAPS xmm7, [edx]
+
+    MOV    edx, posx
+    MOVAPS xmm3, [edx] 
+    MULPS  xmm3, xmm7
+    ADDPS  xmm0, xmm3
+
+    MOV    edx, posy
+    MOVAPS xmm4, [edx] 
+    MULPS  xmm4, xmm7
+    ADDPS  xmm1, xmm4
+
+    MOV    edx, posz
+    MOVAPS xmm5, [edx] 
+    MULPS  xmm5, xmm7
+    ADDPS  xmm2, xmm5
+
+    ADD    posx, 16
+    ADD    posy, 16
+    ADD    posz, 16
+    ADD    q, 16
+    ADD    ebx, 4
+    JMP    loopStart
+
+   outOfLoop:
+
+    // make zero artifical fourth vector
+    XORPS  xmm3, xmm3
+
+    // must add entries in xmm0,1,2,3 store sums to u
+    MOVAPS xmm4, xmm0
+    SHUFPS xmm4, xmm1, 0x44
+    MOVAPS xmm5, xmm0
+    SHUFPS xmm5, xmm1, 0xEE
+    ADDPS  xmm4, xmm5
+
+    MOVAPS xmm6, xmm2
+    SHUFPS xmm6, xmm3, 0x44
+    MOVAPS xmm7, xmm2
+    SHUFPS xmm7, xmm3, 0xEE
+    ADDPS  xmm6, xmm7
+
+    // now, xmm4 and xmm6 are set
+    MOVAPS xmm5, xmm4
+    MOVAPS xmm7, xmm4
+    SHUFPS xmm5, xmm6, 0x88
+    SHUFPS xmm7, xmm6, 0xDD
+    ADDPS  xmm5, xmm7
+
+    MOV    edx, [outputDefo]
+    MOVAPS [edx], xmm5
+  };
+  #endif
+}
+#endif
+
+
+#if ((defined __GNUC__) && (!TARGET_CPU_PPC))
+template <>
+void ModalMatrixTransposed<float>::AssembleSingleVertex_SSE
+  (int vertex, float * q, float * outputDefo) // q and outputDefo must be 16-byte aligned
+{
+  long int rr = sizeof(float) * r;
+  float * posx = &UT[r*3*vertex];
+  long int i = 0;
+
+  asm(
+    //"xor %3, %3\n\t"
+    "xorps %%xmm0, %%xmm0\n\t"
+    "xorps %%xmm1, %%xmm1\n\t"
+    "xorps %%xmm2, %%xmm2\n\t"
+    "\n\t"
+
+  "loopStart:\n\t" 
+"\n\t" 
+    "add $16, %3\n\t"
+    "cmp %1, %3\n\t"
+    "jg  outOfLoop\n\t"
+"\n\t"
+    "// load q into xmm7\n\t"
+    "movaps (%2), %%xmm7\n\t"
+"\n\t"
+    "movaps (%0), %%xmm3\n\t" 
+    "mulps  %%xmm7, %%xmm3\n\t"
+    "addps  %%xmm3, %%xmm0\n\t"
+"\n\t"
+    "movaps (%0,%1,1), %%xmm4\n\t" 
+    "mulps  %%xmm7, %%xmm4\n\t"
+    "addps  %%xmm4, %%xmm1\n\t"
+"\n\t"
+    "movaps (%0,%1,2), %%xmm5\n\t" 
+    "mulps  %%xmm7, %%xmm5\n\t"
+    "addps  %%xmm5, %%xmm2\n\t"
+"\n\t"
+    "add    $16, %0\n\t"
+    "add    $16, %2\n\t"
+    "jmp    loopStart\n\t"
+"\n\t"
+   "outOfLoop:\n\t"
+"\n\t"
+    "// make zero artifical fourth vector\n\t"
+    "xorps  %%xmm3, %%xmm3\n\t"
+"\n\t"
+    "// must add entries in xmm0,1,2,3 store sums to u\n\t"
+    "movaps %%xmm0, %%xmm4\n\t"
+    "shufps $0x44, %%xmm1, %%xmm4\n\t"
+    "movaps %%xmm0, %%xmm5\n\t"
+    "shufps $0xEE, %%xmm1, %%xmm5\n\t"
+    "addps  %%xmm5, %%xmm4\n\t"
+"\n\t"
+    "movaps %%xmm2, %%xmm6\n\t"
+    "shufps $0x44, %%xmm3, %%xmm6\n\t"
+    "movaps %%xmm2, %%xmm7\n\t"
+    "shufps $0xEE, %%xmm3, %%xmm7\n\t"
+    "addps  %%xmm6, %%xmm7\n\t"
+"\n\t"
+    "// now, xmm4 and xmm6 are set\n\t"
+    "movaps %%xmm4, %%xmm5\n\t"
+    "movaps %%xmm4, %%xmm7\n\t"
+    "shufps $0x88, %%xmm6, %%xmm5\n\t"
+    "shufps $0xDD, %%xmm6, %%xmm7\n\t"
+    "addps  %%xmm7, %%xmm5\n\t"
+"\n\t"
+    "movaps %%xmm5, (%4)\n\t"
+    : /* no output registers */
+    : "r" (posx), "r" (rr), "r" (q), "r" (i), "r" (outputDefo)
+    : "%xmm0", "%xmm1", "%xmm2", "%xmm3", 
+      "%xmm4", "%xmm5", "%xmm6", "%xmm7", "memory"
+   );
+}
+#endif
+
+template ModalMatrixTransposed<double>::ModalMatrixTransposed(int n, int r, double * U, int align);
+template ModalMatrixTransposed<float>::ModalMatrixTransposed(int n, int r, float * U, int align);
+
+template ModalMatrixTransposed<double>::~ModalMatrixTransposed();
+template ModalMatrixTransposed<float>::~ModalMatrixTransposed();
+
+template void ModalMatrixTransposed<double>::ProjectSingleVertex
+  (int vertex, double vx, double vy, double vz, double * vreduced);
+template void ModalMatrixTransposed<float>::ProjectSingleVertex
+  (int vertex, float vx, float vy, float vz, float * vreduced);
+
+template void ModalMatrixTransposed<double>::AddProjectSingleVertex
+  (int vertex, double vx, double vy, double vz, double * vreduced);
+template void ModalMatrixTransposed<float>::AddProjectSingleVertex
+  (int vertex, float vx, float vy, float vz, float * vreduced);
+
+template void ModalMatrixTransposed<double>::ProjectVector(double * v, double * vreduced);
+template void ModalMatrixTransposed<float>::ProjectVector(float * v, float * vreduced);
+
+template void ModalMatrixTransposed<double>::AddProjectVector(double * v, double * vreduced);
+template void ModalMatrixTransposed<float>::AddProjectVector(float * v, float * vreduced);
+
+template void ModalMatrixTransposed<double>::ProjectSparseVector
+  (int numSparseEntries, double * sparseVector, 
+	 int * sparseVectorIndices, double * vreduced);
+template void ModalMatrixTransposed<float>::ProjectSparseVector
+  (int numSparseEntries, float * sparseVector, 
+	 int * sparseVectorIndices, float * vreduced);
+
+template void ModalMatrixTransposed<double>::AddProjectSparseVector
+  (int numSparseEntries, double * sparseVector, 
+	 int * sparseVectorIndices, double * vreduced);
+template void ModalMatrixTransposed<float>::AddProjectSparseVector
+  (int numSparseEntries, float * sparseVector, 
+	 int * sparseVectorIndices, float * vreduced);
+
+template void ModalMatrixTransposed<double>::AssembleVector(double * q, double * u);
+template void ModalMatrixTransposed<float>::AssembleVector(float * q, float * u);
+
+template void ModalMatrixTransposed<double>::AddAssembleVector(double * q, double * u);
+template void ModalMatrixTransposed<float>::AddAssembleVector(float * q, float * u);
diff --git a/libraries/modalMatrix/modalMatrixTransposed.h b/libraries/modalMatrix/modalMatrixTransposed.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f77cd1e5ed146f8a362e9ad0270c631a49f5638
--- /dev/null
+++ b/libraries/modalMatrix/modalMatrixTransposed.h
@@ -0,0 +1,169 @@
+#ifndef _MODALMATRIXTRANSPOSED_H_
+#define _MODALMATRIXTRANSPOSED_H_
+
+/*
+* Copyright (c) 2007, Carnegie Mellon University
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Code author: Jernej Barbic
+  Research: Jernej Barbic, Doug L. James
+  Funding: NSF, Link Foundation
+*/
+
+#include "matrixMacros.h"
+
+template<class real>
+class ModalMatrixTransposed 
+{
+public:
+  
+  // n is num vertices, matrix U is 3n x r
+  ModalMatrixTransposed(int n, int r, real * U, int align=0); // makes an internal copy of UT
+  ~ModalMatrixTransposed();
+
+  inline int Getr() { return r; }
+  inline int Getn() { return n; }
+
+  inline real * GetMatrix() { return UT; }
+
+  // computes: vreduced = U^T * v
+  void ProjectVector(real * v, real * vreduced);
+  void ProjectSparseVector(int numSparseEntries, real * sparseVector, int * sparseVectorIndices, real * vreduced);
+  void ProjectSingleVertex(int vertex, real vx, real vy, real vz, real * vreduced);
+
+  // vreduced += U^T * v
+  void AddProjectVector(real * v, real * vreduced);
+  void AddProjectSparseVector(int numSparseEntries, real * sparseVector, int * sparseVectorIndices, real * vreduced);
+  void AddProjectSingleVertex(int vertex, real vx, real vy, real vz, real * vreduced);
+
+  // computes u = U * q
+  void AssembleVector(real * q, real * u);
+  inline void AssembleSingleVertex(int vertex, real * q, real * ux, real * uy, real * uz);
+
+  #if defined(WIN32) || defined(__GNUC__)
+    void AssembleSingleVertex_SSE(int vertex, float * q, float * outputDefo); // q and outputDefo must be 16-byte aligned; function only works for real=float
+  #endif
+
+  inline void getMatrixEntry(int vertex, int mode, real * output);
+
+  // u += U * q
+  void AddAssembleVector(real * q, real * u);
+  inline void AddAssembleSingleVertex(int vertex, real * q, real * ux, real * uy, real * uz);
+
+protected:
+
+  real * UT; // pointer to the deformation basis
+  real * UTBuffer;
+
+  int r; // number of columns
+  int n; // number of vertices
+};
+
+template <class real>
+inline void ModalMatrixTransposed<real>::getMatrixEntry(int vertex, int mode, real * output)
+{
+  output[0] = UT[ELT(r, mode, 3*vertex+0)];
+  output[1] = UT[ELT(r, mode, 3*vertex+1)];
+  output[2] = UT[ELT(r, mode, 3*vertex+2)];
+}
+
+// constructs the deformation of vertex 'vertex', given q
+// result goes into (ux,uy,uz)
+template <class real>
+inline void ModalMatrixTransposed<real>::AssembleSingleVertex
+  (int vertex, real * q, real * ux, real * uy, real * uz)
+{
+  real * posx = &UT[ELT(r,0,3*vertex)];
+  real * posy = posx + r;
+  real * posz = posy + r;
+
+  real regx = 0;
+  real regy = 0;
+  real regz = 0;
+
+  int j;
+  for (j=0; j<r; j+=4) // over all columns of U
+  {
+    real q0 = q[j];
+    real q1 = q[j+1];
+    real q2 = q[j+2];
+    real q3 = q[j+3];
+    regx += posx[j] * q0 + posx[j+1] * q1 + posx[j+2] * q2 + posx[j+3] * q3;
+    regy += posy[j] * q0 + posy[j+1] * q1 + posy[j+2] * q2 + posy[j+3] * q3;
+    regz += posz[j] * q0 + posz[j+1] * q1 + posz[j+2] * q2 + posz[j+3] * q3;
+  }
+
+  for(; j<r; j++)
+  {
+    real q0 = q[j];
+    regx += posx[j] * q0;
+    regy += posy[j] * q0;
+    regz += posz[j] * q0;
+  }
+
+  *ux = regx;
+  *uy = regy;
+  *uz = regz;
+}
+
+template <class real>
+inline void ModalMatrixTransposed<real>::AddAssembleSingleVertex
+  (int vertex, real * q, real * ux, real * uy, real * uz)
+{
+  real * posx = &UT[ELT(r,0,3*vertex)];
+  real * posy = posx + r;
+  real * posz = posy + r;
+
+  real regx = 0;
+  real regy = 0;
+  real regz = 0;
+
+  int j;
+  for (j=0; j<r; j+=4) // over all columns of U
+  {
+    real q0 = q[j];
+    real q1 = q[j+1];
+    real q2 = q[j+2];
+    real q3 = q[j+3];
+    regx += posx[j] * q0 + posx[j+1] * q1 + posx[j+2] * q2 + posx[j+3] * q3;
+	  regy += posy[j] * q0 + posy[j+1] * q1 + posy[j+2] * q2 + posy[j+3] * q3;
+	  regz += posz[j] * q0 + posz[j+1] * q1 + posz[j+2] * q2 + posz[j+3] * q3;
+  }
+
+  for(; j<r; j++)
+  {
+    real q0 = q[j];
+    regx += posx[j] * q0;
+    regy += posy[j] * q0;
+    regz += posz[j] * q0;
+  }
+
+  *ux += regx;
+  *uy += regy;
+  *uz += regz;
+}
+
+#endif
+
diff --git a/libraries/objMesh/createObjMesh.cpp b/libraries/objMesh/createObjMesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..322737208a2580412ec3c2e77f90cca1466354cf
--- /dev/null
+++ b/libraries/objMesh/createObjMesh.cpp
@@ -0,0 +1,127 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "objMesh.h"
+using namespace std;
+
+// create a cylinder without cap, centered at origin and axis at y-direction
+ObjMesh createCylinderWallObjMesh(double radius, double height, int subdivisionAxis, int subdivisionHeight)
+{
+  assert(subdivisionAxis >= 1);
+  assert(subdivisionHeight >= 1);
+
+  ObjMesh mesh;
+  Vec3d x(1,0,0), nz(0,0,-1);
+  double da = 2 * M_PI / subdivisionAxis;
+  double dh = height / subdivisionHeight;
+  for(int i = 0; i < subdivisionHeight+1; i++)
+  {
+    Vec3d base(0, -height/2.0 + dh*i, 0);
+    for(int j = 0; j < subdivisionAxis; j++)
+    {
+      Vec3d p = base + radius * Vec3d(cos(j*da), 0, -sin(j*da));
+//      positions[i*subdivisionAxis + j] = p;
+      mesh.addVertexPosition(p);
+    }
+  }
+
+  ObjMesh::Group group;
+  for(int i = 0; i < subdivisionHeight; i++)
+  {
+    for(int j = 0; j < subdivisionAxis; j++)
+    {
+      int nj = (j+1 < subdivisionAxis ? j+1 : 0);
+      int a = i*subdivisionAxis+j;
+      int b = i*subdivisionAxis+nj;
+      int c = (i+1)*subdivisionAxis+nj;
+      int d = (i+1)*subdivisionAxis+j;
+      ObjMesh::Face face(a, b, c, d);
+      group.addFace(move(face));
+    }
+  }
+
+  mesh.addGroup(move(group));
+  return mesh;
+}
+
+// create a cylinder mesh centered at origin and axis at y-direction
+ObjMesh createCylinderObjMesh(double radius, double height, int subdivisionAxis, int subdivisionHeight)
+{
+  ObjMesh mesh = createCylinderWallObjMesh(radius, height, subdivisionAxis, subdivisionHeight);
+  int lowerCenterID = mesh.getNumVertices();
+  int upperCenterID = lowerCenterID + 1;
+  mesh.addVertexPosition(Vec3d(0, -height/2.0, 0));
+  mesh.addVertexPosition(Vec3d(0, height/2.0, 0));
+  auto & group = mesh.getGroup(0);
+  for(int i = 0; i < subdivisionAxis; i++)
+  {
+    int ni = (i+1)%subdivisionAxis;
+    ObjMesh::Face face(ni, i, lowerCenterID);
+    group.addFace((move(face)));
+  }
+
+  int offsetToUpper = subdivisionAxis * subdivisionHeight;
+  for(int i = 0; i < subdivisionAxis; i++)
+  {
+    int ni = (i+1)%subdivisionAxis;
+    ObjMesh::Face face(offsetToUpper+i, offsetToUpper+ni, upperCenterID);
+    group.addFace((move(face)));
+  }
+
+  return mesh;
+}
+
+ObjMesh createBoundingBoxObjMesh(Vec3d bmin, Vec3d bmax)
+{
+  for(int i = 0; i < 3; i++)
+    assert(bmin[i] <= bmax[i]);
+
+  // vtx order in box:
+  //
+  //     3 - - - 2
+  //    /|      /|
+  //   7 - - - 6 |       y
+  //   | |     | |       |
+  //   | 0 - - | 1       |_ _ _x
+  //   |/      |/       /
+  //   4 - - - 5       z
+  vector<Vec3d> positions = { bmin, { bmax[0], bmin[1], bmin[2] }, { bmax[0], bmax[1], bmin[2] }, { bmin[0], bmax[1], bmin[2] },
+                            { bmin[0], bmin[1], bmax[2] }, { bmax[0], bmin[1], bmax[2] }, bmax, { bmin[0], bmax[1], bmax[2] } };
+  vector<Vec4i> faces = { {0,3,2,1}, {4,5,6,7}, {0,1,5,4}, {3,7,6,2}, {1,2,6,5}, {0,4,7,3} };
+
+  ObjMesh mesh;
+  mesh.addVertexPositions(positions);
+  mesh.addGroup(ObjMesh::Group());
+  for(size_t i = 0; i < faces.size(); i++)
+    mesh.addFaceToGroup(ObjMesh::Face(faces[i]), 0);
+  return mesh;
+}
diff --git a/src/libobjMesh/objMeshEncode.h b/libraries/objMesh/createObjMesh.h
similarity index 56%
rename from src/libobjMesh/objMeshEncode.h
rename to libraries/objMesh/createObjMesh.h
index 8fb4a43b7897d922e0dc3ab052676265383f7fa1..9f870da4e408d8d6bcb8863a4fda5d49ae270880 100644
--- a/src/libobjMesh/objMeshEncode.h
+++ b/libraries/objMesh/createObjMesh.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,10 +30,20 @@
  *                                                                       *
  *************************************************************************/
 
+#ifndef CREATEOBJMESH_H
+#define CREATEOBJMESH_H
+
 #include "objMesh.h"
 
-namespace vega
-{
-void objMeshEncode(ObjMesh * mesh);
-void objMeshDecode(ObjMesh * mesh);
-}
+// create a cylinder without cap, centered at origin and axis at y-direction
+// subdivisionAxis: #cut on the circle
+// subdivisionHeight: #subdivision on the height direction
+ObjMesh createCylinderWallObjMesh(double radius, double height, int subdivisionAxis, int subdivisionHeight);
+
+// create a cylinder mesh centered at origin and axis at y-direction
+ObjMesh createCylinderObjMesh(double radius, double height, int subdivisionAxis, int subdivisionHeight);
+
+ObjMesh createBoundingBoxObjMesh(Vec3d bmin, Vec3d bmax);
+
+
+#endif
diff --git a/src/libobjMesh/objMesh.cpp b/libraries/objMesh/objMesh.cpp
similarity index 57%
rename from src/libobjMesh/objMesh.cpp
rename to libraries/objMesh/objMesh.cpp
index 4f95dacc6d2d44d466b93a7ebc49d0fb9d8be2fa..d25001e728877619254b4334738f9bb72a212a0a 100644
--- a/src/libobjMesh/objMesh.cpp
+++ b/libraries/objMesh/objMesh.cpp
@@ -1,19 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder,     *
+ *               Yili Zhao, Yijing Li                                    *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,7 +31,7 @@
  *                                                                       *
  *************************************************************************/
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #pragma warning(disable : 4996)
   #pragma warning(disable : 4267)
   #pragma warning(disable : 4244)
@@ -37,22 +42,171 @@
 #include <vector>
 #include <string>
 #include <map>
+#include <iomanip>
 #include <set>
 #include <fstream>
 #include <sstream>
 #include <algorithm>
+#include <functional>
+#include <cctype>
 #include <assert.h>
+#include <cassert>
 #include "macros.h"
-#include "objMesh-disjointSet.h"
+#include "disjointSet.h"
 #include "objMesh.h"
 using namespace std;
 
-namespace vega
+// for faster parallel loading of multimesh binary files, enable the -DUSE_TBB macro line in the Makefile-header file (see also documentation)
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+
+namespace // anonymous namespace
 {
-ObjMesh::ObjMesh(const std::string & filename_, int verbose)
+
+bool iendWith(const string & str, const char * substr)
 {
-  filename = filename_;
+  size_t sublen = strlen(substr);
+  if (str.size() < sublen)
+    return false;
+  for(auto iter = str.begin() + (str.size() - sublen); iter != str.end(); iter++, substr++)
+  {
+    if (toupper(*iter) != toupper(*substr))
+      return false;
+  }
+  return true;
+}
+
+// indices are sorted
+template<class InputVector, class IndexRange>
+void removeByIndices(InputVector & inputVector, const IndexRange & indices)
+{
+  int vecSize = inputVector.size();
+//  int indSize = distance(indices.begin(), indices.end());
+  auto ID = indices.begin();
+  int newEnd = 0;
+  for(int i = 0; i < vecSize; i++)
+  {
+    if (ID != indices.end() && i == *ID)
+    {
+      ID++;
+      continue;
+    }
+    if (i != newEnd)
+    {
+      inputVector[newEnd] = move(inputVector[i]);
+    }
+    newEnd++;
+  }
+  inputVector.resize(newEnd);
+}
+
+} // end anonymous namespace
+
+ObjMesh::ObjMesh(const std::string & filename_, fileFormatType fileFormat, int verbose) : filename(filename_)
+{
+  switch (fileFormat)
+  {
+    case ASCII:
+    {
+      loadFromAscii(filename_, verbose);
+    }
+    break;
+
+    case BINARY:
+      loadFromBinary(filename_, verbose);
+    break;
+
+    case BY_EXT:
+    {
+      if (iendWith(filename_, ".objb"))
+        loadFromBinary(filename_, verbose);
+      else if (iendWith(filename_, ".obj"))
+        loadFromAscii(filename_, verbose);
+      else
+      {
+        printf("Unknown file extension when loading %s, try ASCII format...\n", filename_.c_str());
+        loadFromAscii(filename_, verbose);
+      }
+    }
+    break;
+
+    default:
+      printf("Error in ObjMesh::ObjMesh: File format %d is unknown.\n", fileFormat);
+      throw 1;
+    break;
+  }
+
+  computeBoundingBox();
+
+  // statistics
+  if (verbose)
+  {
+    std::cout << "Parsed obj file '" << filename << "'; statistics:" << std::endl;
+    std::cout << "   " << groups.size() << " groups," << std::endl;
+    std::cout << "   " << getNumFaces() << " faces," << std::endl;
+    std::cout << "   " << vertexPositions.size() << " vertices," << std::endl;
+    std::cout << "   " << normals.size() << " normals, " << std::endl;
+    std::cout << "   " << textureCoordinates.size() << " texture coordinates, " << std::endl;
+  }
+}
+
+ObjMesh::ObjMesh(void * binaryInputStream, streamType stream, int verbose)
+{
+  filename = string("");
+  loadFromBinary(binaryInputStream, stream, verbose);
+  computeBoundingBox();
+}
+
+ObjMesh::ObjMesh(int numVertices, const double * vertices, int numTriangles, const int * triangles)
+{
+  filename = string("");
+
+  for(int i=0; i<numVertices; i++)
+    addVertexPosition(Vec3d(vertices[3*i+0], vertices[3*i+1], vertices[3*i+2]));
+
+  materials.emplace_back();
+  groups.emplace_back();
+  for(int i=0; i<numTriangles; i++)
+  {
+    Face face(triangles[3*i+0], triangles[3*i+1], triangles[3*i+2]);
+    addFaceToGroup(face, 0);
+  }
+
+  computeBoundingBox();
+}
 
+ObjMesh::ObjMesh(int numVertices, const double * vertices, int numFaces, const int* faceVertexCounts, const int * faces)
+{
+  filename = string("");
+
+  for(int i = 0; i < numVertices; i++)
+    addVertexPosition(Vec3d(vertices[3*i+0], vertices[3*i+1], vertices[3*i+2]));
+
+  materials.emplace_back();
+  groups.emplace_back();
+  for(int i = 0, k = 0; i < numFaces; i++)
+  {
+    Face face;
+    int faceVertexCount = faceVertexCounts[i];
+    for(int j = 0; j < faceVertexCount; j++)
+    {
+      face.addVertex(Vertex(faces[k]));
+      k++;
+    }
+
+    addFaceToGroup(face, 0);
+  }
+
+  computeBoundingBox();
+}
+
+ObjMesh::ObjMesh(const std::vector<Vec3d> & vertexPositions, const std::vector<Vec3i> & triangles) :
+    ObjMesh(vertexPositions.size(), (double*)vertexPositions.data(), triangles.size(), (int*)triangles.data())
+{}
+
+int ObjMesh::loadFromAscii(const string & filename, int verbose)
+{
   unsigned int numFaces = 0;
 
   const int maxline = 4096;
@@ -62,8 +216,9 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
   unsigned int currentGroup=0;
   unsigned int ignoreCounter=0;
 
-  unsigned int  currentMaterialIndex = 0;
-  addMaterial("default", Vec3d(0.2,0.2,0.2), Vec3d(0.6,0.6,0.6), Vec3d(0.0,0.0,0.0), 65);
+  unsigned int currentMaterialIndex = 0;
+
+  // Note: the default material will be added when encountered in the obj file, or at the end if necessary. One cannot simply add it here at the beginning because a material read from the .mtl file could also be called "default".
 
   if (verbose)
     std::cout << "Parsing .obj file '" << filename << "'." << std::endl;
@@ -71,7 +226,7 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
   if (!ifs)
   {
     std::string message = "Could not open .obj file '";
-    message.append( filename );
+    message.append(filename);
     message.append( "'" );
     throw ObjMeshException( message );
   }
@@ -89,7 +244,7 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
     {
       // if ending in '\\', the next line should be concatenated to the current line
       int lastCharPos = (int)strlen(line)-1;
-      while(line[lastCharPos] == '\\') 
+      while(line[lastCharPos] == '\\')
       {
         line[lastCharPos] = ' ';  // first turn '\' to ' '
         char nextline[maxline];
@@ -99,6 +254,16 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
       }
     }
 
+    std::string lineString(line);
+    // trim white space ahead
+    lineString.erase(lineString.begin(), std::find_if(lineString.begin(), lineString.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
+    // trim white space in the end
+    lineString.erase(std::find_if(lineString.rbegin(), lineString.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), lineString.end());
+
+
+    memset(line, 0, maxline);
+    strcpy(line, lineString.c_str());
+
     convertWhitespaceToSingleBlanks(line);
 
     char command = line[0];
@@ -107,24 +272,20 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
     {
       //std::cout << "v " ;
       Vec3d pos;
-      double x,y,z;
-      if (sscanf(line, "v %lf %lf %lf\n", &x, &y, &z) < 3)
+      if (sscanf(line, "v %lf %lf %lf\n", &pos[0], &pos[1], &pos[2]) < 3)
       {
         throw ObjMeshException("Invalid vertex", filename, lineNum);
       }
-      pos = Vec3d(x,y,z);
       vertexPositions.push_back( pos );
     }
     else if (strncmp(line, "vn ", 3) == 0)
     {
       //std::cout << "vn " ;
       Vec3d normal;
-      double x,y,z;
-      if (sscanf(line,"vn %lf %lf %lf\n", &x, &y, &z) < 3)
+      if (sscanf(line,"vn %lf %lf %lf\n", &normal[0], &normal[1], &normal[2]) < 3)
       {
         throw ObjMeshException("Invalid normal", filename, lineNum);
       }
-      normal = Vec3d(x,y,z);
       normals.push_back(normal);
     }
     else if (strncmp(line, "vt ", 3) == 0 )
@@ -198,7 +359,7 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
       //std::cout << "f " ;
       if (groups.empty())
       {
-        groups.push_back(Group("default"));
+        groups.emplace_back();
         currentGroup = 0;
       }
 
@@ -215,7 +376,7 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
         char * tokenEnd = curPos;
         while ((*tokenEnd != ' ') && (*tokenEnd != '\0'))
           tokenEnd++;
- 
+
         bool whiteSpace = false;
         if (*tokenEnd == ' ')
         {
@@ -223,33 +384,44 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
           whiteSpace = true;
         }
 
-        unsigned int pos;
-        unsigned int nor;
-        unsigned int tex;
+        int pos;
+        int nor;
+        int tex;
         std::pair< bool, unsigned int > texPos;
         std::pair< bool, unsigned int > normal;
 
         // now, parse curPos
-        if (strstr(curPos,"//") != NULL) 
+        if (strstr(curPos,"//") != NULL)
         {
-          // v//n
-          if (sscanf(curPos, "%u//%u", &pos, &nor) < 2)
+          if (sscanf(curPos, "%d//%d", &pos, &nor) < 2)
           {
             throw ObjMeshException( "Invalid face", filename, lineNum);
           }
+
+          // v//n
+          if (pos < 0)
+            pos = (int)vertexPositions.size() + pos + 1;
+          if (nor < 0)
+            nor = (int)normals.size() + nor + 1;
+
           texPos = make_pair(false, 0);
-          normal = make_pair(true, nor);
+          normal = make_pair(true, (unsigned int)nor);
         }
-        else 
+        else
         {
-          if (sscanf(curPos, "%u/%u/%u", &pos, &tex, &nor) != 3)
+          if (sscanf(curPos, "%d/%d/%d", &pos, &tex, &nor) != 3)
           {
             if (strstr(curPos, "/") != NULL)
             {
-              // v/t
-              if (sscanf(curPos, "%u/%u", &pos, &tex) == 2)
+              if (sscanf(curPos, "%d/%d", &pos, &tex) == 2)
               {
-                texPos = make_pair(true, tex);
+                // v/t
+                if (pos < 0)
+                  pos = (int)vertexPositions.size() + pos + 1;
+                if (tex < 0)
+                  tex = (int)textureCoordinates.size() + tex + 1;
+
+                texPos = make_pair(true, (unsigned int)tex);
                 normal = make_pair(false, 0);
               }
               else
@@ -258,9 +430,13 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
               }
             }
             else
-            { // v
-              if (sscanf(curPos, "%u", &pos) == 1)
+            {
+              if (sscanf(curPos, "%d", &pos) == 1)
               {
+                // v
+                if (pos < 0)
+                  pos = (int)vertexPositions.size() + pos + 1;
+
                 texPos = make_pair(false, 0);
                 normal = make_pair(false, 0);
               }
@@ -271,26 +447,38 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
             }
           }
           else
-          { // v/t/n
-            texPos = make_pair(true, tex);
-            normal = make_pair(true, nor);
+          {
+            // v/t/n
+            if (pos < 0)
+              pos = (int)vertexPositions.size() + pos + 1;
+            if (tex < 0)
+              tex = (int)textureCoordinates.size() + tex + 1;
+            if (nor < 0)
+              nor = (int)normals.size() + nor + 1;
+
+            texPos = make_pair(true, (unsigned int)tex);
+            normal = make_pair(true, (unsigned int)nor);
           }
         }
 
         // sanity check
-        if ((pos < 1) || (pos > vertexPositions.size()))
+        if ((pos < 1) || (pos > (int)vertexPositions.size()))
         {
           printf("Error: vertex %d is out of bounds.\n", pos);
           throw 51;
         }
-
-        if (texPos.first && ((tex < 1) || (tex > textureCoordinates.size())))
+        if (texPos.first && tex == 0) // sometimes Maya will output meshes with 0 texture index
+        {
+          printf("Warning: texture index is 0. Skip.\n");
+          texPos = make_pair(false, 0);
+        }
+        else if (texPos.first && ((tex < 1) || (tex > (int)textureCoordinates.size())))
         {
           printf("Error: texture %d is out of bounds.\n", tex);
           throw 53;
         }
 
-        if (normal.first && ((nor < 1) || (nor > normals.size())))
+        if (normal.first && ((nor < 1) || (nor > (int)normals.size())))
         {
           printf("Error: normal %d is out of bounds.\n", nor);
           throw 52;
@@ -303,12 +491,12 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
         if (normal.first)
           normal.second--;
 
-        face.addVertex(Vertex(pos, texPos, normal));
+        face.addVertex(Vertex((unsigned int)pos, texPos, normal));
 
         if (whiteSpace)
         {
           *tokenEnd = ' ';
-          curPos = tokenEnd + 1; 
+          curPos = tokenEnd + 1;
         }
         else
           curPos = tokenEnd;
@@ -319,7 +507,8 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
       numGroupFaces++;
     }
     else if ((strncmp(line, "#", 1) == 0 ) || (strncmp(line, "\0", 1) == 0))
-    { // ignore comment lines and empty lines
+    {
+      // ignore comment lines and empty lines
     }
     else if (strncmp(line, "usemtl", 6) == 0)
     {
@@ -339,6 +528,7 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
         groupCloneIndex++;
       }
 
+      materialSearch:
       bool materialFound = false;
       unsigned int counter = 0;
       char * materialName = &line[7];
@@ -351,7 +541,7 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
           // update current group
           if (groups.empty())
           {
-            groups.push_back(Group("default"));
+            groups.emplace_back();
             currentGroup = 0;
           }
 
@@ -361,10 +551,17 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
         }
         counter++;
       }
+
       if (!materialFound)
       {
+        if (strcmp(materialName, "default") == 0)
+        {
+          addDefaultMaterial();
+          goto materialSearch;
+        }
+
         char msg[4096];
-        sprintf(msg,"Material %s does not exist.\n",materialName);
+        sprintf(msg, "Obj mesh material %s does not exist.\n", materialName);
         throw ObjMeshException(msg);
       }
     }
@@ -376,7 +573,7 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
     }
     else if ((strncmp(line, "s ", 2) == 0 ) || (strncmp(line, "o ", 2) == 0))
     {
-      // ignore 
+      // ignore lines beginning with s and o
       //std::cout << command << " ";
       if (ignoreCounter < 5)
       {
@@ -400,38 +597,25 @@ ObjMesh::ObjMesh(const std::string & filename_, int verbose)
     }
   }
 
-  computeBoundingBox();
-  
-  // statistics
-  if (verbose)
-  {
-    std::cout << "Parsed obj file '" << filename << "'; statistics:" << std::endl;
-    std::cout << "   " << groups.size() << " groups," << std::endl;
-    std::cout << "   " << numFaces << " faces," << std::endl;
-    std::cout << "   " << vertexPositions.size() << " vertices," << std::endl;
-    std::cout << "   " << normals.size() << " normals, " << std::endl;
-    std::cout << "   " << textureCoordinates.size() << " texture coordinates, " << std::endl;
-  }
+  // add the "default" material if it doesn't already exist
+  addDefaultMaterial();
+
+  return 0;
 }
 
-ObjMesh::ObjMesh(int numVertices, double * vertices, int numTriangles, int * triangles)
+void ObjMesh::addDefaultMaterial()
 {
-  for(int i=0; i<numVertices; i++)
-    addVertexPosition(Vec3d(vertices[3*i+0], vertices[3*i+1], vertices[3*i+2]));
-
-  unsigned int materialIndex = 0;
-  addMaterial("default", Vec3d(1,1,1), Vec3d(1,1,1), Vec3d(1,1,1), 0);
-  groups.push_back(Group("defaultGroup", materialIndex));
-  for(int i=0; i<numTriangles; i++)
+  // search if there already is the "default" material
+  unsigned int numObjMaterials = getNumMaterials();
+  for (unsigned int materialIndex=0; materialIndex<numObjMaterials; materialIndex++)
   {
-    Face face;
-    face.addVertex(Vertex(triangles[3*i+0]));
-    face.addVertex(Vertex(triangles[3*i+1]));
-    face.addVertex(Vertex(triangles[3*i+2]));
-    addFaceToGroup(face, 0);
+    if(materials[materialIndex].getName() == "default")
+    {
+      return;
+    }
   }
 
-  computeBoundingBox();
+  materials.emplace_back();
 }
 
 std::vector<std::string> ObjMesh::getGroupNames() const
@@ -444,7 +628,7 @@ std::vector<std::string> ObjMesh::getGroupNames() const
   return result;
 }
 
-ObjMesh::Group ObjMesh::getGroup(const std::string name) const
+const ObjMesh::Group & ObjMesh::getGroup(const std::string & name) const
 {
   for(std::vector<Group>::const_iterator itr = groups.begin(); itr != groups.end(); itr++)
   {
@@ -548,7 +732,7 @@ void ObjMesh::triangulate()
       {
         printf("Warning: encountered a face with fewer than 3 vertices.\n");
       }
-  
+
       unsigned int faceDegree = face->getNumVertices();
 
       if (faceDegree > 3)
@@ -559,7 +743,7 @@ void ObjMesh::triangulate()
         vector<Vertex> vertices;
         for(unsigned int k=0; k<face->getNumVertices(); k++)
           vertices.push_back(face->getVertex(k));
-        
+
         Face newFace;
         newFace.addVertex(vertices[0]);
         newFace.addVertex(vertices[1]);
@@ -588,7 +772,7 @@ void ObjMesh::computeBoundingBox()
 
   for(unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
   {
-    Vec3d p = vertexPositions[i]; 
+    Vec3d p = vertexPositions[i];
 
     if (p[0] < bmin[0])
       bmin[0] = p[0];
@@ -638,7 +822,446 @@ double ObjMesh::getDiameter() const
   return diameter;
 }
 
-void ObjMesh::save(const string & filename, int outputMaterials, int verbose) const
+int ObjMesh::saveObjMeshesToBinary(const std::string & filename, int numObjMeshes, ObjMesh ** objMeshes, int * saveObjMeshesFlag, int outputMaterials, int verbose)
+{
+  FILE * output = fopen(filename.c_str(), "wb");
+  if (output == NULL)
+  {
+    printf("Error in ObjMesh::saveToBinary: cannot open %s to write.\n", filename.c_str());
+    return 1;
+  }
+
+  unsigned int * bytesWritten = (unsigned int*) calloc (numObjMeshes, sizeof(unsigned int));
+
+  // count the number of bytes written to the file for every obj mesh
+  for(int i=0; i<numObjMeshes; i++)
+  {
+    if (saveObjMeshesFlag[i] == 0)
+      continue;
+
+    bool countBytesOnly = true;
+    objMeshes[i]->saveToBinary(NULL, outputMaterials, &bytesWritten[i], countBytesOnly);
+  }
+
+  if (verbose)
+  {
+    printf("number of bytes for each obj mesh: \n");
+    for(int i=0; i<numObjMeshes; i++)
+      printf("%u, ", bytesWritten[i]);
+    printf("\n");
+  }
+
+  // write the header to the file
+  fwrite(&numObjMeshes, sizeof(int), 1, output);
+  fwrite(bytesWritten, sizeof(unsigned int), numObjMeshes, output);
+
+  // write the obj meshes to the file
+  for(int i=0; i<numObjMeshes; i++)
+  {
+    if (saveObjMeshesFlag[i] == 0)
+      continue;
+
+    bool countBytesOnly = false;
+    objMeshes[i]->saveToBinary(output, outputMaterials, &bytesWritten[i], countBytesOnly, verbose);
+  }
+
+  free(bytesWritten);
+  fclose(output);
+
+  return 0;
+}
+
+int ObjMesh::saveToBinary(const std::string & filename_, int outputMaterials, int verbose) const
+{
+  FILE * fout = fopen(filename_.c_str(), "wb");
+  if (fout == NULL)
+  {
+    printf("Error in ObjMesh::saveToBinary: cannot open file %s to write.\n", filename_.c_str());
+  }
+  bool countBytesOnly = false;
+  int code = saveToBinary(fout, outputMaterials, NULL, countBytesOnly, verbose);
+  fclose(fout);
+  return code;
+}
+
+// return:
+// 0 = succeeded
+// 1 = failed
+int ObjMesh::saveToBinary(FILE * binaryOutputStream, int outputMaterials, unsigned int * bytesWritten, bool countBytesOnly, int verbose) const
+{
+  // first pass: count the total number of bytes to be written to the file
+  // second pass: do the actual writing
+  enum {COUNT_BYTES, WRITE_TO_DISK, NUM_PASSES};
+  int totalPasses = NUM_PASSES;
+  if (countBytesOnly)
+    totalPasses = WRITE_TO_DISK;
+
+  unsigned int totalBytes = 0;
+  for(int pass = 0; pass < totalPasses; pass++)
+  {
+    unsigned int bytes = 0;
+    unsigned int items;
+
+    // the header will be the number of bytes (including the totalbytes itself)
+    items = 1;
+    if (pass == WRITE_TO_DISK)
+      items = fwrite(&totalBytes, sizeof(unsigned int), 1, binaryOutputStream);
+    if (items != 1)
+      return 1;
+    bytes += items * sizeof(unsigned int);
+
+    // save the flag that determines whether to output materials or not
+    items = 1;
+    if (pass == WRITE_TO_DISK)
+      items = fwrite(&outputMaterials, sizeof(int), 1, binaryOutputStream);
+    if (items != 1)
+      return 1;
+    bytes += items * sizeof(int);
+
+    // save materials, if necessary
+    if (outputMaterials)
+    {
+      unsigned int numObjMaterials = getNumMaterials();
+
+      // save the number of materials
+      items = 1;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(&numObjMaterials, sizeof(unsigned int), 1, binaryOutputStream);
+      if (items != 1)
+        return 1;
+      bytes += items * sizeof(unsigned int);
+
+      // save the material names
+      for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
+      {
+        std::string materialNameString = materials[materialIndex].getName();
+        char * materialName = (char *)(materialNameString.c_str());
+        unsigned int strLength = strlen(materialName);
+        items = 1;
+        if (pass == WRITE_TO_DISK)
+          items = fwrite(&strLength, sizeof(unsigned int), 1, binaryOutputStream);
+        if (items != 1)
+          return 1;
+        bytes += items * sizeof(unsigned int);
+
+        items = strLength;
+        if (pass == WRITE_TO_DISK)
+          items = fwrite(materialName, sizeof(char), strLength, binaryOutputStream);
+        if (items != strLength)
+          return 1;
+        bytes += items * sizeof(char);
+      }
+
+      // Ka, Kd, Ks, each of which has 3 doubles, plus Ns, a double
+      // So there are 10 doubles for every material
+      enum {KA_0, KA_1, KA_3, KD_0, KD_1, KD_2, KS_0, KS_1, KS_2, NS, NUM_MATERIAL_PROPERTIES};
+      double * materialProperties = (double *) malloc(sizeof(double) * NUM_MATERIAL_PROPERTIES * numObjMaterials);
+
+      std::vector<unsigned int> materialHasTextureImageKd;
+
+      for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
+      {
+        unsigned int offset = materialIndex * NUM_MATERIAL_PROPERTIES;
+
+        Vec3d Ka = materials[materialIndex].getKa();
+        Ka.convertToArray(&materialProperties[offset]);
+
+        Vec3d Kd = materials[materialIndex].getKd();
+        Kd.convertToArray(&materialProperties[offset + 3]);
+
+        Vec3d Ks = materials[materialIndex].getKs();
+        Ks.convertToArray(&materialProperties[offset + 6]);
+
+        materialProperties[offset + 9] = materials[materialIndex].getShininess() * 1000.0 / 128.0;
+
+        if (materials[materialIndex].hasTextureFilename())
+          materialHasTextureImageKd.push_back(materialIndex);
+      }  // for materialIndex
+
+      // save the material properties
+      items = NUM_MATERIAL_PROPERTIES * numObjMaterials;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(materialProperties, sizeof(double), NUM_MATERIAL_PROPERTIES * numObjMaterials, binaryOutputStream);
+      if (items !=  NUM_MATERIAL_PROPERTIES * numObjMaterials)
+        return 1;
+      bytes += items * sizeof(double);
+
+      free(materialProperties);
+
+      // save the number of materials which have map_Kd texture images
+      unsigned int vectorSize = materialHasTextureImageKd.size();
+      items = 1;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(&vectorSize, sizeof(unsigned int), 1, binaryOutputStream);
+      if (items != 1)
+        return 1;
+      bytes += items * sizeof(unsigned int);
+
+      for(unsigned int materialIndex=0; materialIndex < vectorSize; materialIndex++)
+      {
+        // save the material ID
+        unsigned int materialID = materialHasTextureImageKd[materialIndex];
+        items = 1;
+        if (pass == WRITE_TO_DISK)
+          items = fwrite(&materialID, sizeof(unsigned int), 1, binaryOutputStream);
+        if (items != 1)
+          return 1;
+        bytes += items * sizeof(unsigned int);
+
+        // save the material image
+        std::string textureFilenameString = materials[materialID].getTextureFilename();
+        char * textureFilename = (char *)(textureFilenameString.c_str());
+        unsigned int strLength = strlen(textureFilename);
+        items = 1;
+        if (pass == WRITE_TO_DISK)
+          items = fwrite(&strLength, sizeof(unsigned int), 1, binaryOutputStream);
+        if (items != 1)
+          return 1;
+        bytes += items * sizeof(unsigned int);
+
+        items = strLength;
+        if (pass == WRITE_TO_DISK)
+          items = fwrite(textureFilename, sizeof(char), strLength, binaryOutputStream);
+        if (items != strLength)
+          return 1;
+        bytes += items * sizeof(char);
+      }  // for materialIndex
+    }  // if outputMaterials
+
+    // save the number of vertices
+    unsigned int numVertices = vertexPositions.size();
+    items = 1;
+    if (pass == WRITE_TO_DISK)
+      items = fwrite(&numVertices, sizeof(unsigned int), 1, binaryOutputStream);
+    if (items != 1)
+      return 1;
+    bytes += items * sizeof(unsigned int);
+
+    // save vertices
+    items = 3 * numVertices;
+    if (pass == WRITE_TO_DISK && fwrite(&vertexPositions[0][0], sizeof(double), items, binaryOutputStream) != items)
+      return 1;
+    bytes += items * sizeof(double);
+
+    // save the number of texture coordinates
+    unsigned int numTexCoordinates = textureCoordinates.size();
+    items = 1;
+    if (pass == WRITE_TO_DISK)
+      items = fwrite(&numTexCoordinates, sizeof(unsigned int), 1, binaryOutputStream);
+    if (items != 1)
+      return 1;
+    bytes += items * sizeof(unsigned int);
+
+    // save texture coordinates
+    for (unsigned int texCoordinateIndex=0; texCoordinateIndex < numTexCoordinates; texCoordinateIndex++)
+    {
+      Vec3d texCoord_ = getTextureCoordinate(texCoordinateIndex);
+      double temp[3];
+      texCoord_.convertToArray(temp);
+
+      items = 3;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(temp, sizeof(double), 3, binaryOutputStream);
+      if (items != 3)
+        return 1;
+      bytes += items * sizeof(double);
+    }
+
+    // save the number of normals
+    unsigned int numNormals = normals.size();
+    items = 1;
+    if (pass == WRITE_TO_DISK)
+      items = fwrite(&numNormals, sizeof(unsigned int), 1, binaryOutputStream);
+    if (items != 1)
+      return 1;
+    bytes += items * sizeof(unsigned int);
+
+    // save normals
+    for (unsigned int normalIndex=0; normalIndex < numNormals; normalIndex++)
+    {
+      Vec3d normal_ = getNormal(normalIndex);
+      double temp[3];
+      normal_.convertToArray(temp);
+      items = 3;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(temp, sizeof(double), 3, binaryOutputStream);
+      if (items != 3)
+        return 1;
+      bytes += items * sizeof(double);
+    }
+
+    // save the number of groups
+    unsigned int numGroups = groups.size();
+    items = 1;
+    if (pass == WRITE_TO_DISK)
+      items = fwrite(&numGroups, sizeof(unsigned int), 1, binaryOutputStream);
+    if (items != 1)
+      return 1;
+    bytes += items * sizeof(unsigned int);
+
+    // save groups and faces
+    for(unsigned int groupIndex=0; groupIndex < groups.size(); groupIndex++)
+    {
+      // save group name
+      unsigned int strLength = groups[groupIndex].getName().size();
+      items = 1;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(&strLength, sizeof(unsigned int), 1, binaryOutputStream);
+      if (items != 1)
+        return 1;
+      bytes += items * sizeof(unsigned int);
+
+
+      items = strLength;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(groups[groupIndex].getName().c_str(), sizeof(char), strLength, binaryOutputStream);
+      if (items != strLength)
+        return 1;
+      bytes += items * sizeof(char);
+
+      // save the material index of the current group
+      if (outputMaterials)
+      {
+        unsigned int materialIndex = groups[groupIndex].getMaterialIndex();
+        items = 1;
+        if (pass == WRITE_TO_DISK)
+          items = fwrite(&materialIndex, sizeof(unsigned int), 1, binaryOutputStream);
+        if (items != 1)
+          return 1;
+        bytes += items * sizeof(unsigned int);
+      }
+
+      // save the number of faces of the current group
+      unsigned int numFaces = groups[groupIndex].getNumFaces();
+      items = 1;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(&numFaces, sizeof(unsigned int), 1, binaryOutputStream);
+      if (items != 1)
+        return 1;
+      bytes += items * sizeof(unsigned int);
+
+      // save the number of vertices of each face in current group
+      unsigned int totalFaceVertices = 0;
+      unsigned int * numFaceVerticesArray = (unsigned int *) malloc (sizeof(unsigned int) * numFaces);
+      for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
+      {
+        const Face & face = groups[groupIndex].getFace(faceIndex); // get face whose number is faceIndex
+        numFaceVerticesArray[faceIndex] = face.getNumVertices();
+        totalFaceVertices += numFaceVerticesArray[faceIndex];
+      }
+      items = numFaces;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(numFaceVerticesArray, sizeof(unsigned int), numFaces, binaryOutputStream);
+      if (items != numFaces)
+        return 1;
+      bytes += items * sizeof(unsigned int);
+      free(numFaceVerticesArray);
+
+      unsigned int * verticesArray = (unsigned int *) malloc (sizeof(unsigned int) * totalFaceVertices);
+      // because the output is 1-indexed, we can use 0 to represent "no such property"
+      unsigned int * textureCoordinateIndexArray = (unsigned *) malloc (sizeof(unsigned int) * totalFaceVertices);
+      unsigned int * normalIndexArray = (unsigned *) malloc (sizeof(unsigned int) * totalFaceVertices);
+      memset(textureCoordinateIndexArray, 0, sizeof(unsigned int) * totalFaceVertices);
+      memset(normalIndexArray, 0, sizeof(unsigned int) * totalFaceVertices);
+      unsigned int vertexCount = 0;
+      for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
+      {
+        const Face & face = groups[groupIndex].getFace(faceIndex); // get the face whose number is faceIndex
+        unsigned int numFaceVertices = face.getNumVertices();
+
+        // current face
+        for (unsigned int vertexIndex=0; vertexIndex < numFaceVertices; vertexIndex++)
+        {
+          Vertex vertex = face.getVertex(vertexIndex);
+          verticesArray[vertexCount] = vertex.getPositionIndex() + 1; // 1-indexed
+
+          if (vertex.hasTextureCoordinateIndex())
+            textureCoordinateIndexArray[vertexCount] = vertex.getTextureCoordinateIndex() + 1; // 1-indexed
+
+          if (vertex.hasNormalIndex())
+            normalIndexArray[vertexCount] = vertex.getNormalIndex() + 1; // 1-indexed
+
+          vertexCount++;
+        }  // for vertexIndex
+      }  // for faceIndex
+
+      // save the vertices of each face
+      items = totalFaceVertices;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(verticesArray, sizeof(unsigned int), totalFaceVertices, binaryOutputStream);
+      if (items != totalFaceVertices)
+        return 1;
+      bytes += items * sizeof(unsigned int);
+
+      items = totalFaceVertices;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(textureCoordinateIndexArray, sizeof(unsigned int), totalFaceVertices, binaryOutputStream);
+      if (items != totalFaceVertices)
+        return 1;
+      bytes += items * sizeof(unsigned int);
+
+      items = totalFaceVertices;
+      if (pass == WRITE_TO_DISK)
+        items = fwrite(normalIndexArray, sizeof(unsigned int), totalFaceVertices, binaryOutputStream);
+      if (items != totalFaceVertices)
+        return 1;
+      bytes += items * sizeof(unsigned int);
+
+      free(verticesArray);
+      free(textureCoordinateIndexArray);
+      free(normalIndexArray);
+
+    }  // for groupIndex
+
+    if (pass == COUNT_BYTES)
+      totalBytes = bytes;
+  } // for pass
+
+  if (bytesWritten != NULL)
+    *bytesWritten = totalBytes;
+  else
+    if (countBytesOnly)
+    {
+      printf("Warning in ObjMesh::saveToBinary: 'bytesWritten' is set to NULL while 'countBytesOnly' is set true.\n");
+      return 2;
+    }
+
+  return 0;
+}
+
+void ObjMesh::save(const string & filename, int outputMaterials, fileFormatType fileFormat, int verbose) const
+{
+  switch(fileFormat)
+  {
+    case ASCII:
+      saveToAscii(filename, outputMaterials, verbose);
+      break;
+
+    case BINARY:
+      saveToBinary(filename, outputMaterials, verbose);
+      break;
+
+    case BY_EXT:
+    {
+      if (iendWith(filename, ".objb"))
+        saveToBinary(filename, outputMaterials, verbose);
+      else if (iendWith(filename, ".obj"))
+        saveToAscii(filename, outputMaterials, verbose);
+      else
+      {
+        printf("Unknown file extension when saving %s, try ASCII format...\n", filename.c_str());
+        saveToAscii(filename, outputMaterials, verbose);
+      }
+    }
+    break;
+
+    default:
+      printf("Error in ObjMesh::save: file format is unknown.\n");
+      break;
+  }
+}
+
+void ObjMesh::saveToAscii(const string & filename, int outputMaterials, int verbose, int precision) const
 {
   string materialFilename;
   string materialFilenameLocal;
@@ -690,6 +1313,11 @@ void ObjMesh::save(const string & filename, int outputMaterials, int verbose) co
   fout << "# Number of faces: " << numTriangles << endl;
   fout << "# Number of groups: " << groups.size() << endl;
 
+  int maxPrecision = std::numeric_limits<long double>::digits10 + 1;
+  int pcs = (precision > maxPrecision ? maxPrecision : precision);
+  if (pcs > 0)
+    fout << setprecision(pcs);
+
   if (outputMaterials)
     fout << endl << "mtllib " << materialFilenameLocal << endl << endl;
 
@@ -697,21 +1325,21 @@ void ObjMesh::save(const string & filename, int outputMaterials, int verbose) co
   for (unsigned int i=0; i < vertexPositions.size(); i++)
   {
     Vec3d pos = getPosition(i);
-    fout << "v " << pos[0] << " " << pos[1] << " " << pos[2] << endl; 
+    fout << "v " << pos[0] << " " << pos[1] << " " << pos[2] << endl;
   }
 
   // texture coordinates...
   for (unsigned int i=0; i < textureCoordinates.size(); i++)
   {
     Vec3d texCoord_ = getTextureCoordinate(i);
-    fout << "vt " << texCoord_[0] << " " << texCoord_[1] << endl; 
+    fout << "vt " << texCoord_[0] << " " << texCoord_[1] << endl;
   }
 
   // normals...
   for (unsigned int i=0; i < normals.size(); i++)
   {
     Vec3d normal_ = getNormal(i);
-    fout << "vn " << normal_[0] << " " << normal_[1] << " " << normal_[2] << endl; 
+    fout << "vn " << normal_[0] << " " << normal_[1] << " " << normal_[2] << endl;
   }
 
   // groups and faces...
@@ -725,7 +1353,7 @@ void ObjMesh::save(const string & filename, int outputMaterials, int verbose) co
     {
       Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
-      fout << "f";   
+      fout << "f";
 
       if (face.getNumVertices() < 3)
         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
@@ -741,11 +1369,11 @@ void ObjMesh::save(const string & filename, int outputMaterials, int verbose) co
 
           if (vertex.hasTextureCoordinateIndex())
             fout << int(vertex.getTextureCoordinateIndex() + 1);
-    
+
           if (vertex.hasNormalIndex())
           {
             fout << "/";
-    
+
             if (vertex.hasNormalIndex())
               fout << int(vertex.getNormalIndex() + 1);
           }
@@ -756,7 +1384,7 @@ void ObjMesh::save(const string & filename, int outputMaterials, int verbose) co
     }
   }
 
-  fout.close(); 
+  fout.close();
 
   if (outputMaterials)
   {
@@ -802,7 +1430,7 @@ void ObjMesh::saveToAbq(const string & filename) const
   {
     cout << "Error: mesh has faces with more than 4 vertices." << endl;
     return;
-  } 
+  }
 
   vector<double> surfaceAreas ;
   computeSurfaceAreaPerGroup(surfaceAreas);
@@ -836,7 +1464,7 @@ void ObjMesh::saveToAbq(const string & filename) const
   for(unsigned int i = 0; i < groups.size(); i++ )
   {
     printf("Num faces in group %d: %d\n",i+1,(int)(groups[i].getNumFaces()));
- 
+
     if (groups[i].getNumFaces() == 0)
       continue;
 
@@ -868,14 +1496,14 @@ void ObjMesh::saveToAbq(const string & filename) const
           Vertex vertex = face.getVertex(iVertex);
           fprintf(fout,",%d",vertex.getPositionIndex() + 1);
         }
-                                                                                                                                                             
+
         fprintf(fout,"\n");
       }
     }
 
     endIndex.push_back(faceCount);
   }
-  
+
   for(unsigned int i=0; i<startIndex.size(); i++)
   {
     fprintf(fout,"*ELSET,ELSET=%s,GENERATE\n",groupNames[i].c_str());
@@ -1027,14 +1655,12 @@ ObjMesh * ObjMesh::splitIntoConnectedComponents(int withinGroupsOnly, int verbos
     for(unsigned int i=0; i < groups.size(); i++) // over all groups
     {
       DisjointSet * groupDisjointSet = new DisjointSet(getNumVertices());
-      groupDisjointSet->MakeSet();
       dset.push_back(groupDisjointSet);
     }
   }
   else
   {
     DisjointSet * globalDisjointSet = new DisjointSet(getNumVertices());
-    globalDisjointSet->MakeSet();
     for(unsigned int i=0; i < groups.size(); i++) // over all groups
       dset.push_back(globalDisjointSet);
   }
@@ -1042,18 +1668,18 @@ ObjMesh * ObjMesh::splitIntoConnectedComponents(int withinGroupsOnly, int verbos
   // build vertex connections
   for(unsigned int i=0; i < groups.size(); i++) // over all groups
   {
-    if (verbose == 0)
-    {
-      printf("%d ", i);
-      fflush(NULL);
-    }
+    // if (verbose == 0)
+    // {
+    //   printf("%d ", i);
+    //   fflush(NULL);
+    // }
 
     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
     {
       if (verbose)
       {
         if (j % 100 == 1)
-          printf("Processing group %d / %d, face %d / %d.\n", i, (int)groups.size(), j, (int)groups[i].getNumFaces()); 
+          printf("Processing group %d / %d, face %d / %d.\n", i, (int)groups.size(), j, (int)groups[i].getNumFaces());
       }
       const Face * face = groups[i].getFaceHandle(j);
       int faceDegree = (int)face->getNumVertices();
@@ -1061,12 +1687,12 @@ ObjMesh * ObjMesh::splitIntoConnectedComponents(int withinGroupsOnly, int verbos
       {
         int vertex = face->getVertex(vtx).getPositionIndex();
         int vertexNext = face->getVertex(vtx+1).getPositionIndex();
-        dset[i]->UnionSet(vertex, vertexNext);
+        dset[i]->unionSet(vertex, vertexNext);
       }
     }
   }
-  if (verbose == 0)
-    printf("\n");
+  // if (verbose == 0)
+  //   printf("\n");
 
   // determine group for every face
   int numOutputGroups = 0;
@@ -1089,11 +1715,11 @@ ObjMesh * ObjMesh::splitIntoConnectedComponents(int withinGroupsOnly, int verbos
   vector<vector<int> > faceGroup;
   for(unsigned int i=0; i < groups.size(); i++) // over all groups
   {
-    if (verbose == 0)
-    {
-      printf("%d ", i);
-      fflush(NULL);
-    }
+    // if (verbose == 0)
+    // {
+    //   printf("%d ", i);
+    //   fflush(NULL);
+    // }
 
     faceGroup.push_back(vector<int>());
     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
@@ -1101,10 +1727,10 @@ ObjMesh * ObjMesh::splitIntoConnectedComponents(int withinGroupsOnly, int verbos
       if (verbose)
       {
         if (j % 100 == 1)
-          printf("Processing group %d / %d, face %d / %d.\n", i, (int)groups.size(), j, (int)groups[i].getNumFaces()); 
+          printf("Processing group %d / %d, face %d / %d.\n", i, (int)groups.size(), j, (int)groups[i].getNumFaces());
       }
       const Face * face = groups[i].getFaceHandle(j);
-      int rep = dset[i]->FindSet(face->getVertex(0).getPositionIndex());
+      int rep = dset[i]->findSet(face->getVertex(0).getPositionIndex());
 
       map<int,int> :: iterator iter = representatives[i]->find(rep);
       int groupID;
@@ -1121,8 +1747,8 @@ ObjMesh * ObjMesh::splitIntoConnectedComponents(int withinGroupsOnly, int verbos
     }
   }
 
-  if (verbose == 0)
-    printf("\n");
+  // if (verbose == 0)
+  //   printf("\n");
 
   // build output mesh
   ObjMesh * output = new ObjMesh();
@@ -1336,12 +1962,19 @@ ObjMesh * ObjMesh::extractGroup(unsigned int groupID, int keepOnlyUsedNormals, i
   output->addGroup(s);
 
   // set material for the extracted group
-  unsigned int newGroupIndex = output->groups.size() - 1;
-  if (newGroupIndex < 0)
+  //unsigned int newGroupIndex = output->groups.size() - 1;
+  //if (newGroupIndex < 0)
+  //{
+  //  printf("Error: failed to add a new group to the mesh.\n");
+  //  exit(0);
+  //}
+  if (output->groups.size() == 0)
   {
     printf("Error: failed to add a new group to the mesh.\n");
     exit(0);
   }
+  unsigned int newGroupIndex = output->groups.size() - 1;
+
   output->groups[newGroupIndex].setMaterialIndex(groups[groupID].getMaterialIndex());
 
   // add faces to the group
@@ -1358,7 +1991,7 @@ ObjMesh * ObjMesh::extractGroup(unsigned int groupID, int keepOnlyUsedNormals, i
       newVertex.setPositionIndex(newVertexIndex);
 
       if (newVertex.hasNormalIndex() && keepOnlyUsedNormals)
-      { 
+      {
         int oldNormalIndex = face->getVertex(vtx).getNormalIndex();
         int newNormalIndex = oldToNewNormals[oldNormalIndex];
         newVertex.setNormalIndex(newNormalIndex);
@@ -1371,7 +2004,7 @@ ObjMesh * ObjMesh::extractGroup(unsigned int groupID, int keepOnlyUsedNormals, i
         newVertex.setTextureCoordinateIndex(newTextureCoordinateIndex);
       }
 
-      newFace.addVertex(newVertex);      
+      newFace.addVertex(newVertex);
     }
     output->addFaceToGroup(newFace, 0);
   }
@@ -1380,7 +2013,7 @@ ObjMesh * ObjMesh::extractGroup(unsigned int groupID, int keepOnlyUsedNormals, i
   return output;
 }
 
-double ObjMesh::computeTriangleSurfaceArea(Vec3d & p0, Vec3d & p1, Vec3d & p2)
+double ObjMesh::computeTriangleSurfaceArea(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2)
 {
   return 0.5 * (len(cross(p1-p0, p2-p0)));
 }
@@ -1389,7 +2022,7 @@ void ObjMesh::computeCentroids(std::vector<Vec3d> & centroids) const
 {
   interpolateToCentroids(vertexPositions, centroids);
 }
-                                                                                                                                                             
+
 void ObjMesh::interpolateToCentroids(const std::vector<double> & nodalData, std::vector<double> & centroidData) const
 {
   int faceIndex = 0;
@@ -1491,11 +2124,11 @@ void ObjMesh::transformRigidly(const Vec3d & translation, const Mat3d & rotation
   computeBoundingBox();
 }
 
-void ObjMesh::deform(double * u)
+void ObjMesh::deform(const double * u)
 {
   for (unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
     vertexPositions[i] += Vec3d(&u[3*i]);
-  
+
   computeBoundingBox();
 }
 
@@ -1510,39 +2143,41 @@ double ObjMesh::computeVolume() const
     {
       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
-      if (face.getNumVertices() != 3)
-        cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") which is not a triangle." << endl;
-
-      // base vertex
-      Vec3d v0 = getPosition(face.getVertex(0));
-      Vec3d v1 = getPosition(face.getVertex(1));
-      Vec3d v2 = getPosition(face.getVertex(2));
-
-      Vec3d normal = cross(v1-v0, v2-v0);
-      Vec3d center = 1.0 / 3 * (v0 + v1 + v2);
+      for (unsigned int iVertex = 0; iVertex < face.getNumVertices() - 2; iVertex++)
+      {
+        // base vertex
+        Vec3d v0 = getPosition(face.getVertex(0));
+        Vec3d v1 = getPosition(face.getVertex(iVertex+1));
+        Vec3d v2 = getPosition(face.getVertex(iVertex+2));
 
-      volume += dot(normal,center);
+        Vec3d normal = cross(v1-v0, v2-v0);
+        Vec3d center = 1.0 / 3 * (v0 + v1 + v2);
 
+        volume += dot(normal, center);
+      }
     }
   }
-  
+
   volume /= 6.0;
-  
+
   return volume;
 }
 
 Vec3d ObjMesh::computeCenterOfMass_Vertices() const
 {
   Vec3d center(0,0,0);
-  for (unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
-    center += vertexPositions[i];
-  center /= vertexPositions.size();
+  if (vertexPositions.size() > 0)
+  {
+    for (unsigned int i=0; i < vertexPositions.size(); i++) // over all vertices
+      center += vertexPositions[i];
+    center /= vertexPositions.size();
+  }
   return center;
 }
 
 Vec3d ObjMesh::computeCenterOfMass_Triangles(const vector<double> & groupDensities) const
 {
-  Vec3d centerOfMass = 0.0;
+  Vec3d centerOfMass(0.0);
   double totalMass=0.0;
   // over all faces
   for(unsigned int i = 0; i < groups.size(); i++ )
@@ -1555,7 +2190,7 @@ Vec3d ObjMesh::computeCenterOfMass_Triangles(const vector<double> & groupDensiti
       double area = computeFaceSurfaceArea(face);
       double mass = density * area;
       totalMass += mass;
-      Vec3d centroid = computeFaceCentroid(face);       
+      Vec3d centroid = computeFaceCentroid(face);
       centerOfMass += mass * centroid;
     }
   }
@@ -1604,13 +2239,13 @@ double ObjMesh::computeMass(const vector<double> & groupDensities) const
       totalMass += mass;
     }
   }
-  
+
   return totalMass;
 }
 
 void ObjMesh::computeInertiaTensor_Triangles(const vector<double> & groupDensities, double IT[6]) const
 {
-  Vec3d centerOfMass = 0.0;
+  Vec3d centerOfMass(0.0);
   memset(IT, 0, sizeof(double) * 6);
   double totalMass=0.0;
 
@@ -1631,10 +2266,10 @@ void ObjMesh::computeInertiaTensor_Triangles(const vector<double> & groupDensiti
       double area = computeFaceSurfaceArea(face);
       double mass = density * area;
       totalMass += mass;
-      Vec3d centroid = computeFaceCentroid(face);       
+      Vec3d centroid = computeFaceCentroid(face);
       centerOfMass += mass * centroid;
 
-      Vec3d v0 = getPosition(face.getVertex(0)); 
+      Vec3d v0 = getPosition(face.getVertex(0));
       for (unsigned int iVertex = 1; iVertex < face.getNumVertices()-1; iVertex++ )
       {
         Vec3d v1 = getPosition(face.getVertex(iVertex));
@@ -1648,20 +2283,20 @@ void ObjMesh::computeInertiaTensor_Triangles(const vector<double> & groupDensiti
       }
     }
   }
-  
+
   centerOfMass /= totalMass;
 
-  // IT is now the center around the origin  
+  // IT is now the center around the origin
   // transfer tensor to the center of mass
   double a = centerOfMass[0];
   double b = centerOfMass[1];
   double c = centerOfMass[2];
 
-  double correction[6] = 
+  double correction[6] =
        { b*b + c*c, -a*b, -a*c,
                a*a + c*c, -b*c,
                      a*a + b*b };
-                                                                                                                                                             
+
   for(int i=0; i<6; i++)
     IT[i] -= totalMass * correction[i];
 
@@ -1669,7 +2304,7 @@ void ObjMesh::computeInertiaTensor_Triangles(const vector<double> & groupDensiti
 
 Vec3d ObjMesh::computeCenterOfMass_Triangles() const
 {
-  Vec3d centerOfMass = 0.0;
+  Vec3d centerOfMass(0.0);
 
   double totalArea=0.0;
   // over all faces
@@ -1681,13 +2316,13 @@ Vec3d ObjMesh::computeCenterOfMass_Triangles() const
 
       double area = computeFaceSurfaceArea(face);
       totalArea += area;
-      Vec3d centroid = computeFaceCentroid(face);       
+      Vec3d centroid = computeFaceCentroid(face);
       centerOfMass += area * centroid;
     }
   }
-  
+
   centerOfMass /= totalArea;
-  
+
   return centerOfMass;
 }
 
@@ -1720,7 +2355,7 @@ double ObjMesh::computeSurfaceArea() const
       area += computeFaceSurfaceArea(face);
     }
   }
-  
+
   return area;
 }
 
@@ -1765,45 +2400,50 @@ void ObjMesh::computeMassPerVertex(const vector<double> & groupSurfaceMassDensit
 // warning: normal is computed using the first three face vertices (assumes planar face)
 Vec3d ObjMesh::computeFaceNormal(const Face & face) const
 {
+  if (face.getNumVertices() < 3)
+    return Vec3d(0.0, 0.0, 0.0); // return an invalid normal
+
   // the three vertices
   Vec3d pos0 = getPosition(face.getVertex(0));
   Vec3d pos1 = getPosition(face.getVertex(1));
   Vec3d pos2 = getPosition(face.getVertex(2));
   Vec3d normal = norm(cross(pos1 - pos0, pos2 - pos0));
 
-  if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]))
-  {
-    //degenerate geometry; return an arbitrary normal
-    normal = Vec3d(1.0, 0.0, 0.0);
-  }
+  if (normal.hasNaN()) // //degenerate geometry; return an invalid normal
+    normal = Vec3d(0.0, 0.0, 0.0);
 
   return normal;
 }
 
-void ObjMesh::buildFaceNormals()
+void ObjMesh::buildFaceNormals(int verbose)
 {
-  for(unsigned int i = 0; i < groups.size(); i++)
-  {
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
+  for(size_t i = 0; i < groups.size(); i++)
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
     {
-      ObjMesh::Face * faceHandle = (Face*) groups[i].getFaceHandle(iFace); // get face whose number is iFace
-
-      if (faceHandle->getNumVertices() < 3)
+      ObjMesh::Face & face = groups[i].getFace(iFace);
+      if (verbose && face.getNumVertices() < 3)
         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
-      Vec3d normal = computeFaceNormal(*faceHandle);
-      faceHandle->setFaceNormal(normal);
+      Vec3d normal = computeFaceNormal(face);
+      if (verbose && normal == Vec3d(0.0))
+        cout << "Warning: encountered a degenerate face (group=" << i << ",face=" << iFace << ")." << endl;
+      face.setFaceNormal(normal);
     }
-  }
+}
+
+void ObjMesh::clearFaceNormals()
+{
+  for(size_t i = 0; i < groups.size(); i++)
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
+      groups[i].getFaceHandle(iFace)->removeFaceNormal();
 }
 
 void ObjMesh::setNormalsToFaceNormals()
 {
   // over all faces
   normals.clear();
-  for(unsigned int i=0; i < groups.size(); i++)
-  {
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
+  for(size_t i=0; i < groups.size(); i++)
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
     {
       const ObjMesh::Face * faceHandle = groups[i].getFaceHandle(iFace); // get face whose number is iFace
 
@@ -1822,7 +2462,6 @@ void ObjMesh::setNormalsToFaceNormals()
         vertex->setNormalIndex(getNumNormals() - 1);
       }
     }
-  }
 }
 
 void ObjMesh::setNormalsToAverageFaceNormals()
@@ -1831,9 +2470,9 @@ void ObjMesh::setNormalsToAverageFaceNormals()
   vector<unsigned int> normalCount(getNumVertices(),0);
 
   // over all faces
-  for(unsigned int i = 0; i < groups.size(); i++ )
+  for(size_t i = 0; i < groups.size(); i++ )
   {
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
     {
       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
@@ -1852,20 +2491,26 @@ void ObjMesh::setNormalsToAverageFaceNormals()
       Vec3d normal = norm(cross(pos1-pos0,pos2-pos0));
       // this works even for non-triangle meshes
 
-      normalBuffer[index0] += normal;
-      normalBuffer[index1] += normal;
-      normalBuffer[index2] += normal;
-
-      normalCount[index0]++;
-      normalCount[index1]++;
-      normalCount[index2]++;
+//      normalBuffer[index0] += normal;
+//      normalBuffer[index1] += normal;
+//      normalBuffer[index2] += normal;
+//
+//      normalCount[index0]++;
+//      normalCount[index1]++;
+//      normalCount[index2]++;
 
+      for (unsigned k=0; k <face.getNumVertices(); k++)
+      {
+        unsigned int index = face.getVertex(k).getPositionIndex();
+        normalBuffer[index] += normal;
+        normalCount[index]++;
+      }
     }
   }
 
   bool errorMessageSeen=false;
   // normalize the normals
-  for (unsigned int i=0; i < getNumVertices(); i++)
+  for (size_t i=0; i < getNumVertices(); i++)
   {
     if (normalCount[i] == 0)
     {
@@ -1880,13 +2525,13 @@ void ObjMesh::setNormalsToAverageFaceNormals()
 
   // register new normals with the objMesh data structure
   normals.clear();
-  for (unsigned int i=0; i < getNumVertices(); i++)
-    addVertexNormal(normalBuffer[i]);     
+  for (size_t i=0; i < getNumVertices(); i++)
+    addVertexNormal(normalBuffer[i]);
 
-  for(unsigned int i = 0; i < groups.size(); i++ )
+  for(size_t i = 0; i < groups.size(); i++ )
   {
     Group * group = &(groups[i]);
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
     {
       const Face * face = group->getFaceHandle(iFace);
 
@@ -1914,7 +2559,7 @@ unsigned int ObjMesh::getClosestVertex(const Vec3d & queryPos, double * distance
     {
       closestDist2 = candidateDist2;
       indexClosest = i;
-    } 
+    }
   }
 
   if (distance != NULL)
@@ -1934,7 +2579,7 @@ double ObjMesh::computeMinEdgeLength() const
       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
       if (face.getNumVertices() < 3)
-	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
+        cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
       for (unsigned k=0; k<face.getNumVertices(); k++)
       {
@@ -1949,7 +2594,7 @@ double ObjMesh::computeMinEdgeLength() const
       }
     }
   }
-  
+
   return minLength;
 
 }
@@ -1967,7 +2612,7 @@ double ObjMesh::computeAverageEdgeLength() const
       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
       if (face.getNumVertices() < 3)
-	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
+        cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
       for (unsigned k=0; k<face.getNumVertices(); k++)
       {
@@ -1979,7 +2624,7 @@ double ObjMesh::computeAverageEdgeLength() const
       }
     }
   }
-  
+
   return totalLength/numEdges;
 }
 
@@ -1995,7 +2640,7 @@ double ObjMesh::computeMedianEdgeLength() const
       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
       if (face.getNumVertices() < 3)
-	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
+        cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
       for (unsigned k=0; k<face.getNumVertices(); k++)
       {
@@ -2006,7 +2651,7 @@ double ObjMesh::computeMedianEdgeLength() const
       }
     }
   }
-  
+
   sort(lengths.begin(), lengths.end());
 
   return lengths[lengths.size() / 2];
@@ -2024,7 +2669,7 @@ double ObjMesh::computeMaxEdgeLength() const
       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
       if (face.getNumVertices() < 3)
-	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
+        cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
       for (unsigned k=0; k<face.getNumVertices(); k++)
       {
@@ -2036,7 +2681,7 @@ double ObjMesh::computeMaxEdgeLength() const
       }
     }
   }
-  
+
   return maxLength;
 }
 
@@ -2054,7 +2699,7 @@ double ObjMesh::computeMinEdgeLength(int * vtxa, int * vtxb) const
       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
       if (face.getNumVertices() < 3)
-	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
+        cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
       for (unsigned k=0; k<face.getNumVertices(); k++)
       {
@@ -2067,13 +2712,13 @@ double ObjMesh::computeMinEdgeLength(int * vtxa, int * vtxb) const
           minLength = length;
         else if (length < minLength)
           minLength = length;
-        
+
         *vtxa = face.getVertex(k).getPositionIndex();
         *vtxb = face.getVertex((k+1) % face.getNumVertices()).getPositionIndex();
       }
     }
   }
-  
+
   return minLength;
 }
 
@@ -2091,7 +2736,7 @@ double ObjMesh::computeMaxEdgeLength(int * vtxa, int * vtxb) const
       ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
 
       if (face.getNumVertices() < 3)
-	cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
+        cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
       for (unsigned k=0; k<face.getNumVertices(); k++)
       {
@@ -2107,7 +2752,7 @@ double ObjMesh::computeMaxEdgeLength(int * vtxa, int * vtxb) const
       }
     }
   }
-  
+
   return maxLength;
 }
 
@@ -2115,29 +2760,24 @@ unsigned int ObjMesh::getNumFaces() const
 {
   unsigned int counter = 0;
   for (unsigned int i=0; i < groups.size(); i++)
-    counter += groups[i].getNumFaces();    
+    counter += groups[i].getNumFaces();
 
   return counter;
-}  
+}
 
 void ObjMesh::setNormalsToPseudoNormals()
 {
-  // nuke any previous normals
-  normals.clear();
+  // nuke any previous normals and registers pseudonormals as the new normals
+  if (pseudoNormals.size() < getNumVertices())
+    computePseudoNormals();
 
-  // registers pseudonormals as the new normals
-  for(unsigned int i=0; i < getNumVertices(); i++)
-  {
-    normals.push_back(pseudoNormals[i]);
-  }
+  normals.assign(pseudoNormals.begin(), pseudoNormals.end());
 
   // over all faces
   for(unsigned int i = 0; i < groups.size(); i++ )
-  {
     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
     {
       const ObjMesh::Face * face = groups[i].getFaceHandle(iFace); // get face whose number is iFace
-
       // over all vertices of the face
       for (unsigned k=0; k<face->getNumVertices(); k++)
       {
@@ -2145,60 +2785,52 @@ void ObjMesh::setNormalsToPseudoNormals()
         vertex->setNormalIndex(vertex->getPositionIndex());
       }
     }
-  }
 }
 
 void ObjMesh::buildVertexNormals(double angle)
 {
   if (vertexFaceNeighbors.size() == 0)
-  {
-    printf("Error: buildVertexNormals() failed because vertex face neighbors were not built prior to calling buildVertexNormals(). Call \"buildVertexFaceNeighbors\".\n");
-    return;
-  }
+    buildVertexFaceNeighbors();  // call buildVertexFaceNeighbors to get vertexFaceNeighbors data
 
   double cosang = cos(angle * M_PI / 180.0);
 
   normals.clear();
   int averageIndex = 0;
 
-  for(unsigned int i = 0; i < getNumVertices(); i++)
+  for(size_t i = 0; i < getNumVertices(); i++)
   {
-    if (vertexFaceNeighbors[i].size() == 0)
-    {
-      //silly lonely vertex
+    if (vertexFaceNeighbors[i].size() == 0) //silly lonely vertex
       continue;
-    }
-    const Face * firstFace = getGroupHandle(vertexFaceNeighbors[i].begin()->getGroupIndex())->getFaceHandle(vertexFaceNeighbors[i].begin()->getFaceIndex());
-    if (!firstFace->hasFaceNormal())
-    {
-      printf("Warning: face normals not computed\n");
-      return;
-    }
+
+    const auto & firstFaceNeighbor = vertexFaceNeighbors[i][0];
+
+    const Face * firstFace = getGroupHandle(firstFaceNeighbor.groupIndex)->getFaceHandle(firstFaceNeighbor.faceIndex);
+    if (!firstFace->hasFaceNormal()) // if no face normals computed
+      buildFaceNormals(); // call buildFaceNormals to get Face::faceNormal data
+
     Vec3d firstNorm = firstFace->getFaceNormal();
 
-    Vec3d average = Vec3d(0.0);
+    Vec3d average(0.0);
     bool averagedAnything = false;
 
     //find which faces contribute
-    for(std::list<VertexFaceNeighbor>::iterator iter = vertexFaceNeighbors[i].begin(); iter != vertexFaceNeighbors[i].end(); iter++)
+    for(auto & faceNeighbor : vertexFaceNeighbors[i])
     {
-      //get angle
-      const Face * currentFace = getGroupHandle(iter->getGroupIndex())->getFaceHandle(iter->getFaceIndex());
-      if (!currentFace->hasFaceNormal())
-      {
-        printf("Warning: face normals not computed\n");
-        return;
-      }
+      //get face
+      const Face * currentFace = getGroupHandle(faceNeighbor.groupIndex)->getFaceHandle(faceNeighbor.faceIndex);
+
+      if (!currentFace->hasFaceNormal()) // if no face normals computed
+        buildFaceNormals(); // call buildFaceNormals to get Face::faceNormal data
       //dot product
       if (dot(firstNorm, currentFace->getFaceNormal()) > cosang)
       {
         //is good, so contribute to average
         average += currentFace->getFaceNormal();
-        iter->setAveraged(true);
+        faceNeighbor.averaged = true;
         averagedAnything = true;
       }
       else
-        iter->setAveraged(false);
+        faceNeighbor.averaged = false;
     }
 
     if (averagedAnything)
@@ -2208,20 +2840,20 @@ void ObjMesh::buildVertexNormals(double angle)
     }
 
     //determine consequences for associated vertices in each face
-    for(std::list<VertexFaceNeighbor>::iterator iter = vertexFaceNeighbors[i].begin(); iter != vertexFaceNeighbors[i].end(); iter++)
+    for(const auto & faceNeighbor : vertexFaceNeighbors[i])
     {
-      const Face * currentFace = getGroupHandle(iter->getGroupIndex())->getFaceHandle(iter->getFaceIndex());
-      if (iter->getAveraged())
+      const Face * currentFace = getGroupHandle(faceNeighbor.groupIndex)->getFaceHandle(faceNeighbor.faceIndex);
+      if (faceNeighbor.averaged)
       {
         //use average for normal
-        Vertex * vertex = (Vertex*) currentFace->getVertexHandle(iter->getFaceVertexIndex());
+        Vertex * vertex = (Vertex*) currentFace->getVertexHandle(faceNeighbor.faceVertexIndex);
         vertex->setNormalIndex(averageIndex);
       }
       else
       {
         //use face normal for normal
         normals.push_back(currentFace->getFaceNormal());
-        Vertex * vertex = (Vertex*) currentFace->getVertexHandle(iter->getFaceVertexIndex());
+        Vertex * vertex = (Vertex*) currentFace->getVertexHandle(faceNeighbor.faceVertexIndex);
         vertex->setNormalIndex(normals.size() - 1);
       }
     }
@@ -2231,41 +2863,38 @@ void ObjMesh::buildVertexNormals(double angle)
 void ObjMesh::buildVertexNormalsFancy(double angle)
 {
   if (vertexFaceNeighbors.size() == 0)
-  {
-    printf("Error: buildVertexNormalsFancy() failed because vertex face neighbors were not built prior to calling buildVertexNormalsFancy(). Call \"buildVertexFaceNeighbors\".\n");
-    return;
-  }
+    buildVertexFaceNeighbors();  // call buildVertexFaceNeighbors to get vertexFaceNeighbors data
 
   double cosang = cos(angle * M_PI / 180.0);
 
   normals.clear();
 
-  for(unsigned int i = 0; i < groups.size(); i++ )
+  for(size_t i = 0; i < groups.size(); i++ )
   {
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
     {
       const ObjMesh::Face * faceHandle = groups[i].getFaceHandle(iFace); // get face whose number is iFace
 
       if (faceHandle->getNumVertices() < 3)
         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
-      if (!faceHandle->hasFaceNormal())
-        printf("Warning: face has no face normal\n");
+      if (!faceHandle->hasFaceNormal()) // if no face normals computed
+        buildFaceNormals(); // call buildFaceNormals to get Face::faceNormal data
 
       Vec3d faceNorm = faceHandle->getFaceNormal();
 
-      for(unsigned int j = 0; j < faceHandle->getNumVertices(); j++)
+      for(size_t j = 0; j < faceHandle->getNumVertices(); j++)
       {
         //process the neighbors of this vertex
         int vertexIndex = faceHandle->getVertexHandle(j)->getPositionIndex();
         Vec3d newNorm(0.0);
         bool averagedAnything = false;
 
-        for(std::list<VertexFaceNeighbor>::iterator iter = vertexFaceNeighbors[vertexIndex].begin(); iter != vertexFaceNeighbors[vertexIndex].end(); iter++)
+        for(const auto & faceNeighbor : vertexFaceNeighbors[vertexIndex])
         {
-          const Face * neighborFaceHandle = getGroupHandle(iter->getGroupIndex())->getFaceHandle(iter->getFaceIndex());
+          const Face * neighborFaceHandle = getGroupHandle(faceNeighbor.groupIndex)->getFaceHandle(faceNeighbor.faceIndex);
           if (!neighborFaceHandle->hasFaceNormal())
-            printf("Warning: face has no face normal\n");
+            buildFaceNormals();
 
           if (dot(faceNorm, neighborFaceHandle->getFaceNormal()) > cosang)
           {
@@ -2276,7 +2905,7 @@ void ObjMesh::buildVertexNormalsFancy(double angle)
         if (!averagedAnything)
         {
           printf("error in mesh neighbor structure\n");
-          newNorm = Vec3d(1.0);
+          newNorm = Vec3d(1.0, 0.0, 0.0);
         }
         normals.push_back(norm(newNorm));
         Vertex * vertex = (Vertex*) faceHandle->getVertexHandle(j);
@@ -2288,7 +2917,7 @@ void ObjMesh::buildVertexNormalsFancy(double angle)
 
 void ObjMesh::setMaterialAlpha(double alpha)
 {
-  for(unsigned int i = 0; i < materials.size(); i++)
+  for(size_t i = 0; i < materials.size(); i++)
     materials[i].setAlpha(alpha);
 }
 
@@ -2303,23 +2932,21 @@ void ObjMesh::setSingleMaterial(const Material & material)
 void ObjMesh::computePseudoNormals()
 {
   vector<int> vertexDegree(getNumVertices());
-  for(unsigned int i=0; i<getNumVertices(); i++)
-    pseudoNormals.push_back(0.0);
+  pseudoNormals.assign(getNumVertices(), Vec3d(0.0));
 
   // over all faces
-  for(unsigned int i = 0; i < groups.size(); i++ )
-  {
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
+  for(size_t i = 0; i < groups.size(); i++)
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
     {
-      ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
+      const ObjMesh::Face & face = groups[i].getFace(iFace); // get face whose number is iFace
 
       // over all vertices
-      for (unsigned k=0; k<face.getNumVertices(); k++)
+      for (size_t k=0; k<face.getNumVertices(); k++)
       {
         // compute angle at that vertex in radians
         Vec3d pos = getPosition(face.getVertex(k));
         Vec3d posNext, posPrev;
-        if (k != face.getNumVertices() - 1)
+        if (k+1 != face.getNumVertices())
           posNext = getPosition(face.getVertex(k + 1));
         else
           posNext = getPosition(face.getVertex(0));
@@ -2335,11 +2962,11 @@ void ObjMesh::computePseudoNormals()
         double angle = acos(dot(posNext-pos,posPrev-pos)/lenNext/lenPrev);
         Vec3d normal = norm(cross(posNext-pos, posPrev-pos));
 
-        if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]))
+        if (normal.hasNaN())
         {
           cout << "Error (when computing vertex pseudonormals): NaN encountered (face with zero surface area)." << endl;
           cout << "Group: " << i << " Face: " << iFace << " " << endl;
-          normal[0] = 0; normal[1] = 0; normal[2] = 0;
+          normal = Vec3d(0.0);
           //cout << "  vtx0: " << index0 << " vtx1: " << index1 << " vtx2: " << index2 << endl;
           //cout << "  "  << p0 << endl;
           //cout << "  "  << p1 << endl;
@@ -2348,12 +2975,12 @@ void ObjMesh::computePseudoNormals()
         }
         else
         {
-          if ((lenNext == 0) || (lenPrev == 0) || isNaN(angle))
+          if ((lenNext == 0) || (lenPrev == 0) || Vec3d::isNaN(angle))
           {
             cout << "Warning (when computing vertex pseudonormals): encountered zero-length edge" << endl;
             cout << "  lenNext: " << lenNext << " lenPrev: " << lenPrev << " angle: " << angle << endl;
           }
-          else  
+          else
           {
             pseudoNormals[face.getVertex(k).getPositionIndex()] += angle * normal;
             vertexDegree[face.getVertex(k).getPositionIndex()]++;
@@ -2361,26 +2988,25 @@ void ObjMesh::computePseudoNormals()
         }
       }
     }
-  }
 
-  for(unsigned int i=0; i<getNumVertices(); i++)
+  for(size_t i=0; i<getNumVertices(); i++)
   {
     if (vertexDegree[i] != 0)
     {
       Vec3d pseudoNormalRaw = pseudoNormals[i];
       pseudoNormals[i] = norm(pseudoNormalRaw);
-      if (isNaN(pseudoNormals[i][0]) || isNaN(pseudoNormals[i][1]) || isNaN(pseudoNormals[i][2]))
+      if (pseudoNormals[i].hasNaN())
       {
         cout << "Error (when computing vertex pseudonormals): NaN encountered." << endl;
         cout << "Vertex: " << i << " pseudoNormal=" << pseudoNormals[i][0] << " " << pseudoNormals[i][1] << " " << pseudoNormals[i][2] << endl;
         cout << "  Pseudonormal before normalization=";
-        printf("%G %G %G\n", pseudoNormalRaw[0], pseudoNormalRaw[1], pseudoNormalRaw[2]); 
+        printf("%G %G %G\n", pseudoNormalRaw[0], pseudoNormalRaw[1], pseudoNormalRaw[2]);
         cout << "  Vertex degree=" << vertexDegree[i] << endl;
-        pseudoNormals[i] = 0.0;
+        pseudoNormals[i] = Vec3d(0.0);
       }
     }
     else
-      pseudoNormals[i] = 0.0;
+      pseudoNormals[i] = Vec3d(0.0);
   }
 }
 
@@ -2389,29 +3015,32 @@ void ObjMesh::computeEdgePseudoNormals()
   edgePseudoNormals.clear();
 
   // over all faces
-  for(unsigned int i = 0; i < groups.size(); i++ )
-  {
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
+  for(size_t i = 0; i < groups.size(); i++)
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++)
     {
-      ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
+      const ObjMesh::Face & face = groups[i].getFace(iFace); // get face whose number is iFace
+      if (face.getNumVertices() < 3)
+      {
+        cout << "Warning (while computing edge pseudonormals): face with fewer than three vertices encountered." << endl;
+        continue;
+      }
       Vec3d pos0 = getPosition(face.getVertex(0));
       Vec3d pos1 = getPosition(face.getVertex(1));
       Vec3d pos2 = getPosition(face.getVertex(2));
       Vec3d normal = norm(cross(pos1-pos0, pos2-pos0));
 
-      if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]))
+      if (normal.hasNaN())
       {
-        cout << "Error: nan encountered (face with zero surface area)." << endl;
+        cout << "Error: nan encountered (face with zero surface area) when computing edge pseudo normals." << endl;
         cout << "Group: " << i << " Face: " << iFace << " " << endl;
         exit(1);
       }
 
       // over all edges at the face
-      for (unsigned k=0; k<face.getNumVertices(); k++)
+      for (size_t k=0; k<face.getNumVertices(); k++)
       {
-
-        unsigned int startVertex = face.getVertex(k).getPositionIndex();
-        unsigned int endVertex = face.getVertex( (k+1) % face.getNumVertices()).getPositionIndex();
+        unsigned int startVertex = face.getVertexPositionIndex(k);
+        unsigned int endVertex = face.getVertexPositionIndex((k+1) % face.getNumVertices());
 
         pair<unsigned int, unsigned int> edge;
         if (startVertex < endVertex)
@@ -2426,71 +3055,324 @@ void ObjMesh::computeEdgePseudoNormals()
         }
 
         map< pair<unsigned int, unsigned int>, Vec3d > :: iterator iter = edgePseudoNormals.find(edge);
-        
+
         if (iter == edgePseudoNormals.end())
-        {
           edgePseudoNormals.insert(make_pair(edge,normal));
-        }
         else
-        {
           iter->second += normal;
-        }
       }
     }
-  }
 
   // normalize normals
   map< pair<unsigned int, unsigned int>, Vec3d > :: iterator iter;
   for(iter = edgePseudoNormals.begin(); iter != edgePseudoNormals.end(); ++iter)
   {
     Vec3d normal = norm(iter->second);
-    if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]))
+    if (normal.hasNaN())
     {
       cout << "Warning (while computing edge pseudonormals): NaN encountered (face with zero surface area)." << endl;
-      normal[0] = 1; normal[1] = 0; normal[2] = 0;
+      normal = Vec3d(0.0);
     }
     iter->second = normal;
   }
 }
 
-int ObjMesh::removeZeroAreaFaces()
+int ObjMesh::removeZeroAreaFaces(int verbose)
 {
   int numZeroAreaFaces = 0;
-
   // over all faces
-  for(unsigned int i = 0; i < groups.size(); i++ )
+  for(size_t i = 0; i < groups.size(); i++ )
   {
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
+    auto newEnd = remove_if(groups[i].faces.begin(), groups[i].faces.end(), [&](const ObjMesh::Face & face)
     {
-      ObjMesh::Face face = groups[i].getFace(iFace); // get face whose number is iFace
+      size_t iFace = distance(const_cast<const ObjMesh::Face*>(groups[i].faces.data()), &face);
+      if ((verbose == 1) && (iFace % 100 == 0))
+      {
+       printf("Processing face %lu in group %lu...\n", iFace, i);
+       fflush(NULL);
+      }
+
+      bool identicalVertex = false;
+
       Vec3d pos0 = getPosition(face.getVertex(0));
       Vec3d pos1 = getPosition(face.getVertex(1));
       Vec3d pos2 = getPosition(face.getVertex(2));
-      Vec3d normal = norm(cross(pos1-pos0, pos2-pos0));
+      double norm2 = len2(cross(pos1-pos0, pos2-pos0));
+
+      for(size_t jj=0; jj< face.getNumVertices(); jj++)
+        for(size_t kk=jj+1; kk< face.getNumVertices(); kk++)
+        {
+          if (face.getVertex(jj).getPositionIndex() == face.getVertex(kk).getPositionIndex())
+            identicalVertex = true;
+        }
+
+      return (norm2 == 0 || identicalVertex);
+    });
+    int numRemainedFaces = distance(groups[i].faces.begin(), newEnd);
+    numZeroAreaFaces += (groups[i].faces.size() - numRemainedFaces);
+    groups[i].faces.resize(numRemainedFaces);
+  }
+
+  return numZeroAreaFaces;
+}
+
+int ObjMesh::removeInvalidFaces()
+{
+  int numInvalidFaces = 0;
 
+  // over all faces
+  for(size_t i = 0; i < groups.size(); i++ )
+  {
+    auto newEnd = remove_if(groups[i].faces.begin(), groups[i].faces.end(), [&](const ObjMesh::Face & face)
+    {
       bool identicalVertex = false;
-      for(unsigned int jj=0; jj< face.getNumVertices(); jj++)
-        for(unsigned int kk=jj+1; kk< face.getNumVertices(); kk++)
+      for(size_t jj=0; jj< face.getNumVertices(); jj++)
+        for(size_t kk=jj+1; kk< face.getNumVertices(); kk++)
         {
           if (face.getVertex(jj).getPositionIndex() == face.getVertex(kk).getPositionIndex())
             identicalVertex = true;
         }
+      return identicalVertex;
+    });
+    int numRemainedFaces = distance(groups[i].faces.begin(), newEnd);
+    numInvalidFaces += (groups[i].faces.size() - numRemainedFaces);
+    groups[i].faces.resize(numRemainedFaces);
+  }
+
+  return numInvalidFaces;
+}
+
+int ObjMesh::removeHangingFaces()
+{
+  map< pair<unsigned int,unsigned int>, vector<pair<unsigned int, unsigned int> > > facesAdjacentToEdge;
 
-      if (isNaN(normal[0]) || isNaN(normal[1]) || isNaN(normal[2]) || identicalVertex)
+  // build facesAdjacentToEdge
+  // over all faces
+  for(unsigned int iGroup = 0; iGroup < groups.size(); iGroup++ )
+  {
+    for(unsigned int iFace = 0; iFace < groups[iGroup].getNumFaces(); iFace++ )
+    {
+      ObjMesh::Face face = groups[iGroup].getFace(iFace); // get face whose number is iFace
+      for(unsigned int j=0; j<face.getNumVertices(); j++) // over all vertices of this face
       {
-        groups[i].removeFace(iFace);
-        iFace--;
-        numZeroAreaFaces++;
+        unsigned int vtxIndexA = face.getVertex(j).getPositionIndex();
+        unsigned int vtxIndexB = face.getVertex((j + 1) % face.getNumVertices()).getPositionIndex();
+        if (vtxIndexA > vtxIndexB)
+          std::swap(vtxIndexA, vtxIndexB);
+
+        std::pair<unsigned int, unsigned int> myPair(vtxIndexA, vtxIndexB);
+        if (facesAdjacentToEdge.find(myPair) == facesAdjacentToEdge.end())
+          facesAdjacentToEdge.insert(make_pair(myPair, vector<pair<unsigned int, unsigned int> >()));
+        facesAdjacentToEdge[myPair].push_back(make_pair(iGroup, iFace));
       }
     }
   }
 
-  return numZeroAreaFaces;
+  set<pair<unsigned int, unsigned int> > eraseList;
+
+  // check the map for edges with more than two neighbors
+  for(map< pair<unsigned int,unsigned int>, vector<pair<unsigned int, unsigned int> > > :: iterator iter = facesAdjacentToEdge.begin(); iter != facesAdjacentToEdge.end(); iter++)
+  {
+    if ((iter->second).size() > 2)
+    {
+      // edge has more than two neighboring faces
+
+      // check all adjacent faces, to see if any of them has an edge that has no other neighbor
+      for(unsigned int i=0; i<(iter->second).size(); i++)
+      {
+        unsigned int iGroup = (iter->second)[i].first;
+        unsigned int iFace = (iter->second)[i].second;
+
+        ObjMesh::Face face = groups[iGroup].getFace(iFace); // get face whose number is iFace
+        for(unsigned int j=0; j<face.getNumVertices(); j++) // over all vertices
+        {
+          unsigned int vtxIndexA = face.getVertex(j).getPositionIndex();
+          unsigned int vtxIndexB = face.getVertex((j + 1) % face.getNumVertices()).getPositionIndex();
+          if (vtxIndexA > vtxIndexB)
+            std::swap(vtxIndexA, vtxIndexB);
+
+          std::pair<unsigned int, unsigned int> myPair(vtxIndexA, vtxIndexB);
+          if (facesAdjacentToEdge[myPair].size() == 1)
+          {
+            // found an edge with only one neighboring face (this face)
+            // erase the face
+            eraseList.insert((iter->second)[i]);
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  // erase faces whose all three edges are not shared by any other face
+  // over all faces
+  for(unsigned int iGroup = 0; iGroup < groups.size(); iGroup++ )
+  {
+    for(unsigned int iFace = 0; iFace < groups[iGroup].getNumFaces(); iFace++ )
+    {
+      int eraseFace = 1;
+      ObjMesh::Face face = groups[iGroup].getFace(iFace); // get face whose number is iFace
+      for(unsigned int j=0; j<face.getNumVertices(); j++) // over all vertices of this face
+      {
+        unsigned int vtxIndexA = face.getVertex(j).getPositionIndex();
+        unsigned int vtxIndexB = face.getVertex((j + 1) % face.getNumVertices()).getPositionIndex();
+        if (vtxIndexA > vtxIndexB)
+          std::swap(vtxIndexA, vtxIndexB);
+
+        std::pair<unsigned int, unsigned int> myPair(vtxIndexA, vtxIndexB);
+        if (facesAdjacentToEdge[myPair].size() > 1)
+        {
+          eraseFace = 0;
+          break;
+        }
+      }
+
+      if (eraseFace)
+        eraseList.insert(make_pair(iGroup, iFace));
+    }
+  }
+
+  //printf("Erase list size is: %d\n", (int)eraseList.size());
+
+  // erase the scheduled faces
+  // must iterate from the back to front, to have correct indexing
+  for(set<pair<unsigned int, unsigned int> > :: reverse_iterator iter = eraseList.rbegin(); iter != eraseList.rend(); iter++)
+  {
+    unsigned int iGroup = iter->first;
+    unsigned int iFace = iter->second;
+    Group * groupHandle = (Group*) getGroupHandle(iGroup);
+    groupHandle->removeFace(iFace);
+  }
+
+  return (int) eraseList.size();
+}
+
+int ObjMesh::removeNonManifoldEdges()
+{
+  map< pair<unsigned int,unsigned int>, vector<pair<unsigned int, unsigned int> > > facesAdjacentToEdge;
+
+  // build facesAdjacentToEdge
+  // over all faces
+  for(unsigned int iGroup = 0; iGroup < groups.size(); iGroup++ )
+  {
+    for(unsigned int iFace = 0; iFace < groups[iGroup].getNumFaces(); iFace++ )
+    {
+      ObjMesh::Face face = groups[iGroup].getFace(iFace); // get face whose number is iFace
+      for(unsigned int j=0; j<face.getNumVertices(); j++) // over all vertices of this face
+      {
+        unsigned int vtxIndexA = face.getVertex(j).getPositionIndex();
+        unsigned int vtxIndexB = face.getVertex((j + 1) % face.getNumVertices()).getPositionIndex();
+        if (vtxIndexA > vtxIndexB)
+          std::swap(vtxIndexA, vtxIndexB);
+
+        std::pair<unsigned int, unsigned int> myPair(vtxIndexA, vtxIndexB);
+        if (facesAdjacentToEdge.find(myPair) == facesAdjacentToEdge.end())
+          facesAdjacentToEdge.insert(make_pair(myPair, vector<pair<unsigned int, unsigned int> >()));
+        facesAdjacentToEdge[myPair].push_back(make_pair(iGroup, iFace));
+      }
+    }
+  }
+
+  vector<pair<unsigned int, unsigned int> > eraseList;
+
+  // check the map for edges with more than two neighbors
+  for(map<pair<unsigned int,unsigned int>, vector<pair<unsigned int, unsigned int> > > :: iterator iter = facesAdjacentToEdge.begin(); iter != facesAdjacentToEdge.end(); iter++)
+  {
+    if ((iter->second).size() > 2)
+      eraseList.push_back(iter->first);
+  }
+
+  sort(eraseList.begin(), eraseList.end());
+  //printf("Erase list size: %d\n", eraseList.size());
+
+  int removedEdges = 0;
+
+  for(unsigned int i=0; i<eraseList.size(); i++)
+  {
+    if (eraseList[i].first == eraseList[i].second)
+      continue;
+
+    //printf("Removing edge: %d to %d.\n", eraseList[i].first, eraseList[i].second);
+
+    int removeIsolatedVertices_ = 0;
+    collapseEdge(eraseList[i].first, eraseList[i].second, removeIsolatedVertices_);
+    removedEdges++;
+
+    // renumber all future pairs
+    for(unsigned int j=i+1; j<eraseList.size(); j++)
+    {
+      if (eraseList[j].first == eraseList[i].second)
+        eraseList[j].first = eraseList[i].first;
+      if (eraseList[j].second == eraseList[i].second)
+        eraseList[j].second = eraseList[i].first;
+
+      if (eraseList[j].first > eraseList[j].second)
+        std::swap(eraseList[j].first, eraseList[j].second);
+    }
+  }
+
+  removeIsolatedVertices();
+
+  return removedEdges;
+}
+
+void ObjMesh::collapseEdge(unsigned int vertexA, unsigned int vertexB, int removeIsolatedVertices_)
+{
+  if (vertexA > vertexB)
+    std::swap(vertexA, vertexB);
+
+  // over all faces
+  for(unsigned int iGroup = 0; iGroup < groups.size(); iGroup++)
+  {
+    Group * group = (Group*) getGroupHandle(iGroup);
+    vector<unsigned int> eraseList;
+    for(unsigned int iFace = 0; iFace < groups[iGroup].getNumFaces(); iFace++)
+    {
+      int eraseFace = 0;
+      Face * face = (Face*) group->getFaceHandle(iFace); // get face whose number is iFace
+      for(unsigned int j=0; j<face->getNumVertices(); j++) // over all vertices of this face
+      {
+        unsigned int vtxIndex = face->getVertex(j).getPositionIndex();
+        if (vtxIndex == vertexB)
+        {
+          Vertex * vertex = (Vertex*) face->getVertexHandle(j);
+          vertex->setPositionIndex(vertexA);
+        }
+      }
+
+      // remove consecutive vertices
+      for(unsigned int j=0; j<face->getNumVertices(); j++) // over all vertices of this face
+      {
+        unsigned int vtxIndexA = face->getVertex(j).getPositionIndex();
+        unsigned int vtxIndexB = face->getVertex((j + 1) % face->getNumVertices()).getPositionIndex();
+        if (vtxIndexA == vtxIndexB)
+        {
+          if (face->getNumVertices() <= 3)
+            eraseFace = 1;
+          else
+            face->removeVertex(j);
+          break;
+        }
+      }
+
+      if (eraseFace)
+        eraseList.push_back(iFace);
+    }
+
+    for(int i=(int)eraseList.size()-1; i>=0; i--)
+    {
+      //printf("Erasing face %d\n", eraseList[i]);
+      group->removeFace(eraseList[i]);
+    }
+  }
+
+  if (removeIsolatedVertices_)
+    removeIsolatedVertices();
 }
 
 // returns 1 on success, 0 otherwise
 int ObjMesh::getEdgePseudoNormal(unsigned int i, unsigned int j, Vec3d * pseudoNormal) const
-{ 
+{
   pair<unsigned int,unsigned int> edge;
 
   if (i < j)
@@ -2516,12 +3398,12 @@ int ObjMesh::getEdgePseudoNormal(unsigned int i, unsigned int j, Vec3d * pseudoN
 }
 
 double ObjMesh::computeFaceSurfaceArea(const Face & face) const
-{ 
+{
   double faceSurfaceArea = 0;
 
   // base vertex
   Vec3d basePos = getPosition(face.getVertex(0));
-                                                                                                                                                             
+
   for ( unsigned int iVertex = 1; iVertex < face.getNumVertices()-1; iVertex++ )
   {
      Vec3d pos1 = getPosition(face.getVertex(iVertex));
@@ -2533,8 +3415,8 @@ double ObjMesh::computeFaceSurfaceArea(const Face & face) const
 }
 
 Vec3d ObjMesh::computeFaceCentroid(const Face & face) const
-{ 
-  Vec3d centroid = 0.0;
+{
+  Vec3d centroid(0.0);
   for ( unsigned int iVertex = 0; iVertex < face.getNumVertices(); iVertex++ )
      centroid += getPosition(face.getVertex(iVertex));
   centroid /= face.getNumVertices();
@@ -2545,28 +3427,28 @@ Vec3d ObjMesh::computeFaceCentroid(const Face & face) const
 void ObjMesh::computeSpecificInertiaTensor(Vec3d & v0, Vec3d & v1, Vec3d & v2, double t[6]) const
 {
 
-  t[0] = (v0[1]*v0[1] + v0[2]*v0[2] + v1[1]*v1[1] + v1[2]*v1[2] + 
-    v1[1]*v2[1] + v2[1]*v2[1] + v0[1]*(v1[1] + v2[1]) + 
-    v1[2]*v2[2] + v2[2]*v2[2] + v0[2]*(v1[2] + v2[2]))/6; 
+  t[0] = (v0[1]*v0[1] + v0[2]*v0[2] + v1[1]*v1[1] + v1[2]*v1[2] +
+    v1[1]*v2[1] + v2[1]*v2[1] + v0[1]*(v1[1] + v2[1]) +
+    v1[2]*v2[2] + v2[2]*v2[2] + v0[2]*(v1[2] + v2[2]))/6;
 
   t[1] = (-2*v1[0]*v1[1] - v1[1]*v2[0] - v0[1]*(v1[0] + v2[0])
-     - v1[0]*v2[1] - 2*v2[0]*v2[1] - 
-     v0[0]*(2*v0[1] + v1[1] + v2[1]))/12; 
+     - v1[0]*v2[1] - 2*v2[0]*v2[1] -
+     v0[0]*(2*v0[1] + v1[1] + v2[1]))/12;
 
-  t[2] = (-2*v1[0]*v1[2] - v1[2]*v2[0] - v0[2]*(v1[0] + v2[0]) - 
-    v1[0]*v2[2] - 2*v2[0]*v2[2] - 
-    v0[0]*(2*v0[2] + v1[2] + v2[2]))/12; 
+  t[2] = (-2*v1[0]*v1[2] - v1[2]*v2[0] - v0[2]*(v1[0] + v2[0]) -
+    v1[0]*v2[2] - 2*v2[0]*v2[2] -
+    v0[0]*(2*v0[2] + v1[2] + v2[2]))/12;
 
-  t[3] =  (v0[0]*v0[0] + v0[2]*v0[2] + v1[0]*v1[0] + v1[2]*v1[2] + 
-    v1[0]*v2[0] + v2[0]*v2[0] + v0[0]*(v1[0] + v2[0]) + 
-    v1[2]*v2[2] + v2[2]*v2[2] + v0[2]*(v1[2] + v2[2]))/6; 
+  t[3] =  (v0[0]*v0[0] + v0[2]*v0[2] + v1[0]*v1[0] + v1[2]*v1[2] +
+    v1[0]*v2[0] + v2[0]*v2[0] + v0[0]*(v1[0] + v2[0]) +
+    v1[2]*v2[2] + v2[2]*v2[2] + v0[2]*(v1[2] + v2[2]))/6;
 
-  t[4] = (-2*v1[1]*v1[2] - v1[2]*v2[1] - 
-    v0[2]*(v1[1] + v2[1]) - v1[1]*v2[2] - 2*v2[1]*v2[2] - 
-    v0[1]*(2*v0[2] + v1[2] + v2[2]))/12; 
+  t[4] = (-2*v1[1]*v1[2] - v1[2]*v2[1] -
+    v0[2]*(v1[1] + v2[1]) - v1[1]*v2[2] - 2*v2[1]*v2[2] -
+    v0[1]*(2*v0[2] + v1[2] + v2[2]))/12;
 
-  t[5] = (v0[0]*v0[0] + v0[1]*v0[1] + v1[0]*v1[0] + v1[1]*v1[1] + 
-    v1[0]*v2[0] + v2[0]*v2[0] + v0[0]*(v1[0] + v2[0]) + 
+  t[5] = (v0[0]*v0[0] + v0[1]*v0[1] + v1[0]*v1[0] + v1[1]*v1[1] +
+    v1[0]*v2[0] + v2[0]*v2[0] + v0[0]*(v1[0] + v2[0]) +
     v1[1]*v2[1] + v2[1]*v2[1] + v0[1]*(v1[1] + v2[1]))/6;
 }
 
@@ -2580,13 +3462,13 @@ void ObjMesh::dirname(const char * path, char * result)
   while (*ch != 0)
   {
     if (*ch == '\\')
-	  lastPos = pos;
+      lastPos = pos;
 
     if (*ch == '/')
-	  lastPos = pos;
+      lastPos = pos;
 
-	ch++;
-	pos++;
+    ch++;
+    pos++;
   }
 
   if (lastPos != -1)
@@ -2596,29 +3478,29 @@ void ObjMesh::dirname(const char * path, char * result)
   }
   else
   {
-	result[0] = '.';
-	result[1] = 0;
+    result[0] = '.';
+    result[1] = 0;
   }
 }
 
-void ObjMesh::parseMaterials(const char * objMeshFilename, const char * materialFilename, int verbose)
+void ObjMesh::parseMaterials(const std::string & objMeshFilename, const std::string & materialFilename, int verbose)
 {
   FILE * file;
-  char buf[128];
-  unsigned int numMaterials;
-  
+  //char buf[128];
+  //unsigned int numMaterials;
+
   char objMeshFilenameCopy[4096];
-  strcpy(objMeshFilenameCopy, objMeshFilename);
+  strcpy(objMeshFilenameCopy, objMeshFilename.c_str());
 
   char dir[4096];
   dirname(objMeshFilenameCopy,dir);
   char filename[4096];
   strcpy(filename, dir);
   strcat(filename, "/");
-  strcat(filename, materialFilename);
+  strcat(filename, materialFilename.c_str());
 
   file = fopen(filename, "r");
-  if (!file) 
+  if (!file)
   {
     fprintf(stderr, " parseMaterials() failed: can't open material file %s.\n", filename);
     std::string message = "Failed to open material file '";
@@ -2626,104 +3508,79 @@ void ObjMesh::parseMaterials(const char * objMeshFilename, const char * material
     message.append("'");
     throw ObjMeshException(message);
   }
-  
-  /* count the number of materials in the file */
-  numMaterials = 1;
-  while(fscanf(file, "%s", buf) != EOF) 
-  {
-    switch(buf[0]) 
-    {
-      case '#': 
-        //
-        // eat up rest of line 
-        fgets_(buf, sizeof(buf), file);
-      break;
-
-      case 'n':
-        // newmtl 
-        fgets_(buf, sizeof(buf), file);
-        numMaterials++;
-        sscanf(buf, "%s %s", buf, buf);
-      break;
 
-      default:
-        /* eat up rest of line */
-        fgets_(buf, sizeof(buf), file);
-      break;
-    }
-  }
-  
-  rewind(file);
-    
-  double Ka[3];
-  double Kd[3];
-  double Ks[3];
-  double shininess;
+  // default material
+  Material defaultMat;
+  Vec3d Ka = defaultMat.getKa();
+  Vec3d Kd = defaultMat.getKd();
+  Vec3d Ks = defaultMat.getKs();
+  double shininess = defaultMat.getShininess();
   string matName;
-  string textureFile = string();
+  string textureFile;
 
-  /* now, read in the data */
-  numMaterials = 0;
-  while(fscanf(file, "%s", buf) != EOF) 
+  // now, read in the data
+  char buf[4096];
+  unsigned int numMaterials = 0;
+  while(fscanf(file, "%s", buf) != EOF)
   {
-    switch(buf[0]) 
+    switch(buf[0])
     {
       case '#':
-        /* comment */
-        /* ignore the rest of line */
+        // comment
+        // ignore the rest of line
         fgets_(buf, sizeof(buf), file);
       break;
 
-      case 'n':               
-        /* newmtl */
+      case 'n':
+        // newmtl
         if (numMaterials >= 1) // flush previous material
-          addMaterial(matName, Vec3d(Ka[0], Ka[1], Ka[2]), Vec3d(Kd[0], Kd[1], Kd[2]), Vec3d(Ks[0], Ks[1], Ks[2]), shininess, textureFile);
+          addMaterial(matName, Ka, Kd, Ks, shininess, textureFile);
 
         // reset to default
-        Ka[0] = 0.1; Ka[1] = 0.1; Ka[2] = 0.1;
-        Kd[0] = 0.5; Kd[1] = 0.5; Kd[2] = 0.5;
-        Ks[0] = 0.0; Ks[1] = 0.0; Ks[2] = 0.0;
-        shininess = 65;
-        textureFile = string();
+        Ka = defaultMat.getKa();
+        Kd = defaultMat.getKd();
+        Ks = defaultMat.getKs();
+        shininess = defaultMat.getShininess();
+        textureFile.clear();
 
         fgets_(buf, sizeof(buf), file);
         sscanf(buf, "%s %s", buf, buf);
         numMaterials++;
-        matName = string(buf);
+        matName = buf;
       break;
 
       case 'N':
         if (buf[1] == 's')
         {
           if (fscanf(file, "%lf", &shininess) < 1)
-            printf("Warning: bad file syntax. Unable to read shininess.\n");
-          // wavefront shininess is from [0, 1000], so scale for OpenGL 
+            printf("Warning: incorect mtl file syntax. Unable to read shininess.\n");
+          // wavefront shininess is from [0, 1000], so scale for OpenGL
           shininess *= 128.0 / 1000.0;
         }
         else
-          fgets_(buf, sizeof(buf), file); //eat rest of line
+          fgets_(buf, sizeof(buf), file); // ignore the rest of the line
       break;
 
       case 'K':
-        switch(buf[1]) 
+        switch(buf[1])
         {
           case 'd':
             if (fscanf(file, "%lf %lf %lf", &Kd[0], &Kd[1], &Kd[2]) < 3)
-              printf("Warning: bad file syntax. Unable to read Kd.\n");
+              printf("Warning: incorect mtl file syntax. Unable to read Kd.\n");
           break;
 
           case 's':
             if (fscanf(file, "%lf %lf %lf", &Ks[0], &Ks[1], &Ks[2]) < 3)
-              printf("Warning: bad file syntax. Unable to read Ks.\n");
+              printf("Warning: incorect mtl file syntax. Unable to read Ks.\n");
            break;
 
           case 'a':
             if (fscanf(file, "%lf %lf %lf", &Ka[0], &Ka[1], &Ka[2]) < 3)
-              printf("Warning: bad file syntax. Unable to read Ka.\n");
+              printf("Warning: incorect mtl file syntax. Unable to read Ka.\n");
           break;
 
           default:
-            // eat up rest of line 
+            // ignore the rest of the line
             fgets_(buf, sizeof(buf), file);
           break;
         }
@@ -2741,14 +3598,14 @@ void ObjMesh::parseMaterials(const char * objMeshFilename, const char * material
       break;
 
       default:
-        /* eat up rest of line */
+        // ignore the rest of the line
         fgets_(buf, sizeof(buf), file);
       break;
     }
   }
 
   if (numMaterials >= 1) // flush last material
-    addMaterial(matName, Vec3d(Ka[0], Ka[1], Ka[2]), Vec3d(Kd[0], Kd[1], Kd[2]), Vec3d(Ks[0], Ks[1], Ks[2]), shininess, textureFile);
+    addMaterial(matName, Ka, Kd, Ks, shininess, textureFile);
 
   fclose(file);
 }
@@ -2762,14 +3619,14 @@ void ObjMesh::initSurfaceSampling()
 
   double totalSurfaceArea = computeSurfaceArea();
   double area = 0;
-  
+
   // over all faces
   for(unsigned int i = 0; i < groups.size(); i++ )
   {
     for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
     {
-      surfaceSamplingAreas.push_back(make_pair(area, groups[i].getFaceHandle(iFace)));
-      ObjMesh::Face face = groups[i].getFace(iFace); 
+      surfaceSamplingAreas.push_back(make_pair(area, make_pair(i, iFace)));
+      ObjMesh::Face face = groups[i].getFace(iFace);
       area += computeFaceSurfaceArea(face) / totalSurfaceArea;
     }
   }
@@ -2781,13 +3638,14 @@ Vec3d ObjMesh::getSurfaceSamplePosition(double sample) const
   unsigned int facePosition;
   for(facePosition=0; facePosition< surfaceSamplingAreas.size()-1; facePosition++)
   {
-    if ((surfaceSamplingAreas[facePosition].first <= sample) && 
+    if ((surfaceSamplingAreas[facePosition].first <= sample) &&
         (surfaceSamplingAreas[facePosition+1].first > sample))
       break;
   }
 
   // facePosition now contains the index of the face to sample from
-  const Face * face = surfaceSamplingAreas[facePosition].second;
+  auto groupFace = surfaceSamplingAreas[facePosition].second;
+  const Face & face = groups[groupFace.first].faces[groupFace.second];
 
   // sample at random on the face
   double alpha, beta;
@@ -2795,14 +3653,14 @@ Vec3d ObjMesh::getSurfaceSamplePosition(double sample) const
   {
     alpha = 1.0 * rand() / RAND_MAX;
     beta = 1.0 * rand() / RAND_MAX;
-  }  
+  }
   while (alpha + beta > 1);
 
   double gamma = 1 - alpha - beta;
 
-  Vec3d v0 = getPosition(face->getVertex(0)); 
-  Vec3d v1 = getPosition(face->getVertex(1)); 
-  Vec3d v2 = getPosition(face->getVertex(2)); 
+  Vec3d v0 = getPosition(face.getVertex(0));
+  Vec3d v1 = getPosition(face.getVertex(1));
+  Vec3d v2 = getPosition(face.getVertex(2));
 
   Vec3d sampledPos = alpha * v0 + beta * v1 + gamma * v2;
   return sampledPos;
@@ -2834,27 +3692,21 @@ void ObjMesh::getMeshGeometricParameters(Vec3d * centroid, double * radius) cons
 
 void ObjMesh::buildVertexFaceNeighbors()
 {
-  vertexFaceNeighbors.clear();
-  for(unsigned int i=0; i<getNumVertices(); i++)
-    vertexFaceNeighbors.push_back(std::list<VertexFaceNeighbor>());
+  // initialize vertexFaceNeighbors
+  vertexFaceNeighbors.assign(getNumVertices(), {});
 
   //go through each of the faces
-  for(unsigned int i = 0; i < groups.size(); i++ )
-  {
-    for(unsigned int iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
+  for(size_t i = 0; i < groups.size(); i++ )
+    for(size_t iFace = 0; iFace < groups[i].getNumFaces(); iFace++ )
     {
       const ObjMesh::Face * faceHandle = groups[i].getFaceHandle(iFace); // get face whose number is iFace
 
       if (faceHandle->getNumVertices() < 3)
         cout << "Warning: encountered a face (group=" << i << ",face=" << iFace << ") with fewer than 3 vertices." << endl;
 
-      for(unsigned int j = 0; j < faceHandle->getNumVertices(); j++)
-      {
-        const ObjMesh::Vertex * vertexHandle = faceHandle->getVertexHandle(j);
-        vertexFaceNeighbors[vertexHandle->getPositionIndex()].push_back(ObjMesh::VertexFaceNeighbor(i, iFace, j));
-      }
+      for(size_t j = 0; j < faceHandle->getNumVertices(); j++)
+        vertexFaceNeighbors[faceHandle->getVertexPositionIndex(j)].emplace_back(i, iFace, j);
     }
-  }
 }
 
 void ObjMesh::clearVertexFaceNeighbors()
@@ -2867,6 +3719,11 @@ void ObjMesh::Group::removeFace(unsigned int i)
   faces.erase(faces.begin() + i);
 }
 
+void ObjMesh::Group::removeFaces(const std::set<int> & faceIDs)
+{
+  removeByIndices(faces, faceIDs);
+}
+
 ObjMeshException::ObjMeshException(const std::string & reason)
 {
   std::ostringstream oss;
@@ -2894,21 +3751,21 @@ bool ObjMesh::Material::operator==(const Material & mat2) const
 
   if (fabs(shininess - mat2.shininess) > 1e-7)
     return false;
-   
+
   return true;
 }
 
 int ObjMesh::removeDuplicatedMaterials()
 {
   unsigned int numMaterials = getNumMaterials();
-  vector<int> reNumberVector(numMaterials);  
+  vector<int> reNumberVector(numMaterials);
 
   vector<Material> newMaterials;
 
   // detected duplicated materials
   for(unsigned int i=0; i<numMaterials; i++)
   {
-    bool newMaterial = true; 
+    bool newMaterial = true;
     for(unsigned int j=0; j<newMaterials.size(); j++)
     {
       if (newMaterials[j] == materials[i])
@@ -2924,7 +3781,7 @@ int ObjMesh::removeDuplicatedMaterials()
       newMaterials.push_back(materials[i]);
       reNumberVector[i] = newMaterials.size() - 1;
     }
-  } 
+  }
 
   materials = newMaterials;
 
@@ -2935,54 +3792,85 @@ int ObjMesh::removeDuplicatedMaterials()
   return materials.size();
 }
 
-void ObjMesh::exportGeometry(int * numVertices, double ** vertices, int * numTriangles , int ** triangles, int * numGroups, int ** triangleGroups) const
+void ObjMesh::exportTriangles(std::vector<Vec3i> & triangles) const
 {
-  // set vertices
-  *numVertices = vertexPositions.size();
-  *vertices = (double*) malloc (sizeof(double) * 3 * *numVertices);
-  for(int i=0; i< *numVertices; i++)
+  forEachFace([&](const Face & face)
   {
-    Vec3d vtx = getPosition(i);
-    (*vertices)[3*i+0] = vtx[0];
-    (*vertices)[3*i+1] = vtx[1];
-    (*vertices)[3*i+2] = vtx[2];
-  }
+    // triangulate the face
+    for(int k=0; k<face.getNumTriangles(); k++)
+    {
+      triangles.emplace_back(face.getIndicesInTriangle(k));
+    }
+  }, true);
+}
 
-  if (numTriangles == NULL)
+void ObjMesh::exportTriangles(std::vector<Vec3i> & triangles, std::vector<std::pair<int,int>> & orginalGroupAndFaceIDs) const
+{
+  forEachFace([&](int gID, int fID, const Face & face)
   {
-    //printf("Exported %d vertices.\n", *numVertices);
-    return;
+    // triangulate the face
+    for(int k=0; k<face.getNumTriangles(); k++)
+    {
+      triangles.emplace_back(face.getIndicesInTriangle(k));
+      orginalGroupAndFaceIDs.emplace_back(gID, fID);
+    }
+  }, true);
+}
+
+void ObjMesh::exportGeometry(std::vector<Vec3d> & vertices, std::vector<Vec3i> & triangles) const
+{
+  vertices = vertexPositions;
+  exportTriangles(triangles);
+}
+
+void ObjMesh::exportGeometry(int * numVertices, double ** vertices, int * numTriangles , int ** triangles, int * numGroups, int ** triangleGroups) const
+{
+  // set vertices
+  if (numVertices != NULL)
+    *numVertices = vertexPositions.size();
+  if (vertices != NULL)
+  {
+    *vertices = (double*) malloc (sizeof(double) * 3 * vertexPositions.size());
+    for(size_t i=0; i< vertexPositions.size(); i++)
+    {
+      Vec3d vtx = getPosition(i);
+      (*vertices)[3*i+0] = vtx[0];
+      (*vertices)[3*i+1] = vtx[1];
+      (*vertices)[3*i+2] = vtx[2];
+    }
   }
 
-  // set triangles
-  *numTriangles = 0;
+  if (numGroups != NULL)
+    *numGroups = getNumGroups();
+
+  if (numTriangles == NULL && triangles == NULL && triangleGroups == NULL)
+    return;
+
+  // count #triangles
+  int nt = 0;
   for(unsigned int i=0; i < groups.size(); i++) // over all groups
     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
     {
       const Face * face = groups[i].getFaceHandle(j);
       if (face->getNumVertices() < 3)
         continue;
-      *numTriangles += face->getNumVertices() - 2;
+      nt += face->getNumVertices() - 2;
     }
 
-  *triangles = (int*) malloc (sizeof(int) * 3 * *numTriangles);
-  
-  // ===
-  // set triangle groups while setting triangle positions (easy addition)
-  if (numGroups != NULL)
-    *numGroups = getNumMaterials();
+  if(numTriangles != NULL)
+    *numTriangles = nt;
+  if (triangles == NULL && triangleGroups == NULL)
+    return;
+
+  // set triangles
+  if(triangles != NULL)
+    *triangles = (int*) malloc (sizeof(int) * 3 * nt);
 
+  // set triangle groups while setting triangle positions (easy addition)
   if (triangleGroups != NULL)
-  {
-    *triangleGroups = (int*) malloc (sizeof(int) * *numTriangles);
-    // set all groups to 0 just in case
-    for (int i=0; i<*numTriangles; i++)
-      (*triangleGroups)[i] = 0;
-  }
-  // ===
+    *triangleGroups = (int*) calloc (nt, sizeof(int)); // set all groups to 0 just in case
 
-  int tri = 0;
-  for(unsigned int i=0; i < groups.size(); i++) // over all groups
+  for(unsigned int i=0, tri=0; i < groups.size(); i++) // over all groups
   {
     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
     {
@@ -2993,80 +3881,76 @@ void ObjMesh::exportGeometry(int * numVertices, double ** vertices, int * numTri
         continue;
       }
 
-      unsigned int faceDegree = face->getNumVertices();
-
       // triangulate the face
+      for(unsigned int k=1; k<face->getNumVertices()-1; k++)
+      {
+        // set triangle vertex positions
+        if(triangles != NULL)
+        {
+          (*triangles)[3*tri+0] = face->getVertexPositionIndex(0);
+          (*triangles)[3*tri+1] = face->getVertexPositionIndex(k);
+          (*triangles)[3*tri+2] = face->getVertexPositionIndex(k+1);
+        }
 
-      // get the vertices:
-      vector<Vertex> vertices;
-      for(unsigned int k=0; k<face->getNumVertices(); k++)
-        vertices.push_back(face->getVertex(k));
-      
-      // set triangle vertex positions
-      (*triangles)[3*tri+0] = vertices[0].getPositionIndex();
-      (*triangles)[3*tri+1] = vertices[1].getPositionIndex();
-      (*triangles)[3*tri+2] = vertices[2].getPositionIndex();
-      
-      // set triangle group
-      if (triangleGroups != NULL)
-        (*triangleGroups)[tri] = i;
-      
-      // increment triangle counter
-      tri++;
-
-      for(unsigned int k=2; k<faceDegree-1; k++)
-      {
-        (*triangles)[3*tri+0] = vertices[0].getPositionIndex();
-        (*triangles)[3*tri+1] = vertices[k].getPositionIndex();
-        (*triangles)[3*tri+2] = vertices[k+1].getPositionIndex();
-        
         // set triangle group
         if (triangleGroups != NULL)
           (*triangleGroups)[tri] = i;
-        
-        // increment triangle counter
+
         tri++;
       }
     }
   }
-  
-  //printf("Exported %d vertices and %d triangles.\n", *numVertices, *numTriangles);
+  //printf("Exported %d vertices and %d triangles.\n", vertexPositions.size(), nt);
 }
 
-void ObjMesh::exportFaceGeometry(int * numVertices, double ** vertices, int * numFaces, int ** faceCardinality, int ** faces) const
+void ObjMesh::exportFaceGeometry(int * numVertices, double ** vertices, int * numFaces, int ** faceCardinality, int ** faces, int * numGroups, int ** faceGroups) const
 {
   // set vertices
-  *numVertices = vertexPositions.size();
-  *vertices = (double*) malloc (sizeof(double) * 3 * *numVertices);
-  for(int i=0; i< *numVertices; i++)
+  if (numVertices != NULL)
+    *numVertices = vertexPositions.size();
+  if (vertices != NULL)
   {
-    Vec3d vtx = getPosition(i);
-    (*vertices)[3*i+0] = vtx[0];
-    (*vertices)[3*i+1] = vtx[1];
-    (*vertices)[3*i+2] = vtx[2];
+    *vertices = (double*) malloc (sizeof(double) * 3 * *numVertices);
+    for(int i=0; i< *numVertices; i++)
+    {
+      Vec3d vtx = getPosition(i);
+      (*vertices)[3*i+0] = vtx[0];
+      (*vertices)[3*i+1] = vtx[1];
+      (*vertices)[3*i+2] = vtx[2];
+    }
   }
 
-  if (numFaces == NULL)
-  {
-    printf("Exported %d vertices.\n", *numVertices);
+  if(numGroups != NULL)
+    *numGroups = getNumGroups();
+
+  if (numFaces == NULL && faceCardinality == NULL && faces == NULL && faceGroups == NULL)
     return;
-  }
 
   // set faces
-  *numFaces = 0;
-  int totalCardinality = 0;
+  int nf = 0, totalCardinality = 0;
   for(unsigned int i=0; i < groups.size(); i++) // over all groups
     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
     {
       const Face * face = groups[i].getFaceHandle(j);
       if (face->getNumVertices() < 3)
         continue;
-      (*numFaces)++;
+      nf++;
       totalCardinality += face->getNumVertices();
     }
 
-  *faceCardinality = (int*) malloc (sizeof(int) * *numFaces);
-  *faces = (int*) malloc (sizeof(int) * totalCardinality);
+  if(numFaces != NULL)
+    (*numFaces) = nf;
+  if (faceCardinality == NULL && faces == NULL && faceGroups == NULL)
+    return;
+
+  if(faceCardinality != NULL)
+    *faceCardinality = (int*) malloc (sizeof(int) * nf);
+
+  if(faces != NULL)
+    *faces = (int*) malloc (sizeof(int) * totalCardinality);
+
+  if(faceGroups != NULL)
+    *faceGroups = (int*) calloc (*numFaces, sizeof(int));
 
   int faceCounter = 0;
   int tc = 0;
@@ -3081,15 +3965,17 @@ void ObjMesh::exportFaceGeometry(int * numVertices, double ** vertices, int * nu
       }
 
       int faceDegree = (int)face->getNumVertices();
-      (*faceCardinality)[faceCounter] = faceDegree;
+      if(faceCardinality != NULL)
+        (*faceCardinality)[faceCounter] = faceDegree;
+      if(faceGroups != NULL)
+        (*faceGroups)[faceCounter] = i;
+      if(faces != NULL)
+        for(unsigned int k=0; k<face->getNumVertices(); k++)
+          (*faces)[tc + k] = face->getVertex(k).getPositionIndex();
 
-      for(unsigned int k=0; k<face->getNumVertices(); k++)
-        (*faces)[tc + k] = face->getVertex(k).getPositionIndex();
-        
       faceCounter++;
       tc += faceDegree;
     }
-
   //printf("Exported %d vertices and %d faces. Average number of vertices: %G\n", *numVertices, *numFaces, 1.0 * totalCardinality / (*numFaces));
 }
 
@@ -3155,7 +4041,7 @@ void ObjMesh::exportUVGeometry(int * numUVVertices, double ** UVvertices, int *
 }
 
 // allows one to query the vertex indices of each triangle
-// order of triangles is same as in "exportGeometry": for every group, traverse all faces, and tesselate each face into triangles 
+// order of triangles is same as in "exportGeometry": for every group, traverse all faces, and tesselate each face into triangles
 void ObjMesh::initTriangleLookup()
 {
   int numVertices;
@@ -3197,9 +4083,9 @@ void ObjMesh::renumberVertices(const vector<int> & permutation)
   vector<Vec3d> vertexPositionsBuffer(numVertices);
   for(unsigned int i=0; i < numVertices; i++)
     vertexPositionsBuffer[permutation[i]] = vertexPositions[i];
-  
+
   vertexPositions = vertexPositionsBuffer;
- 
+
   // renumber faces
   for(unsigned int i=0; i < groups.size(); i++) // over all groups
     for (unsigned int j=0; j < groups[i].getNumFaces(); j++) // over all faces
@@ -3215,7 +4101,7 @@ void ObjMesh::renumberVertices(const vector<int> & permutation)
       {
         Vertex * vtx = (Vertex*) face->getVertexHandle(k);
         vtx->setPositionIndex(permutation[vtx->getPositionIndex()]);
-      }       
+      }
     }
 }
 
@@ -3287,7 +4173,7 @@ int ObjMesh::removeIsolatedVertices()
         vertex->setPositionIndex(oldToNew[oldPositionIndex]);
       }
     }
- 
+
   return numIsolatedVertices;
 }
 
@@ -3345,7 +4231,7 @@ int ObjMesh::removeIsolatedNormals()
         }
       }
     }
- 
+
   return numIsolatedNormals;
 }
 
@@ -3403,7 +4289,7 @@ int ObjMesh::removeIsolatedTextureCoordinates()
         }
       }
     }
- 
+
   return numIsolatedTextureCoordinates;
 }
 
@@ -3417,7 +4303,7 @@ void ObjMesh::mergeGroups(const vector<int> & groupIndicesIn)
   sort(groupIndices.begin(), groupIndices.end());
 
   // the groups will be copied into the group with the smallest index
-  Group * groupDest = (Group*) getGroupHandle(*(groupIndices.begin())); 
+  Group * groupDest = (Group*) getGroupHandle(*(groupIndices.begin()));
 
   int count = 0;
   for(vector<int> :: reverse_iterator iter = groupIndices.rbegin(); iter != groupIndices.rend(); iter++)
@@ -3438,13 +4324,90 @@ void ObjMesh::mergeGroups(const vector<int> & groupIndicesIn)
 
 void ObjMesh::removeEmptyGroups()
 {
-  for(vector<Group> :: iterator iter = groups.begin(); iter != groups.end(); iter++)
+  auto newEnd = remove_if(groups.begin(), groups.end(), [&](const Group & g)
   {
-    if (iter->getNumFaces() == 0)
+    return (g.getNumFaces() == 0);
+  });
+  groups.resize(distance(groups.begin(), newEnd));
+}
+
+void ObjMesh::moveFacesToGroup(const std::vector<std::pair<int,int>> & groupFaceIDs, int targetGroupID)
+{
+  auto numFaces = getNumFaces();
+  vector<vector<int>> fIDs(groups.size());
+  for(const auto & p : groupFaceIDs)
+  {
+    fIDs[p.first].push_back(p.second);
+  }
+
+  for(size_t gID = 0; gID < groups.size(); gID++)
+  {
+    sort(fIDs[gID].begin(), fIDs[gID].end());
+    auto & faces = groups[gID].faces;
+    for(int fID : fIDs[gID])
+      groups[targetGroupID].addFace(move(faces[fID]));
+
+    removeByIndices(faces, fIDs[gID]);
+  }
+
+  assert(numFaces == getNumFaces());
+}
+
+void ObjMesh::removeGroups(const std::vector<int> & groupIDs)
+{
+  removeByIndices(groups, groupIDs);
+}
+
+void ObjMesh::removeFaces(const std::map<int, std::set<int>> & faceIDsAtGroup)
+{
+  for(const auto & p : faceIDsAtGroup)
+  {
+    int gID = p.first;
+    assert(gID >= 0 && gID < (int)groups.size());
+    groups[gID].removeFaces(p.second);
+  }
+}
+
+void ObjMesh::appendMesh(const ObjMesh * mesh)
+{
+  // add vertices
+  int numVerticesCurrent = getNumVertices();
+  vertexPositions.insert(vertexPositions.end(), mesh->vertexPositions.begin(), mesh->vertexPositions.end());
+
+  // add normals
+  int numNormalsCurrent = getNumNormals();
+  normals.insert(normals.end(), mesh->normals.begin(), mesh->normals.end());
+
+  // add texture coordinates
+  int numTextureCoordinatesCurrent = getNumTextureCoordinates();
+  textureCoordinates.insert(textureCoordinates.end(), mesh->textureCoordinates.begin(), mesh->textureCoordinates.end());
+
+  // add materials
+  int numMaterialsCurrent = getNumMaterials();
+  materials.insert(materials.end(), mesh->materials.begin(), mesh->materials.end());
+
+  for(unsigned int i=0; i<mesh->getNumGroups(); i++)
+  {
+    Group newGroup = mesh->getGroup(i);
+
+    newGroup.setMaterialIndex(numMaterialsCurrent + newGroup.getMaterialIndex());
+
+    // over all faces in the group of the current obj file
+    for(unsigned int j=0; j<newGroup.getNumFaces(); j++)
     {
-      *iter = groups.back();
-      groups.pop_back();
+      ObjMesh::Face * face = newGroup.getFaceHandle(j);
+      for(unsigned int k=0; k<face->getNumVertices(); k++)
+      {
+        ObjMesh::Vertex * vertex = face->getVertexHandle(k);
+        vertex->setPositionIndex(vertex->getPositionIndex() + numVerticesCurrent);
+        if (vertex->hasNormalIndex())
+          vertex->setNormalIndex(vertex->getNormalIndex() + numNormalsCurrent);
+        if (vertex->hasTextureCoordinateIndex())
+          vertex->setTextureCoordinateIndex(vertex->getTextureCoordinateIndex() + numTextureCoordinatesCurrent);
+      }
     }
+
+    addGroup(move(newGroup));
   }
 }
 
@@ -3455,7 +4418,7 @@ void ObjMesh::removeWhitespace(char * s)
   while (*p != 0)
   {
     // erase empty space
-    while (*p == ' ') 
+    while (*p == ' ')
     {
       char * q = p;
       while (*q != 0) // move characters to the left, by one character
@@ -3475,7 +4438,7 @@ void ObjMesh::convertWhitespaceToSingleBlanks(char * s)
   while (*p != 0)
   {
     // erase consecutive empty space characters, or end-of-string spaces
-    while ((*p == ' ') && ((*(p+1) == 0) || (*(p+1) == ' '))) 
+    while ((*p == ' ') && ((*(p+1) == 0) || (*(p+1) == ' ')))
     {
       char * q = p;
       while (*q != 0) // move characters to the left, by one character
@@ -3496,7 +4459,7 @@ void ObjMesh::fgets_(char * s, int n, FILE * stream)
   return;
 }
 
-unsigned int ObjMesh::getGroupIndex(const std::string name) const
+unsigned int ObjMesh::getGroupIndex(const std::string & name) const
 {
   int count = 0;
   for(std::vector<Group>::const_iterator itr = groups.begin(); itr != groups.end(); itr++)
@@ -3513,13 +4476,30 @@ unsigned int ObjMesh::getGroupIndex(const std::string name) const
   return 0;
 }
 
+unsigned int ObjMesh::getMaterialIndex(const std::string & name) const
+{
+  for(size_t i = 0; i < materials.size(); i++)
+  {
+    if (materials[i].getName() == name)
+      return i;
+  }
+
+  throw ObjMeshException("Invalid material name: '" + name + "'.");
+  return 0;
+}
+
 void ObjMesh::removeGroup(const int groupIndex)
 {
+  if ((groupIndex >= (int) groups.size()) || (groupIndex < 0))
+  {
+    printf("Warning: cannot remove group %d. Invalid group number.\n", groupIndex);
+    return;
+  }
   groups[groupIndex] = groups[groups.size() - 1];
   groups.pop_back();
   computeBoundingBox();
 }
- 
+
 void ObjMesh::removeGroup(const std::string name)
 {
   int groupIndex = getGroupIndex(name);
@@ -3539,9 +4519,440 @@ int ObjMesh::usesTextureMapping()
   for(std::vector<Group>::const_iterator itr = groups.begin(); itr != groups.end(); itr++)
   {
     const Material * material = getMaterialHandle(itr->getMaterialIndex());
-    result = result | material->hasTextureFilename();
+    result = result | ((int)material->hasTextureFilename());
   }
   return result;
 }
 
+int ObjMesh::loadObjMeshesFromBinary(const std::string & binaryFilename, int * numObjMeshes, ObjMesh *** objMeshes, int verbose)
+{
+  FILE * fin = fopen(binaryFilename.c_str(), "rb");
+  if (fin == NULL)
+  {
+    if (verbose)
+      printf("Error in ObjMesh::loadObjMeshesFromBinary: cannot open %s to load.\n", binaryFilename.c_str());
+    return 1;
+  }
+
+  int code = loadObjMeshesFromBinary(fin, numObjMeshes, objMeshes, verbose);
+
+  for(int objMeshIndex = 0; objMeshIndex < *numObjMeshes; objMeshIndex++)
+    if ((*objMeshes)[objMeshIndex])
+      (*objMeshes)[objMeshIndex]->filename = binaryFilename;
+
+  return code;
+}
+
+int ObjMesh::loadObjMeshesFromBinary(FILE * fin, int * numObjMeshes, ObjMesh *** objMeshes, int verbose)
+{
+  // read the number of obj meshes
+  unsigned int items = fread(numObjMeshes, sizeof(int), 1, fin);
+  if (items != 1)
+    return 1;
+
+  int numMeshes = *numObjMeshes;
+
+  if (verbose)
+    printf("number of obj meshes to be read from binary: %d\n", numMeshes);
+
+  // read how many bytes are stored for every obj mesh
+  unsigned int * bytesWritten = (unsigned int *) calloc (numMeshes, sizeof(unsigned int));
+  items = fread(bytesWritten, sizeof(unsigned int), numMeshes, fin);
+  if ((int)items != numMeshes)
+    return 1;
+
+  if (verbose)
+  {
+    printf("number of bytes for each obj mesh: \n");
+    for(int i=0; i<numMeshes; i++)
+      printf("%u, ", bytesWritten[i]);
+    printf("\n");
+  }
+
+  // compute the total bytes
+  unsigned int totalBytes = 0;
+  for(int i=0; i<numMeshes; i++)
+    totalBytes += bytesWritten[i];
+
+  // allocate memory for obj meshes
+  (*objMeshes) = (ObjMesh **) malloc (sizeof(ObjMesh *) * numMeshes);
+  for (int i=0; i<numMeshes; i++)
+    (*objMeshes)[i] = NULL;
+
+  // read entire block from the memory
+  unsigned char * memory = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes);
+  items = fread(memory, sizeof(unsigned char), totalBytes, fin);
+
+  if (verbose)
+    printf("total bytes excluding header: %u\n", totalBytes);
+
+  // compute the offset for every obj mesh
+  unsigned int * offset = (unsigned int *) calloc (numMeshes, sizeof(unsigned int));
+  for(int i=1; i<numMeshes; i++)
+    offset[i] = offset[i-1] + bytesWritten[i-1];
+
+  // load every obj mesh from memory
+
+#ifdef USE_TBB
+  tbb::parallel_for(0, numMeshes, [&](int i)
+#else
+  for(int i = 0; i < numMeshes; i++)
+#endif
+  {
+    if (bytesWritten[i] != 0)
+    {
+      unsigned char * location = memory + offset[i];
+      streamType stream = MEMORY_STREAM;
+      int verbose = 0;
+      (*objMeshes)[i] = new ObjMesh((void *)location, stream, verbose);
+    }
+  }
+#ifdef USE_TBB
+  );
+#endif
+
+  free(bytesWritten);
+  free(memory);
+  free(offset);
+
+  return 0;
+}
+
+int ObjMesh::loadFromBinary(const std::string & filename_, int verbose)
+{
+  filename = filename_;
+  FILE * binaryInputStream = fopen(filename_.c_str(), "rb");
+  if (binaryInputStream == NULL)
+  {
+    printf("Error in ObjMesh::loadFromBinary: cannot load obj from binary file %s.\n", filename_.c_str());
+    return 1;
+  }
+
+  if (verbose)
+    std::cout << "Parsing .obj file '" << filename << "'." << std::endl;
+
+  streamType stream = FILE_STREAM;
+  int code = loadFromBinary(binaryInputStream, stream, verbose);
+  fclose(binaryInputStream);
+
+  return code;
+}
+
+int ObjMesh::loadFromBinary(void * binaryInputStream_, streamType stream, int verbose)
+{
+  unsigned int (*genericRead)(void *, unsigned int, unsigned int, void *);
+  void * binaryInputStream;
+  if (stream == MEMORY_STREAM)
+  {
+    genericRead = &ObjMesh::readFromMemory;
+    binaryInputStream = &binaryInputStream_; // a wrapper for input stream
+  }
+  else
+  {
+    genericRead = &ObjMesh::readFromFile;
+    binaryInputStream = binaryInputStream_;
+  }
+
+  const unsigned int defaultMaterialIndex = 0;
+
+  unsigned int totalBytes;
+  unsigned int items = genericRead(&totalBytes, sizeof(unsigned int), 1, binaryInputStream);
+  if (items != 1)
+  {
+    if (verbose)
+      printf("Error in ObjMesh::loadFromBinary: cannot read the number of total bytes.\n");
+    return 1;
+  }
+
+  // important: subtract the bytes used to save totalBytes
+  totalBytes -= sizeof(unsigned int);
+
+  vector<unsigned char> objMeshBuffer;
+  unsigned char * objMeshBufferPtr = NULL;
+  if (stream == FILE_STREAM)
+  {
+    try
+    {
+      objMeshBuffer.resize(totalBytes);
+    }
+    catch (std::bad_alloc const &)
+    {
+      printf("Error in ObjMesh::loadFromBinary: cannot allocate buffer to read entire obj mesh.\n");
+      return 1;
+    }
+    objMeshBufferPtr = &objMeshBuffer[0];
+
+    items = genericRead(objMeshBufferPtr, sizeof(unsigned char), totalBytes, binaryInputStream);
+    if (items != totalBytes)
+    {
+      if (verbose)
+        printf("Error in ObjMesh::loadFromBinary: cannot read from the binary file.\n");
+      return 1;
+    }
+  }
+  else
+    objMeshBufferPtr = (unsigned char *) binaryInputStream_; // use current input stream directly, NOT the binaryInputStream wrapper
+
+  void * binaryInputBuffer = &objMeshBufferPtr;
+
+  // read whether the mesh has materials or not
+  int hasMaterials = 0;
+  readFromMemory(&hasMaterials, sizeof(int), 1, binaryInputBuffer);
+  if (!hasMaterials)
+  {
+    // add default material
+    materials.emplace_back();
+  }
+
+  vector<double> doubleVec;
+  if (hasMaterials)
+  {
+    // number of materials
+    unsigned int numObjMaterials;
+    readFromMemory(&numObjMaterials, sizeof(unsigned int), 1, binaryInputBuffer);
+    if (numObjMaterials == 0)
+    {
+      // add default materials
+      materials.emplace_back();
+    }
+
+    // name of the materials
+    for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
+    {
+      // the length of current material name
+      unsigned int strLength;
+      readFromMemory(&strLength, sizeof(unsigned int), 1, binaryInputBuffer);
+
+      // material name
+      char * materialName = (char *) malloc (sizeof(char) * (strLength + 1));
+      readFromMemory(materialName, sizeof(char), strLength, binaryInputBuffer);
+      materialName[strLength] = '\0';
+
+      // add a new material
+      materials.emplace_back(materialName);
+    }
+
+    // material properties
+    // Ka, Kd, Ks, each of which has 3 doubles, plus Ns, a double
+    // So there are 10 doubles for every material
+    const int numDoubles = 10;
+    doubleVec.resize(numDoubles * numObjMaterials);
+    readFromMemory(&doubleVec[0], sizeof(double), numDoubles * numObjMaterials, binaryInputBuffer);
+    for (unsigned int materialIndex=0; materialIndex < numObjMaterials; materialIndex++)
+    {
+      unsigned int offset = materialIndex * numDoubles;
+      Vec3d Ka(&doubleVec[offset]);
+      Vec3d Kd(&doubleVec[offset+3]);
+      Vec3d Ks(&doubleVec[offset+6]);
+      double shininess = doubleVec[offset+9] * 128.0 / 1000.0;
+      materials[materialIndex].setKa(Ka);
+      materials[materialIndex].setKd(Kd);
+      materials[materialIndex].setKs(Ks);
+      materials[materialIndex].setShininess(shininess);
+    }
+
+    // number of materials which have map_Kd images
+    unsigned int numMaterialsHasKdImages;
+    readFromMemory(&numMaterialsHasKdImages, sizeof(unsigned int), 1, binaryInputBuffer);
+    for (unsigned int materialIndex=0; materialIndex < numMaterialsHasKdImages; materialIndex++)
+    {
+      // the material ID
+      unsigned int materialID;
+      readFromMemory(&materialID, sizeof(unsigned int), 1, binaryInputBuffer);
+
+      // material image
+      unsigned int strLength;
+      readFromMemory(&strLength, sizeof(unsigned int), 1, binaryInputBuffer);
+      char textureFilename[4096];
+      readFromMemory(textureFilename, sizeof(char), strLength, binaryInputBuffer);
+      textureFilename[strLength] = '\0';
+
+      // set the image
+      materials[materialID].setTextureFilename(textureFilename);
+    }  // for materialIndex
+  }  // if (hasMaterials)
+
+  // the number of vertices
+  unsigned int numVertices;
+  readFromMemory(&numVertices, sizeof(unsigned int), 1, binaryInputBuffer);
+
+  // vertices
+  doubleVec.resize(numVertices * 3);
+  readFromMemory(&doubleVec[0], sizeof(double), numVertices * 3, binaryInputBuffer);
+  for(unsigned int vertexIndex=0; vertexIndex < numVertices; vertexIndex++)
+  {
+    unsigned int offset = vertexIndex * 3;
+    Vec3d pos(doubleVec[offset], doubleVec[offset+1], doubleVec[offset+2]);
+    vertexPositions.push_back(pos);
+  }
+
+  // the number of texture coordinates
+  unsigned int numTexCoordinates;
+  readFromMemory(&numTexCoordinates, sizeof(unsigned int), 1, binaryInputBuffer);
+
+  // texture coordinates
+  if (numTexCoordinates > 0)
+  {
+    doubleVec.resize(numTexCoordinates * 3);
+    readFromMemory(&doubleVec[0], sizeof(double), numTexCoordinates * 3, binaryInputBuffer);
+    for(unsigned int textureCoordinateIndex=0; textureCoordinateIndex < numTexCoordinates; textureCoordinateIndex++)
+    {
+      unsigned int offset = textureCoordinateIndex * 3;
+      Vec3d tex(doubleVec[offset], doubleVec[offset+1], doubleVec[offset+2]);
+      textureCoordinates.push_back(tex);
+    }
+  }
+
+  // the number of normals
+  unsigned int numNormals;
+  readFromMemory(&numNormals, sizeof(unsigned int), 1, binaryInputBuffer);
+
+  // normals
+  if (numNormals > 0)
+  {
+    doubleVec.resize(numNormals * 3);
+    readFromMemory(&doubleVec[0], sizeof(double), numNormals * 3, binaryInputBuffer);
+    for(unsigned int normalIndex=0; normalIndex < numNormals; normalIndex++)
+    {
+      unsigned int offset = normalIndex * 3;
+      Vec3d normal(doubleVec[offset], doubleVec[offset+1], doubleVec[offset+2]);
+      normals.push_back(normal);
+    }
+  }
+
+  // the number of groups
+  unsigned int numGroups;
+  readFromMemory(&numGroups, sizeof(unsigned int), 1, binaryInputBuffer);
+
+  // groups and faces
+  for(unsigned int groupIndex=0; groupIndex < numGroups; groupIndex++)
+  {
+    // group name
+    unsigned int strLength;
+    readFromMemory(&strLength, sizeof(unsigned int), 1, binaryInputBuffer);
+    char groupName[4096];
+    readFromMemory(groupName, sizeof(char), strLength, binaryInputBuffer);
+    groupName[strLength] = '\0';
+
+    // group material index
+    if (hasMaterials)
+    {
+      // material index
+      unsigned int materialIndex;
+      readFromMemory(&materialIndex, sizeof(unsigned int), 1, binaryInputBuffer);
+      groups.push_back(Group(groupName, materialIndex));
+    }
+    else
+    {
+      // use default material
+      groups.push_back(Group(groupName, defaultMaterialIndex));
+    }
+
+    // the number of faces of the current group
+    unsigned int numFaces;
+    readFromMemory(&numFaces, sizeof(unsigned int), 1, binaryInputBuffer);
+
+    // the number of vertices in every face
+    unsigned int * numFaceVerticesArray = (unsigned int *) malloc (sizeof(unsigned int) * numFaces);
+    readFromMemory(numFaceVerticesArray, sizeof(unsigned int), numFaces, binaryInputBuffer);
+
+    unsigned int totalFaceVertices = 0;
+    for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
+      totalFaceVertices += numFaceVerticesArray[faceIndex];
+
+    // vertex indices of every face in the current group
+    unsigned int * vertexArray = (unsigned int *) malloc (sizeof(unsigned int) * totalFaceVertices);
+    readFromMemory(vertexArray, sizeof(unsigned int), totalFaceVertices, binaryInputBuffer);
+
+    // texture coordinate indices of every face in the current group
+    unsigned int * texCoordinateArray = (unsigned int *) malloc (sizeof(unsigned int) * totalFaceVertices);
+    readFromMemory(texCoordinateArray, sizeof(unsigned int), totalFaceVertices, binaryInputBuffer);
+
+    // normal indices of every face in the current group
+    unsigned int * normalArray = (unsigned int *) malloc (sizeof(unsigned int) * totalFaceVertices);
+    readFromMemory(normalArray, sizeof(unsigned int), totalFaceVertices, binaryInputBuffer);
+
+    unsigned int vertexCount = 0;
+    for (unsigned int faceIndex=0; faceIndex < numFaces; faceIndex++)
+    {
+      Face currentFace;
+      for (unsigned int vertexIndex=0; vertexIndex < numFaceVerticesArray[faceIndex]; vertexIndex++)
+      {
+        unsigned int pos = vertexArray[vertexCount] - 1; // 0-indexed
+        std::pair< bool, unsigned int > texPos;
+        std::pair< bool, unsigned int > normal;
+
+        if (texCoordinateArray[vertexCount] == 0)  // no texture coordinate index
+        {
+          texPos.first = false;
+          texPos.second = 0;
+        }
+        else
+        {
+          texPos.first = true;
+          texPos.second = texCoordinateArray[vertexCount] - 1;
+        }
+
+        if (normalArray[vertexCount] == 0)  // no normal index
+        {
+          normal.first = false;
+          normal.second = 0;
+        }
+        else
+        {
+          normal.first = true;
+          normal.second = normalArray[vertexCount] - 1;
+        }
+        currentFace.addVertex(Vertex(pos, texPos, normal));
+
+        vertexCount++;
+      }  // for vertexIndex (current face)
+
+      groups[groupIndex].addFace(currentFace);
+    }
+
+    free(numFaceVerticesArray);
+    free(vertexArray);
+    free(texCoordinateArray);
+    free(normalArray);
+  }  // for groupIndex
+
+  // search if there is a "default" material, if not, add it
+  addDefaultMaterial();
+
+  return 0;
+}
+
+unsigned int ObjMesh::readFromMemory(void * buf, unsigned int elementSize, unsigned int numElements, void * memoryLocation_)
+{
+  unsigned char * memoryLocation = (unsigned char *)(*(void **)(memoryLocation_));
+  unsigned int numBytes = elementSize * numElements;
+  memcpy(buf, memoryLocation, numBytes);
+  (*(void **)(memoryLocation_)) = (void *)((unsigned char *)(*(void **)(memoryLocation_)) + numBytes);
+  return numElements;
+}
+
+unsigned int ObjMesh::readFromFile(void * buf, unsigned int elementSize, unsigned int numElements, void * fin)
+{
+  return fread(buf, elementSize, numElements, (FILE*)fin);
+}
+
+void ObjMesh::removeAllVertexNormals()
+{
+  normals.clear();
+  forEachFace([](ObjMesh::Face & f)
+  {
+    for(size_t i = 0; i < f.getNumVertices(); i++)
+      f.getVertex(i).removeNormalIndex();
+  });
+}
+
+void ObjMesh::removeAllTextureCoordinates()
+{
+  textureCoordinates.clear();
+  forEachFace([](ObjMesh::Face & f)
+  {
+    for(size_t i = 0; i < f.getNumVertices(); i++)
+      f.getVertex(i).removeTextureCoordinateIndex();
+  });
 }
diff --git a/src/libobjMesh/objMesh.h b/libraries/objMesh/objMesh.h
similarity index 51%
rename from src/libobjMesh/objMesh.h
rename to libraries/objMesh/objMesh.h
index d350b53b1d03b68b8a3a37fff3df7a6c4600c541..9bf6ddab4141cea0ad7f9256c8858516fcf6dccd 100644
--- a/src/libobjMesh/objMesh.h
+++ b/libraries/objMesh/objMesh.h
@@ -1,19 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder,     *
+ *               Yili Zhao, Yijing Li                                    *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,37 +34,35 @@
 #ifndef _OBJMESH_H_
 #define _OBJMESH_H_
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 #include <math.h>
 #include <string>
 #include <vector>
-#include <list>
 #include <map>
 #include <iostream>
+#include <set>
 #include <algorithm>
 #include <assert.h>
+#include <exception>
+#include <memory>
+#include <functional>
 #include "minivector.h"
 
-namespace vega
-{
 /*
-   This class stores a 3D surface mesh, loaded from an .obj file.  
+   This class stores a 3D surface mesh, loaded from an .obj file.
    It makes it possible to access the mesh geometric primitives and perform
    various geometric calculations and operations on the mesh.
 
    A quick summary of the obj format:
-     1.  vertices, normals, and texture coordinates are all specified in 
-         a global 1-based namespace.  
+     1.  vertices, normals, and texture coordinates are all specified in
+         a global 1-based namespace.
      2.  faces are divided into groups
      3.  each face consists of a listing of vertices, like this:
             f 1/1/1 2/2/2 3/3/3
          These numbers are references to the vertices, normals, and texture
          coordinates, all of which were specified (as mentioned above) in
-         a global 1-based namespace.  
-  
+         a global 1-based namespace. The values can be negative. A value of -1
+         means the *last* vertex, -2 is next-to-last vertex and so on.
+
    To access a group/face/vertex from the ObjMesh file once it has been constructed, do the following:
      1.  Get the list of groups out of the .obj file using the "getGroupNames" function.
      2.  Select the group you want, and retrieve it using the "getGroup" function.
@@ -69,18 +72,20 @@ namespace vega
          using the member functions of the Vertex class.
      6.  Look these up in the .obj file global namspace using the "getPosition",
          "getTextureCoordinate", and "getNormal" member functions of the ObjMesh class.
-  
-   Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder, 
+
+   Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder,
                  CMU, 2001-2007, MIT 2007-2009, USC 2009-2011
 */
 
 // thrown by the ObjMesh constructor
-class ObjMeshException
+class ObjMeshException : public std::exception
 {
 public:
   ObjMeshException(const std::string & reason);
   ObjMeshException(const std::string & reason, const std::string & filename, unsigned int line);
-  std::string getReason() const { return reason; }
+  virtual ~ObjMeshException() throw() {}
+  const std::string & getReason() const throw() { return reason; }
+  virtual const char * what() const throw() { return reason.c_str(); }
 
 protected:
   std::string reason;
@@ -90,144 +95,35 @@ class ObjMesh
 {
 public:
 
-  // ======= member classes =======
-
-  class Vertex
-  {
-    public:
-      explicit Vertex(const unsigned int & positionIndex_)
-        : positionIndex(positionIndex_), textureIndex(std::make_pair(false, 0)), normalIndex(std::make_pair(false, 0)) {}
-
-      explicit Vertex(const unsigned int & positionIndex_, const unsigned int & textureIndex_) 
-        : positionIndex(positionIndex_), textureIndex(std::make_pair(true, textureIndex_)), normalIndex(std::make_pair(false, 0)) {}
-
-      explicit Vertex(const unsigned int & positionIndex_, const unsigned int & textureIndex_, const unsigned int & normalIndex_)
-        : positionIndex(positionIndex_), textureIndex(std::make_pair(true, textureIndex_)), normalIndex(std::make_pair(true, normalIndex_)) {}
-
-      explicit Vertex(const unsigned int & positionIndex_, const std::pair<bool, unsigned int> textureIndex_, const std::pair<bool, unsigned int> normalIndex_)
-        : positionIndex(positionIndex_), textureIndex(textureIndex_), normalIndex(normalIndex_) {}
-
-      inline unsigned int getPositionIndex() const { return positionIndex; }
-      inline unsigned int getNormalIndex() const { assert(normalIndex.first); return normalIndex.second; }
-      inline unsigned int getTextureCoordinateIndex() const { assert(textureIndex.first); return textureIndex.second; }
-      inline std::pair< bool, unsigned int > getTextureIndexPair() const { return textureIndex; }
-      inline std::pair< bool, unsigned int > getNormalIndexPair() const { return normalIndex; }
-    
-      // Normals and texture coordinates are not considered "required" in the
-      // obj file format standard.  Check these before retrieving them.
-      inline bool hasNormalIndex() const { return normalIndex.first; }
-      inline bool hasTextureCoordinateIndex() const { return textureIndex.first; }
-                
-      inline void setPositionIndex(unsigned int positionIndex_) { positionIndex = positionIndex_; }
-      inline void setNormalIndex(unsigned int normalIndex_) { normalIndex = std::pair<bool,unsigned int>(true, normalIndex_); }
-      inline void setTextureCoordinateIndex(unsigned int textureCoordinate_) { textureIndex = std::pair<bool,unsigned int>(true, textureCoordinate_); }
-
-    protected:
-      unsigned int positionIndex; 
-      std::pair< bool, unsigned int > textureIndex;
-      std::pair< bool, unsigned int > normalIndex;	
-  };
+  typedef enum {ASCII, BINARY, BY_EXT, NUM_FILE_FORMATS} fileFormatType;
+  typedef enum {FILE_STREAM, MEMORY_STREAM} streamType;
 
-  class Material
-  {
-    public:
-      explicit Material(const std::string name_, const Vec3d & Ka_, const Vec3d & Kd_, const Vec3d & Ks_, double shininess_=0, const std::string textureFilename_=std::string()): 
-        Ka(Ka_), Kd(Kd_), Ks(Ks_), shininess(shininess_), alpha(1.0), name(name_), textureFilename(textureFilename_) {}
-
-      explicit Material(): 
-        Ka(Vec3d(1,1,1)), Kd(Vec3d(1,1,1)), Ks(Vec3d(1,1,1)), shininess(0), alpha(1.0), name(std::string("default")) {}
-
-      inline std::string getName() const { return name; }
-      inline Vec3d getKa() const { return Ka; }
-      inline Vec3d getKd() const { return Kd; }
-      inline Vec3d getKs() const { return Ks; }
-      inline double getShininess() const { return shininess; }
-      inline double getAlpha() const { return alpha; }
-
-      inline void setName(const std::string & name_) { name = name_; }
-      inline void setKa(Vec3d & Ka_) { Ka = Ka_; }
-      inline void setKd(Vec3d & Kd_) { Kd = Kd_; }
-      inline void setKs(Vec3d & Ks_) { Ks = Ks_; }
-      inline void setShininess(double shininess_) { shininess = shininess_; }
-      inline void setAlpha(double alpha_) { alpha = alpha_; }
-
-      inline bool hasTextureFilename() const { return (textureFilename.size() > 0); }
-      inline std::string getTextureFilename() const { return textureFilename; }
-
-      bool operator==(const Material & material2) const;
-
-    protected:
-      Vec3d Ka, Kd, Ks;
-      double shininess;
-      double alpha;
-      std::string name;
-      std::string textureFilename;
-  };
-
-  class Face
-  {
-    public:
-      explicit Face() : faceNormal(std::make_pair(false, Vec3d())) { vertices.reserve(3); }
-      explicit Face(const Vertex & v1, const Vertex & v2, const Vertex & v3) : faceNormal(std::make_pair(false, Vec3d()))
-      {
-        vertices.reserve(3);
-        vertices.push_back(v1);
-        vertices.push_back(v2);
-        vertices.push_back(v3);
-      }
-
-      inline size_t getNumVertices() const { return vertices.size(); }
-      inline Vertex getVertex(unsigned int vertex) const { return vertices[vertex]; }
-      inline const Vertex * getVertexHandle(unsigned int vertex) const { return &(vertices[vertex]); }
-
-      inline void setFaceNormal(const Vec3d & normal) { faceNormal = std::pair<bool, Vec3d>(true, normal); }
-      inline bool hasFaceNormal() const { return faceNormal.first; };
-      inline Vec3d getFaceNormal() const { assert(faceNormal.first); return faceNormal.second; }
-          
-      inline void addVertex(const Vertex & v) { vertices.push_back(v); }
-      inline void reverseVertices() { reverse(vertices.begin(), vertices.end()); }
-      inline void printVertices() const { for(unsigned int i=0; i<vertices.size(); i++) std::cout << vertices[i].getPositionIndex() << " "; }
-
-    protected:
-      std::vector< Vertex > vertices;
-      std::pair< bool, Vec3d > faceNormal;
-  };
-
-  class Group
-  {
-    public:
-      explicit Group(const std::string name_, unsigned int materialIndex_=0)
-        : name(name_), materialIndex(materialIndex_) {}
-
-      inline size_t getNumFaces() const { return faces.size(); }
-      inline Face getFace(unsigned int face) const { return faces[face]; }
-      inline const Face * getFaceHandle(unsigned int face) const { return &(faces[face]); }
-      inline std::string getName() const { return name; }
-      void setName(const std::string & name_) { name = name_; }
-      inline unsigned int getMaterialIndex() const { return materialIndex; }
-      inline void setMaterialIndex(unsigned int materialIndex_) { materialIndex = materialIndex_; }
-
-      inline void addFace(const Face & face) { faces.push_back(face); }
-      inline void reverseFace(unsigned int face) { faces[face].reverseVertices(); }
-      void removeFace(unsigned int face);
-   
-    protected:
-      std::string name;
-      unsigned int materialIndex;
-      std::vector< Face > faces;
-  };
+  // ======= member class declarations: Vertex, Material, Face, Group =======
+  class Vertex;
+  class Material;
+  class Face;
+  class Group;
 
   // ======= constructors =======
 
-  // Constructs the OBJ file and reads it in.  Throws an ObjMeshException if
-  // it fails for any reason (e.g., file not there, etc.).
-  explicit ObjMesh(const std::string & filename, int verbose=1);
-
+  // Constructs the OBJ file and reads it in.  Throws an ObjMeshException if it fails for any reason (file not there, etc.).
+  explicit ObjMesh(const std::string & filename, fileFormatType fileFormat = BY_EXT, int verbose = 0);
   // makes an empty structure
-  explicit ObjMesh() {}
-
+  explicit ObjMesh() : diameter(0.0), bmin(0.0), bmax(0.0), center(0.0), cubeHalf(0.0) {}
+  // creates a triangle mesh with a single group
+  explicit ObjMesh(int numVertices, const double * vertices, int numTriangles, const int * triangles);
   // creates a mesh with a single group
-  explicit ObjMesh(int numVertices, double * vertices, int numTriangles, int * triangles);
+  explicit ObjMesh(int numVertices, const double * vertices, int numFaces, const int* faceVertexCounts, const int * faces);
+
+  explicit ObjMesh(const std::vector<Vec3d> & vertexPositions, const std::vector<Vec3i> & triangles);
+  // copy constructor
+  ObjMesh(const ObjMesh & objMesh) = default;
+  ObjMesh(ObjMesh && objMesh) = default;
+  ObjMesh & operator = (const ObjMesh & objMesh) = default;
+  ObjMesh & operator = (ObjMesh && objMesh) = default;
+  // advanced usage:
+  // stream is usually FILE_STREAM
+  explicit ObjMesh(void * binaryInputStream, streamType stream, int verbose = 0);
 
   // ======= basic mesh info / stats =======
 
@@ -240,22 +136,23 @@ public:
   // retrieve a list of all the group names in the obj file.
   std::vector<std::string> getGroupNames() const;
   // the filename from which this obj mesh was loaded (if it was loaded)
-  inline std::string getFilename() { return filename; }
+  inline const std::string & getFilename() const { return filename; }
 
   // prints info on the obj model
-  void printInfo() const; 
+  void printInfo() const;
 
   // ======= member data getters / setters =======
 
   // all locations are 0-indexed
   inline int getVertexIndex(unsigned int group, unsigned int face, unsigned int vertex) const { return groups[group].getFace(face).getVertex(vertex).getPositionIndex(); } // returns the global integer index of a specified group/face/vertex vertex
 
-  inline Vec3d getPosition(int vertexIndex) const { return vertexPositions[vertexIndex]; }
-  inline Vec3d getPosition(const Vertex & vertex) const { return vertexPositions[vertex.getPositionIndex()]; }
-  inline Vec3d getTextureCoordinate(int textureCoordinateIndex) const { return textureCoordinates[textureCoordinateIndex]; }
-  inline Vec3d getTextureCoordinate(const Vertex & vertex) const { return textureCoordinates[vertex.getTextureCoordinateIndex()]; }
-  inline Vec3d getNormal(int normalIndex) const { return normals[normalIndex]; }
-  inline Vec3d getNormal(const Vertex & vertex) const { return normals[vertex.getNormalIndex()]; }
+  inline const Vec3d & getPosition(int vertexIndex) const { return vertexPositions[vertexIndex]; }
+  inline const Vec3d & getPosition(const Vertex & vertex) const { return vertexPositions[vertex.getPositionIndex()]; }
+  inline const Vec3d & getPosition(const Face & f, int faceVtxID) const { return vertexPositions[f.getVertexPositionIndex(faceVtxID)]; }
+  inline const Vec3d & getTextureCoordinate(int textureCoordinateIndex) const { return textureCoordinates[textureCoordinateIndex]; }
+  inline const Vec3d & getTextureCoordinate(const Vertex & vertex) const { return textureCoordinates[vertex.getTextureCoordinateIndex()]; }
+  inline const Vec3d & getNormal(int normalIndex) const { return normals[normalIndex]; }
+  inline const Vec3d & getNormal(const Vertex & vertex) const { return normals[vertex.getNormalIndex()]; }
 
   inline void setPosition(int vertexIndex, const Vec3d & position) { vertexPositions[vertexIndex] = position; }
   inline void setPosition(Vertex & vertex, const Vec3d & position) { vertexPositions[vertex.getPositionIndex()] = position; }
@@ -264,53 +161,117 @@ public:
   inline void setNormal(int normalIndex, const Vec3d & normal) { normals[normalIndex] = normal; }
   inline void setNormal(Vertex & vertex, const Vec3d & normal) { normals[vertex.getNormalIndex()] = normal; }
 
-  Group getGroup(const std::string name) const; // retrieve a group by its name
-  unsigned int getGroupIndex(const std::string name) const; // obtain a group index by its name
+  const Group & getGroup(const std::string & name) const; // retrieve a group by its name
+  const Group & getGroup(unsigned int groupIndex) const { return groups[groupIndex]; }
+  Group & getGroup(unsigned int groupIndex) { return groups[groupIndex]; }
+  unsigned int getGroupIndex(const std::string & name) const; // obtain a group index by its name
+  // get group pointer. Warning: This pointer will be invalided if groups are modified by ObjMesh::removeGroup() or ObjMesh::addGroup() due to vector reallocation
   inline const Group * getGroupHandle(unsigned int groupIndex) const { return &(groups[groupIndex]); }
-
-  inline Material getMaterial(unsigned int materialIndex) const { return materials[materialIndex]; }
-  inline const Material * getMaterialHandle(unsigned int materialIndex) { return &materials[materialIndex]; }
+  inline Group * getGroupHandle(unsigned int groupIndex) { return &(groups[groupIndex]); }
+
+  // visit each face
+  // if skipNonFace, print warnings when face vtx is less than three and skip those faces
+  inline void forEachFace(std::function<void(ObjMesh::Face &)> f, bool skipNonFace = false);
+  inline void forEachFace(std::function<void(const ObjMesh::Face &)> f, bool skipNonFace = false) const;
+  inline void forEachFace(std::function<void(int gID, int fID, ObjMesh::Face &)> f, bool skipNonFace = false);
+  inline void forEachFace(std::function<void(int gID, int fID, const ObjMesh::Face &)> f, bool skipNonFace = false) const;
+
+  inline const Material & getMaterial(unsigned int materialIndex) const { return materials[materialIndex]; }
+  unsigned int getMaterialIndex(const std::string & name) const; // obtain a material index by its name
+  // get material pointer. Warning: This pointer will be invalided if materials are modified by ObjMesh::addMaterial() due to vector reallocation
+  inline const Material * getMaterialHandle(unsigned int materialIndex) const { return &materials[materialIndex]; }
+  inline Material * getMaterialHandle(unsigned int materialIndex) { return &materials[materialIndex]; }
   void setMaterialAlpha(double alpha);
   void setSingleMaterial(const Material & material); // erases all materials and sets a single material for the entire mesh
   int usesTextureMapping(); // 0 = no group uses a material that references a texture image, 1 = otherwise
 
   // ======= member data adders =======
 
+  void addDefaultMaterial();
   inline void addMaterial(const Material & material) { materials.push_back(material); }
-  inline void addMaterial(const std::string name, const Vec3d & Ka, const Vec3d & Kd, const Vec3d & Ks, double shininess, const std::string textureFilename=std::string()) { materials.push_back(Material(name, Ka, Kd, Ks, shininess, textureFilename));}
+  inline void addMaterial(const std::string & name, const Vec3d & Ka, const Vec3d & Kd, const Vec3d & Ks, double shininess, const std::string textureFilename=std::string()) { materials.emplace_back(name, Ka, Kd, Ks, shininess, textureFilename);}
   inline void addGroup(const Group & group) { groups.push_back(group);}
-  inline void addGroup(const std::string name) { groups.push_back(Group(name));}
+  inline void addGroup(Group && group) { groups.emplace_back(std::move(group));}
+  inline void addGroup(const std::string & name) { groups.push_back(Group(name));}
+  void removeGroups(const std::vector<int> & groupIDs); // groupIDs must be sorted
   void removeGroup(const int groupIndex);
   void removeGroup(const std::string name);
   void removeAllGroups();
+  void removeFaces(const std::map<int, std::set<int>> & faceIDsAtGroup); // faceIDsAtGroup: groupID -> faceIDs to remove
   inline void addVertexPosition (const Vec3d & pos) { vertexPositions.push_back(pos); }
+  template<class Vec3dContainer> void addVertexPositions(const Vec3dContainer & pos) { vertexPositions.insert(vertexPositions.end(), pos.begin(), pos.end()); }
   inline void addVertexNormal (const Vec3d & normal) { normals.push_back(normal); }
   inline void addTextureCoordinate (const Vec3d & textCoord) { textureCoordinates.push_back(textCoord); }
   inline void addFaceToGroup(const Face & face, unsigned int group) { groups[group].addFace(face); }
+  void removeAllVertexNormals(); // clear normals buffer and set all Vertices to have no normal indices
+  void removeAllTextureCoordinates(); // clear textureCoordinates buffer and set all Vertices to have no texture coordinate indices
 
   // ======= optional member data setters =======
+  // used to set optional values that are not filled upon construction
 
-  // used to set values that are not filled upon construction
-
-  void computePseudoNormals(); // vertex pseudonormals
-  inline Vec3d getPseudoNormal(unsigned int i) const { return pseudoNormals[i]; } // must first call "computePseudoNormals"
-
-  void computeEdgePseudoNormals(); // assumes that the faces are oriented coherently
-  int getEdgePseudoNormal(unsigned int i, unsigned int j, Vec3d * pseudoNormal) const; // must first call "computeEdgePseudoNormals"
+  // ===== vertex-face neighbors =====
 
+  // builds vertex face neighbors data and stores them in internal data vertexFaceNeighbors
   void buildVertexFaceNeighbors();
   void clearVertexFaceNeighbors();
-  void buildVertexNormals(double angle); // must generate vertex face neighbors and face normals first
-  void buildVertexNormalsFancy(double angle); // must generate vertex face neighbors and face normals first
+  // must call buildVertexFaceNeighbors before
+  int getVertexNumNeighborFaces(int vtxID) const { return vertexFaceNeighbors[vtxID].size(); }
+  // neighborID < getVertexNumNeighborFaces(vtxID)
+  // return <groupID, faceID>
+  std::pair<int,int> getVertexNeighborFace(int vtxID, int neighborID) const { return std::make_pair(vertexFaceNeighbors[vtxID][neighborID].groupIndex, vertexFaceNeighbors[vtxID][neighborID].faceIndex); }
+
+  // ===== vtx/face/edge normals =====
+
+  // computes vertex pseudo normals from sums of angle-weighted neighboring triangle normals; stores them in internal data pseudoNormals
+  void computePseudoNormals();
+  // must first call "buildPseudoNormals"
+  inline const Vec3d & getPseudoNormal(unsigned int vtx) const { return pseudoNormals[vtx]; }
+
+  // warning: the normal is computed based on the first three vertices in a face (assumes planar face)
+  // computes by a cross-product of face edges; return Vec3d(0.0) if a degenerate or invalid face encounted
+  Vec3d computeFaceNormal(const Face & face) const;
+  // builds face normals for all the faces
+  // writes normals internally to each Face object, stored in Face::faceNormal
+  // faceNormals can be detected by Face::hasFaceNormal() and retrieved by Face::getFaceNormal()
+  // if verbose != 0, print warning messages when encountering invalid or degenerate faces
+  void buildFaceNormals(int verbose = 0);
+  void clearFaceNormals();
+
+  // assumes that the faces are oriented coherently; computes edge pseudo normals from sums of neighboring triangle normals
+  // stores them in internal data edgePseudoNormals
+  void computeEdgePseudoNormals();
+  // must first call "computeEdgePseudoNormals"; returns 0 if edge (i,j) found, 1 otherwise
+  int getEdgePseudoNormal(unsigned int i, unsigned int j, Vec3d * pseudoNormal) const;
+
+  // buildVertexNormals(Fancy):
+  // assigns all vertex normals as average neighboring face normals
+  // note: uses stored vertex face neighbors data; calls buildVertexFaceNeighbors() if not yet called
+  // note: also uses stored face normals data; calls buildFaceNormals() if not yet called
+  // input angle is in degrees, if faces around a vertex have angles >= input angle, they are considered as having hard edges and those faces have separated normals on the vertex,
+  // otherwise, those faces are joined by soft edges and they share the same vertex normal as averages of the face normals for the soft edge
+  void buildVertexNormals(double angle);
+  // another version of buildVertexNormals: assumes no hard edges, assigns vertex normals as average face normals, prints errors when hard edges that form angles >= input angles
+  void buildVertexNormalsFancy(double angle);
+
+  // generates vertex normals by averaging normals for adjacent faces
+  // any pre-specified normals are overwritten by these new normals
+  // does not assume a triangular mesh
+  void setNormalsToAverageFaceNormals();
+  // sets vertex normals to face normals
+  void setNormalsToFaceNormals();
+  // sets vertex normals to vertex pseudonormals
+  void setNormalsToPseudoNormals();
+
+  // ===== other data =====
 
-  void computeSurfaceAreaPerVertex(); 
+  void computeSurfaceAreaPerVertex();
   inline double getSurfaceAreaPerVertex(unsigned int i) const { return surfaceAreaPerVertex[i]; } // must first call "computeSurfaceAreaPerVertex"
 
   void initSurfaceSampling();
   Vec3d getSurfaceSamplePosition(double sample) const; // sample should be between 0 and 1; must call "initSurfaceSampling" first
 
   // allows one to query the vertex indices of each triangle
-  // order of triangles is same as in "exportGeometry": for every group, traverse all faces, and tesselate each face into triangles 
+  // order of triangles is same as in "exportGeometry": for every group, traverse all faces, and tesselate each face into triangles
   void initTriangleLookup(); // call this first
   void clearTriangleLookup();
   void getTriangle(int triangleIndex, int * vtxA, int * vtxB, int * vtxC); // must call "initTriangleLookup" first
@@ -344,30 +305,31 @@ public:
   void getMeshRadius(const Vec3d & centroid, double * radius) const;
   void getMeshGeometricParameters(Vec3d * centroid, double * radius) const;
 
-  void exportGeometry(int * numVertices, double ** vertices, int * numTriangles = NULL, int ** triangles = NULL, 
-    int * numGroups = NULL, int ** triangleGroups = NULL) const; // all faces are triangulated before exporting 
-  void exportFaceGeometry(int * numVertices, double ** vertices, int * numFaces = NULL, int ** faceCardinalities = NULL, int ** faces = NULL) const; // faces are not triangulated before exporting
+  void exportGeometry(int * numVertices, double ** vertices, int * numTriangles = NULL, int ** triangles = NULL,
+    int * numGroups = NULL, int ** triangleGroups = NULL) const; // all faces are triangulated before exporting
+  void exportGeometry(std::vector<Vec3d> & vertices, std::vector<Vec3i> & triangles) const;
+  void exportFaceGeometry(int * numVertices, double ** vertices, int * numFaces = NULL, int ** faceCardinalities = NULL, int ** faces = NULL,
+    int * numGroups = NULL, int ** faceGroups = NULL) const; // faces are not triangulated before exporting
   void exportUVGeometry(int * numUVVertices, double ** UVVertices, int * numUVTriangles, int ** UVTriangles) const; // exports the geometry in the texture coordinate space
+  void exportTriangles(std::vector<Vec3i> & triangles) const; // push_back all triangulated faces into triangles
+  // also push back <groupID, faceID> each triagnle belongs to
+  void exportTriangles(std::vector<Vec3i> & triangles, std::vector<std::pair<int,int>> & orginalGroupAndFaceIDs) const;
 
   Vec3d computeFaceCentroid(const Face & face) const;
   double computeFaceSurfaceArea(const Face & face) const; // of a single face
   void computeFaceSurfaceAreas(std::vector<double> & surfaceAreas) const; // of all faces
 
-  // warning: the normal is computed based on the first three vertices in a face (assumes planar face):
-  Vec3d computeFaceNormal(const Face & face) const; // using a cross-product of face edges; does not modify "face"
-  void buildFaceNormals(); // builds face normals for all the faces (and writes them internally to each Face object)
-
   double computeMass(const std::vector<double> & groupSurfaceMassDensities) const; // of the entire mesh; second argument gives the surface mass density for each group; its length must equal the number of groups
   Vec3d computeCenterOfMass_Vertices() const; // of the vertices
   Vec3d computeCenterOfMass_Triangles() const; // of the triangular surface
 
   Vec3d computeCenterOfMass_Triangles(const std::vector<double> & groupSurfaceMassDensities) const; // second argument gives the surface mass density for each group
 
-  void computeInertiaTensor_Triangles(double IT[6]) const; // of the triangular surface, with respect to the center of mass, assumes uniform mass density on the triangles = 1 
+  void computeInertiaTensor_Triangles(double IT[6]) const; // of the triangular surface, with respect to the center of mass, assumes uniform mass density on the triangles = 1
   void computeInertiaTensor_Triangles(double mass, double IT[6]) const; // of the triangular surface, with respect to the center of mass, assumes uniform density on the triangles, which is set such that the total object mass equals "mass"
   void computeInertiaTensor_Triangles(const std::vector<double> & groupSurfaceMassDensities, double IT[6]) const; // // of the triangular surface, with respect to the center of mass, based on the given surface mass density for each group
 
-  static double computeTriangleSurfaceArea(Vec3d & p0, Vec3d & p1, Vec3d & p2); // compute surface area of a triangle
+  static double computeTriangleSurfaceArea(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2); // compute surface area of a triangle
   double computeSurfaceArea() const; // of the entire mesh
   void computeSurfaceAreaPerGroup(std::vector<double> & surfaceAreas) const; // for each group separately
   // computes masses "belonging" to each vertex, given the surface mass densities
@@ -386,48 +348,66 @@ public:
 
   void scaleUniformly(const Vec3d & center, double factor); // scales the model uniformly, with center being the center of the scaling
   void transformRigidly(const Vec3d & translation, const Mat3d & rotation);
-  void deform(double * u);
+  void deform(const double * u);
 
   int removeDuplicatedMaterials();
   int removeIsolatedVertices(); // removes vertices that don't appear in any triangle
   int removeIsolatedTextureCoordinates(); // removes texture coordinates that are not referenced
   int removeIsolatedNormals(); // removes normals that are not referenced
-  int removeZeroAreaFaces();
-
-  // generates vertex normals by averaging normals for adjacent faces
-  // any pre-specified normals are overwritten by these new normals
-  // does not assume a triangular mesh
-  void setNormalsToAverageFaceNormals(); 
-  // sets vertex normals to face normals
-  void setNormalsToFaceNormals();       
-  // sets vertex normals to vertex pseudonormals
-  void setNormalsToPseudoNormals();
-
+  // removes faces with zero area due to duplicate vertex indices or vertices lie on the same line
+  // Note: only works on triangle meshes
+  // returns num zero faces removed
+  int removeZeroAreaFaces(int verbose=0);
+  int removeInvalidFaces(); // removes faces with duplicate vertex indices
+  // removes faces that have an edge shared by two other faces AND an edge not shared by any other face (making the mesh more manifold)
+  // this function does one iteration of this process; you may need to call it again to continue removing faces, until the function returns 0
+  int removeHangingFaces();
+  // collapses edges that are shared by more than two faces
+  // this function does one iteration of this process; you may need to call it again to continue removing faces, until the function returns 0
+  int removeNonManifoldEdges();
+  void collapseEdge(unsigned int vertexA, unsigned int vertexB, int removeIsolatedVertices=1); // collapses the edge between vertices vertexA and vertexB
+
+  // permutation: old vtxID -> new vtx ID, its size is #vertices
+  // this method can also be used to merge vertices
   void renumberVertices(const std::vector<int> & permutation);
 
-  // merges all the specified groups into a single group 
+  // merges all the specified groups into a single group
   // groupIndices need not be sorted
   // the index of the merged group is set to the smallest index among "groupIndices"
   void mergeGroups(const std::vector<int> & groupIndices); // 0-indexed
 
   void removeEmptyGroups();
 
+  // move faces to target group
+  // groupFaceIDs stores <groupID, faceID> pair for each face
+  void moveFacesToGroup(const std::vector<std::pair<int,int>> & groupFaceIDs, int targetGroupID);
+
+  void appendMesh(const ObjMesh * mesh); // appends "mesh" to this mesh
+
   // ======= mesh cloning (with modifications) =======
 
   // creates a cloned mesh, keeping the specified faces in groups
-  ObjMesh * clone(const std::vector<std::pair<int, int> > & groupsAndFaces, int removeIsolatedVertices=1) const; 
+  ObjMesh * clone(const std::vector<std::pair<int, int> > & groupsAndFaces, int removeIsolatedVertices=1) const;
 
   // splits the mesh into groups, one per each connected component
   // if withinGroupsOnly=0, splitting is global, which means that some groups may be fused into one bigger group
   // if withinGroupsOnly=1, splitting is performed within each group only
-  ObjMesh * splitIntoConnectedComponents(int withinGroupsOnly=0, int verbose=1) const;
-  // extracts a specified group
+  ObjMesh * splitIntoConnectedComponents(int withinGroupsOnly=0, int verbose=0) const;
+  // extracts a specified group, vtxIDs are relabelled to contain only the vertices used in the group
   ObjMesh * extractGroup(unsigned int group, int keepOnlyUsedNormals = 1, int keepOnlyUsedTextureCoordinates = 1) const;
 
   // ======= file output =======
 
-  // saves to an obj file (including saving materials to filename.mtl if so requested)
-  void save(const std::string & filename, int outputMaterials=0, int verbose=1) const;
+  // saves to an obj file (including saving materials to filename.mtl if outputMaterials=1)
+  void save(const std::string & filename, int outputMaterials=0, fileFormatType fileFormat = BY_EXT, int verbose=1) const;
+
+  // precision: #digits in the output floating-point values, -1: use default precision in c++
+  void saveToAscii(const std::string & filename, int outputMaterials=0, int verbose=1, int precision = -1) const;
+  // saves obj and mtl together to a binary file
+  // return 0 if succeeded
+  int saveToBinary(const std::string & filename, int outputMaterials = 0, int verbose = 0) const;
+
+  static int saveObjMeshesToBinary(const std::string & binaryFilename, int numObjMeshes, ObjMesh ** objMeshes, int * saveObjMeshesFlag, int outputMaterials = 0, int verbose = 0);
 
   // saves to a stl file (only saves geometry (not materials))
   void saveToStl(const std::string & filename) const;
@@ -442,14 +422,179 @@ public:
   // extracts directory name from a given path
   static void dirname(const char * path, char * result);
 
-  inline static bool isNaN(double x) { return (x != x); }
+  // ======= multifile input ========
+
+  // 0: succeeded
+  // 1: failed
+  static int loadObjMeshesFromBinary(const std::string & binaryFilename, int * numObjMeshes, ObjMesh *** objMeshes, int verbose = 0);
 
   // ======= advanced usage =======
- 
+
   // computes internal axis-aligned bounding box
   void computeBoundingBox(); // sets diameter, bmin, bmax, center, cubeHalf
 
+  // ======= member class definitions: Vertex, Material, Face, Group =======
+
+  class Vertex
+  {
+    public:
+      explicit Vertex(const unsigned int & positionIndex_)
+        : positionIndex(positionIndex_), textureIndex(std::make_pair(false, 0)), normalIndex(std::make_pair(false, 0)) {}
+
+      explicit Vertex(const unsigned int & positionIndex_, const unsigned int & textureIndex_)
+        : positionIndex(positionIndex_), textureIndex(std::make_pair(true, textureIndex_)), normalIndex(std::make_pair(false, 0)) {}
+
+      explicit Vertex(const unsigned int & positionIndex_, const unsigned int & textureIndex_, const unsigned int & normalIndex_)
+        : positionIndex(positionIndex_), textureIndex(std::make_pair(true, textureIndex_)), normalIndex(std::make_pair(true, normalIndex_)) {}
+
+      explicit Vertex(const unsigned int & positionIndex_, const std::pair<bool, unsigned int> textureIndex_, const std::pair<bool, unsigned int> normalIndex_)
+        : positionIndex(positionIndex_), textureIndex(textureIndex_), normalIndex(normalIndex_) {}
+
+      inline unsigned int getPositionIndex() const { return positionIndex; }
+      inline unsigned int getNormalIndex() const { assert(normalIndex.first); return normalIndex.second; }
+      inline unsigned int getTextureCoordinateIndex() const { assert(textureIndex.first); return textureIndex.second; }
+      inline std::pair< bool, unsigned int > getTextureIndexPair() const { return textureIndex; }
+      inline std::pair< bool, unsigned int > getNormalIndexPair() const { return normalIndex; }
+
+      // Normals and texture coordinates are not considered "required" in the
+      // obj file format standard.  Check these before retrieving them.
+      inline bool hasNormalIndex() const { return normalIndex.first; }
+      inline bool hasTextureCoordinateIndex() const { return textureIndex.first; }
+
+      inline void setPositionIndex(unsigned int positionIndex_) { positionIndex = positionIndex_; }
+      inline void setNormalIndex(unsigned int normalIndex_) { normalIndex = std::pair<bool,unsigned int>(true, normalIndex_); }
+      inline void setTextureCoordinateIndex(unsigned int textureCoordinate_) { textureIndex = std::pair<bool,unsigned int>(true, textureCoordinate_); }
+      inline void removeNormalIndex() { normalIndex.first = false; }
+      inline void removeTextureCoordinateIndex() { textureIndex.first = false; }
+
+    protected:
+      unsigned int positionIndex;
+      std::pair< bool, unsigned int > textureIndex;
+      std::pair< bool, unsigned int > normalIndex;
+  };
+
+  class Material
+  {
+    public:
+      explicit Material(const std::string name_="default", const Vec3d & Ka_=Vec3d(0.2), const Vec3d & Kd_=Vec3d(0.6), const Vec3d & Ks_=Vec3d(0.0), double shininess_=65.0, const std::string textureFilename_=std::string()):
+        Ka(Ka_), Kd(Kd_), Ks(Ks_), shininess(shininess_), alpha(1.0), name(name_), textureFilename(textureFilename_) {}
+
+      inline std::string getName() const { return name; }
+      inline const Vec3d & getKa() const { return Ka; }
+      inline const Vec3d & getKd() const { return Kd; }
+      inline const Vec3d & getKs() const { return Ks; }
+      inline double getShininess() const { return shininess; }
+      inline double getAlpha() const { return alpha; }
+
+      inline void setName(const std::string & name_) { name = name_; }
+      inline void setKa(const Vec3d & Ka_) { Ka = Ka_; }
+      inline void setKd(const Vec3d & Kd_) { Kd = Kd_; }
+      inline void setKs(const Vec3d & Ks_) { Ks = Ks_; }
+      inline void setShininess(double shininess_) { shininess = shininess_; }
+      inline void setAlpha(double alpha_) { alpha = alpha_; }
+      inline void setTextureFilename(const std::string & textureFilename_) { textureFilename = textureFilename_; }
+      inline void setTextureFilename(const char * textureFilename_) { textureFilename = std::string(textureFilename_); }
+
+      inline bool hasTextureFilename() const { return (textureFilename.size() > 0); }
+      inline std::string getTextureFilename() const { return textureFilename; }
+
+      bool operator==(const Material & material2) const;
+
+    protected:
+      Vec3d Ka, Kd, Ks;
+      double shininess;
+      double alpha;
+      std::string name;
+      std::string textureFilename;
+  };
+
+  class Face
+  {
+    public:
+      explicit Face() : faceNormal(std::make_pair(false, Vec3d())) { vertices.reserve(3); }
+      explicit Face(const Vertex & v1, const Vertex & v2, const Vertex & v3) :
+          vertices{{v1,v2,v3}} , faceNormal(std::make_pair(false, Vec3d())) {}
+      explicit Face(unsigned int posIndex1, unsigned int posIndex2, unsigned int posIndex3) :
+          vertices{{Vertex(posIndex1), Vertex(posIndex2), Vertex(posIndex3)}}, faceNormal(std::make_pair(false, Vec3d())) {}
+      explicit Face(unsigned int posIndex1, unsigned int posIndex2, unsigned int posIndex3, unsigned int posIndex4) :
+          vertices{{Vertex(posIndex1), Vertex(posIndex2), Vertex(posIndex3), Vertex(posIndex4)}}, faceNormal(std::make_pair(false, Vec3d())) {}
+      explicit Face(const Vec3i & posIndices) : Face(posIndices[0], posIndices[1], posIndices[2]) {}
+      explicit Face(const Vec4i & posIndices) : Face(posIndices[0], posIndices[1], posIndices[2], posIndices[3]) {}
+
+      inline size_t getNumVertices() const { return vertices.size(); }
+      // get vertex pointer. Warning: This pointer will be invalided if vertices are modified by Face::removeVertex(), Face::reverseVertices() or Face::addVertex() due to vector reallocation
+      inline Vertex & getVertex(unsigned int vertex) { return vertices[vertex]; }
+      inline const Vertex & getVertex(unsigned int vertex) const { return vertices[vertex]; }
+      inline Vertex * getVertexHandle(unsigned int vertex) { return &(vertices[vertex]); }
+      inline const Vertex * getVertexHandle(unsigned int vertex) const { return &(vertices[vertex]); }
+      inline unsigned int getVertexPositionIndex(unsigned int vertex) const { return vertices[vertex].getPositionIndex(); }
+      // get #triangles if triangulated
+      int getNumTriangles() const { return vertices.size() < 2 ? 0 : vertices.size() - 2; }
+      // get vertex indices in the triangle if triangulated
+      Vec3i getIndicesInTriangle(int triID) const { return Vec3i(vertices[0].getPositionIndex(), vertices[triID+1].getPositionIndex(), vertices[triID+2].getPositionIndex()); }
+
+      inline void setFaceNormal(const Vec3d & normal) { faceNormal = std::pair<bool, Vec3d>(true, normal); }
+      inline bool hasFaceNormal() const { return faceNormal.first; };
+      inline const Vec3d & getFaceNormal() const { assert(faceNormal.first); return faceNormal.second; }
+      inline void removeFaceNormal() { faceNormal.first = false; }
+
+      inline void addVertex(const Vertex & v) { vertices.push_back(v); }
+      inline void removeVertex(unsigned int i) { vertices.erase(vertices.begin() + i); }
+      inline void reverseVertices() { reverse(vertices.begin(), vertices.end()); }
+      inline void printVertices() const { for(unsigned int i=0; i<vertices.size(); i++) std::cout << vertices[i].getPositionIndex() << " "; }
+
+    protected:
+      std::vector< Vertex > vertices;
+      std::pair< bool, Vec3d > faceNormal;
+  };
+
+  class Group
+  {
+    public:
+      explicit Group(const std::string & name_ = "defaultGroup", unsigned int materialIndex_=0)
+        : name(name_), materialIndex(materialIndex_) {}
+
+      inline size_t getNumFaces() const { return faces.size(); }
+      // get face pointer. Warning: This pointer will be invalided if faces are modified by Group::removeFace() or ObjMesh::addFace() due to vector reallocation
+      inline Face & getFace(unsigned int face) { return faces[face]; }
+      inline const Face & getFace(unsigned int face) const { return faces[face]; }
+      inline const Face * getFaceHandle(unsigned int face) const { return &(faces[face]); }
+      inline Face * getFaceHandle(unsigned int face) { return &(faces[face]); }
+      inline const std::string & getName() const { return name; }
+      void setName(const std::string & name_) { name = name_; }
+      inline unsigned int getMaterialIndex() const { return materialIndex; }
+      inline void setMaterialIndex(unsigned int materialIndex_) { materialIndex = materialIndex_; }
+
+      inline void addFace(const Face & face) { faces.push_back(face); }
+      inline void addFace(Face && face) { faces.emplace_back(std::move(face)); }
+      inline void reverseFace(unsigned int face) { faces[face].reverseVertices(); }
+      void removeFace(unsigned int face);
+      void removeFaces(const std::set<int> & faceIDs);
+    protected:
+      friend class ObjMesh;
+      std::string name;
+      unsigned int materialIndex;
+      std::vector< Face > faces;
+  };
+
+
 protected:
+
+  static int loadObjMeshesFromBinary(FILE * fin, int * numObjMeshes, ObjMesh *** objMeshes, int verbose = 0);
+
+  // ======= file load =======
+  // return number of elments read from the memoryLocation
+  static unsigned int readFromMemory(void * buf, unsigned int elementSize, unsigned int numElements, void * memoryLocation);
+  static unsigned int readFromFile(void * buf, unsigned int elementSize, unsigned int numElements, void * fin);
+  int loadFromAscii(const std::string & filename, int verbose = 0);
+  int loadFromBinary(const std::string & filename, int verbose = 0);
+  int loadFromBinary(void * binaryInputStream, streamType stream = FILE_STREAM, int verbose = 0);
+
+  // ======= file save =======
+  int saveToBinary(FILE * binaryOutputStream, int outputMaterials = 0, unsigned int * bytesWritten = NULL, bool countBytesOnly = false, int verbose = 0) const;
+
+
+
   std::vector< Material > materials;
   std::vector< Group > groups;
   std::vector< Vec3d > vertexPositions;
@@ -465,44 +610,95 @@ protected:
   std::vector<Vec3d> pseudoNormals;
 
   std::vector<int> triangles; // for triangle vertex lookup
-       
+
   // index assumes that the first int is smaller than the second
   std::map< std::pair<unsigned int,unsigned int>, Vec3d > edgePseudoNormals;
 
   // stores the information about a face that is adjacent to a vertex
-  class VertexFaceNeighbor
+  struct VertexFaceNeighbor
   {
   public:
-    explicit VertexFaceNeighbor(int groupIndex_, int faceIndex_, int faceVertexIndex_, bool averaged_=false) : groupIndex(groupIndex_), faceIndex(faceIndex_), faceVertexIndex(faceVertexIndex_), averaged(averaged_) {}
-
-    inline int getGroupIndex() const { return groupIndex; }
-    inline int getFaceIndex() const { return faceIndex; }
-    inline int getFaceVertexIndex() const { return faceVertexIndex; }
-    inline bool getAveraged() const { return averaged; }
-
-    inline void setAveraged(bool averaged_) { averaged = averaged_; }
-
-  protected:
-    int groupIndex;  //the group containing the face w/ the vertex position
-    int faceIndex;  //the face containing the vertex position
-    int faceVertexIndex;  //the index of the face vertex at this vertex position
-    bool averaged;  //indicates if it was averaged
+    explicit VertexFaceNeighbor(int groupIndex_, int faceIndex_, int faceVertexIndex_, bool averaged_=false) :
+        groupIndex(groupIndex_), faceIndex(faceIndex_), faceVertexIndex(faceVertexIndex_), averaged(averaged_) {}
+    int groupIndex = -1;  //the group containing the face w/ the vertex position
+    int faceIndex = -1;  //the face containing the vertex position
+    int faceVertexIndex = -1;  //the index of the face vertex at this vertex position
+    bool averaged = false;  //indicates if it was averaged
   };
 
-  std::vector<std::list<VertexFaceNeighbor> > vertexFaceNeighbors;
+  std::vector<std::vector<VertexFaceNeighbor> > vertexFaceNeighbors;
 
   // inertia tensor around the origin, assuming the triangle has mass 1
   void computeSpecificInertiaTensor(Vec3d & v0, Vec3d & v1, Vec3d & v2, double t[6]) const;
 
-  void parseMaterials(const char * objMeshname, const char * materialFilename, int verbose=1);
+  void parseMaterials(const std::string & objMeshname, const std::string & materialFilename, int verbose=1);
 
-  std::vector<std::pair<double, const Face*> > surfaceSamplingAreas;
+  std::vector<std::pair<double, std::pair<int,int>> > surfaceSamplingAreas;
 
   static void removeWhitespace(char * s);
   static void convertWhitespaceToSingleBlanks(char * s);
 
   static void fgets_(char * s, int n, FILE * stream);
 };
+
+inline void ObjMesh::forEachFace(std::function<void(ObjMesh::Face &)> f, bool v)
+{
+  for(size_t i = 0; i < groups.size(); i++)
+    for(size_t j = 0; j < groups[i].getNumFaces(); j++)
+    {
+      Face & face = groups[i].getFace(j);
+      if (v && face.getNumVertices() < 3)
+      {
+        printf("Warning: encountered a face with fewer than 3 vertices.\n");
+        continue;
+      }
+      f(face);
+    }
+}
+
+inline void ObjMesh::forEachFace(std::function<void(const ObjMesh::Face &)> f, bool v) const
+{
+  for(size_t i = 0; i < groups.size(); i++)
+    for(size_t j = 0; j < groups[i].getNumFaces(); j++)
+    {
+      const Face & face = groups[i].getFace(j);
+      if (v && face.getNumVertices() < 3)
+      {
+        printf("Warning: encountered a face with fewer than 3 vertices.\n");
+        continue;
+      }
+      f(face);
+    }
+}
+
+inline void ObjMesh::forEachFace(std::function<void(int gID, int fID, ObjMesh::Face &)> f, bool v)
+{
+  for(size_t i = 0; i < groups.size(); i++)
+    for(size_t j = 0; j < groups[i].getNumFaces(); j++)
+    {
+      Face & face = groups[i].getFace(j);
+      if (v && face.getNumVertices() < 3)
+      {
+        printf("Warning: encountered a face with fewer than 3 vertices.\n");
+        continue;
+      }
+      f(i, j, face);
+    }
+}
+
+inline void ObjMesh::forEachFace(std::function<void(int gID, int fID, const ObjMesh::Face &)> f, bool v) const
+{
+  for(size_t i = 0; i < groups.size(); i++)
+    for(size_t j = 0; j < groups[i].getNumFaces(); j++)
+    {
+      const Face & face = groups[i].getFace(j);
+      if (v && face.getNumVertices() < 3)
+      {
+        printf("Warning: encountered a face with fewer than 3 vertices.\n");
+        continue;
+      }
+      f(i, j, face);
+    }
 }
 #endif
 
diff --git a/src/libobjMesh/objMeshBinaryLoader.cpp b/libraries/objMesh/objMeshBinaryLoader.cpp
similarity index 93%
rename from src/libobjMesh/objMeshBinaryLoader.cpp
rename to libraries/objMesh/objMeshBinaryLoader.cpp
index 8677e7c5d36c67964f054aca2eeba91aff77eeb3..c320e837fbb2f1b696eb55902387042d246fef57 100644
--- a/src/libobjMesh/objMeshBinaryLoader.cpp
+++ b/libraries/objMesh/objMeshBinaryLoader.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,7 +30,7 @@
  *                                                                       *
  *************************************************************************/
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #pragma warning(disable : 4996)
   #pragma warning(disable : 4267)
   #pragma warning(disable : 4244)
@@ -45,8 +49,6 @@ using namespace std;
 
 //#define VERBOSE
 
-namespace vega
-{
 int ObjMeshBinaryLoader::readStringFromBinary(std::string & name, std::ifstream * fin)
 {
   #ifdef VERBOSE
@@ -173,7 +175,7 @@ int ObjMeshBinaryLoader::writeToBinaryFile(ObjMesh * objMesh, const std::string
   unsigned int numVertexPositions = objMesh->getNumVertices();
   fout.write((char*)&numVertexPositions, sizeof(unsigned int));
   for(unsigned int i=0; i<numVertexPositions; i++)
-  {  
+  {
     Vec3d pos = objMesh->getPosition(i);
     writeVec3dToBinary(pos, &fout);
   }
@@ -257,7 +259,7 @@ int ObjMeshBinaryLoader::readFromBinary(ObjMesh::Face ** face, std::ifstream * f
   for(unsigned int i=0; i<numVertices; i++)
   {
     ObjMesh::Vertex * vertex;
-    readFromBinary(&vertex, fin);  
+    readFromBinary(&vertex, fin);
     (*face)->addVertex(*vertex);
   }
 
@@ -295,7 +297,7 @@ int ObjMeshBinaryLoader::readFromBinary(ObjMesh::Group ** group, std::ifstream *
   for(unsigned int i=0; i<numFaces; i++)
   {
     ObjMesh::Face * face;
-    readFromBinary(&face, fin);  
+    readFromBinary(&face, fin);
     (*group)->addFace(*face);
   }
 
@@ -327,7 +329,7 @@ int ObjMeshBinaryLoader::readFromBinary(ObjMesh::Material ** material, std::ifst
 
   string name;
   readStringFromBinary(name, fin);
-  
+
   #ifdef VERBOSE
     cout << name << endl;
   #endif
@@ -366,4 +368,3 @@ int ObjMeshBinaryLoader::writeToBinary(ObjMesh::Material * material, std::ofstre
   return 0;
 }
 
-}
diff --git a/src/libobjMesh/objMeshBinaryLoader.h b/libraries/objMesh/objMeshBinaryLoader.h
similarity index 82%
rename from src/libobjMesh/objMeshBinaryLoader.h
rename to libraries/objMesh/objMeshBinaryLoader.h
index 13f8f8ef9f0eb4288d07bcdb1b92bc74e5b6ec8c..3ba5da1219279eafaaa3ba22bff33a6d60042323 100644
--- a/src/libobjMesh/objMeshBinaryLoader.h
+++ b/libraries/objMesh/objMeshBinaryLoader.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,8 +41,6 @@
 #include "objMesh.h"
 #include <string>
 
-namespace vega
-{
 class ObjMeshBinaryLoader
 {
 public:
@@ -60,6 +62,6 @@ protected:
   static int writeStringToBinary(const std::string name, std::ofstream * fout);
   static int writeVec3dToBinary(Vec3d & vec, std::ofstream * fout);
 };
-}
+
 #endif
 
diff --git a/libraries/objMesh/objMeshClose.cpp b/libraries/objMesh/objMeshClose.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..26ea46326cd8f82f45458863c2a744fb20374b87
--- /dev/null
+++ b/libraries/objMesh/objMeshClose.cpp
@@ -0,0 +1,142 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+//  Closes a manifold mesh
+
+#include <set>
+using namespace std;
+
+#include "objMeshClose.h"
+#include "objMeshOrientable.h"
+
+int ObjMeshClose::Close(ObjMesh * objMesh)
+{
+  ObjMeshOrientable * objMeshOrientable = NULL;
+  try
+  {
+    int * numOrientationFlips = NULL;
+    int verbose=0;
+    objMeshOrientable = new ObjMeshOrientable(objMesh, 1, numOrientationFlips, verbose);
+  }
+  catch (int)
+  {
+    printf("Mesh is non-orientable.\n");
+    return 1;
+  }
+
+  int numBoundaryEdges = (int)objMeshOrientable->numBoundaryEdges();
+  set<int> boundaryEdges;
+  for(int i=0; i<numBoundaryEdges; i++)
+    boundaryEdges.insert(objMeshOrientable->boundaryEdge(i));
+
+  while (boundaryEdges.size() > 0)
+  {
+    int firstEdge = *(boundaryEdges.begin());
+
+    vector<int> boundaryVertices;
+
+    // iterate around the boundary loop
+    int edge = firstEdge;
+    do
+    {
+      //printf("edge %d ", edge);
+      // push vertices into "boundaryVertices", and erase edges from boundaryEdges
+      ObjMeshOrientable::HalfEdge halfEdge = objMeshOrientable->halfEdge(edge);
+      boundaryVertices.push_back(halfEdge.startVertex());
+      boundaryEdges.erase(edge);
+
+      // find the next boundary edge
+      ObjMeshOrientable::HalfEdge nextHalfEdge = halfEdge;
+      do
+      {
+        nextHalfEdge = objMeshOrientable->edgeNext(nextHalfEdge);
+        if (nextHalfEdge.isBoundary())
+          break;
+        nextHalfEdge = objMeshOrientable->edgeOpposite(nextHalfEdge);
+      }
+      while (nextHalfEdge != halfEdge);
+
+      if (nextHalfEdge == halfEdge)
+      {
+        printf("Mesh is non-orientable (cannot detect next edge along the hole).\n");
+        return 2;
+      }
+
+      edge = nextHalfEdge.position();
+    }
+    while (edge != firstEdge);
+    //printf("\n");
+
+    // compute centroid
+    Vec3d avg(0,0,0);
+    for(unsigned int i=0; i<boundaryVertices.size(); i++)
+      avg += objMesh->getPosition(boundaryVertices[i]);
+    avg /= boundaryVertices.size();
+
+    int numVertices = objMesh->getNumVertices();
+    objMesh->addVertexPosition(avg);
+
+    // find group containing edge "firstEdge"
+    ObjMeshOrientable::HalfEdge firstHalfEdge = objMeshOrientable->halfEdge(firstEdge);
+    unsigned int group = firstHalfEdge.groupID();
+    ObjMesh::Group * groupHandle = (ObjMesh::Group*) objMesh->getGroupHandle(group);
+
+    // add triangles to close the face (a "tent")
+    //printf("Adding triangles:\n");
+    for(unsigned int i=0; i<boundaryVertices.size(); i++)
+    {
+      int vtxIndex[3] = { numVertices, boundaryVertices[(i+1) % boundaryVertices.size()], boundaryVertices[i] };
+      //printf("[%d %d %d] ", vtxIndex[0], vtxIndex[1], vtxIndex[2]);
+
+      // compute flat normal
+      Vec3d normal = norm(cross(objMesh->getPosition(vtxIndex[1]) - objMesh->getPosition(vtxIndex[0]), objMesh->getPosition(vtxIndex[2]) - objMesh->getPosition(vtxIndex[0]) ));
+
+      // add the normal
+      int numNormals = objMesh->getNumNormals();
+      objMesh->addVertexNormal(normal);
+
+      // create and add new face
+      ObjMesh::Vertex vertex0(vtxIndex[0]);
+      vertex0.setNormalIndex(numNormals);
+      ObjMesh::Vertex vertex1(vtxIndex[1]);
+      vertex1.setNormalIndex(numNormals);
+      ObjMesh::Vertex vertex2(vtxIndex[2]);
+      vertex2.setNormalIndex(numNormals);
+      ObjMesh::Face face(vertex0, vertex1, vertex2);
+      groupHandle->addFace(face);
+    }
+    //printf("\n");
+  }
+
+  return 0;
+}
+
diff --git a/src/libsparseMatrix/sparseMatrixMT.h b/libraries/objMesh/objMeshClose.h
similarity index 64%
rename from src/libsparseMatrix/sparseMatrixMT.h
rename to libraries/objMesh/objMeshClose.h
index 464170fd1dfdc6c755ba55c6b8baa362a8b53384..985969a6351120a89c02412a59c50d01615cfb58 100644
--- a/src/libsparseMatrix/sparseMatrixMT.h
+++ b/libraries/objMesh/objMeshClose.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseMatrixMT" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,25 +30,23 @@
  *                                                                       *
  *************************************************************************/
 
-#ifndef _SPARSE_MATRIX_MT_H_
-#define _SPARSE_MATRIX_MT_H_
+#ifndef _OBJMESHCLOSE_H_
+#define _OBJMESHCLOSE_H_
 
-/*
-  Multithreaded version of the sparse matrix library. Performs matrix-vector multiplications in parallel, using OpenMP.
-  To use it, you should enable the USE_OPENMP flag in sparseMatrixMT.cpp, and compile the code with the flag -fopenmp.
-*/
+//  Closes a manifold mesh
 
-#include "sparseMatrix.h"
+#include "objMesh.h"
 
-namespace vega
-{
-class SparseMatrixMT
+class ObjMeshClose
 {
 public:
+  // detects all boundary edges and inserts faces to close the holes
+  // mesh must be manifold
+  // returns 0 if successful and non-zero otherwise
+  static int Close(ObjMesh * objMesh);
 
-  // multiplies the sparse matrix with the given vector
-  static void MultiplyVector(const SparseMatrix * A, const double * input, double * result, int numThreads=-1); // result = A * input
+protected:
 };
-}
+
 #endif
 
diff --git a/src/libobjMesh/objMeshGraph.cpp b/libraries/objMesh/objMeshGraph.cpp
similarity index 75%
rename from src/libobjMesh/objMeshGraph.cpp
rename to libraries/objMesh/objMeshGraph.cpp
index 0c400cc86140c311f1c229576320c8e0cf288399..71d3b64a8ef0bd86e12bc23c8ed60ef42101afaa 100644
--- a/src/libobjMesh/objMeshGraph.cpp
+++ b/libraries/objMesh/objMeshGraph.cpp
@@ -1,9 +1,39 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic,                                          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
 #include <map>
 #include "objMeshGraph.h"
 using namespace std;
 
-namespace vega
-{
 ObjMeshGraph::ObjMeshGraph(ObjMesh * objMesh_): objMesh(objMesh_)
 {
   if (!(objMesh->isTriangularMesh()))
@@ -40,7 +70,7 @@ ObjMeshGraph::ObjMeshGraph(ObjMesh * objMesh_): objMesh(objMesh_)
       {
         int vtxA = face.getVertex(iVertex).getPositionIndex();
         int vtxB = face.getVertex((iVertex + 1) % face.getNumVertices()).getPositionIndex();
- 
+
         // ensure that vtxA <= vtxB
         if (vtxA >= vtxB)
         {
@@ -48,7 +78,7 @@ ObjMeshGraph::ObjMeshGraph(ObjMesh * objMesh_): objMesh(objMesh_)
           vtxA = vtxB;
           vtxB = buffer;
         }
- 
+
         meshEdges.insert(make_pair(make_pair(vtxA, vtxB), 0));
       }
     }
@@ -73,7 +103,7 @@ ObjMeshGraph::ObjMeshGraph(ObjMesh * objMesh_): objMesh(objMesh_)
       iter != meshEdges.end(); iter++)
   {
     iter->second = numMeshEdges;
-    numMeshEdges++; 
+    numMeshEdges++;
   }
 
   // build graph adjacency
@@ -140,9 +170,9 @@ ObjMeshGraph::ObjMeshGraph(ObjMesh * objMesh_): objMesh(objMesh_)
   numEdges = edges.size();
 
   printf("Graph generated.\n");
-  printf("Mesh vertices: %d\n", nObj); 
-  printf("Mesh edges: %d\n", eObj); 
-  printf("Mesh faces: %d\n", fObj); 
+  printf("Mesh vertices: %d\n", nObj);
+  printf("Mesh edges: %d\n", eObj);
+  printf("Mesh faces: %d\n", fObj);
 
   BuildVertexNeighbors();
 }
@@ -218,14 +248,14 @@ int ObjMeshGraph::graphID(int faceID, int siteIndex)
 
   if (graphVertex > numVertices)
   {
-    printf("Error: incorrect graph vertex (%d). faceID=%d site:%d\n", 
+    printf("Error: incorrect graph vertex (%d). faceID=%d site:%d\n",
       graphVertex, faceID, siteIndex);
   }
 
   return graphVertex;
 }
 
-Graph * ObjMeshGraph::GenerateVertexGraph(ObjMesh * objMesh, int faceClique)
+Graph * ObjMeshGraph::GenerateVertexGraph(const ObjMesh * objMesh, int faceClique)
 {
   // Generate springs:
   typedef pair<int,int> edge;
@@ -283,4 +313,3 @@ Graph * ObjMeshGraph::GenerateVertexGraph(ObjMesh * objMesh, int faceClique)
   #undef SORTED
 }
 
-}
diff --git a/libraries/objMesh/objMeshGraph.h b/libraries/objMesh/objMeshGraph.h
new file mode 100644
index 0000000000000000000000000000000000000000..2a548b15c9e2dc7b74cfcaf26a77fb3a49e22320
--- /dev/null
+++ b/libraries/objMesh/objMeshGraph.h
@@ -0,0 +1,105 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic,                                          *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _OBJMESHGRAPH_H_
+#define _OBJMESHGRAPH_H_
+
+/*
+   Jernej Barbic, CMU, MIT, USC, 2007-2012
+
+   A graph where the nodes are obj mesh vertices, edges and faces.
+   Two nodes are connected if they are adjacent in the mesh.
+
+   There is also a static function, "GenerateVertexGraph", which computes a graph where the nodes
+   are obj mesh vertices, and two nodes are connected if they are adjacent in the mesh.
+*/
+
+#include "triple.h"
+#include "graph.h"
+#include "objMesh.h"
+
+class ObjMeshGraph : public Graph
+{
+public:
+  ObjMeshGraph(ObjMesh * objMesh); // will be triangulated if necessary
+
+  int GetVertexID(int meshVertex);
+  int GetEdgeID(int meshEdge);
+  int GetFaceID(int meshFace);
+
+  //void meshID(int graphVertex, int & siteType, int & siteID);
+  // converts graph vertex to the integer indices of the particular site
+  // sitetpye: 0 = vtx, 1 = edge, 2 = face
+  void meshID(int graphVertex, int & siteType, int meshVtxData[3]);
+
+  // converts distance-field style (face, siteIndex) pair to the graph vertex ID
+  // siteIndex:
+  //  0: vertex0
+  //  1: vertex1
+  //  2: vertex2
+  //  3: edge among 01
+  //  4: edge among 12
+  //  5: edge among 20
+  //  6: the face itself
+  int graphID(int faceID, int siteIndex);
+
+  // Creates a graph where the nodes are obj mesh vertices. Two nodes are connected if they are adjacent in the mesh.
+  // Face clique:
+  // 0: connect every vertex to the next and previous vertex along the face perimeter (but not to other vertices of that face)
+  // 1: connect every vertex to all other face vertices
+  static Graph * GenerateVertexGraph(const ObjMesh * objMesh, int faceClique=0);
+
+protected:
+  int nObj, eObj, fObj;
+  std::vector<std::pair<int, int> > meshEdgesVector; // maps each integer identifier to the corresponding edge
+  std::vector<triple<int, int, int> > meshFaceVerticesVector; // for every mesh triangle, gives indices of its vertices
+  std::vector<triple<int, int, int> > meshFaceEdgesVector; // for every mesh triangle, gives indices of its edges
+  ObjMesh * objMesh;
+};
+
+inline int ObjMeshGraph::GetVertexID(int vertex)
+{
+  return vertex;
+}
+
+inline int ObjMeshGraph::GetEdgeID(int edge)
+{
+  return nObj + edge;
+}
+
+inline int ObjMeshGraph::GetFaceID(int face)
+{
+  return nObj + eObj + face;
+}
+
+#endif
+
diff --git a/src/libobjMesh/objMeshOctree.cpp b/libraries/objMesh/objMeshOctree.cpp
similarity index 82%
rename from src/libobjMesh/objMeshOctree.cpp
rename to libraries/objMesh/objMeshOctree.cpp
index 12503f7ea3bfbe5c42ab578e3e65b452f6653453..1d4b447c9dc0ce8ad01b2992c809b34a08f2c786 100644
--- a/src/libobjMesh/objMeshOctree.cpp
+++ b/libraries/objMesh/objMeshOctree.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,18 +32,17 @@
 
 //  Jernej Barbic, CMU
 //  builds an octree on top of the geometry from a given obj file
-//  Can be intersected with a sphere or a line segment. 
+//  Can be intersected with a sphere or a line segment.
 
 #include "triple.h"
-using namespace std;
 #include "objMeshOctree.h"
-namespace vega
-{
+using namespace std;
+
 template<class TriangleClass>
 const double ObjMeshOctree<TriangleClass>::bboxExpansionRatio = 1.05;
 
 template<class TriangleClass>
-ObjMeshOctree<TriangleClass>::ObjMeshOctree( ObjMesh * objMeshIn, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo ) : 
+ObjMeshOctree<TriangleClass>::ObjMeshOctree(const ObjMesh * objMeshIn, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo) :
   maxNumTrianglesInLeafNode(maxNumTrianglesInLeafNode_), maxTreeDepth(maxTreeDepth_)
 {
   // copy mesh
@@ -54,7 +57,7 @@ ObjMeshOctree<TriangleClass>::ObjMeshOctree( ObjMesh * objMeshIn, int maxNumTria
   }
   else
     cout << "yes" << endl;
- 
+
   int triangleIndex = 0;
   triangles.clear();
   for(unsigned int i=0; i < objMesh->getNumGroups(); i++) // over all groups
@@ -73,7 +76,7 @@ ObjMeshOctree<TriangleClass>::ObjMeshOctree( ObjMesh * objMeshIn, int maxNumTria
 
   // build the octree
   cout << "Building the octree data structure... " << endl;
-  root = new Octree<TriangleClass>(maxTreeDepth); 
+  root = new Octree<TriangleClass>(maxTreeDepth);
   root->setBuildPrintInfo(printInfo);
   root->build(triangles, maxNumTrianglesInLeafNode);
 
@@ -90,7 +93,7 @@ ObjMeshOctree<TriangleClass>::ObjMeshOctree( ObjMesh * objMeshIn, int maxNumTria
 }
 
 template<>
-ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh * objMeshIn, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo) :
+ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree(const ObjMesh * objMeshIn, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo) :
   maxNumTrianglesInLeafNode(maxNumTrianglesInLeafNode_), maxTreeDepth(maxTreeDepth_)
 {
   // copy mesh
@@ -134,17 +137,20 @@ ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh
       if (pseudoNormalObjMesh->getEdgePseudoNormal(index0, index1, &pseudoNormals[3]) != 0)
       {
         cout << "Error: encountered an edge without a pseudonormal. Degenerate face? Vertices: " << index0 << " " << index1 << endl;
-        exit(1);
+        delete(objMesh);
+        throw(1);
       }
       if (pseudoNormalObjMesh->getEdgePseudoNormal(index1, index2, &pseudoNormals[4]) != 0)
       {
         cout << "Error: encountered an edge without a pseudonormal. Degenerate face? Vertices: " << index1 << " " << index2 << endl;
-        exit(1);
+        delete(objMesh);
+        throw(1);
       }
       if (pseudoNormalObjMesh->getEdgePseudoNormal(index2, index0, &pseudoNormals[5]) != 0)
       {
         cout << "Error: encountered an edge without a pseudonormal. Degenerate face? Vertices: " << index2 << " " << index0 << endl;
-        exit(1);
+        delete(objMesh);
+        throw(1);
       }
 
       // face pseudo normal
@@ -152,13 +158,11 @@ ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh
       Vec3d p1 = pseudoNormalObjMesh->getPosition(index1);
       Vec3d p2 = pseudoNormalObjMesh->getPosition(index2);
 
-      pseudoNormals[6] = norm(cross(p1-p0,p2-p0)); 
+      pseudoNormals[6] = norm(cross(p1-p0,p2-p0));
 
       for(int normali=0; normali < 7; normali++)
       {
-        if (ObjMesh::isNaN(pseudoNormals[normali][0]) || 
-            ObjMesh::isNaN(pseudoNormals[normali][1]) || 
-            ObjMesh::isNaN(pseudoNormals[normali][2]))
+        if (pseudoNormals[normali].hasNaN())
         {
           cout << "Error: nan encountered: " << pseudoNormals[normali][0] << " " << pseudoNormals[normali][1] << " " << pseudoNormals[normali][2] << endl;
           cout << "Group: " << i << " Triangle: " << j << " " << endl;
@@ -167,7 +171,8 @@ ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh
           cout << "  "  << p1 << endl;
           cout << "  "  << p2 << endl;
           cout << "Feature: " << normali << endl;
-          exit(1);
+          delete(objMesh);
+          throw(1);
         }
       }
 
@@ -177,7 +182,7 @@ ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh
   }
 
   cout << "Total number of triangles is: " << triangles.size() << endl;
-  
+
   // build the octree
   Vec3d bmin, bmax;
   objMesh->getCubicBoundingBox(1.0, &bmin, &bmax);
@@ -186,13 +191,13 @@ ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh
   cout << "xmin: " << bmin[0] << " xmax: " << bmax[0] << endl;
   cout << "ymin: " << bmin[1] << " ymax: " << bmax[1] << endl;
   cout << "zmin: " << bmin[2] << " zmax: " << bmax[2] << endl;
-    
+
   BoundingBox bboxOctree(bmin, bmax);
   bboxOctree.expand(bboxExpansionRatio);
 
   cout << "Starting the octree creation algorithm..." << endl;
 
-  root = new Octree<TriangleWithCollisionInfoAndPseudoNormals>(maxTreeDepth); 
+  root = new Octree<TriangleWithCollisionInfoAndPseudoNormals>(maxTreeDepth);
   root->setBuildPrintInfo(printInfo);
   root->build(triangles, bboxOctree, maxNumTrianglesInLeafNode);
 
@@ -211,9 +216,9 @@ ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh
   cout << "Octree creation completed successfully." << endl;
 }
 
-template ObjMeshOctree<TriangleBasic>::ObjMeshOctree( ObjMesh * objMesh, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo );  
-template ObjMeshOctree<TriangleWithCollisionInfo>::ObjMeshOctree( ObjMesh * objMesh, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo );  
-#ifndef WIN32
-  template ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh * objMesh, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo );  
+template ObjMeshOctree<TriangleBasic>::ObjMeshOctree(const ObjMesh * objMesh, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo);
+template ObjMeshOctree<TriangleWithCollisionInfo>::ObjMeshOctree(const ObjMesh * objMesh, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo);
+#if defined(_WIN32) || defined(WIN32)
+  //template ObjMeshOctree<TriangleWithCollisionInfoAndPseudoNormals>::ObjMeshOctree( ObjMesh * objMesh, int maxNumTrianglesInLeafNode_, int maxTreeDepth_, int printInfo );
 #endif
-}
+
diff --git a/src/libobjMesh/objMeshOctree.h b/libraries/objMesh/objMeshOctree.h
similarity index 77%
rename from src/libobjMesh/objMeshOctree.h
rename to libraries/objMesh/objMeshOctree.h
index 93eddfbbd80aa2085e9ed2cfc934c8c28733d395..0dc0d090e350923fc1220e60f90df624971a158d 100644
--- a/src/libobjMesh/objMeshOctree.h
+++ b/libraries/objMesh/objMeshOctree.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,31 +33,26 @@
 #ifndef _OBJMESHOCTREE_H_
 #define _OBJMESHOCTREE_H_
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 //  Jernej Barbic, CMU
 //  builds an octree on top of the geometry from a given obj file
-//  Can be intersected with a sphere or a line segment. 
+//  Can be intersected with a sphere or a line segment.
 
 #include "octree.h"
 #include "objMesh.h"
-namespace vega
-{
+
 template<class TriangleClass>
-class ObjMeshOctree 
+class ObjMeshOctree
 {
 public:
   // builds an octree on top of the objMesh
   // maxNumTrianglesInLeafNode = max number of triangles in an octree leaf node
   // maxTreeDepth = max tree depth
   // (if tree depth were to exceed maxTreeDepth, a leaf node may have more than maxNumTrianglesInLeafNode triangles)
-  ObjMeshOctree( ObjMesh * objMesh, int maxNumTrianglesInLeafNode, int maxTreeDepth, int printInfo = 1);
+  ObjMeshOctree(const ObjMesh * objMesh, int maxNumTrianglesInLeafNode, int maxTreeDepth, int printInfo = 0);
   ~ObjMeshOctree() { delete(root); }
 
   // sphere-triangles query
-  // retrieves all triangles intersected by the sphere 
+  // retrieves all triangles intersected by the sphere
   // (can potentially return the same triangle several times; call <TriangleClass>::makeUniqueList to make the list unique if needed)
   void rangeQuery(std::vector< TriangleClass* > & triangleList, const SimpleSphere & simpleSphere)
     { root->buildCollisionList(triangleList, simpleSphere); }
@@ -61,8 +60,8 @@ public:
   // line segment-triangles query
   // retrieves all triangles intersected by the line segment
   // (can potentially return the same triangle several times; call <TriangleClass>::makeUniqueList to make the list unique if needed)
-  void lineSegmentIntersection(std::vector< TriangleClass* > & triangleList, Vec3d segmentStart, Vec3d segmentEnd)
-    { root->buildCollisionList(triangleList, segmentStart, segmentEnd); }
+  void lineSegmentIntersection(std::vector< TriangleClass* > & triangleList, const Vec3d & segmentStart, const Vec3d & segmentEnd, std::vector<Vec3d> * intersectionList = NULL)
+    { root->buildCollisionList(triangleList, segmentStart, segmentEnd, intersectionList); }
 
   void renderOctree() { root->render(); }
   void renderOctree(int level) { root->render(level); }
@@ -77,9 +76,9 @@ protected:
   Octree<TriangleClass> * root;
   int maxNumTrianglesInLeafNode; // max number of triangles in an octree leaf node
   int maxTreeDepth; // max tree depth
-  
+
   static const double bboxExpansionRatio;
 };
-}
+
 #endif
 
diff --git a/src/libobjMesh/objMeshOffsetVoxels.cpp b/libraries/objMesh/objMeshOffsetVoxels.cpp
similarity index 87%
rename from src/libobjMesh/objMeshOffsetVoxels.cpp
rename to libraries/objMesh/objMeshOffsetVoxels.cpp
index b184f58a3607220c8dada0c88fd6425cc9ca5b5a..c2758415f510974fb0e48d458f513cbe5ab30427 100644
--- a/src/libobjMesh/objMeshOffsetVoxels.cpp
+++ b/libraries/objMesh/objMeshOffsetVoxels.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,7 +35,7 @@
   Generates a voxel representation of an offset surface
 */
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #pragma warning(disable : 4996)
   #pragma warning(disable : 4267)
   #pragma warning(disable : 4244)
@@ -41,19 +45,18 @@
 #include "matrixMacros.h"
 #include <fstream>
 #include <iomanip>
+#include <string.h>
 using namespace std;
 
-namespace vega
+ObjMeshOffsetVoxels::ObjMeshOffsetVoxels(const ObjMesh * objMesh_, const int resolution_[3], int depth_, Vec3d bmin_, Vec3d bmax_ )
 {
-ObjMeshOffsetVoxels::ObjMeshOffsetVoxels( ObjMesh * objMesh, int resolution_[3], int depth_, Vec3d bmin_, Vec3d bmax_ )
-{
-  objMesh = new ObjMesh(*objMesh);
+  objMesh = objMesh_;
   init(resolution_, depth_, bmin_, bmax_);
 }
 
-ObjMeshOffsetVoxels::ObjMeshOffsetVoxels( ObjMesh * objMesh_, int resolution_[3], int depth_, double expansionFactor )
+ObjMeshOffsetVoxels::ObjMeshOffsetVoxels(const ObjMesh * objMesh_, const int resolution_[3], int depth_, double expansionFactor )
 {
-  objMesh = new ObjMesh(*objMesh_);
+  objMesh = objMesh_;
 
   // build mesh bounding box
   Vec3d bmin_, bmax_;
@@ -62,7 +65,7 @@ ObjMeshOffsetVoxels::ObjMeshOffsetVoxels( ObjMesh * objMesh_, int resolution_[3]
   init(resolution_, depth_, bmin_, bmax_);
 }
 
-void ObjMeshOffsetVoxels::init(int resolution_[3], int depth_, Vec3d bmin_, Vec3d bmax_)
+void ObjMeshOffsetVoxels::init(const int resolution_[3], int depth_, Vec3d bmin_, Vec3d bmax_)
 {
   resolution[0] = resolution_[0];
   resolution[1] = resolution_[1];
@@ -75,16 +78,8 @@ void ObjMeshOffsetVoxels::init(int resolution_[3], int depth_, Vec3d bmin_, Vec3
   //cout << "Resolution is " << resolution[0] << " x " << resolution[1] << " x " << resolution[2] << " ..." << endl;
 
   //cout << "Checking if mesh is triangular... ";
-  if (!(objMesh->isTriangularMesh()))
-  {
-    //cout << "mesh was not triangular: triangulating... ";
-    objMesh->triangulate();
-    //cout << "done" << endl;
-  }
-  else
-  {
-    //cout << "yes" << endl;
-  }
+  vector<Vec3i> triangles;
+  objMesh->exportTriangles(triangles);
 
   side = bmax - bmin;
   inc[0] = side[0] / resolution[0];
@@ -98,85 +93,81 @@ void ObjMeshOffsetVoxels::init(int resolution_[3], int depth_, Vec3d bmin_, Vec3
   // for every triangle, find the voxel containing its center of mass
   // then, grow the voxels until they don't intersect the triangle any more
 
-  voxels.clear(); // will contain voxels intersecting the triangles 
+  voxels.clear(); // will contain voxels intersecting the triangles
 
   // local search helpers:
   set<voxel> checkedVoxels; // used to mark what voxels have already been visited
   vector<voxel> scheduledVoxels; // contains voxels still to be processed
-  for (unsigned int i=0; i<objMesh->getNumGroups(); i++)
+  for(size_t i = 0; i < triangles.size(); i++)
   {
-    const ObjMesh::Group * getGroupHandle = objMesh->getGroupHandle(i);
-      
-    for (unsigned int j=0; j<getGroupHandle->getNumFaces(); j++)
+    Vec3i t = triangles[i];
+    Vec3d p0 = objMesh->getPosition(t[0]);
+    Vec3d p1 = objMesh->getPosition(t[1]);
+    Vec3d p2 = objMesh->getPosition(t[2]);
+    TriangleBasic triangle(p0,p1,p2);
+
+    Vec3d center = 1.0 / 3 * (p0 + p1 + p2);
+    Vec3d relCenter = center-bmin;
+
+    // find voxel containing center
+    int vi,vj,vk;
+    vi = (int)(relCenter[0] / inc[0]);
+    vj = (int)(relCenter[1] / inc[1]);
+    vk = (int)(relCenter[2] / inc[2]);
+
+    checkedVoxels.clear();
+    checkedVoxels.insert(voxel(vi,vj,vk));
+
+    scheduledVoxels.clear();
+    scheduledVoxels.push_back(voxel(vi,vj,vk));
+
+    // while there are still some scheduled voxels:
+    //   take one
+    //   check if intersecting the triangle
+    //   if yes
+    //     add voxel to voxels
+    //     queue all neighbors that haven't been visited yet
+
+    while (!scheduledVoxels.empty())
     {
-      Vec3d p0 = objMesh->getPosition(getGroupHandle->getFace(j).getVertex(0).getPositionIndex());
-      Vec3d p1 = objMesh->getPosition(getGroupHandle->getFace(j).getVertex(1).getPositionIndex());
-      Vec3d p2 = objMesh->getPosition(getGroupHandle->getFace(j).getVertex(2).getPositionIndex());
-      TriangleBasic triangle(p0,p1,p2);
-
-      Vec3d center = 1.0 / 3 * (p0 + p1 + p2);
-      Vec3d relCenter = center-bmin;
-
-      // find voxel containing center
-      int vi,vj,vk;
-      vi = (int)(relCenter[0] / inc[0]);
-      vj = (int)(relCenter[1] / inc[1]);
-      vk = (int)(relCenter[2] / inc[2]);
-
-      checkedVoxels.clear();
-      checkedVoxels.insert(voxel(vi,vj,vk));
-
-      scheduledVoxels.clear();
-      scheduledVoxels.push_back(voxel(vi,vj,vk));
-
-      // while there are still some scheduled voxels:
-      //   take one
-      //   check if intersecting the triangle
-      //   if yes
-      //     add voxel to voxels
-      //     queue all neighbors that haven't been visited yet
-     
-      while (!scheduledVoxels.empty())
+      voxel v = scheduledVoxels.back();
+      scheduledVoxels.pop_back();
+
+      // make bounding box for voxel
+      Vec3d bbmin = bmin + Vec3d(v.first * inc[0], v.second * inc[1], v.third * inc[2]);
+      BoundingBox bbox(bbmin, bbmin + inc);
+
+      if (triangle.doesIntersectBox(bbox)) // intersection test
       {
-        voxel v = scheduledVoxels.back();
-        scheduledVoxels.pop_back();
-     
-        // make bounding box for voxel
-        Vec3d bbmin = bmin + Vec3d(v.first * inc[0], v.second * inc[1], v.third * inc[2]);
-        BoundingBox bbox(bbmin, bbmin + inc);
-
-        if (triangle.doesIntersectBox(bbox)) // intersection test
-        {
-	  // add the voxel to the final list of hits
-          voxels.insert(v);
-          // queue all neighbors of v, and also put them into checkedVoxels
-          // (but don't do anything if they have already been queued)
-          voxel neighbor;
-          #define PROCESS(ii,jj,kk)\
-          neighbor = voxel(v.first+(ii),v.second+(jj),v.third+(kk));\
-          if ((neighbor.first >= 0) && (neighbor.first <= resolution[0]) &&\
-              (neighbor.second >= 0) && (neighbor.second <= resolution[1]) &&\
-              (neighbor.third >= 0) && (neighbor.third <= resolution[2])) \
+  // add the voxel to the final list of hits
+        voxels.insert(v);
+        // queue all neighbors of v, and also put them into checkedVoxels
+        // (but don't do anything if they have already been queued)
+        voxel neighbor;
+        #define PROCESS(ii,jj,kk)\
+        neighbor = voxel(v.first+(ii),v.second+(jj),v.third+(kk));\
+        if ((neighbor.first >= 0) && (neighbor.first <= resolution[0]) &&\
+            (neighbor.second >= 0) && (neighbor.second <= resolution[1]) &&\
+            (neighbor.third >= 0) && (neighbor.third <= resolution[2])) \
+        {\
+          if (checkedVoxels.find(neighbor) == checkedVoxels.end())\
           {\
-            if (checkedVoxels.find(neighbor) == checkedVoxels.end())\
-            {\
-              checkedVoxels.insert(neighbor);\
-              scheduledVoxels.push_back(neighbor);\
-            }\
-          }
-          for (int iii=-1; iii<=1; iii++)
-            for (int jjj=-1; jjj<=1; jjj++)
-              for (int kkk=-1; kkk<=1; kkk++)
-              {
-                if ((iii == 0) && (jjj ==0) && (kkk==0))
-                  continue;
-                PROCESS(iii,jjj,kkk)
-              }
-        }  
+            checkedVoxels.insert(neighbor);\
+            scheduledVoxels.push_back(neighbor);\
+          }\
+        }
+        for (int iii=-1; iii<=1; iii++)
+          for (int jjj=-1; jjj<=1; jjj++)
+            for (int kkk=-1; kkk<=1; kkk++)
+            {
+              if ((iii == 0) && (jjj ==0) && (kkk==0))
+                continue;
+              PROCESS(iii,jjj,kkk)
+            }
       }
-
-      // now, voxels contains all voxels that intersect the given triangle (plus everything from previous triangles) 
     }
+
+    // now, voxels contains all voxels that intersect the given triangle (plus everything from previous triangles)
   }
 
   // now, voxels contains all voxels intersecting any triangle
@@ -199,9 +190,9 @@ void ObjMeshOffsetVoxels::init(int resolution_[3], int depth_, Vec3d bmin_, Vec3
           (neighbor.second >= 0) && (neighbor.second <= resolution[1]) &&\
           (neighbor.third >= 0) && (neighbor.third <= resolution[2])) \
       {\
-	voxeli.insert(neighbor);\
+        voxeli.insert(neighbor);\
       }
-    
+
     set<voxel>::iterator vox;
 
     for (vox = voxels.begin(); vox != voxels.end(); ++vox) // over all members of voxels
@@ -219,7 +210,7 @@ void ObjMeshOffsetVoxels::init(int resolution_[3], int depth_, Vec3d bmin_, Vec3
   //cout << "Building unique list of faces..." << endl;
   // build unique list of faces
 
-  buildUniqueListOfFaces(); 
+  buildUniqueListOfFaces();
 }
 
 void ObjMeshOffsetVoxels::emptyComponents(vector<Vec3d> & componentSeeds, vector<int> & componentSize, bool interiorOnly)
@@ -252,8 +243,7 @@ void ObjMeshOffsetVoxels::emptyComponents(vector<Vec3d> & componentSeeds, vector
 
 void ObjMeshOffsetVoxels::floodFill(Vec3d seed)
 {
-  cout << "Flood-filling from seed: " << seed << endl;
-
+  //cout << "Flood-filling from seed: " << seed << endl;
   floodFillFromSet(seed, voxels);
   buildUniqueListOfFaces();
 }
@@ -262,7 +252,7 @@ void ObjMeshOffsetVoxels::floodFill(vector<Vec3d> & seeds)
 {
   for(unsigned int i=0; i<seeds.size(); i++)
   {
-    cout << "Flood-filling from seed: " << seeds[i] << endl;
+    //cout << "Flood-filling from seed: " << seeds[i] << endl;
     floodFillFromSet(seeds[i], voxels);
   }
 
@@ -287,7 +277,7 @@ bool ObjMeshOffsetVoxels::floodFillFromSet(Vec3d seed, set<voxel> & voxelSet)
     printf("Warning: flood-filling seed is outside the bounding box. Performing no flood-fill.\n");
     return false;
   }
-  
+
 
   voxel seedVoxel(vi,vj,vk);
 
@@ -303,7 +293,7 @@ bool ObjMeshOffsetVoxels::floodFillFromSet(Vec3d seed, set<voxel> & voxelSet)
     queue.erase(vox);
 
     voxel neighbor;
-    
+
     //printf("vox: %d %d %d\n",vox.first,vox.second,vox.third);
 
     // process all 8 neighbors
@@ -340,16 +330,16 @@ void ObjMeshOffsetVoxels::buildUniqueListOfFaces()
 {
   surfaceFaces.clear();
   interiorFaces.clear();
-  
+
   set<voxel>::iterator vox;
   for (vox = voxels.begin(); vox != voxels.end(); ++vox) // over all members of voxels
   {
     // for each face of vox:
     //   if already on the list of surface faces, erase it from there, and add it among interior faces
     //   else add it among surface faces
-  
+
     //cout << "Voxel: " << vox->first << " " << vox->second << " " << vox->third << endl;
-  
+
     gridPoint pmin(vox->first,vox->second,vox->third);
     gridPoint pmax(vox->first+1,vox->second+1,vox->third+1);
 
@@ -357,14 +347,14 @@ void ObjMeshOffsetVoxels::buildUniqueListOfFaces()
     gridPoint p1(pmax.first,pmin.second,pmin.third);
     gridPoint p2(pmax.first,pmax.second,pmin.third);
     gridPoint p3(pmin.first,pmax.second,pmin.third);
-  
+
     gridPoint p4(pmin.first,pmin.second,pmax.third);
     gridPoint p5(pmax.first,pmin.second,pmax.third);
     gridPoint p6(pmax.first,pmax.second,pmax.third);
     gridPoint p7(pmin.first,pmax.second,pmax.third);
-  
+
     TopologicalFace * face;
-    
+
     #define PROCESS_FACE(q0,q1,q2,q3)\
     face = new TopologicalFace((q0),(q1),(q2),(q3));\
     if (surfaceFaces.find(*face) != surfaceFaces.end())\
@@ -442,7 +432,7 @@ ObjMesh * ObjMeshOffsetVoxels::surfaceOffsetMesh()
   // now, vertices contains all vertices with no duplications
 
   // create default group
-  objMesh->addGroup("Default");    
+  objMesh->addGroup("Default");
 
   // add all vertices into a map, together with their corresponding position
   // also, add vertices to objMesh
@@ -476,9 +466,7 @@ ObjMesh * ObjMeshOffsetVoxels::surfaceOffsetMesh()
     objMesh->addFaceToGroup(newFace,0);
   }
 
-
   return objMesh;
-
 }
 
 bool ObjMeshOffsetVoxels::FaceOrder::operator()(const TopologicalFace & x, const TopologicalFace & y) const
@@ -535,7 +523,7 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
   // create a list of vertices in all voxels
   set<gridPoint> vertices;
   // insert all voxel vertices
-  set<voxel>::iterator aVoxel; 
+  set<voxel>::iterator aVoxel;
   for (aVoxel = voxels.begin(); aVoxel != voxels.end(); ++aVoxel) // over all voxels
   {
     unsigned int i1,j1,k1;
@@ -552,18 +540,18 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
     vertices.insert(gridPoint(i1+1,j1,k1+1));
     vertices.insert(gridPoint(i1+1,j1+1,k1+1));
     vertices.insert(gridPoint(i1,j1+1,k1+1));
-  } 
-  
+  }
+
   // now, vertices contains all voxel vertices with no duplications
 
-  cout << "Num voxels: " << voxels.size() << " Num voxel vertices: " << vertices.size() << endl; 
+  cout << "Num voxels: " << voxels.size() << " Num voxel vertices: " << vertices.size() << endl;
 
   // open up the objMesh for the output surface mesh
   ObjMesh * objMesh = new ObjMesh();
 
   // create default group
   objMesh->addGroup("Default");
-  
+
   // add all voxel vertices into a map, together with their corresponding index (i.e. serial number of a voxel vertex in the set order)
   map<gridPoint,int> vertices2;
   set<gridPoint>:: iterator v; // will run over all voxel vertices
@@ -583,8 +571,8 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
 
     // write vertex to file
     fout << setprecision (16) << position << " " << pos[0] << " " << pos[1] << " " << pos[2] << endl;
-  } 
-  
+  }
+
   fout << endl;
   fout << "*ELEMENTS" << endl;
   fout << "CUBIC" << endl;
@@ -603,7 +591,7 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
 
     #define PROCESS_CORNER(di,dj,dk)\
     index = 1+(vertices2.find(gridPoint(i1+(di),j1+(dj),k1+(dk))))->second;\
-    fout << " " << index;    
+    fout << " " << index;
 
     PROCESS_CORNER(0,0,0);
     PROCESS_CORNER(1,0,0);
@@ -617,7 +605,7 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
     fout << endl;
 
     position++;
-  } 
+  }
 
   fout.close();
 
@@ -637,12 +625,12 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
   // for every vertex of the mesh, find what voxel it is in
   // then find indices of the vertices of that voxel
   // and compute the barycentric coordinates
-  
+
   for (unsigned int i=0; i < objMesh->getNumVertices(); i++) // over all vertices of the mesh
   {
     Vec3d pos = objMesh->getPosition(i);
     unsigned int i1,j1,k1;
-    
+
     Vec3d relPos = pos-bmin;
 
     // find voxel containing 'pos'
@@ -652,9 +640,9 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
 
     // compute barycentric coordinates
     Vec3d w = pos - (bmin + Vec3d(i1 * inc[0], j1 * inc[1], k1 * inc[2]));
-    double alpha = w[0] / inc[0];     
-    double beta = w[1] / inc[1];     
-    double gamma = w[2] / inc[2];     
+    double alpha = w[0] / inc[0];
+    double beta = w[1] / inc[1];
+    double gamma = w[2] / inc[2];
 
     unsigned int c000 = vertices2.find(gridPoint(i1+0,j1+0,k1+0))->second;
     unsigned int c100 = vertices2.find(gridPoint(i1+1,j1+0,k1+0))->second;
@@ -699,8 +687,8 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
     int index[4];
     for (int i=0; i<4; i++)
       index[i] = (vertices2.find(face->vertex(i)))->second;
- 
-    std::pair< bool, unsigned int > texPos(false,0); // no textures 
+
+    std::pair< bool, unsigned int > texPos(false,0); // no textures
     std::pair< bool, unsigned int > normal(false,0); // no normals
 
 /*
@@ -731,23 +719,23 @@ void ObjMeshOffsetVoxels::generateCubicMesh(const string & filenameVeg, const st
     objMesh->addFaceToGroup(newFace,0);
   }
 
+  // search if there already is "default" material; if there is not, add it
+  objMesh->addDefaultMaterial();
+  objMesh->computeBoundingBox();
+
   objMesh->save(filenameObj);
 
   delete(objMesh);
 }
 
-void ObjMeshOffsetVoxels::generateCubicMesh(
-  int * numCubeVertices, double ** cubeVertices, 
-  int * numCubes, int ** cubes,
-  int ** interpolationVertices, double ** interpolationWeights,
-  ObjMesh ** surfaceMesh)
+void ObjMeshOffsetVoxels::generateCubicMesh(int * numCubeVertices, double ** cubeVertices, int * numCubes, int ** cubes, int ** interpolationVertices, double ** interpolationWeights, ObjMesh ** surfaceMesh)
 {
   //cout << "Generating cubic mesh..." << endl;
 
   // create a list of vertices in all voxels
   set<gridPoint> vertices;
   // insert all voxel vertices
-  set<voxel>::iterator aVoxel; 
+  set<voxel>::iterator aVoxel;
   for (aVoxel = voxels.begin(); aVoxel != voxels.end(); ++aVoxel) // over all voxels
   {
     unsigned int i1,j1,k1;
@@ -764,17 +752,19 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
     vertices.insert(gridPoint(i1+1,j1,k1+1));
     vertices.insert(gridPoint(i1+1,j1+1,k1+1));
     vertices.insert(gridPoint(i1,j1+1,k1+1));
-  } 
-  
+  }
+
   // now, "vertices" contains all voxel vertices with no duplications
-  //cout << "Num voxels: " << voxels.size() << " Num voxel vertices: " << vertices.size() << endl; 
+  //cout << "Num voxels: " << voxels.size() << " Num voxel vertices: " << vertices.size() << endl;
 
   // creates an empty objMesh (for the output surface mesh)
-  *surfaceMesh = new ObjMesh();
-
+  if (surfaceMesh)
+  {
+    *surfaceMesh = new ObjMesh();
   // create default group
-  (*surfaceMesh)->addGroup("Default");
-  
+    (*surfaceMesh)->addGroup("Default");
+  }
+
   // add all voxel vertices into a map, together with their corresponding index (i.e. serial number of a voxel vertex in the set order)
   map<gridPoint,int> vertices2;
   set<gridPoint>:: iterator v; // will run over all voxel vertices
@@ -786,7 +776,8 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
   {
     vertices2.insert(pair<gridPoint,int>(*v,position));
     Vec3d pos = bmin + Vec3d(v->first*inc[0], v->second*inc[1], v->third*inc[2]);
-    (*surfaceMesh)->addVertexPosition(pos);
+    if (surfaceMesh)
+      (*surfaceMesh)->addVertexPosition(pos);
 
     (*cubeVertices)[3*position+0] = pos[0];
     (*cubeVertices)[3*position+1] = pos[1];
@@ -794,7 +785,7 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
 
     //cout << "Position " << position << ": " << pos << endl;
     position++;
-  } 
+  }
 
   unsigned int index;
   position = 0;
@@ -820,22 +811,24 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
     PROCESS_CORNER_SHORT(0,1,1); (*cubes)[8*position+7] = index;
 
     position++;
-  } 
+  }
 
   // generate the barycentric masks
 
   // for every vertex of the mesh, find what voxel it is in
   // then, find indices of the vertices of that voxel
   // and compute the barycentric coordinates
-  
-  *interpolationVertices = (int*) malloc (sizeof(int) * 8 * objMesh->getNumVertices());
-  *interpolationWeights = (double*) malloc (sizeof(double) * 8 * objMesh->getNumVertices());
+
+  if (interpolationVertices)
+    *interpolationVertices = (int*) malloc (sizeof(int) * 8 * objMesh->getNumVertices());
+  if (interpolationWeights)
+    *interpolationWeights = (double*) malloc (sizeof(double) * 8 * objMesh->getNumVertices());
 
   for (unsigned int i=0; i < objMesh->getNumVertices(); i++) // over all vertices of the mesh
   {
     Vec3d pos = objMesh->getPosition(i);
     unsigned int i1,j1,k1;
-    
+
     Vec3d relPos = pos-bmin;
 
     // find voxel containing 'pos'
@@ -845,9 +838,9 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
 
     // compute barycentric coordinates
     Vec3d w = pos - (bmin + Vec3d(i1 * inc[0], j1 * inc[1], k1 * inc[2]));
-    double alpha = w[0] / inc[0];     
-    double beta = w[1] / inc[1];     
-    double gamma = w[2] / inc[2];     
+    double alpha = w[0] / inc[0];
+    double beta = w[1] / inc[1];
+    double gamma = w[2] / inc[2];
 
     unsigned int c000, c100, c110, c010, c001, c101, c111, c011;
     unsigned int * cArray[8] = { &c000, &c100, &c110, &c010, &c001, &c101, &c111, &c011 };
@@ -881,23 +874,29 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
     double f111 = (alpha)*(beta)*(gamma);
     double f011 = (1-alpha)*(beta)*(gamma);
 
-    (*interpolationVertices)[8*i+0] = c000;
-    (*interpolationVertices)[8*i+1] = c100;
-    (*interpolationVertices)[8*i+2] = c110;
-    (*interpolationVertices)[8*i+3] = c010;
-    (*interpolationVertices)[8*i+4] = c001;
-    (*interpolationVertices)[8*i+5] = c101;
-    (*interpolationVertices)[8*i+6] = c111;
-    (*interpolationVertices)[8*i+7] = c011;
-
-    (*interpolationWeights)[8*i+0] = f000;
-    (*interpolationWeights)[8*i+1] = f100;
-    (*interpolationWeights)[8*i+2] = f110;
-    (*interpolationWeights)[8*i+3] = f010;
-    (*interpolationWeights)[8*i+4] = f001;
-    (*interpolationWeights)[8*i+5] = f101;
-    (*interpolationWeights)[8*i+6] = f111;
-    (*interpolationWeights)[8*i+7] = f011;
+    if (interpolationVertices)
+    {
+      (*interpolationVertices)[8*i+0] = c000;
+      (*interpolationVertices)[8*i+1] = c100;
+      (*interpolationVertices)[8*i+2] = c110;
+      (*interpolationVertices)[8*i+3] = c010;
+      (*interpolationVertices)[8*i+4] = c001;
+      (*interpolationVertices)[8*i+5] = c101;
+      (*interpolationVertices)[8*i+6] = c111;
+      (*interpolationVertices)[8*i+7] = c011;
+    }
+
+    if (interpolationWeights)
+    {
+      (*interpolationWeights)[8*i+0] = f000;
+      (*interpolationWeights)[8*i+1] = f100;
+      (*interpolationWeights)[8*i+2] = f110;
+      (*interpolationWeights)[8*i+3] = f010;
+      (*interpolationWeights)[8*i+4] = f001;
+      (*interpolationWeights)[8*i+5] = f101;
+      (*interpolationWeights)[8*i+6] = f111;
+      (*interpolationWeights)[8*i+7] = f011;
+    }
   }
 
   // by now, surfaceFaces contains a unique list of all surface faces
@@ -908,11 +907,11 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
     int index[4];
     for (int i=0; i<4; i++)
       index[i] = (vertices2.find(face->vertex(i)))->second;
- 
-    std::pair< bool, unsigned int > texPos(false,0); // no textures 
+
+    std::pair< bool, unsigned int > texPos(false,0); // no textures
     std::pair< bool, unsigned int > normal(false,0); // no normals
 
-/*
+    /*
     // triangulate the face into two triangles
 
     Face newFace1;
@@ -927,7 +926,7 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
 
     objMesh->addFaceToGroup(newFace1,0);
     objMesh->addFaceToGroup(newFace2,0);
-*/
+     */
 
     // make one quad face
 
@@ -937,10 +936,16 @@ void ObjMeshOffsetVoxels::generateCubicMesh(
     newFace.addVertex( ObjMesh::Vertex( index[2], texPos, normal ) );
     newFace.addVertex( ObjMesh::Vertex( index[3], texPos, normal ) );
 
-    (*surfaceMesh)->addFaceToGroup(newFace,0);
+    if (surfaceMesh)
+      (*surfaceMesh)->addFaceToGroup(newFace,0);
   }
 
-  (*surfaceMesh)->computeBoundingBox();
+  // search if there already is "default" material; if there is not, add it
+  if (surfaceMesh)
+  {
+    (*surfaceMesh)->addDefaultMaterial();
+    (*surfaceMesh)->computeBoundingBox();
+  }
 }
 
 void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInterp, const string & inputObjMesh)
@@ -951,7 +956,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
   set<gridPoint> vertices;
 
   // insert all voxel vertices
-  set<voxel>::iterator aVoxel; 
+  set<voxel>::iterator aVoxel;
   for (aVoxel = voxels.begin(); aVoxel != voxels.end(); ++aVoxel) // over all voxels
   {
     unsigned int i1,j1,k1;
@@ -968,8 +973,8 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     vertices.insert(gridPoint(i1+1,j1,k1+1));
     vertices.insert(gridPoint(i1+1,j1+1,k1+1));
     vertices.insert(gridPoint(i1,j1+1,k1+1));
-  } 
-  
+  }
+
   // now, vertices contains all voxel vertices with no duplications
 
   // add all voxel vertices into a map, together with their corresponding index (i.e. serial number of a voxel vertex in the set order)
@@ -982,8 +987,8 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     //Vec3d pos = bmin + Vec3d(v->first*inc[0], v->second*inc[1], v->third*inc[2]);
     //cout << "Position " << position << ": " << pos << endl;
     position++;
-  } 
-  
+  }
+
   // generate the barycentric masks
 
   cout << "Writing interpolation info to file " << filenameInterp << " ." << endl;
@@ -1002,7 +1007,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
   // and compute the barycentric coordinates
 
   ObjMesh inputMesh(inputObjMesh);
-  
+
   // will contain all voxels that contain at least one vertex of the external mesh
   set<voxel> visitedVoxels;
 
@@ -1014,7 +1019,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
 
     Vec3d pos = inputMesh.getPosition(i);
     unsigned int i1,j1,k1;
-    
+
     Vec3d relPos = pos-bmin;
 
     // find voxel containing 'pos'
@@ -1027,9 +1032,9 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
 
     // compute barycentric coordinates
     Vec3d w = pos - (bmin + Vec3d(i1 * inc[0], j1 * inc[1], k1 * inc[2]));
-    double alpha = w[0] / inc[0];     
-    double beta = w[1] / inc[1];     
-    double gamma = w[2] / inc[2];     
+    double alpha = w[0] / inc[0];
+    double beta = w[1] / inc[1];
+    double gamma = w[2] / inc[2];
 
     map<gridPoint,int> :: iterator voxelVertexEntry;
 
@@ -1037,7 +1042,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 000" << endl;
-      return; 
+      return;
     }
     unsigned int c000 = voxelVertexEntry->second;
 
@@ -1045,7 +1050,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 100" << endl;
-      return; 
+      return;
     }
     unsigned int c100 = voxelVertexEntry->second;
 
@@ -1053,7 +1058,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 110" << endl;
-      return; 
+      return;
     }
     unsigned int c110 = voxelVertexEntry->second;
 
@@ -1061,7 +1066,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 010" << endl;
-      return; 
+      return;
     }
     unsigned int c010 = voxelVertexEntry->second;
 
@@ -1069,7 +1074,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 001" << endl;
-      return; 
+      return;
     }
     unsigned int c001 = voxelVertexEntry->second;
 
@@ -1077,7 +1082,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 101" << endl;
-      return; 
+      return;
     }
     unsigned int c101 = voxelVertexEntry->second;
 
@@ -1085,7 +1090,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 111" << endl;
-      return; 
+      return;
     }
     unsigned int c111 = voxelVertexEntry->second;
 
@@ -1093,7 +1098,7 @@ void ObjMeshOffsetVoxels::generateInterpolationMasks(const string & filenameInte
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 011" << endl;
-      return; 
+      return;
     }
     unsigned int c011 = voxelVertexEntry->second;
 
@@ -1200,7 +1205,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
   set<gridPoint> vertices;
 
   // insert all voxel vertices
-  set<voxel>::iterator aVoxel; 
+  set<voxel>::iterator aVoxel;
   for (aVoxel = voxels.begin(); aVoxel != voxels.end(); ++aVoxel) // over all voxels
   {
     unsigned int i1,j1,k1;
@@ -1217,8 +1222,8 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     vertices.insert(gridPoint(i1+1,j1,k1+1));
     vertices.insert(gridPoint(i1+1,j1+1,k1+1));
     vertices.insert(gridPoint(i1,j1+1,k1+1));
-  } 
-  
+  }
+
   // now, vertices contains all voxel vertices with no duplications
 
   // add all voxel vertices into a map, together with their corresponding index (i.e. serial number of a voxel vertex in the set order)
@@ -1231,8 +1236,8 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     //Vec3d pos = bmin + Vec3d(v->first*inc[0], v->second*inc[1], v->third*inc[2]);
     //cout << "Position " << position << ": " << pos << endl;
     position++;
-  } 
-  
+  }
+
   // === load the voxel modal matrix
   cout << "Loading the voxel modal matrix " << filenameVoxelModalMatrix << " ." << endl;
 
@@ -1289,7 +1294,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
 
     Vec3d pos = inputMesh.getPosition(i);
     unsigned int i1,j1,k1;
-    
+
     Vec3d relPos = pos-bmin;
 
     // find voxel containing 'pos'
@@ -1299,9 +1304,9 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
 
     // compute barycentric coordinates
     Vec3d w = pos - (bmin + Vec3d(i1 * inc[0], j1 * inc[1], k1 * inc[2]));
-    double alpha = w[0] / inc[0];     
-    double beta = w[1] / inc[1];     
-    double gamma = w[2] / inc[2];     
+    double alpha = w[0] / inc[0];
+    double beta = w[1] / inc[1];
+    double gamma = w[2] / inc[2];
 
     // locate voxel vertices
     map<gridPoint,int> :: iterator voxelVertexEntry;
@@ -1310,7 +1315,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 000" << endl;
-      return; 
+      return;
     }
     unsigned int c000 = voxelVertexEntry->second;
 
@@ -1318,7 +1323,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 100" << endl;
-      return; 
+      return;
     }
     unsigned int c100 = voxelVertexEntry->second;
 
@@ -1326,7 +1331,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 110" << endl;
-      return; 
+      return;
     }
     unsigned int c110 = voxelVertexEntry->second;
 
@@ -1334,7 +1339,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 010" << endl;
-      return; 
+      return;
     }
     unsigned int c010 = voxelVertexEntry->second;
 
@@ -1342,7 +1347,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 001" << endl;
-      return; 
+      return;
     }
     unsigned int c001 = voxelVertexEntry->second;
 
@@ -1350,7 +1355,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 101" << endl;
-      return; 
+      return;
     }
     unsigned int c101 = voxelVertexEntry->second;
 
@@ -1358,7 +1363,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 111" << endl;
-      return; 
+      return;
     }
     unsigned int c111 = voxelVertexEntry->second;
 
@@ -1366,7 +1371,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
     if (voxelVertexEntry == vertices2.end())
     {
       cout << "Error: vertex " << i+1 << " at location " << pos << " doesn't have corner neighbor 011" << endl;
-      return; 
+      return;
     }
     unsigned int c011 = voxelVertexEntry->second;
 
@@ -1424,7 +1429,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
       coef += dot(gradf101,normal) * u101;
       coef += dot(gradf111,normal) * u111;
       coef += dot(gradf011,normal) * u011;
-      
+
       outputMatrix[ELT(3*n, 3*i+0, j)] = coef[0];
       outputMatrix[ELT(3*n, 3*i+1, j)] = coef[1];
       outputMatrix[ELT(3*n, 3*i+2, j)] = coef[2];
@@ -1435,9 +1440,7 @@ void ObjMeshOffsetVoxels::generateNormalCorrectionMatrix(const string filenameCo
   cout << endl;
 
   WriteMatrixToDisk_((char*)filenameCorrectionMatrix.c_str(), 3*n, r, outputMatrix);
- 
+
   free(outputMatrix);
   free(voxelModalMatrix);
 }
-
-}
diff --git a/src/libobjMesh/objMeshOffsetVoxels.h b/libraries/objMesh/objMeshOffsetVoxels.h
similarity index 54%
rename from src/libobjMesh/objMeshOffsetVoxels.h
rename to libraries/objMesh/objMeshOffsetVoxels.h
index 254971096e63a852bd6f25c93f5bd4c7d8ea7468..020358d5696edb828470e9f1af39a666baea201e 100644
--- a/src/libobjMesh/objMeshOffsetVoxels.h
+++ b/libraries/objMesh/objMeshOffsetVoxels.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,13 +33,9 @@
 #ifndef _OBJMESHOFFSETVOXELS_H_
 #define _OBJMESHOFFSETVOXELS_H_
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 /*
   Author: Jernej Barbic, 2003
-  Generates a voxel representation of an offset surface
+  Voxelizes the given triangular geometry.
 */
 
 #include <set>
@@ -46,20 +46,52 @@
 #include "boundingBox.h"
 #include "minivector.h"
 
-namespace vega
-{
-class ObjMeshOffsetVoxels 
+class ObjMeshOffsetVoxels
 {
 public:
-  ObjMeshOffsetVoxels( ObjMesh * objMesh, int resolution[3], int depth, double expansionFactor=1.2); // cube box, with the given expansion ratio
-  ObjMeshOffsetVoxels( ObjMesh * objMesh, int resolution[3], int depth, Vec3d boxbmin, Vec3d boxbmax);
+  // voxelizes the given triangular geometry
+  // resolution is the grid resolution in xyz
+  // "depth" means that the output includes voxels that are grown a "depth" number of layers away from the originally interesecting voxels (good default: 0)
+  // note: cubic mesh means voxel mesh in this class
+  // expansionFactor: cube box; size is computed by expanding the tight-fitting cube by the given expansion ratio
+  ObjMeshOffsetVoxels(const ObjMesh * objMesh, const int resolution[3], int depth=0, double expansionFactor=1.2);
+  // boxbmin, boxbmax, explicitly provide the bounding box
+  ObjMeshOffsetVoxels(const ObjMesh * objMesh, const int resolution[3], int depth, Vec3d boxbmin, Vec3d boxbmax);
+
+  // flood-fills the space outward from the voxel containing seed, until hitting existing voxels
+  void floodFill(Vec3d seed);
+  void floodFill(std::vector<Vec3d> & seeds);
+  // finds all empty components in the voxel structure, and gives seeds and sizes for each of them
+  void emptyComponents(std::vector<Vec3d> & componentSeeds, std::vector<int> & componentSize, bool interiorOnly = true);
+
+  size_t numVoxels() { return voxels.size(); }
+  double voxelSpacing() { return inc[0]; }
+
+  // generates the voxel mesh vertices, the voxel elements, the interpolation structure to interpolate quantities from the voxel mesh to the obj mesh
+  void generateCubicMesh(
+      int * numVertices, double ** vertices,
+      int * numElements, int ** elements,
+      int ** interpolationVertices = NULL, double ** interpolationWeights = NULL,
+      ObjMesh ** surfaceMesh = NULL);
+
+  // generates the surface mesh of the computed voxel mesh
+  ObjMesh * surfaceOffsetMesh();
+
+  // writes the voxel mesh file to disk, as well as the interpolation file and the surface mesh file
+  void generateCubicMesh(const std::string & filenameVeg, const std::string & filenameInterp, const std::string & filenameObj);
+
+  // generates the interpolation mask for the geometry from an external file 'inputObjMesh'
+  void generateInterpolationMasks(const std::string & filenameInterp, const std::string & inputObjMesh);
+
+  //  generates the normal correction matrix for the vertices from the external file 'inputObjMesh'
+  void generateNormalCorrectionMatrix(const std::string filenameCorrectionMatrix,
+      const std::string inputObjMesh,
+      const std::string filenameVoxelModalMatrix,
+      const std::string filenameNormals);
 
   typedef triple<int,int,int> voxel;
   typedef triple<int,int,int> gridPoint;
 
-  void render();
-  void renderSurfaceFaces();
-
   class TopologicalFace
   {
   public:
@@ -80,45 +112,14 @@ public:
     bool operator()(const TopologicalFace & x, const TopologicalFace & y) const;
   };
 
- void renderTopologicalFace(const TopologicalFace & face) const;
-
- // generates the offset surface mesh
- ObjMesh * surfaceOffsetMesh();
-
- // writes the cubic mesh file to disk, as well as the interpolation file and the surface mesh file
- void generateCubicMesh(const std::string & filenameVeg, const std::string & filenameInterp, const std::string & filenameObj);
-
- // generates the "squashing cubes" data (in memory)
- void generateCubicMesh(
-   int * numVertices, double ** vertices, 
-   int * numElements, int ** elements,
-   int ** interpolationVertices, 
-   double ** interpolationWeights,  ObjMesh ** surfaceMesh);
-
- // generates the interpolation mask for the geometry from an external file 'inputObjMesh'
- void generateInterpolationMasks(const std::string & filenameInterp, const std::string & inputObjMesh);
-
- //  generates the normal correction matrix for the vertices from the external file 'inputObjMesh'
- void generateNormalCorrectionMatrix(const std::string filenameCorrectionMatrix, 
-                const std::string inputObjMesh, 
-		const std::string filenameVoxelModalMatrix,
- 	        const std::string filenameNormals);
-
-  // flood-fills the space outward from the voxel containing seed, until hitting existing voxels
-  void floodFill(Vec3d seed);
-  void floodFill(std::vector<Vec3d> & seeds);
-
-  // finds all empty components in the voxel structure, and gives seeds and sizes for each of them
-  void emptyComponents(std::vector<Vec3d> & componentSeeds, 
-    std::vector<int> & componentSize, bool interiorOnly = true);
-
-  size_t numVoxels() { return voxels.size(); }
-  double voxelSpacing() { return inc[0]; }
+  void render();
+  void renderSurfaceFaces();
+  void renderTopologicalFace(const TopologicalFace & face) const;
 
 protected:
-  ObjMesh * objMesh;
+  const ObjMesh * objMesh = nullptr;
 
-  int resolution[3]; 
+  int resolution[3];
   int depth;
   std::set<voxel> voxels;
   std::set<TopologicalFace,FaceOrder> surfaceFaces;
@@ -135,8 +136,8 @@ protected:
   // returns whether a voxel that touches the bounding box was added
   bool floodFillFromSet(Vec3d seed, std::set<voxel> & voxelSet);
 
-  void init(int resolution[3], int depth, Vec3d bmin, Vec3d bmax);
+  void init(const int resolution[3], int depth, Vec3d bmin, Vec3d bmax);
 };
-}
+
 #endif
 
diff --git a/src/libobjMesh/objMeshOrientable.cpp b/libraries/objMesh/objMeshOrientable.cpp
similarity index 73%
rename from src/libobjMesh/objMeshOrientable.cpp
rename to libraries/objMesh/objMeshOrientable.cpp
index 7c41c07c352665412a428ce66cb97f9376ff4b95..aec7ed929a0d8510850c7ef490593df0dcc87f2f 100644
--- a/src/libobjMesh/objMeshOrientable.cpp
+++ b/libraries/objMesh/objMeshOrientable.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,7 +30,7 @@
  *                                                                       *
  *************************************************************************/
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #pragma warning(disable : 4996)
   #pragma warning(disable : 4267)
   #pragma warning(disable : 4244)
@@ -44,8 +48,6 @@
 using namespace std;
 #include "objMeshOrientable.h"
 
-namespace vega
-{
 /*
   Generates a half edge datastructure, assuming tha the given obj mesh is orientable.
   Author: Jernej Barbic, 2004
@@ -64,36 +66,35 @@ ObjMeshOrientable::~ObjMeshOrientable()
     delete(objMesh);
 }
 
-ObjMeshOrientable::ObjMeshOrientable( const std::string& filename, 
-  int generateHalfEdges, int * numOrientationFlips_ ) 
+ObjMeshOrientable::ObjMeshOrientable(const std::string & filename, int generateHalfEdges, int * numOrientationFlips_, int verbose)
 {
   internalAllocation = 1;
-  objMesh = new ObjMesh(filename);
-
-  Init(generateHalfEdges, numOrientationFlips_);
+  objMesh = new ObjMesh(filename, ObjMesh::ASCII, verbose);
+  Init(generateHalfEdges, numOrientationFlips_, verbose);
 }
 
-ObjMeshOrientable::ObjMeshOrientable( ObjMesh * objMesh, 
-  int generateHalfEdges, int * numOrientationFlips_ ) 
+ObjMeshOrientable::ObjMeshOrientable(ObjMesh * objMesh, int generateHalfEdges, int * numOrientationFlips_, int verbose)
 {
   internalAllocation = 0;
   this->objMesh = objMesh;
-  
-  Init(generateHalfEdges, numOrientationFlips_);
+  Init(generateHalfEdges, numOrientationFlips_, verbose);
 }
 
-void ObjMeshOrientable::Init(int generateHalfEdges, int * numOrientationFlips_ )
+void ObjMeshOrientable::Init(int generateHalfEdges, int * numOrientationFlips_, int verbose)
 {
   if (generateHalfEdges)
   {
-    int numOrientationFlips = GenerateHalfEdgeDataStructure();
+    int numOrientationFlips = GenerateHalfEdgeDataStructure(verbose);
 
     if (numOrientationFlips_ != NULL)
       *numOrientationFlips_ = numOrientationFlips;
+
+    if (numOrientationFlips < 0)
+      throw 1;
   }
 }
 
-void ObjMeshOrientable::PrintHalfEdges()
+void ObjMeshOrientable::PrintHalfEdges() const
 {
   for (unsigned int i=0; i<halfEdges_.size(); i++)
   {
@@ -111,16 +112,18 @@ void ObjMeshOrientable::PrintHalfEdges()
 }
 
 // returns the number of edges flipped
-int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
+int ObjMeshOrientable::GenerateHalfEdgeDataStructure(int verbose)
 {
-  std::cout << "Building the half edge data structure..." << std::endl;
+  if (verbose)
+    std::cout << "Building the half edge data structure..." << std::endl;
 
   // Step 1: iterate over all faces
   // for each face, add all the edges onto the list of half-edges
 
-  std::cout << "Step 1: Generating the list of half edges..." << std::endl;
+  if (verbose)
+    std::cout << "Step 1: Generating the list of half edges..." << std::endl;
 
-  typedef std::vector<ObjMesh::Group> SGroup;
+  //typedef std::vector<ObjMesh::Group> SGroup;
 
   int coutCounter = 0;
 
@@ -128,8 +131,11 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
   {
     const ObjMesh::Group * currentGroup = objMesh->getGroupHandle(i);
 
-    std::cout << "  Processing obj group '" << currentGroup->getName() << std::endl;
-    std::cout << "  Iterating through group faces..." << std::endl;
+    if (verbose)
+    {
+      std::cout << "  Processing obj group '" << currentGroup->getName() << std::endl;
+      std::cout << "  Iterating through group faces..." << std::endl;
+    }
 
     for( unsigned int iFace = 0; iFace < currentGroup->getNumFaces(); ++iFace )
     {
@@ -137,12 +143,14 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
 
       if (coutCounter < 100)
       {
-        std::cout << face.getNumVertices() ;
+        if (verbose)
+          std::cout << face.getNumVertices() ;
         coutCounter++;
       }
       if (coutCounter == 100)
       {
-        cout << "...[and more]";
+        if (verbose)
+          cout << "...[and more]";
         coutCounter++;
       }
 
@@ -153,36 +161,39 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
         // create a half edge for each edge, store -1 for half-edge adjacent edge for now
         // index vertices starting from 0
         int nextEdge = edgesSoFar + ((iVertex + 1) % face.getNumVertices());
-        HalfEdge halfEdge(edgesSoFar + iVertex, face.getVertex(iVertex).getPositionIndex(), face.getVertex((iVertex + 1) % face.getNumVertices()).getPositionIndex(),
-		 	  iVertex, (iVertex + 1) % face.getNumVertices(), i, iFace, -1, nextEdge); 
+        HalfEdge halfEdge(edgesSoFar + iVertex, face.getVertex(iVertex).getPositionIndex(), face.getVertex((iVertex + 1) % face.getNumVertices()).getPositionIndex(), iVertex, (iVertex + 1) % face.getNumVertices(), i, iFace, -1, nextEdge);
 
         halfEdges_.push_back(halfEdge);
       }
     }
-    std::cout << std::endl;
+    if (verbose)
+      std::cout << std::endl;
   }
 
-/*  
+/*
   for (unsigned int i=0; i<halfEdges_.size(); i++)
   {
     cout << "Half edge "<< i << " :" << endl;
-    cout << "  Opposite edge: " << halfEdges_[i].opposite() << endl; 
-    cout << "  Next edge: " << halfEdges_[i].next() << endl; 
-    cout << "  Group: " << halfEdges_[i].groupID() << endl; 
-    cout << "  Face: " << halfEdges_[i].face() << endl; 
-    cout << "  Start vertex: " << halfEdges_[i].startVertex() << endl; 
-    cout << "  End vertex: " << halfEdges_[i].endVertex() << endl; 
-    cout << "  Start vertex (local): " << halfEdges_[i].startV() << endl; 
-    cout << "  End vertex (local): " << halfEdges_[i].endV() << endl; 
-    cout << "  Is boundary: " << halfEdges_[i].isBoundary() << endl; 
+    cout << "  Opposite edge: " << halfEdges_[i].opposite() << endl;
+    cout << "  Next edge: " << halfEdges_[i].next() << endl;
+    cout << "  Group: " << halfEdges_[i].groupID() << endl;
+    cout << "  Face: " << halfEdges_[i].face() << endl;
+    cout << "  Start vertex: " << halfEdges_[i].startVertex() << endl;
+    cout << "  End vertex: " << halfEdges_[i].endVertex() << endl;
+    cout << "  Start vertex (local): " << halfEdges_[i].startV() << endl;
+    cout << "  End vertex (local): " << halfEdges_[i].endV() << endl;
+    cout << "  Is boundary: " << halfEdges_[i].isBoundary() << endl;
   }
 */
 
   // Step 2: build correspondence among half-dges
   // for each half-edge, search for the opposite half-edge, if it exists
 
-  std::cout << "Step 2: Building correspondence among half-edges..." << std::endl;
-  std::cout << "Boundary edges: ";
+  if (verbose)
+  {
+    std::cout << "Step 2: Building correspondence among half-edges..." << std::endl;
+    std::cout << "Boundary edges: ";
+  }
 
   // insert all edges into a binary tree
 
@@ -193,12 +204,15 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
   {
     int vertex1 = halfEdges_[i].startVertex();
     int vertex2 = halfEdges_[i].endVertex();
-   
+
     if (vertex1 == vertex2)
     {
-      std::cout << "Error: encountered a degenerated edge with equal starting and ending vertex." << std::endl;
-      std::cout << "  Group:" << halfEdges_[i].groupID() << "  Face #: " << halfEdges_[i].face() << "Vertex ID: " << vertex1 << std::endl;
-      exit(1);
+      if (verbose)
+      {
+        std::cout << "Error: encountered a degenerated edge with equal starting and ending vertex." << std::endl;
+        std::cout << "  Group:" << halfEdges_[i].groupID() << "  Face #: " << halfEdges_[i].face() << "Vertex ID: " << vertex1 << std::endl;
+      }
+      return -1;
     }
 
     if (vertex1 > vertex2) // swap
@@ -209,15 +223,15 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
     }
 
     std::pair<unsigned int, unsigned int> vertices(vertex1,vertex2);
-    edges.insert(std::make_pair(vertices,i)); 
-  }  
- 
+    edges.insert(std::make_pair(vertices,i));
+  }
+
   // retrieve one by one and build correspondence
   for (unsigned int i=0; i < halfEdges_.size(); i++)
   {
     int vertex1 = halfEdges_[i].startVertex();
     int vertex2 = halfEdges_[i].endVertex();
-   
+
     if (vertex1 > vertex2) // swap
     {
       int buffer = vertex1;
@@ -232,57 +246,64 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
     int hits = 0;
     int candidates = 0;
     BinaryTree::iterator pos;
-    for (pos = edges.lower_bound(vertices); 
-         pos != edges.upper_bound(vertices);
-         ++pos)
+    for (pos = edges.lower_bound(vertices); pos != edges.upper_bound(vertices); ++pos)
     {
       candidates++;
       // check if we found ourselves
-      if (pos->second != i) 
-      { // not ourselves
+      if (pos->second != i)
+      {
+        // not ourselves
         halfEdges_[i].setOpposite(pos->second);
         hits++;
       }
     }
 
-    if (candidates >= 3) 
+    if (candidates >= 3)
     {
-      std::cout << "Error: encountered an edge that appears in more than two triangles. Geometry is non-manifold. Exiting." << std::endl;
+      if (verbose)
+        std::cout << "Error: encountered an edge that appears in more than two triangles. Geometry is non-manifold. Exiting." << std::endl;
       int faceNum = halfEdges_[i].face();
-      std::cout << "  Group:" << halfEdges_[i].groupID() << std::endl << "  Face #: " << faceNum << std::endl;
-      std::cout << "  Edge: " << vertex1 << " " << vertex2 << std::endl;
-      std::cout << "  Vertices: " << objMesh->getPosition(vertex1) << " " << objMesh->getPosition(vertex2) << std::endl;
-      exit(1);
+      if (verbose)
+      {
+        std::cout << "  Group:" << halfEdges_[i].groupID() << std::endl << "  Face #: " << faceNum << std::endl;
+        std::cout << "  Edge: " << vertex1 << " " << vertex2 << std::endl;
+        std::cout << "  Vertices: " << objMesh->getPosition(vertex1) << " " << objMesh->getPosition(vertex2) << std::endl;
+      }
+      return -1;
     }
 
     if (hits == 0) // boundary edge
-    {  
-      //std::cout << "B"; 
-      std::cout << "B(" << vertex1 << "," << vertex2 << ") "; 
+    {
+      //std::cout << "B";
+      if (verbose)
+        std::cout << "B(" << vertex1 << "," << vertex2 << ") ";
       boundaryEdges_.push_back(i);
     }
-  }  
+  }
+
+  if (verbose)
+    std::cout << " total: " << boundaryEdges_.size() << std::endl;
 
-  std::cout << " total: " << boundaryEdges_.size() << std::endl;
-/*  
+/*
   for (unsigned int i=0; i<halfEdges_.size(); i++)
   {
     cout << "Half edge "<< i << " :" << endl;
-    cout << "  Opposite edge: " << halfEdges_[i].opposite() << endl; 
-    cout << "  Next edge: " << halfEdges_[i].next() << endl; 
-    cout << "  Group: " << halfEdges_[i].groupID() << endl; 
-    cout << "  Face: " << halfEdges_[i].face() << endl; 
-    cout << "  Start vertex: " << halfEdges_[i].startVertex() << endl; 
-    cout << "  End vertex: " << halfEdges_[i].endVertex() << endl; 
-    cout << "  Start vertex (local): " << halfEdges_[i].startV() << endl; 
-    cout << "  End vertex (local): " << halfEdges_[i].endV() << endl; 
-    cout << "  Is boundary: " << halfEdges_[i].isBoundary() << endl; 
+    cout << "  Opposite edge: " << halfEdges_[i].opposite() << endl;
+    cout << "  Next edge: " << halfEdges_[i].next() << endl;
+    cout << "  Group: " << halfEdges_[i].groupID() << endl;
+    cout << "  Face: " << halfEdges_[i].face() << endl;
+    cout << "  Start vertex: " << halfEdges_[i].startVertex() << endl;
+    cout << "  End vertex: " << halfEdges_[i].endVertex() << endl;
+    cout << "  Start vertex (local): " << halfEdges_[i].startV() << endl;
+    cout << "  End vertex (local): " << halfEdges_[i].endV() << endl;
+    cout << "  Is boundary: " << halfEdges_[i].isBoundary() << endl;
   }
 */
   // now, each half-edge knows its mirror edge, but orientations of faces might be inconsistent
 
   // orient all half-edges consistently
-  std::cout << "Step 3: Attempting to orient the faces coherently..." << std::endl;
+  if (verbose)
+    std::cout << "Step 3: Attempting to orient the faces coherently..." << std::endl;
 
   // generate marks for all the edges
   std::vector<int> marks(halfEdges_.size(), 0);
@@ -309,7 +330,8 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
     }
     else
     {
-      cout << "Found a new connected component. Seed half-edge is: " << unmarkedEdge << endl;
+      if (verbose)
+        cout << "Found a new connected component. Seed half-edge is: " << unmarkedEdge << endl;
       connectedComponents++;
       queue.insert(unmarkedEdge);
 
@@ -324,14 +346,14 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
         //std::cout << "Marking all the edges on this face: ";
         // first, mark all the edges on this face as visited
         int loop = edge;
-        do 
+        do
         {
           marks[loop] = 1;
           //std::cout << loop << " ";
           loop = halfEdges_[loop].next();
         }
         while (loop != edge);
-        //std::cout << std::endl; 
+        //std::cout << std::endl;
 
         // check if edge is consistent with the opposite edge orientation
         // careful: edge might be on the boundary
@@ -350,7 +372,8 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
 
         if (exitFlag == 1) // all edges are boundary; this is an isolated face
         {
-          cout << "Encountered an isolated face." << endl;
+          if (verbose)
+            cout << "Encountered an isolated face." << endl;
           //cout << "none found." << endl;
           continue; // no need to queue anything or flip anything, this was an isolated face
           // also, this case can only happen during the first iteration of the while loop, which will also be the last one
@@ -365,8 +388,8 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
 
         //std::cout << "Orientation flip necessary for this face: " << orientationFlipNecessary << std::endl;
 
-        if (orientationFlipNecessary) 
-        { 
+        if (orientationFlipNecessary)
+        {
           // flip all edges along this face
           //cout << "Orientation flip" << endl;
           numOrientationFlips++;
@@ -387,9 +410,14 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
           int faceID = halfEdges_[loop].face();
 
           ObjMesh::Group * currentGroup = (ObjMesh::Group*) objMesh->getGroupHandle(groupID);
-          currentGroup->getFace(faceID).printVertices();
+
+          if (verbose)
+            currentGroup->getFace(faceID).printVertices();
+
           currentGroup->reverseFace(faceID);
-          currentGroup->getFace(faceID).printVertices();
+
+          if (verbose)
+            currentGroup->getFace(faceID).printVertices();
         }
 
         // check if new orientation is consistent eveywhere along the face
@@ -400,7 +428,7 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
         {
           if (!halfEdges_[loop].isBoundary()) // skip boundary edges
           {
-            // if opposite unmarked, queue the opposite edge 
+            // if opposite unmarked, queue the opposite edge
             if (marks[halfEdges_[loop].opposite()] == 0)
             {
               queue.insert(halfEdges_[loop].opposite());
@@ -408,19 +436,20 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
               //std::cout << "visiting edge: " << loop << " pushing opposite: " << halfEdges_[loop].opposite() << std::endl;
             }
             else
-            { 
+            {
               // opposite edge is marked as already visited
-              // if orientation consistent, do nothing 
+              // if orientation consistent, do nothing
               // if orientation not consistent, surface is not orientable
-        
-              bool orientationConsistent = (halfEdges_[loop].startVertex() == (edgeOpposite(halfEdges_[loop])).endVertex()); 
+
+              bool orientationConsistent = (halfEdges_[loop].startVertex() == (edgeOpposite(halfEdges_[loop])).endVertex());
 
               //std::cout << "visiting edge: " << loop << " opposite marked " << std::endl;
 
               if (!orientationConsistent)
               {
-                std::cout << "Error: surface is non-orientable. Offending edge: [" << halfEdges_[loop].startVertex() << "," << halfEdges_[loop].endVertex() << "]" << std::endl;
-                exit(1);
+                if (verbose)
+                  std::cout << "Error: surface is non-orientable. Offending edge: [" << halfEdges_[loop].startVertex() << "," << halfEdges_[loop].endVertex() << "]" << std::endl;
+                return -1;
               }
             }
           }
@@ -430,15 +459,19 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
 
       }
     }
-  } // end of while  
+  } // end of while
 
-  printf("Consistent orientation generated. Performed %d orientation flips.\n", numOrientationFlips);
+  if (verbose)
+    printf("Consistent orientation generated. Performed %d orientation flips.\n", numOrientationFlips);
 
   //PrintHalfEdges();
 
   // step 4: for every vertex, find a half-edge emanating out of it
-  std::cout << "Step 4: For every vertex, caching a half-edge emanating out of it..." << std::endl;
-  std::cout << "        For every face, caching a half-edge on it..." << std::endl;
+  if (verbose)
+  {
+    std::cout << "Step 4: For every vertex, caching a half-edge emanating out of it..." << std::endl;
+    std::cout << "        For every face, caching a half-edge on it..." << std::endl;
+  }
 
   for (unsigned int i=0; i< objMesh->getNumVertices(); i++)
     edgesAtVertices_.push_back(-1); // value of -1 corresponds to no edge (i.e. isolated vertex)
@@ -459,8 +492,8 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
       numIsolatedVertices++;
       continue;
     }
-    HalfEdge * loop = &edgeAtVertex(i);
-    HalfEdge * start = loop; 
+    const HalfEdge * loop = &edgeAtVertex(i);
+    const HalfEdge * start = loop;
     do
     {
       if (loop->isBoundary())
@@ -473,10 +506,11 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
     }
     while (*loop != *start);
     // if we came around, no need to change edgeAtVertices[i]
-  } 
+  }
 
   if (numIsolatedVertices > 0)
-    printf("Warning: mesh has %d isolated vertices.\n", numIsolatedVertices);
+    if (verbose)
+      printf("Warning: mesh has %d isolated vertices.\n", numIsolatedVertices);
 
   // build the cache for faces, first reset to -1
   for (unsigned int i=0; i < objMesh->getNumGroups(); i++)
@@ -485,11 +519,11 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
     std::vector<int> dataForThisGroup;
     dataForThisGroup.clear();
     for (unsigned int j=0; j < currentGroup->getNumFaces(); j++)
-    { 
+    {
       dataForThisGroup.push_back(-1);
     }
     edgesAtFaces_.push_back(dataForThisGroup);
-  }   
+  }
   for (unsigned int i=0; i < halfEdges_.size(); i++)
     edgesAtFaces_[halfEdges_[i].groupID()][halfEdges_[i].face()] = i;
 
@@ -499,7 +533,10 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
     const ObjMesh::Group * currentGroup = objMesh->getGroupHandle(i);
     for (unsigned int j=0; j < currentGroup->getNumFaces(); j++)
       if (edgesAtFaces_[i][j] == -1)
-        cout << "Warning: face on group " << i << "(" << currentGroup->getName() << "), position " << j << " has no edges." << endl;
+      {
+        if (verbose)
+          cout << "Warning: face on group " << i << "(" << currentGroup->getName() << "), position " << j << " has no edges." << endl;
+      }
   }
 
   determineIfSurfaceHasBoundary();
@@ -517,7 +554,7 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
   {
     cout << "Halfedge into vertex " << i << ": " << edgeAtVertex(i).position() << endl;
   }
- 
+
   // testing: print out associated edges for every face
   for (unsigned int i=0; i < groups_.size(); i++)
     for (unsigned int j=0; j < groups_[i].getNumFaces(); j++)
@@ -528,11 +565,11 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
   // testing: loop around every vertex
   for (unsigned int i=0; i < vertexPositions_.size(); i++)
   {
-    cout << "Looping around vertex " << i << ":"; 
+    cout << "Looping around vertex " << i << ":";
     int flag = 0;
     HalfEdge * start = &edgeAtVertex(i);
     HalfEdge * loop = start;
-    do 
+    do
     {
       cout << loop->position() << " ";
 
@@ -549,44 +586,46 @@ int ObjMeshOrientable::GenerateHalfEdgeDataStructure()
   }
   */
 
-  std::cout << "Half-edge datastructure constructed successfully." << std::endl;
+  if (verbose)
+  {
+    std::cout << "Half-edge datastructure constructed successfully." << std::endl;
+    std::cout << "Statistics: " << std::endl;
+    std::cout << "  Half-edges: " << halfEdges_.size() << std::endl;
+    std::cout << "  Boundary half-edges: " << boundaryEdges_.size() << std::endl;
+    std::cout << "  Connected components: " << connectedComponents << std::endl;
+  }
 
-  std::cout << "Statistics: " << std::endl;
-  std::cout << "  Half-edges: " << halfEdges_.size() << std::endl;
-  std::cout << "  Boundary half-edges: " << boundaryEdges_.size() << std::endl;
-  std::cout << "  Connected components: " << connectedComponents << std::endl;
-  
   return numOrientationFlips;
-} 
+}
 
 // returns the previous halfedge to the given half-edge
 // does so by looping around the face (pointers to previous edges are not explicitly stored), so this is slower than edgeNext
-ObjMeshOrientable::HalfEdge & ObjMeshOrientable::edgePrevious ( HalfEdge & halfedge )
+const ObjMeshOrientable::HalfEdge & ObjMeshOrientable::edgePrevious ( const HalfEdge & halfedge ) const
 {
-  HalfEdge * loop = &halfedge;
+  const HalfEdge * loop = &halfedge;
   while (edgeNext(*loop) != halfedge)
     loop = &(edgeNext(*loop));
 
-  HalfEdge & prevEdge = *loop;
+  const HalfEdge & prevEdge = *loop;
 
   return prevEdge;
 }
 
 // loops around the vertex (vertex is defined as the ending position of the halfedge)
 // consists of taking the next edge, then taking the opposite edge
-// if boundary edge encountered, can't take the opposite edge; it this case flag=1 is returned 
+// if boundary edge encountered, can't take the opposite edge; it this case flag=1 is returned
 //     and the edge returned is the boundary edge pointing away from the vertex
 // if taking the opposite edge is possible, the returned edge points into the vertex and flag is set to 0
-ObjMeshOrientable::HalfEdge & ObjMeshOrientable::loopVertex(HalfEdge & halfedge, int & flag)
+const ObjMeshOrientable::HalfEdge & ObjMeshOrientable::loopVertex(const HalfEdge & halfedge, int & flag)
 {
-  HalfEdge * loop = &halfedge;
+  const HalfEdge * loop = &halfedge;
   loop = &(edgeNext(*loop));
 
   if (loop->isBoundary())
   {
     flag = 1;
     // return boundary edge pointing away from the vertex (there is no corresponding edge pointing into the vertex)
-    HalfEdge & result = *loop;
+    const HalfEdge & result = *loop;
     return result;
   }
   else
@@ -594,7 +633,7 @@ ObjMeshOrientable::HalfEdge & ObjMeshOrientable::loopVertex(HalfEdge & halfedge,
     flag = 0;
     loop = &(edgeOpposite(*loop));
     // return edge pointing into the vertex
-    HalfEdge & result = *loop;
+    const HalfEdge & result = *loop;
     return result;
   }
 }
@@ -624,7 +663,7 @@ void ObjMeshOrientable::CopyHalfEdgeTopologyFrom(ObjMeshOrientable * source) //
   hasBoundary_ = source->hasBoundary_;
 }
 
-ObjMesh * ObjMeshOrientable::GenerateOrientedMesh()
+ObjMesh * ObjMeshOrientable::GenerateOrientedMesh() const
 {
   ObjMesh * outputObjMesh = new ObjMesh(*objMesh);
 
@@ -655,4 +694,3 @@ ObjMesh * ObjMeshOrientable::GenerateOrientedMesh()
 
   return outputObjMesh;
 }
-}
diff --git a/src/libobjMesh/objMeshOrientable.h b/libraries/objMesh/objMeshOrientable.h
similarity index 56%
rename from src/libobjMesh/objMeshOrientable.h
rename to libraries/objMesh/objMeshOrientable.h
index abcbb65691860fdc09a11440e951a41f890e95aa..c5f4787e6d1bae1aab62da5aded43a3f5935b266 100644
--- a/src/libobjMesh/objMeshOrientable.h
+++ b/libraries/objMesh/objMeshOrientable.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,22 +35,21 @@
 
 #include "objMesh.h"
 
-namespace vega
-{
 /*
-  Generates a half edge datastructure, assuming tha the given obj mesh is orientable.
+  Generates a half-edge datastructure, assuming tha the given obj mesh is orientable.
   Author: Jernej Barbic, 2004
 */
 
 class ObjMeshOrientable
 {
 public:
-  // calls ObjMesh
-  // if generateHalfEdges flag is on, it also generates the half edges (otherwise class not fully initialized)
+  // generates the half-edge datastructure
+  // if generateHalfEdges flag is 1 (default), it also generates the half edges (otherwise class is not fully initialized)
   // if numOrientationFlips is not NULL, returns the number of edges that were flipped to orient the surface coherently
-  ObjMeshOrientable( const std::string& filename, int generateHalfEdges=1, int * numOrientationFlips = NULL);
+  // if the mesh is non-orientable, throws an exception
+  ObjMeshOrientable(const std::string & filename, int generateHalfEdges=1, int * numOrientationFlips = NULL, int verbose=0);
 
-  ObjMeshOrientable( ObjMesh * objMesh, int generateHalfEdges=1, int * numOrientationFlips = NULL);
+  ObjMeshOrientable(ObjMesh * objMesh, int generateHalfEdges=1, int * numOrientationFlips = NULL, int verbose=0);
 
   ~ObjMeshOrientable();
 
@@ -56,44 +59,44 @@ public:
   class HalfEdge
   {
     public:
-      explicit HalfEdge( const unsigned int & position_g, const unsigned int& startVertex_g, const unsigned int& endVertex_g, const unsigned int& startV_g, const unsigned int& endV_g, const unsigned int groupID_g, const unsigned int& face_g, const int opposite_g, const unsigned int next_g // value of -1 denotes boundary edge const unsigned int next_g
+      explicit HalfEdge(const unsigned int & position_g, const unsigned int& startVertex_g, const unsigned int& endVertex_g, const unsigned int& startV_g, const unsigned int& endV_g, const unsigned int groupID_g, const unsigned int& face_g, const int opposite_g, const unsigned int next_g // value of -1 denotes boundary edge const unsigned int next_g
 )
-      :	position_(position_g), startVertex_(startVertex_g), endVertex_(endVertex_g), 
+      :  position_(position_g), startVertex_(startVertex_g), endVertex_(endVertex_g),
         startV_(startV_g), endV_(endV_g),
         groupID_(groupID_g),face_(face_g),
         opposite_(opposite_g), next_(next_g) {}
 
         // accessors for getting global edge position
-        unsigned int position() { return position_;}
+        unsigned int position() const { return position_;}
 
         // accessors for starting and ending vertices of the edge (global indexing)
-        unsigned int startVertex() { return startVertex_; }
-        unsigned int endVertex() { return endVertex_; }
+        unsigned int startVertex() const { return startVertex_; }
+        unsigned int endVertex() const { return endVertex_; }
 
         // accessors for starting and ending vertices of the edge (local indexing on the local face)
-        unsigned int startV() { return startV_; }
-        unsigned int endV() { return endV_; }
+        unsigned int startV() const { return startV_; }
+        unsigned int endV() const { return endV_; }
 
         // accessors for the face on the left of the edge
-        unsigned int groupID() { return groupID_; }
-        unsigned int face() { return face_; }
+        unsigned int groupID() const { return groupID_; }
+        unsigned int face() const { return face_; }
 
         // accessors for opposite and next edges
-        int opposite() { return opposite_; }
-        unsigned int next() { return next_; }
+        int opposite() const { return opposite_; }
+        unsigned int next() const { return next_; }
 
         // mutator for opposite and next edges
         void setOpposite(int opposite_g) { opposite_ = opposite_g; }
         void setNext(unsigned int next_g) { next_ = next_g; }
 
         // is this edge a boundary edge
-        bool isBoundary() { return (opposite_ == -1);}
+        bool isBoundary() const { return (opposite_ == -1);}
 
         void flipOrientation(); // flips orientation of the edge (careful, structure not coherent any more now)
 
-        bool operator== (HalfEdge & halfEdge2) { return (position_ == halfEdge2.position_);} 
-        bool operator!= (HalfEdge & halfEdge2) { return (position_ != halfEdge2.position_);} 
- 
+        bool operator== (const HalfEdge & halfEdge2) const { return (position_ == halfEdge2.position_);}
+        bool operator!= (const HalfEdge & halfEdge2) const { return (position_ != halfEdge2.position_);}
+
 
     protected:
         unsigned int position_; // the global position of the half-edge in the data structure
@@ -104,16 +107,16 @@ public:
         int opposite_;
         unsigned int next_;
   };
-        
+
 protected:
-  ObjMesh * objMesh;
-  void Init(int generateHalfEdges, int * numOrientationFlips);
+  ObjMesh * objMesh = nullptr;
+  void Init(int generateHalfEdges, int * numOrientationFlips, int verbose);
 
   std::vector< HalfEdge > halfEdges_;
   std::vector< int > boundaryEdges_;
-  unsigned int connectedComponents; // the total number of connected components of the mesh
+  unsigned int connectedComponents = 0; // the total number of connected components of the mesh
 
-  bool hasBoundary_; // does the surface have boundary
+  bool hasBoundary_ = false; // does the surface have boundary
 
   std::vector<int> edgesAtVertices_; // for every vertex, contains one half-edge emanating out of it
   std::vector<std::vector<int> > edgesAtFaces_; // for every face, contains one half-edge on this face
@@ -121,77 +124,78 @@ protected:
   void determineIfSurfaceHasBoundary();
 
 public:
-  size_t numHalfEdges() { return halfEdges_.size(); }
-  HalfEdge & halfEdge(unsigned int i) { return halfEdges_[i]; }
+  size_t numHalfEdges() const { return halfEdges_.size(); }
+  const HalfEdge & halfEdge(unsigned int i) const { return halfEdges_[i]; }
 
   // this function is mostly called internally, but can sometimes also be called from the outside
-  int GenerateHalfEdgeDataStructure(); // generates the whole datastructure, assuming the base objMesh class has been initialized
   // returns the number of edges that were flipped to orient the surface coherently
   // (which will be zero if the input mesh already is oriented coherently)
+  // if the data structure cannot be generated (non-manifold geometry), returns -1
+  int GenerateHalfEdgeDataStructure(int verbose=0); // generates the whole datastructure, assuming the base objMesh class has been initialized
 
   void CopyHalfEdgeTopologyFrom(ObjMeshOrientable * source); // makes the half-edge topological info equal to that of source
 
   // returns the opposite halfedge to the given half-edge
   // this function will fail for boundary edges, should always check first with isBoundary()
-  HalfEdge & edgeOpposite(HalfEdge & halfedge) { return halfEdges_[halfedge.opposite()]; } 
+  const HalfEdge & edgeOpposite(const HalfEdge & halfedge) const { return halfEdges_[halfedge.opposite()]; }
 
   // returns the next halfedge to the given half-edge
-  HalfEdge & edgeNext(HalfEdge & halfedge) { return halfEdges_[halfedge.next()]; } 
+  const HalfEdge & edgeNext(const HalfEdge & halfedge) const { return halfEdges_[halfedge.next()]; }
 
   // returns the previous halfedge to the given half-edge
   // does so by looping around the face (pointers to previous edges are not explicitly stored), so this is slower than edgeNext
-  HalfEdge & edgePrevious(HalfEdge & halfedge);
+  const HalfEdge & edgePrevious(const HalfEdge & halfedge) const;
 
   // loops around the vertex (vertex is defined as the ending position of the halfedge)
   // consists of taking the next edge, then taking the opposite edge
   // if boundary edge encountered, can't take the opposite edge; it this case flag=1 is returned and the edge returned is the boundary edge pointing away from the vertex
   // if taking the opposite edge is possible, the returned edge points into the vertex and flag is set to 0
   // this is effectively looping in the clockwise (negative) orientation
-  HalfEdge & loopVertex(HalfEdge & halfedge, int & flag); 
+  const HalfEdge & loopVertex(const HalfEdge & halfedge, int & flag);
 
   // returns the the group that contains the given half-edge
-  ObjMesh::Group edgeGroup(HalfEdge & halfedge) { return *(objMesh->getGroupHandle(halfedge.groupID())); } 
+  const ObjMesh::Group & edgeGroup(const HalfEdge & halfedge) const { return *(objMesh->getGroupHandle(halfedge.groupID())); }
 
   // returns the face to the left of the given half-edge
-  ObjMesh::Face edgeFace(HalfEdge & halfedge) 
+  const ObjMesh::Face & edgeFace(const HalfEdge & halfedge) const
   { const ObjMesh::Group * getGroupHandle = objMesh->getGroupHandle(halfedge.groupID());
     return getGroupHandle->getFace(halfedge.face()); }
 
   // returns the starting vertex of the given half-edge
-  ObjMesh::Vertex edgeStartVertex (HalfEdge & halfedge) 
+  const ObjMesh::Vertex & edgeStartVertex (const HalfEdge & halfedge) const
   { const ObjMesh::Group * getGroupHandle = objMesh->getGroupHandle(halfedge.groupID());
     return (getGroupHandle->getFace(halfedge.face())).getVertex(halfedge.startV()); }
 
   // returns the ending vertex of the given half-edge
-  ObjMesh::Vertex edgeEndVertex (HalfEdge & halfedge) 
+  const ObjMesh::Vertex & edgeEndVertex (const HalfEdge & halfedge) const
   { const ObjMesh::Group * getGroupHandle = objMesh->getGroupHandle(halfedge.groupID());
     return (getGroupHandle->getFace(halfedge.face())).getVertex(halfedge.endV()); }
 
-  unsigned int numConnectedComponents() { return connectedComponents;}
+  unsigned int numConnectedComponents() const { return connectedComponents;}
 
-  bool isIsolatedVertex(unsigned int vertex) { return (edgesAtVertices_[vertex] == -1); } // returns true if vertex is isolated
-  bool isBoundaryVertex(unsigned int vertex) { return halfEdges_[edgesAtVertices_[vertex]].isBoundary(); } // returns true if vertex is a mesh boundary vertex
+  bool isIsolatedVertex(unsigned int vertex) const { return (edgesAtVertices_[vertex] == -1); } // returns true if vertex is isolated
+  bool isBoundaryVertex(unsigned int vertex) const { return halfEdges_[edgesAtVertices_[vertex]].isBoundary(); } // returns true if vertex is a mesh boundary vertex
 
-  void PrintHalfEdges(); // prints the half-edges out
+  void PrintHalfEdges() const; // prints the half-edges out
 
-  ObjMesh * GenerateOrientedMesh(); // generates oriented mesh (same mesh as the input ObjMesh, but oriented consistently)
+  ObjMesh * GenerateOrientedMesh() const; // generates oriented mesh (same mesh as the input ObjMesh, but oriented consistently)
 
   // returns some halfedge emanating out of a given vertex (returns always the same edge)
   // in case vertex is a boundary vertex, it will return the edge such that there is no clockwise edge to the given edge around the given vertex
   // this function will fail for isolated vertices; should always check first with isIsolatedVertex()
-  HalfEdge & edgeAtVertex( unsigned int vertex ) { return halfEdges_[edgesAtVertices_[vertex]]; }
+  const HalfEdge & edgeAtVertex( unsigned int vertex ) const { return halfEdges_[edgesAtVertices_[vertex]]; }
 
   // returns some halfedge on the given face (returns always the same edge)
-  HalfEdge & edgeAtFace( unsigned int groupID, unsigned int faceID ) { return halfEdges_[edgesAtFaces_[groupID][faceID]]; }
+  const HalfEdge & edgeAtFace( unsigned int groupID, unsigned int faceID ) const { return halfEdges_[edgesAtFaces_[groupID][faceID]]; }
 
-  // returns true if surface has boundary and false if it closed
-  bool hasBoundary() { return hasBoundary_;}
+  // returns true if surface has boundary and false if it is closed
+  bool hasBoundary() const { return hasBoundary_;}
 
-  size_t numBoundaryEdges() { return boundaryEdges_.size(); }
-  int boundaryEdge(int i) { return boundaryEdges_[i]; }
+  size_t numBoundaryEdges() const { return boundaryEdges_.size(); }
+  int boundaryEdge(int i) const { return boundaryEdges_[i]; }
 
-  int internalAllocation;
+  int internalAllocation = 0;
 };
-}
+
 #endif
 
diff --git a/src/libobjMesh/objMeshRender.cpp b/libraries/objMesh/objMeshRender.cpp
similarity index 67%
rename from src/libobjMesh/objMeshRender.cpp
rename to libraries/objMesh/objMeshRender.cpp
index de599fa858183e5f5c8b123dcf34fd0d237d27fe..4e1cb0b4834cabf8366b62f050fb14d9c451f183 100644
--- a/src/libobjMesh/objMeshRender.cpp
+++ b/libraries/objMesh/objMeshRender.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
 *                                                                       *
-* Vega FEM Simulation Library Version 2.0                               *
+* Vega FEM Simulation Library Version 4.0                               *
 *                                                                       *
-* "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+* "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
 * All rights reserved.                                                  *
 *                                                                       *
 * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
-* http://www.jernejbarbic.com/code                                      *
+* http://www.jernejbarbic.com/vega                                      *
 *                                                                       *
-* Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+* Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+*           Danyong Zhao, Bohan Wang,                                   *
+*           Fun Shing Sin, Daniel Schroeder,                            *
 *           Doug L. James, Jovan Popovic                                *
 *                                                                       *
 * Funding: National Science Foundation, Link Foundation,                *
 *          Singapore-MIT GAMBIT Game Lab,                               *
-*          Zumberge Research and Innovation Fund at USC                 *
+*          Zumberge Research and Innovation Fund at USC,                *
+*          Sloan Foundation, Okawa Foundation,                          *
+*          USC Annenberg Foundation                                     *
 *                                                                       *
 * This library is free software; you can redistribute it and/or         *
 * modify it under the terms of the BSD-style license that is            *
@@ -26,17 +30,19 @@
 *                                                                       *
 *************************************************************************/
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #pragma warning(disable : 4996)
   #pragma warning(disable : 4267)
   #pragma warning(disable : 4244)
   #define isnan _isnan
 #endif
 
+#include "openGL-headers.h"
 #include <string.h>
-using namespace std;
 #include "objMeshRender.h"
+#include "openGLHelper.h"
 #include "imageIO.h"
+using namespace std;
 
 // this file (in the imageIO library) defines what image file formats are supported
 // note: PPM and uncompressed TGA are always supported
@@ -46,8 +52,6 @@ using namespace std;
 // Written by Daniel Schroeder and Jernej Barbic, 2011
 // Transparency and anisotropic filtering added by Jernej Barbic, 2012
 
-namespace vega
-{
 void ObjMeshRender::Texture::loadTextureImage(string fullPath, int * width, int * height, int * bpp, unsigned char ** texData)
 {
   ImageIO imageIO;
@@ -87,12 +91,18 @@ void ObjMeshRender::Texture::loadTexture(string fullPath, int textureMode_)
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 
-  if((textureMode & OBJMESHRENDER_MIPMAPBIT) == OBJMESHRENDER_GL_USEMIPMAP)
+  if ((textureMode & OBJMESHRENDER_MIPMAPBIT) == OBJMESHRENDER_GL_USEMIPMAP)
+  {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+  }
   else
+  {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+  }
 
-  if((textureMode & OBJMESHRENDER_LIGHTINGMODULATIONBIT) == OBJMESHRENDER_GL_REPLACE)
+  if ((textureMode & OBJMESHRENDER_LIGHTINGMODULATIONBIT) == OBJMESHRENDER_GL_REPLACE)
     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   else
     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
@@ -109,14 +119,14 @@ void ObjMeshRender::Texture::loadTexture(string fullPath, int textureMode_)
     format = GL_RGBA;
   }
 
-  if((textureMode & OBJMESHRENDER_MIPMAPBIT) == OBJMESHRENDER_GL_NOMIPMAP)
+  if ((textureMode & OBJMESHRENDER_MIPMAPBIT) == OBJMESHRENDER_GL_NOMIPMAP)
     glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, texData);
   else
   {
     char * versionString = (char*) glGetString(GL_VERSION);
     double version = 0.0;
     if (versionString != NULL)  // glGetString will return NULL in an invalid OpenGL context; this is to protect from a seg fault if versionString is NULL on the next line
-      version = strtod(versionString, NULL); 
+      version = strtod(versionString, NULL);
     if (version >= 3.0)
     {
       //unsigned char * texData1 = (unsigned char*) malloc (sizeof(unsigned char) * width * height * bytesPerPixel);
@@ -179,8 +189,9 @@ void ObjMeshRender::Texture::flipImage(int width, int height, int bpp, unsigned
   free(rowBuffer);
 }
 
-ObjMeshRender::ObjMeshRender(ObjMesh * mesh_) : mesh(mesh_) 
+ObjMeshRender::ObjMeshRender(const ObjMesh * mesh_) : mesh(mesh_)
 {
+  alphaBlendingThreshold = OBJMESHRENDER_DEFAULT_ALPHA_BLENDING_THRESHOLD;
 }
 
 ObjMeshRender::~ObjMeshRender()
@@ -196,49 +207,54 @@ ObjMeshRender::~ObjMeshRender()
   textures.clear();
 }
 
-void ObjMeshRender::renderGroup(unsigned int groupIndex, int geometryMode, int renderMode)
+void ObjMeshRender::renderGroup(int groupIndex, int geometryMode, int renderMode, int giveWarnings)
 {
-  render(geometryMode, renderMode, groupIndex);
+  render(geometryMode, renderMode, groupIndex, giveWarnings);
 }
 
-void ObjMeshRender::renderGroup(char * groupName, int geometryMode, int renderMode)
+void ObjMeshRender::renderGroup(const char * groupName, int geometryMode, int renderMode, int giveWarnings)
 {
   // get the group
-  std::string name(groupName);
-  unsigned int groupIndex = mesh->getGroupIndex(name);
-  render(geometryMode, renderMode, groupIndex);
+  unsigned int groupIndex = mesh->getGroupIndex(groupName);
+  render(geometryMode, renderMode, groupIndex, giveWarnings);
 }
 
-void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGroup)
+void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGroup, int giveWarnings)
 {
   if (renderMode & OBJMESHRENDER_TRANSPARENCY)
   {
     //printf("Two-pass render.\n");
     // two-pass render
-    int modifiedRenderMode = ((unsigned int)renderMode) & (~OBJMESHRENDER_TRANSPARENCY); 
+    int modifiedRenderMode = ((unsigned int)renderMode) & (~OBJMESHRENDER_TRANSPARENCY);
+
+    // save OpenGL states
+    glPushAttrib(GL_ENABLE_BIT|GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
     glEnable(GL_LIGHTING);
-  
+
     // pass 1
     glEnable(GL_ALPHA_TEST);
-    glAlphaFunc(GL_EQUAL, 1.0);
+    // glAlphaFunc(GL_EQUAL, 1.0);
+    glAlphaFunc(GL_GREATER, alphaBlendingThreshold);
     glDepthMask(GL_TRUE);
     glDisable(GL_BLEND);
-    render(geometryMode, modifiedRenderMode, renderSingleGroup);
+    render(geometryMode, modifiedRenderMode, renderSingleGroup, giveWarnings);
 
     glEnable(GL_LIGHTING);
- 
+
     // pass 2
     glEnable(GL_ALPHA_TEST);
-    glAlphaFunc(GL_LESS, 1.0);
+    //glAlphaFunc(GL_LESS, 1.0);
+    glAlphaFunc(GL_LEQUAL, alphaBlendingThreshold);
     glDepthMask(GL_FALSE);
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    render(geometryMode, modifiedRenderMode, renderSingleGroup);
+    render(geometryMode, modifiedRenderMode, renderSingleGroup, giveWarnings);
 
-    glDepthMask(GL_TRUE);
-    glDisable(GL_BLEND);
-    glDisable(GL_ALPHA_TEST);
+    glPopAttrib(); // restore OpenGL states
+//    glDepthMask(GL_TRUE);
+//    glDisable(GL_BLEND);
+//    glDisable(GL_ALPHA_TEST);
 
     return;
   }
@@ -249,74 +265,87 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
   bool warnMissingTextures = false;
   //bool warnUnimplementedFlat = false;
 
-  GLboolean lightingInitiallyEnabled = false;
-  glGetBooleanv(GL_LIGHTING, &lightingInitiallyEnabled);
-  //printf("lighting initially enabled: %d\n", lightingInitiallyEnabled);
+  // save OpenGL states
+  glPushAttrib(GL_ENABLE_BIT|GL_POLYGON_BIT|GL_LIGHTING_BIT|GL_TEXTURE_BIT);
+
+//  GLboolean lightingInitiallyEnabled = false;
+//  glGetBooleanv(GL_LIGHTING, &lightingInitiallyEnabled);
 
   // resolve conflicts in render mode settings and/or mesh data
-  if((renderMode & OBJMESHRENDER_FLAT) && (renderMode & OBJMESHRENDER_SMOOTH))
+  if ((renderMode & OBJMESHRENDER_FLAT) && (renderMode & OBJMESHRENDER_SMOOTH))
   {
     printf("Requested both FLAT and SMOOTH rendering; SMOOTH used\n");
     renderMode &= ~OBJMESHRENDER_FLAT;
   }
 
-  if((renderMode & OBJMESHRENDER_COLOR) && (renderMode & OBJMESHRENDER_MATERIAL))
+  if ((renderMode & OBJMESHRENDER_COLOR) && (renderMode & OBJMESHRENDER_MATERIAL))
   {
     printf("Requested both COLOR and MATERIAL rendering; MATERIAL used\n");
     renderMode &= ~OBJMESHRENDER_COLOR;
   }
 
-  if(renderMode & OBJMESHRENDER_COLOR)
+  // GL_COLOR_MATERIAL enabled: let following glColor3f() command modify material parameters
+  if (renderMode & OBJMESHRENDER_COLOR)
     glEnable(GL_COLOR_MATERIAL);
-  else if(renderMode & OBJMESHRENDER_MATERIAL)
+  else if (renderMode & OBJMESHRENDER_MATERIAL)
     glDisable(GL_COLOR_MATERIAL);
 
-  if(renderMode & OBJMESHRENDER_CUSTOMCOLOR)
+  if (renderMode & OBJMESHRENDER_CUSTOMCOLOR)
     glDisable(GL_LIGHTING);
 
   // render triangles
-  if(geometryMode & OBJMESHRENDER_TRIANGLES)
+  if (geometryMode & OBJMESHRENDER_TRIANGLES)
   {
     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-    if(geometryMode & (OBJMESHRENDER_EDGES | OBJMESHRENDER_VERTICES))
+    PolygonOffsetFillState polygonFillState(1.0, 1.0);
+    if (geometryMode & (OBJMESHRENDER_EDGES | OBJMESHRENDER_VERTICES))
     {
       //glEnable(GL_POLYGON_OFFSET_FILL);
       //glPolygonOffset(2.0, 2.0);
     }
 
+    int faceCount = 0;
+    ObjMesh::Material defaultMaterial;
     for(unsigned int i=0; i < mesh->getNumGroups(); i++)
     {
-      if ((renderSingleGroup >= 0) && ((int)i != renderSingleGroup))     
+      if ((renderSingleGroup >= 0) && ((int)i != renderSingleGroup))
         continue;
 
+      auto hiddenIt = hiddenFaces.find(i);
+      bool hasHidden = (hiddenIt != hiddenFaces.end());
+
       const ObjMesh::Group * groupHandle = mesh->getGroupHandle(i);
       // set material
       const ObjMesh::Material * materialHandle = mesh->getMaterialHandle(groupHandle->getMaterialIndex());
       //printf("Material: %d\n",group.materialIndex());
+      if (materialHandle == nullptr)
+      {
+        materialHandle = &defaultMaterial;
+      }
       Vec3d Ka = materialHandle->getKa();
       Vec3d Kd = materialHandle->getKd();
       Vec3d Ks = materialHandle->getKs();
 
       float shininess = materialHandle->getShininess();
       float alpha = materialHandle->getAlpha();
-      float ambient[4] = { Ka[0], Ka[1], Ka[2], alpha };
-      float diffuse[4] = { Kd[0], Kd[1], Kd[2], alpha };
-      float specular[4] = { Ks[0], Ks[1], Ks[2], alpha };
-      if(renderMode & OBJMESHRENDER_MATERIAL)
+      float ambient[4] = { (float)Ka[0], (float)Ka[1], (float)Ka[2], alpha };
+      float diffuse[4] = { (float)Kd[0], (float)Kd[1], (float)Kd[2], alpha };
+      float specular[4] = { (float)Ks[0], (float)Ks[1], (float)Ks[2], alpha };
+      if (renderMode & OBJMESHRENDER_MATERIAL)
       {
         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
         glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
       }
-      else if(renderMode & OBJMESHRENDER_COLOR)
+      else if (renderMode & OBJMESHRENDER_COLOR)
       {
         glColor3fv(diffuse);
       }
 
-      if((renderMode & OBJMESHRENDER_TEXTURE) && materialHandle->hasTextureFilename())
+      if ((renderMode & OBJMESHRENDER_TEXTURE) && materialHandle->hasTextureFilename())
       {
-        if(groupHandle->getMaterialIndex() >= textures.size())
+        if (groupHandle->getMaterialIndex() >= textures.size())
         {
           //textures are missing
           warnMissingTextures = true;
@@ -328,13 +357,17 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
           Texture * textureHandle = textures[groupHandle->getMaterialIndex()];
           glBindTexture(GL_TEXTURE_2D, textureHandle->getTexture());
           glEnable(GL_TEXTURE_2D);
-          if((textureHandle->getTextureMode() & OBJMESHRENDER_LIGHTINGMODULATIONBIT) == OBJMESHRENDER_GL_REPLACE)
+          if ((textureHandle->getTextureMode() & OBJMESHRENDER_LIGHTINGMODULATIONBIT) == OBJMESHRENDER_GL_REPLACE)
             glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
           else
             glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
           glEnable(GL_TEXTURE_2D);
         }
       }
+      else
+      {
+        glDisable(GL_TEXTURE_2D);
+      }
 
       //printf("amb: %G %G %G\n", Ka[0], Ka[1], Ka[2]);
       //printf("dif: %G %G %G\n", Kd[0], Kd[1], Kd[2]);
@@ -342,12 +375,13 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
 
       for(unsigned int iFace = 0; iFace < groupHandle->getNumFaces(); iFace++)
       {
+        if (hasHidden && hiddenIt->second.find(iFace) != hiddenIt->second.end()) continue;
         const ObjMesh::Face * faceHandle = groupHandle->getFaceHandle(iFace);
 
         glBegin(GL_POLYGON);
-        if(renderMode & OBJMESHRENDER_FLAT)
+        if (renderMode & OBJMESHRENDER_FLAT)
         {
-          if(faceHandle->hasFaceNormal())
+          if (faceHandle->hasFaceNormal())
           {
             Vec3d fnormal = faceHandle->getFaceNormal();
             glNormal3d(fnormal[0],fnormal[1],fnormal[2]);
@@ -356,6 +390,15 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
             warnMissingFaceNormals = true;
         }
 
+        Vec3d faceColor(0,0,0);
+        if (renderMode & OBJMESHRENDER_CUSTOMCOLORFACES)
+        {
+          if (customColorsFaces.size() == 0)
+            faceColor = Vec3d(0,0,1);
+          else
+            faceColor = customColorsFaces[faceCount+iFace];
+        }
+
         for(unsigned int iVertex = 0; iVertex < faceHandle->getNumVertices(); iVertex++)
         {
           const ObjMesh::Vertex * vertexHandle = faceHandle->getVertexHandle(iVertex);
@@ -363,9 +406,9 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
           int vertexPositionIndex = vertexHandle->getPositionIndex();
 
           // set normal
-          if(renderMode & OBJMESHRENDER_SMOOTH)
+          if (renderMode & OBJMESHRENDER_SMOOTH)
           {
-            if(vertexHandle->hasNormalIndex())
+            if (vertexHandle->hasNormalIndex())
             {
               Vec3d normal = mesh->getNormal(*vertexHandle);
               if ((!isnan(normal[0])) && (!isnan(normal[1])) && (!isnan(normal[2])))
@@ -378,9 +421,9 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
           }
 
           // set texture coordinate
-          if(renderMode & OBJMESHRENDER_TEXTURE)
+          if (renderMode & OBJMESHRENDER_TEXTURE)
           {
-            if(vertexHandle->hasTextureCoordinateIndex())
+            if (vertexHandle->hasTextureCoordinateIndex())
             {
               Vec3d vtexture = mesh->getTextureCoordinate(*vertexHandle);
               glTexCoord2d(vtexture[0], vtexture[1]);
@@ -389,7 +432,7 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
               warnMissingTextureCoordinates = true;
           }
 
-          if(renderMode & OBJMESHRENDER_CUSTOMCOLOR)
+          if (renderMode & OBJMESHRENDER_CUSTOMCOLOR)
           {
             if (customColors.size() == 0)
               glColor3f(0,0,1);
@@ -397,29 +440,32 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
               glColor3f(customColors[vertexPositionIndex][0], customColors[vertexPositionIndex][1], customColors[vertexPositionIndex][2]);
           }
 
+          if (renderMode & OBJMESHRENDER_CUSTOMCOLORFACES)
+            glColor3f(faceColor[0], faceColor[1], faceColor[2]);
+
           // set position
           glVertex3d(v[0],v[1],v[2]);
         }
-
         glEnd();
       }
+      faceCount += groupHandle->getNumFaces();
 
-      if((renderMode & OBJMESHRENDER_TEXTURE))
+      if ((renderMode & OBJMESHRENDER_TEXTURE))
         glDisable(GL_TEXTURE_2D);
     }
 
-    if(geometryMode & (OBJMESHRENDER_EDGES | OBJMESHRENDER_VERTICES))
+    if (geometryMode & (OBJMESHRENDER_EDGES | OBJMESHRENDER_VERTICES))
     {
       //glDisable(GL_POLYGON_OFFSET_FILL);
     }
   }
 
-  // render vertices 
+  // render vertices
   glDisable(GL_COLOR_MATERIAL);
   glDisable(GL_TEXTURE_2D);
   glDisable(GL_LIGHTING);
 
-  if(geometryMode & OBJMESHRENDER_VERTICES)
+  if (geometryMode & OBJMESHRENDER_VERTICES)
   {
     if (renderSingleGroup < 0)
     {
@@ -427,8 +473,8 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
 
       for(int i = 0; i < numVertices; i++)
       {
-        if(renderMode & OBJMESHRENDER_SELECTION)
-          glLoadName(i);  
+        if (renderMode & OBJMESHRENDER_SELECTION)
+          glLoadName(i);
         glBegin(GL_POINTS);
         Vec3d pos = mesh->getPosition(i);
         glVertex3f(pos[0], pos[1], pos[2]);
@@ -438,10 +484,13 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
     else
     {
       const ObjMesh::Group * groupHandle = mesh->getGroupHandle(renderSingleGroup);
+      auto hiddenIt = hiddenFaces.find(renderSingleGroup);
+      bool hasHidden = (hiddenIt != hiddenFaces.end());
       for(unsigned int iFace = 0; iFace < groupHandle->getNumFaces(); ++iFace)
       {
+        if (hasHidden && hiddenIt->second.find(iFace) != hiddenIt->second.end()) continue;
         const ObjMesh::Face * faceHandle = groupHandle->getFaceHandle(iFace);
-        if(geometryMode & OBJMESHRENDER_VERTICES)
+        if (geometryMode & OBJMESHRENDER_VERTICES)
         {
           glBegin(GL_POINTS);
           for(unsigned int iVertex = 0; iVertex < faceHandle->getNumVertices(); ++iVertex)
@@ -456,23 +505,25 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
     }
   }
 
-  // render edges 
+  // render edges
   if (geometryMode & OBJMESHRENDER_EDGES)
   {
-    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-    glEnable(GL_POLYGON_OFFSET_LINE);
-    //glPolygonOffset(1.0, 1.0);
-    glPolygonOffset(-1.0, -1.0);
+    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // render boundary edges on both front and back polygons
+    //glEnable(GL_POLYGON_OFFSET_LINE);          // if polygon is rendered in GL_LINE mode, add an offset to depth value
+    //glPolygonOffset(-1.0, -1.0);               // the depth value is modified to be smaller
     for(unsigned int i=0; i < mesh->getNumGroups(); i++)
     {
       if ((renderSingleGroup >= 0) && ((int)i != renderSingleGroup))
         continue;
+      auto hiddenIt = hiddenFaces.find(i);
+      bool hasHidden = (hiddenIt != hiddenFaces.end());
 
       const ObjMesh::Group * groupHandle = mesh->getGroupHandle(i);
       for(unsigned int iFace = 0; iFace < groupHandle->getNumFaces(); ++iFace)
       {
+        if (hasHidden && hiddenIt->second.find(iFace) != hiddenIt->second.end()) continue;
         const ObjMesh::Face * faceHandle = groupHandle->getFaceHandle(iFace);
-        if(geometryMode & OBJMESHRENDER_EDGES)
+        if (geometryMode & OBJMESHRENDER_EDGES)
         {
           glBegin(GL_POLYGON);
           for(unsigned int iVertex = 0; iVertex < faceHandle->getNumVertices(); ++iVertex)
@@ -485,22 +536,27 @@ void ObjMeshRender::render(int geometryMode, int renderMode, int renderSingleGro
         }
       }
     }
-    glDisable(GL_POLYGON_OFFSET_LINE);
-    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+//    glDisable(GL_POLYGON_OFFSET_LINE);
+//    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   }
 
-  if (lightingInitiallyEnabled)
-    glEnable(GL_LIGHTING);
+//  if (lightingInitiallyEnabled)
+//    glEnable(GL_LIGHTING);
+
+  glPopAttrib(); // restore OpenGL states
 
   //print warnings
-  if(warnMissingNormals)
-    printf("Warning: used SMOOTH rendering with missing vertex normal(s).\n");
-  if(warnMissingFaceNormals)
-    printf("Warning: used FLAT rendering with missing face normal(s).\n");
-  if(warnMissingTextureCoordinates)
-    printf("Warning: used TEXTURE rendering with missing texture coordinate(s).\n");
-  if(warnMissingTextures)
-    printf("Warning: used TEXTURE rendering with un-setup texture(s).\n");
+  if (giveWarnings)
+  {
+    if (warnMissingNormals)
+      printf("Warning: used SMOOTH rendering with missing vertex normal(s).\n");
+    if (warnMissingFaceNormals)
+      printf("Warning: used FLAT rendering with missing face normal(s).\n");
+    if (warnMissingTextureCoordinates)
+      printf("Warning: used TEXTURE rendering with missing texture coordinate(s).\n");
+    if (warnMissingTextures)
+      printf("Warning: used TEXTURE rendering with un-setup texture(s).\n");
+  }
 }
 
 unsigned int ObjMeshRender::createDisplayList(int geometryMode, int renderMode)
@@ -530,19 +586,27 @@ void ObjMeshRender::renderVertex(int index)
   glVertex3f(pos[0], pos[1], pos[2]);
 }
 
-void ObjMeshRender::renderGroupEdges(char * groupName)
+void ObjMeshRender::renderGroupEdges(const char * groupName)
+{
+  renderGroupEdges(mesh->getGroupIndex(groupName));
+}
+
+void ObjMeshRender::renderGroupEdges(int groupIndex)
 {
   //get the group
-  string name(groupName);
-  ObjMesh::Group group = mesh->getGroup(name);
+  const ObjMesh::Group & group = mesh->getGroup(groupIndex);
 
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
   glEnable(GL_POLYGON_OFFSET_LINE);
   glPolygonOffset(1.0, 1.0);
 
+  auto hiddenIt = hiddenFaces.find(groupIndex);
+  bool hasHidden = (hiddenIt != hiddenFaces.end());
+
   for( unsigned int iFace = 0; iFace < group.getNumFaces(); ++iFace )
   {
-    ObjMesh::Face face = group.getFace(iFace);
+    if (hasHidden && hiddenIt->second.find(iFace) != hiddenIt->second.end()) continue;
+    const ObjMesh::Face & face = group.getFace(iFace);
 
     glBegin(GL_POLYGON);
     for( unsigned int iVertex = 0; iVertex < face.getNumVertices(); ++iVertex )
@@ -565,7 +629,7 @@ int ObjMeshRender::numTextures()
   {
     const ObjMesh::Material * material = mesh->getMaterialHandle(i);
 
-    if(material->hasTextureFilename())
+    if (material->hasTextureFilename())
       numTextures++;
   }
   return numTextures;
@@ -597,7 +661,7 @@ void ObjMeshRender::loadTextures(int textureMode, std::vector<Texture*> * textur
   for(int i = 0; i < numMaterials; i++)
   {
     material = mesh->getMaterialHandle(i);
-    if(!material->hasTextureFilename())
+    if (!material->hasTextureFilename())
     {
       ownTexture[i] = 0;
       continue;
@@ -614,7 +678,7 @@ void ObjMeshRender::loadTextures(int textureMode, std::vector<Texture*> * textur
         std::string poolFullPath = (*texturePool)[j]->getFullPath();
         if (fullPath == poolFullPath)
         {
-          printf("Texture %s discovered in the texture pool. Avoiding reload.\n", fullPath.c_str());
+          //printf("Texture %s discovered in the texture pool. Avoiding reload.\n", fullPath.c_str());
           textures[i] = (*texturePool)[j];
           ownTexture[i] = 0;
           found = 1;
@@ -639,6 +703,12 @@ ObjMeshRender::Texture * ObjMeshRender::getTextureHandle(int textureIndex)
   return textures[textureIndex];
 }
 
+ObjMeshRender::Texture::~Texture()
+{
+  if (texture.first)
+    glDeleteTextures(1, &(texture.second));
+}
+
 void ObjMeshRender::outputOpenGLRenderCode()
 {
   for(unsigned int i=0; i < mesh->getNumGroups(); i++)
@@ -674,14 +744,14 @@ void ObjMeshRender::outputOpenGLRenderCode()
         Vec3d v = mesh->getPosition(*vertexHandle);
 
         // set normal
-        if( vertexHandle->hasNormalIndex() )
+        if ( vertexHandle->hasNormalIndex() )
         {
           Vec3d normal = mesh->getNormal(*vertexHandle);
           printf("glNormal3d(%f,%f,%f);\n", normal[0], normal[1], normal[2]);
         }
 
         // set texture coordinate
-        if(vertexHandle->hasTextureCoordinateIndex())
+        if (vertexHandle->hasTextureCoordinateIndex())
         {
           Vec3d textureCoordinate = mesh->getTextureCoordinate(*vertexHandle);
           printf("glTexCoord2d(%f,%f);\n", textureCoordinate[0], textureCoordinate[1]);
@@ -702,10 +772,14 @@ void ObjMeshRender::renderNormals(double normalLength)
 
   for(unsigned int i=0; i < mesh->getNumGroups(); i++)
   {
+    auto hiddenIt = hiddenFaces.find(i);
+    bool hasHidden = (hiddenIt != hiddenFaces.end());
+
     const ObjMesh::Group * groupHandle = mesh->getGroupHandle(i);
     for(unsigned int iFace = 0; iFace < groupHandle->getNumFaces(); ++iFace)
     {
-      ObjMesh::Face face = groupHandle->getFace(iFace);
+      if (hasHidden && hiddenIt->second.find(iFace) != hiddenIt->second.end()) continue;
+      const ObjMesh::Face & face = groupHandle->getFace(iFace);
 
       for(unsigned int iVertex = 0; iVertex < face.getNumVertices(); ++iVertex)
       {
@@ -716,7 +790,7 @@ void ObjMeshRender::renderNormals(double normalLength)
 
         // compute endpoint
         Vec3d vnormalOffset;
-        if(vertexHandle->hasNormalIndex())
+        if (vertexHandle->hasNormalIndex())
           vnormalOffset = v + normalLength * diameter * mesh->getNormal(*vertexHandle);
         else
           vnormalOffset = v;
@@ -729,7 +803,7 @@ void ObjMeshRender::renderNormals(double normalLength)
   glEnd();
 }
 
-void ObjMeshRender::setCustomColors(Vec3d color)
+void ObjMeshRender::setCustomColors(const Vec3d & color)
 {
   int numVertices = (int)mesh->getNumVertices();
   customColors.clear();
@@ -737,7 +811,7 @@ void ObjMeshRender::setCustomColors(Vec3d color)
     customColors.push_back(color);
 }
 
-void ObjMeshRender::setCustomColors(vector<Vec3d> colors)
+void ObjMeshRender::setCustomColors(const vector<Vec3d> & colors)
 {
   int numVertices = (int)mesh->getNumVertices();
   customColors.clear();
@@ -745,6 +819,22 @@ void ObjMeshRender::setCustomColors(vector<Vec3d> colors)
     customColors.push_back(colors[i]);
 }
 
+void ObjMeshRender::setCustomColorsFaces(const Vec3d & color)
+{
+  int numFaces = (int)mesh->getNumFaces();
+  customColorsFaces.clear();
+  for(int i=0; i<numFaces; i++)
+    customColorsFaces.push_back(color);
+}
+
+void ObjMeshRender::setCustomColorsFaces(const vector<Vec3d> & colors)
+{
+  int numFaces = (int)mesh->getNumFaces();
+  customColorsFaces.clear();
+  for(int i=0; i<numFaces; i++)
+    customColorsFaces.push_back(colors[i]);
+}
+
 int ObjMeshRender::maxBytesPerPixelInTextures()
 {
   int maxBytes = 0;
@@ -753,7 +843,7 @@ int ObjMeshRender::maxBytesPerPixelInTextures()
   for(int i=0; i<numMaterials; i++)
   {
     const ObjMesh::Material * material = mesh->getMaterialHandle(i);
-    if(!material->hasTextureFilename())
+    if (!material->hasTextureFilename())
       continue;
 
     Texture * tex = getTextureHandle(i);
@@ -765,4 +855,59 @@ int ObjMeshRender::maxBytesPerPixelInTextures()
   return maxBytes;
 }
 
+void ObjMeshRender::renderBoundaryEdges()
+{
+
+}
+
+void ObjMeshRender::renderCreaseEdges(double thresholdAngle)
+{
+
+}
+
+void ObjMeshRender::renderSilhouetteEdges(double cameraPos[3])
+{
+
+}
+
+unsigned int ObjMeshRender::createBoundaryEdgesDisplayList()
+{
+  unsigned int list = glGenLists(1);
+  glNewList(list, GL_COMPILE);
+  renderBoundaryEdges();
+  glEndList();
+  return list;
+}
+
+unsigned int ObjMeshRender::createCreaseEdgesDisplayList(double thresholdAngle)
+{
+  unsigned int list = glGenLists(1);
+  glNewList(list, GL_COMPILE);
+  renderCreaseEdges(thresholdAngle);
+  glEndList();
+  return list;
+}
+
+unsigned int ObjMeshRender::createSilhouetteEdgesDisplayList(double cameraPos[3])
+{
+  unsigned int list = glGenLists(1);
+  glNewList(list, GL_COMPILE);
+  renderSilhouetteEdges(cameraPos);
+  glEndList();
+  return list;
+}
+
+void ObjMeshRender::unhideFace(int groupID, int faceID)
+{
+  auto it = hiddenFaces.find(groupID);
+  if (it == hiddenFaces.end()) return;
+  it->second.erase(faceID);
+  if (it->second.size() == 0) hiddenFaces.erase(groupID);
+}
+
+bool ObjMeshRender::isFaceHidden(int groupID, int faceID) const
+{
+  auto it = hiddenFaces.find(groupID);
+  if (it == hiddenFaces.end()) return false;
+  return it->second.find(faceID) != it->second.end();
 }
diff --git a/libraries/objMesh/objMeshRender.h b/libraries/objMesh/objMeshRender.h
new file mode 100644
index 0000000000000000000000000000000000000000..fbd33db57e740b31c76d3e608c97baf74141fc5c
--- /dev/null
+++ b/libraries/objMesh/objMeshRender.h
@@ -0,0 +1,183 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+// Renders the obj mesh.
+// Written by Daniel Schroeder and Jernej Barbic, 2011
+
+#ifndef _OBJMESHRENDER_H_
+#define _OBJMESHRENDER_H_
+
+#include <vector>
+#include <map>
+#include <set>
+#include <assert.h>
+#include "objMesh.h"
+
+//flags for ObjMeshRender:
+//geometry mode
+#define OBJMESHRENDER_TRIANGLES (1 << 0)
+#define OBJMESHRENDER_EDGES (1 << 1)
+#define OBJMESHRENDER_VERTICES (1 << 2)
+
+//rendering mode
+#define OBJMESHRENDER_NONE                (0)            /* render with only vertices */
+#define OBJMESHRENDER_FLAT                (1 << 0)       /* render with facet normals */
+#define OBJMESHRENDER_SMOOTH              (1 << 1)       /* render with vertex normals */
+#define OBJMESHRENDER_TEXTURE             (1 << 2)       /* render with texture coords */
+#define OBJMESHRENDER_COLOR               (1 << 3)       /* render with color materials */
+#define OBJMESHRENDER_MATERIAL            (1 << 4)       /* render with materials */
+#define OBJMESHRENDER_SELECTION           (1 << 5)       /* render with OpenGL selection (only applies to vertices, otherwise ignored) */
+#define OBJMESHRENDER_CUSTOMCOLOR         (1 << 6)       /* render with custom vertex colors */
+#define OBJMESHRENDER_TRANSPARENCY        (1 << 7)       /* render in two passes, to handle transparencies */
+#define OBJMESHRENDER_CUSTOMCOLORFACES    (1 << 8)       /* render with custom face colors */
+
+//texture mode: replace vs modulate
+#define OBJMESHRENDER_LIGHTINGMODULATIONBIT 1
+#define OBJMESHRENDER_GL_REPLACE 0
+#define OBJMESHRENDER_GL_MODULATE 1
+
+//texture mode: mipmapping
+#define OBJMESHRENDER_MIPMAPBIT 2
+#define OBJMESHRENDER_GL_NOMIPMAP 0
+#define OBJMESHRENDER_GL_USEMIPMAP 2
+
+//texture mode: anisotropic filtering
+#define OBJMESHRENDER_ANISOTROPICFILTERINGBIT 4
+#define OBJMESHRENDER_GL_NOANISOTROPICFILTERING 0
+#define OBJMESHRENDER_GL_USEANISOTROPICFILTERING 4
+
+//default alpha blending threshold for 2-pass rendering (for transparent textures)
+#define OBJMESHRENDER_DEFAULT_ALPHA_BLENDING_THRESHOLD 0.5f
+
+class ObjMeshRender
+{
+public:
+
+  class Texture
+  {
+  public:
+    Texture() : fullPath(std::string("__none")), texture(std::make_pair(false, 0)), textureMode(OBJMESHRENDER_GL_NOMIPMAP | OBJMESHRENDER_GL_MODULATE), bytesPerPixel(3) {}
+    virtual ~Texture();
+
+    static void loadTextureImage(std::string fullPath, int * width, int * height, int * bpp, unsigned char ** texData);
+
+    void loadTexture(std::string fullPath, int textureMode); // also sets up OpenGl
+
+    bool hasTexture() { return texture.first; }
+    unsigned int getTexture() { assert( texture.first ); return texture.second; }
+
+    std::string getFullPath() { return fullPath; }
+    int getTextureMode() { return textureMode; }
+    int getBytesPerPixel() { return bytesPerPixel; }
+
+  protected:
+    std::string fullPath;
+    std::pair< bool, unsigned int > texture; // OpenGL texture ID
+    int textureMode;
+    int bytesPerPixel;
+
+    static void flipImage(int width, int height, int bpp, unsigned char * image);
+  };
+
+  ObjMeshRender(const ObjMesh * mesh);
+  virtual ~ObjMeshRender();
+
+  // render faces/edges/vertices
+  // most OpenGL states are not modified by this function, except those volatile ones like current color, texture and normal
+  // external lighting setting before this function is called affects faces rendering
+  // external color affects edges and vertices rendering
+  // external line width affects edges rendering
+  void render(int geometryMode, int renderMode, int renderSingleGroup=-1, int giveWarnings=0);
+  unsigned int createDisplayList(int geometryMode, int renderMode);
+
+  // set custom colors, for OBJMESHRENDER_CUSTOMCOLOR mode
+  void setCustomColors(const Vec3d & color); // constant color for each mesh vertex
+  void setCustomColors(const std::vector<Vec3d> & colors); // specific color for every mesh vertex
+
+  // set custom colors, for OBJMESHRENDER_CUSTOMCOLORFACES mode
+  void setCustomColorsFaces(const Vec3d & color); // constant color for each mesh face
+  void setCustomColorsFaces(const std::vector<Vec3d> & colors); // specific color for every mesh face
+
+  void renderSpecifiedVertices(int * specifiedVertices, int numSpecifiedVertices);
+  void renderVertex(int index);
+
+  // the more specific rendering versions
+  void renderGroup(int groupIndex, int geometryMode, int renderMode, int giveWarnings=0);
+  void renderGroup(const char * groupName, int geometryMode, int renderMode, int giveWarnings=0);
+  void renderGroupEdges(const char * groupName);
+  void renderGroupEdges(int groupIndex);
+
+  // render boundary edges
+  void renderBoundaryEdges(); // does not use the display list
+  unsigned int createBoundaryEdgesDisplayList();
+
+  // render crease edges
+  void renderCreaseEdges(double thresholdAngle=90.0); // does not use the display list
+  unsigned int createCreaseEdgesDisplayList(double thresholdAngle=90.0);
+
+  // render silhouette edges
+  void renderSilhouetteEdges(double cameraPos[3]); // does not use the display list
+  unsigned int createSilhouetteEdgesDisplayList(double cameraPos[3]);
+
+  int numTextures();
+  int maxBytesPerPixelInTextures();
+  Texture * getTextureHandle(int textureIndex);
+  void loadTextures(int textureMode, std::vector<Texture*> * texturePool=NULL, int updatePool=0);
+
+  void renderNormals(double normalLength);
+
+  // outputs OpenGL code to render faces
+  void outputOpenGLRenderCode();
+
+  // get/set alpha blending value, for OBJMESHRENDER_TRANSPARENCY mode
+  double getAlphaBlendingThreshold() const { return alphaBlendingThreshold; }
+  void setAlphaBlendingThreshold(double threshold) { alphaBlendingThreshold = threshold; }
+
+  // hide faces when rendering
+  void hideFace(int groupID, int faceID) { hiddenFaces[groupID].insert(faceID); }
+  void unhideFace(int groupID, int faceID);
+  void unhideAllFaces() { hiddenFaces.clear(); }
+  bool isFaceHidden(int groupID, int faceID) const;
+  const std::map<int, std::set<int>> & getHiddenFaces() const { return hiddenFaces; }
+
+protected:
+  const ObjMesh * mesh;
+  std::vector<Vec3d> customColors;
+  std::vector<Vec3d> customColorsFaces;
+  std::vector< Texture* > textures;
+  std::vector<int> ownTexture;
+  double alphaBlendingThreshold;
+  std::map<int, std::set<int>> hiddenFaces;
+};
+
+#endif
+
diff --git a/src/libobjMesh/octree.cpp b/libraries/objMesh/octree.cpp
similarity index 67%
rename from src/libobjMesh/octree.cpp
rename to libraries/objMesh/octree.cpp
index a7f6591fdb3682f30bb7c33d4caf865db9cbb761..592fcf05c82f485bb0830677cb9aca328d043c94 100644
--- a/src/libobjMesh/octree.cpp
+++ b/libraries/objMesh/octree.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,17 +33,13 @@
 //  An octree storing triangles. Can be intersected with a sphere or a line segment.
 //  Jernej Barbic, CMU
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 #include "openGL-headers.h"
 #include <iostream>
 #include "octree.h"
-namespace vega
-{
+using namespace std;
+
 template<class TriangleClass>
-Octree<TriangleClass>::Octree(int maxDepth_, int depth_): maxDepth(maxDepth_), depth(depth_) 
+Octree<TriangleClass>::Octree(int maxDepth_, int depth_): maxDepth(maxDepth_), depth(depth_)
 {
   for(int i=0; i<8; i++)
     childrenNodes[i] = NULL;
@@ -68,14 +68,14 @@ bool Octree<TriangleClass>::build(std::vector<TriangleClass> & triangleList, Bou
   // set the bounding box
   boundingBox = parentCube;
 
-  // total number of triangles 
+  // total number of triangles
   int numTriangles = (int)triangleList.size();
 
   // if there are fewer triangles than the threshold value, or if max depth has been reached, this node becomes a leaf which stores the triangles
   // max depth checking is necessary, since otherwise vertices with high valence will always be contained in some box, and that box will be split forever
   if ((numTriangles <= maxNumTriangles) || (depth >= maxDepth))
   {
-    //cout << "L" << numTriangles << " " << depth << " ";
+    // cout << "L" << numTriangles << " " << depth << " " << endl;
     for(int i=0; i<numTriangles; i++)
       triangles.push_back(triangleList[i]);
 
@@ -118,8 +118,9 @@ bool Octree<TriangleClass>::build(std::vector<TriangleClass> & triangleList, Bou
       if (triangleList[i].doesIntersectBox(childCubeBoxes[j]))
         childTriangles[j].push_back(triangleList[i]);
 
-  //for (int i=0; i<8; i++)
-  //  cout << "S" << i << ":" << childTriangles[i].size() << " ";
+  // for (int i=0; i<8; i++)
+  //   cout << "S" << i << ":" << childTriangles[i].size() << " ";
+  // cout << endl;
 
   // for any child with intersecting triangles, create and recursively build the subtree
   for(int i=0; i<8; i++)
@@ -188,7 +189,7 @@ void Octree<TriangleClass>::buildCollisionList(std::vector<TriangleClass*> & tri
 }
 
 template<class TriangleClass>
-void Octree<TriangleClass>::buildCollisionList(std::vector<TriangleClass*> & triangleList,  Vec3d segmentStartPoint, Vec3d segmentEndPoint)
+void Octree<TriangleClass>::buildCollisionList(std::vector<TriangleClass*> & triangleList,  const Vec3d & segmentStartPoint, const Vec3d & segmentEndPoint, vector<Vec3d> * intersectionList)
 {
   // if the bounding box does not intersect the line segment, there can be no collision
   Vec3d intersectionPoint;
@@ -204,14 +205,18 @@ void Octree<TriangleClass>::buildCollisionList(std::vector<TriangleClass*> & tri
       Vec3d intersectionPoint;
       int collisionStatus = triangles[i].lineSegmentIntersection(segmentStartPoint, segmentEndPoint, &intersectionPoint);
       if (collisionStatus == 1)
+      {
         triangleList.push_back(&(triangles[i]));
+        if(intersectionList)
+          intersectionList->push_back(intersectionPoint);
+      }
     }
   }
   else
   {
     for(int i=0; i<8; i++)
       if(childrenNodes[i] != NULL)
-        childrenNodes[i]->buildCollisionList(triangleList, segmentStartPoint, segmentEndPoint);
+        childrenNodes[i]->buildCollisionList(triangleList, segmentStartPoint, segmentEndPoint, intersectionList);
   }
 }
 
@@ -235,7 +240,7 @@ void Octree<TriangleClass>::render(int level)
       childrenNodes[i]->render(level);
 
   // render only non-empty leaf nodes at the requested depth
-  if ((triangles.size() > 0) && (level == depth)) 
+  if ((triangles.size() > 0) && (level == depth))
     boundingBox.render();
 }
 
@@ -247,7 +252,7 @@ void Octree<TriangleClass>::render(int level, int boxIndex)
 }
 
 template<class TriangleClass>
-int Octree<TriangleClass>::renderCounter; 
+int Octree<TriangleClass>::renderCounter;
 
 template<class TriangleClass>
 int Octree<TriangleClass>::printRenderInfo = 0;
@@ -343,50 +348,22 @@ void Octree<TriangleClass>::createChildCubes(BoundingBox * childCubeBoxes)
     childCubeBoxes[i].verifyBox();
 }
 
-template Octree<TriangleBasic>::Octree(int maxDepth_g, int depth_g);
-template bool Octree<TriangleBasic>::build(std::vector<TriangleBasic> &triangleList, BoundingBox &parentCube, int maxNumTriangles);
-template bool Octree<TriangleBasic>::build(std::vector<TriangleBasic> &triangleList, int maxNumTriangles);
-template void Octree<TriangleBasic>::setBuildPrintInfo(int info); 
-template void Octree<TriangleBasic>::getBuildInfo(int * numMaxDepthExceededCases, int * numMaxTriInDepthExceededCases);
-template int Octree<TriangleBasic>::getDepth();
-template void Octree<TriangleBasic>::buildCollisionList(std::vector<TriangleBasic*> &triangleList, const SimpleSphere &simpleSphere);
-template void Octree<TriangleBasic>::buildCollisionList(std::vector<TriangleBasic*> &triangleList, Vec3d segmentStartPoint, Vec3d segmentEndPoint);
-template void Octree<TriangleBasic>::render();
-template void Octree<TriangleBasic>::render(int level);
-template void Octree<TriangleBasic>::render(int level, int boxIndex);
-template void Octree<TriangleBasic>::renderHelper(int level, int boxIndex);
-
-template void Octree<TriangleBasic>::deallocate();
-template void Octree<TriangleBasic>::createChildCubes(BoundingBox * childCubeBoxes);
-
-template Octree<TriangleWithCollisionInfo>::Octree(int maxDepth_g, int depth_g);
-template bool Octree<TriangleWithCollisionInfo>::build(std::vector<TriangleWithCollisionInfo> &triangleList, BoundingBox &parentCube, int maxNumTriangles);
-template bool Octree<TriangleWithCollisionInfo>::build(std::vector<TriangleWithCollisionInfo> &triangleList, int maxNumTriangles);
-template void Octree<TriangleWithCollisionInfo>::setBuildPrintInfo(int info); 
-template void Octree<TriangleWithCollisionInfo>::getBuildInfo(int * numMaxDepthExceededCases, int * numMaxTriInDepthExceededCases);
-template void Octree<TriangleWithCollisionInfo>::buildCollisionList(std::vector<TriangleWithCollisionInfo*> &triangleList, const SimpleSphere &simpleSphere);
-template void Octree<TriangleWithCollisionInfo>::buildCollisionList(std::vector<TriangleWithCollisionInfo*> &triangleList, Vec3d segmentStartPoint, Vec3d segmentEndPoint);
-template int Octree<TriangleWithCollisionInfo>::getDepth();
-template void Octree<TriangleWithCollisionInfo>::render();
-template void Octree<TriangleWithCollisionInfo>::render(int level);
-template void Octree<TriangleWithCollisionInfo>::render(int level, int boxIndex);
-template void Octree<TriangleWithCollisionInfo>::renderHelper(int level, int boxIndex);
-template void Octree<TriangleWithCollisionInfo>::deallocate();
-template void Octree<TriangleWithCollisionInfo>::createChildCubes(BoundingBox * childCubeBoxes);
-
-template Octree<TriangleWithCollisionInfoAndPseudoNormals>::Octree(int maxDepth_g, int depth_g);
-template bool Octree<TriangleWithCollisionInfoAndPseudoNormals>::build(std::vector<TriangleWithCollisionInfoAndPseudoNormals> &triangleList, BoundingBox &parentCube, int maxNumTriangles);
-template bool Octree<TriangleWithCollisionInfoAndPseudoNormals>::build(std::vector<TriangleWithCollisionInfoAndPseudoNormals> &triangleList, int maxNumTriangles);
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::setBuildPrintInfo(int info); 
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::getBuildInfo(int * numMaxDepthExceededCases, int * numMaxTriInDepthExceededCases);
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::buildCollisionList(std::vector<TriangleWithCollisionInfoAndPseudoNormals*> &triangleList, const SimpleSphere &simpleSphere);
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::buildCollisionList(std::vector<TriangleWithCollisionInfoAndPseudoNormals*> &triangleList, Vec3d segmentStartPoint, Vec3d segmentEndPoint);
-template int Octree<TriangleWithCollisionInfoAndPseudoNormals>::getDepth();
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::render();
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::render(int level);
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::render(int level, int boxIndex);
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::renderHelper(int level, int boxIndex);
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::deallocate();
-template void Octree<TriangleWithCollisionInfoAndPseudoNormals>::createChildCubes(BoundingBox * childCubeBoxes);
+template<class TriangleClass>
+bool Octree<TriangleClass>::isLeaf() const
+{
+  for (int i = 0; i < 8; i++)
+    if (childrenNodes[i])
+      return false;
+  return true;
+}
 
+template<class TriangleClass>
+void Octree<TriangleClass>::removeChild(const int i)
+{
+  delete childrenNodes[i];
+  childrenNodes[i] = NULL;
 }
+
+template class Octree<TriangleBasic>;
+template class Octree<TriangleWithCollisionInfo>;
+template class Octree<TriangleWithCollisionInfoAndPseudoNormals>;
diff --git a/src/libobjMesh/octree.h b/libraries/objMesh/octree.h
similarity index 81%
rename from src/libobjMesh/octree.h
rename to libraries/objMesh/octree.h
index ae94382a6c9ac3f6df2d1050c02bc192ce51e513..92a173c1d06defe508f6478398d30399767100d8 100644
--- a/src/libobjMesh/octree.h
+++ b/libraries/objMesh/octree.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
+ * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,11 +33,7 @@
 #ifndef _OCTREE_H_
 #define _OCTREE_H_
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
-//  An octree storing triangles. Can be intersected with a sphere or a line segment. 
+//  An octree storing triangles. Can be intersected with a sphere or a line segment.
 //  Jernej Barbic, CMU
 
 #include <vector>
@@ -41,10 +41,9 @@
 #include "boundingBox.h"
 #include "triangle.h"
 #include "simpleSphere.h"
-namespace vega
-{
-template<class TriangleClass> 
-class Octree 
+
+template<class TriangleClass>
+class Octree
 {
 public:
   // make empty octree
@@ -62,7 +61,7 @@ public:
 
   // note: these two routines might return the same colliding triangle several times; call <TriangleClass>::makeUniqueList to make the list unique if needed
   void buildCollisionList(std::vector<TriangleClass*> & triangleList, const SimpleSphere & simpleSphere);
-  void buildCollisionList(std::vector<TriangleClass*> & triangleList, Vec3d segmentStartPoint, Vec3d segmentEndPoint);
+  void buildCollisionList(std::vector<TriangleClass*> & triangleList, const Vec3d & segmentStartPoint, const Vec3d & segmentEndPoint, std::vector<Vec3d> * intersectionList = NULL);
 
   void render(); // openGL rendering
   void render(int level); // only render leaf boxes at depth level 'level'
@@ -70,7 +69,12 @@ public:
   void setRenderInfo(int info) { printRenderInfo = info; }
 
   int getDepth(); // compute tree depth
-  BoundingBox getBoundingBox() { return boundingBox; }
+  const BoundingBox & getBoundingBox() const { return boundingBox; }
+
+  inline Octree * getChildHandle(const int i) const { return childrenNodes[i]; }
+  inline bool isLeaf() const;
+
+  inline void removeChild(const int i);
 
 protected:
 
@@ -111,6 +115,6 @@ inline void Octree<TriangleClass>::setBuildPrintInfo(int info)
     numMaxTriInDepthExceededCases = 0;
   }
 }
-}
+
 #endif
 
diff --git a/src/libobjMeshGPUDeformer/coarseToFine-shaders.cpp b/libraries/objMeshGPUDeformer/coarseToFine-shaders.cpp
similarity index 98%
rename from src/libobjMeshGPUDeformer/coarseToFine-shaders.cpp
rename to libraries/objMeshGPUDeformer/coarseToFine-shaders.cpp
index be3402c285073ec93a8ef53f9431003e7a66af98..8d1cabc52e06d23c5a8ebe282f16077e616c6f9e 100644
--- a/src/libobjMeshGPUDeformer/coarseToFine-shaders.cpp
+++ b/libraries/objMeshGPUDeformer/coarseToFine-shaders.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libobjMeshGPUDeformer/glh_extensions.h b/libraries/objMeshGPUDeformer/glh_extensions.h
similarity index 97%
rename from src/libobjMeshGPUDeformer/glh_extensions.h
rename to libraries/objMeshGPUDeformer/glh_extensions.h
index 64aaa12df2c731eab0257f5f3f99af88f3b6d646..a85b30aed701447a77f5622b1b7a3d07d4cd7406 100644
--- a/src/libobjMeshGPUDeformer/glh_extensions.h
+++ b/libraries/objMeshGPUDeformer/glh_extensions.h
@@ -183,7 +183,9 @@ int glh_init_extensions(const char *origReqExts)
 
     if (NULL == origReqExts)
         return GL_TRUE;
-    reqExts = strdup(origReqExts);
+    reqExts = (char*)malloc(strlen(origReqExts) + 5);
+    strcpy(reqExts, origReqExts);
+    //reqExts = strdup(origReqExts); // VS2013 forbids strdup because it's not standard 
     reqExtsLen = strlen(reqExts);
     if (NULL == unsupportedExts) {
         unsupportedExts = (char*)malloc(reqExtsLen + 2);
diff --git a/src/libobjMeshGPUDeformer/glh_genext.h b/libraries/objMeshGPUDeformer/glh_genext.h
similarity index 100%
rename from src/libobjMeshGPUDeformer/glh_genext.h
rename to libraries/objMeshGPUDeformer/glh_genext.h
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer.cpp b/libraries/objMeshGPUDeformer/objMeshGPUDeformer.cpp
similarity index 93%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer.cpp
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer.cpp
index 2aad70dbce0ce59eda100bd2af7707d9c34e91d8..e41e1c2b970f87ef877fe33e0cfde1e138b7e353 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer.cpp
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,11 +40,13 @@
 #define GLH_EXT_SINGLE_FILE
 #include "glh_extensions.h"
 
-#ifndef WIN32
+#if defined(_WIN32) || defined(WIN32)
+#else
+//#ifndef WIN32
   #define GL_GLEXT_PROTOTYPES 1
 #endif
 
-#if defined(linux)
+#if defined(linux) || defined(__linux__)
   GLAPI void APIENTRY glDeleteBuffersARB (GLsizei, const GLuint *);
   GLAPI void APIENTRY glBindBufferARB (GLenum, GLuint);
   GLAPI void APIENTRY glGenBuffersARB (GLsizei, GLuint *);
@@ -51,6 +57,8 @@
   #include "vbo.h"
 #endif
 
+using namespace std;
+
 ObjMeshGPUDeformer::ObjMeshGPUDeformer()
 {
   numGroupTriangles = NULL;
@@ -364,16 +372,16 @@ void ObjMeshGPUDeformer::RenderMaster(int masterMode, void * data)
     int materialIndex = groupHandle->getMaterialIndex();
     const ObjMesh::Material * materialHandle = mesh->getMaterialHandle(materialIndex);
 
-    double alpha = materialHandle->getAlpha();
+    float alpha = (float)(materialHandle->getAlpha());
 
     Vec3d Ka = materialHandle->getKa();
-    float Kav[4] = { Ka[0], Ka[1], Ka[2], alpha };
+    float Kav[4] = { (float)Ka[0], (float)Ka[1], (float)Ka[2], alpha };
 
     Vec3d Kd = materialHandle->getKd();
-    float Kdv[4] = { Kd[0], Kd[1], Kd[2], alpha };
+    float Kdv[4] = { (float)Kd[0], (float)Kd[1], (float)Kd[2], alpha };
 
     Vec3d Ks = materialHandle->getKs();
-    float Ksv[4] = { Ks[0], Ks[1], Ks[2], alpha };
+    float Ksv[4] = { (float)Ks[0], (float)Ks[1], (float)Ks[2], alpha };
 
     float shininess = materialHandle->getShininess();
 
@@ -525,8 +533,7 @@ void ObjMeshGPUDeformer::RenderEdges()
 
       glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboEdgesID[3*groupNo+2]);
 
-      int numGroupEdges = 3 * numGroupTriangles[groupNo];
-      glDrawElements(GL_LINES, 2 * numGroupEdges, GL_UNSIGNED_INT, 0);
+      glDrawElements(GL_LINES, 2 * numGroupEdges[groupNo], GL_UNSIGNED_INT, 0);
     }
 
     // unbind VBOs
@@ -618,7 +625,7 @@ void ObjMeshGPUDeformer::MakeDisplayListsTriangles(int mode)
       // triangulate the face on the fly
       for(unsigned int iVtx = 0; iVtx < faceHandle->getNumVertices() - 2; iVtx++)
       {
-        int triangleVertex[3] = { 0, iVtx + 1, iVtx + 2 };
+        unsigned int triangleVertex[3] = { 0, iVtx + 1, iVtx + 2 };
 
         for (int vtx=0; vtx<3; vtx++)
         {
@@ -762,13 +769,14 @@ void ObjMeshGPUDeformer::MakeDisplayListsEdges()
     displayListEdgesStart = glGenLists(numGroups);
   #endif
 
+  vector<float> vtxBuffer, stBuffer;
+  vector<GLuint> indexBuffer;
+  numGroupEdges.resize(numGroups);
   for(int groupNo=0; groupNo<numGroups; groupNo++)
   {
-    int numGroupEdges = 3 * numGroupTriangles[groupNo];
-    float * vtxBuffer = (float*) malloc (sizeof(float) * 6 * numGroupEdges);
-    float * stBuffer = (float*) malloc (sizeof(float) * 4 * numGroupEdges);
-    GLuint * indexBuffer = (GLuint*) malloc (sizeof(GLuint) * 2 * numGroupEdges);
-
+    vtxBuffer.clear();
+    stBuffer.clear();
+    indexBuffer.clear();
     const ObjMesh::Group * groupHandle = mesh->getGroupHandle(groupNo);
     int edgeCount = 0;
 
@@ -778,7 +786,7 @@ void ObjMeshGPUDeformer::MakeDisplayListsEdges()
 
       for(unsigned int iVtx = 0; iVtx < faceHandle->getNumVertices() - 1; iVtx++)
       {
-        int edgeVertex[2] = { iVtx, iVtx + 1 };
+        unsigned int edgeVertex[2] = { iVtx, iVtx + 1 };
 
         for (int vtx=0; vtx<2; vtx++)
         {
@@ -786,44 +794,42 @@ void ObjMeshGPUDeformer::MakeDisplayListsEdges()
           int vertexIndex = vertex->getPositionIndex();
           Vec3d pos = mesh->getPosition(*vertex);
           for (int dof=0; dof<3; dof++)
-            vtxBuffer[6*edgeCount + 3*vtx + dof] = pos[dof];
+            vtxBuffer.push_back(pos[dof]);
 
           float s = gpgpuVertexTextureCoordinates[2*vertexIndex+0];
           float t = gpgpuVertexTextureCoordinates[2*vertexIndex+1];
-          float st[2] = {s, t};
-
-          for (int dof=0; dof<2; dof++)
-            stBuffer[4*edgeCount + 2*vtx + dof] = st[dof];
-
-          for (int dof=0; dof<2; dof++)
-            indexBuffer[2 * edgeCount + dof] = 2 * edgeCount + dof;
+          stBuffer.push_back(s);
+          stBuffer.push_back(t);
         }
+        indexBuffer.push_back(2 * edgeCount + 0);
+        indexBuffer.push_back(2 * edgeCount + 1);
        
         edgeCount++;
       }
     }
+    numGroupEdges[groupNo] = edgeCount;
 
     #ifdef OBJMESHGPUDEFORMER_USING_VBOS
       glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboEdgesID[3*groupNo+0]);
-      glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(float) * 6 * numGroupEdges, vtxBuffer, GL_STATIC_DRAW_ARB);
+      glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(float) * 6 * edgeCount, vtxBuffer.data(), GL_STATIC_DRAW_ARB);
 
       glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboEdgesID[3*groupNo+1]);
-      glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(float) * 4 * numGroupEdges, stBuffer, GL_STATIC_DRAW_ARB);
+      glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(float) * 4 * edgeCount, stBuffer.data(), GL_STATIC_DRAW_ARB);
 
       glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, vboEdgesID[3*groupNo+2]); 
-      glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(GLuint) * 2 * numGroupEdges, indexBuffer, GL_STATIC_DRAW_ARB);
+      glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(GLuint) * 2 * edgeCount, indexBuffer.data(), GL_STATIC_DRAW_ARB);
     #else
       PrintGLerror("before starting a new edge list");
       glNewList(displayListEdgesStart + groupNo, GL_COMPILE);
 
       glEnableClientState(GL_VERTEX_ARRAY);
-      glVertexPointer(3, GL_FLOAT, 0, vtxBuffer);
+      glVertexPointer(3, GL_FLOAT, 0, vtxBuffer.data());
 
       glClientActiveTextureARB(GL_TEXTURE0_ARB); 
-      glTexCoordPointer(2, GL_FLOAT, 0, stBuffer); 
+      glTexCoordPointer(2, GL_FLOAT, 0, stBuffer.data()); 
       glEnableClientState(GL_TEXTURE_COORD_ARRAY); 
 
-      glDrawElements(GL_LINES, 2 * numGroupEdges, GL_INT, indexBuffer);
+      glDrawElements(GL_LINES, 2 * edgeCount, GL_INT, indexBuffer.data());
 
       glDisableClientState(GL_VERTEX_ARRAY);
       glClientActiveTextureARB(GL_TEXTURE0_ARB); 
@@ -831,10 +837,6 @@ void ObjMeshGPUDeformer::MakeDisplayListsEdges()
 
       glEndList();    
     #endif
-
-    free(indexBuffer);
-    free(stBuffer);
-    free(vtxBuffer);
   }
 
   #ifdef OBJMESHGPUDEFORMER_USING_VBOS
@@ -875,22 +877,47 @@ void ObjMeshGPUDeformer::ReadBack_u(double * u)
 void ObjMeshGPUDeformer::DeleteCGShaders()
 {
   if (VertexPass2Program) 
+  {
+    #ifndef __APPLE__
+      cgGLUnloadProgram(VertexPass2Program);
+    #endif
     cgDestroyProgram(VertexPass2Program);
+  }
   
   if (VertexPass2ProgramShadow)
+  {
+    #ifndef __APPLE__
+      cgGLUnloadProgram(VertexPass2ProgramShadow);
+    #endif
     cgDestroyProgram(VertexPass2ProgramShadow);
+  }
 
   //if (VertexPass2ProgramDeformedNormals)
     //cgDestroyProgram(VertexPass2ProgramDeformedNormals);
 
-  if (VertexPass2ProgramPoints) 
+  if (VertexPass2ProgramPoints)
+  {
+    #ifndef __APPLE__
+      cgGLUnloadProgram(VertexPass2ProgramPoints);
+    #endif
     cgDestroyProgram(VertexPass2ProgramPoints);
+  }
     
   if (VertexPass2ProgramEdges)
+  {
+    #ifndef __APPLE__
+      cgGLUnloadProgram(VertexPass2ProgramEdges);
+    #endif
     cgDestroyProgram(VertexPass2ProgramEdges);
+  }
 
   if (FragmentPass2Program)
+  {
+    #ifndef __APPLE__
+      cgGLUnloadProgram(FragmentPass2Program);
+    #endif
     cgDestroyProgram(FragmentPass2Program);
+  }
     
   if (Context)
     cgDestroyContext(Context);
@@ -943,7 +970,7 @@ void ObjMeshGPUDeformer::cgErrorCallback(void)
   }
 }
 
-void ObjMeshGPUDeformer::PrintGLerror( char *msg )
+void ObjMeshGPUDeformer::PrintGLerror( const char *msg )
 {
   GLenum errCode;
   const GLubyte *errStr;
@@ -956,7 +983,7 @@ void ObjMeshGPUDeformer::PrintGLerror( char *msg )
   }
 }
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #include <malloc.h>
 
   void ObjMeshGPUDeformer::heap_check_()
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer.h b/libraries/objMeshGPUDeformer/objMeshGPUDeformer.h
similarity index 86%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer.h
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer.h
index e9f864942199273d2e772452cc203b4778ac124b..b39b0573fd9029a73ba43215ca5802e32d989c59 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer.h
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,17 +34,18 @@
 #ifndef _OBJMESHGPUDEFORMER__H_
 #define _OBJMESHGPUDEFORMER__H_
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #include <windows.h>
 #else
   #define GL_GLEXT_PROTOTYPES 1
 #endif
 
 #include "openGL-headers.h"
-#ifndef __APPLE__
-#  include <GL/glext.h>
-#else
-#  include <OpenGL/glext.h>
+
+#if defined(_WIN32) || defined(linux) || defined(__linux__)
+  #include <GL/glext.h>
+#elif defined(__APPLE__)
+  #include <OpenGL/glext.h>
 #endif
 
 #include <Cg/cg.h>
@@ -83,6 +88,7 @@ protected:
   int numGroups;
   int numTriangles;
   int * numGroupTriangles;
+  std::vector<int> numGroupEdges;
 
   int renderingMode;
   int displayListStart;
@@ -114,7 +120,7 @@ protected:
 
   static int glh_extension_supported(const char * extension);
   static void cgErrorCallback(void);
-  void PrintGLerror(char * msg);
+  void PrintGLerror(const char * msg);
 
   virtual void EnableRTT() = 0;
   virtual void DisableRTT() = 0;
@@ -131,7 +137,7 @@ protected:
   int * vboNormalEnabled;
   int * vboTex1Enabled;
 
-  #ifdef WIN32
+  #if defined(_WIN32) || defined(WIN32)
     void heap_check_();
   #endif
 
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.cpp b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.cpp
similarity index 97%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.cpp
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.cpp
index 00c061e5faecd882c31bbc3566bbc1ad7aa65eba..f439d19030cca5fa02337314542d7882e64917a9 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.cpp
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,7 +35,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
   #include "GL/glew.h"
 #endif
 #include "objMeshGPUDeformer_coarseToFine.h"
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.h b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.h
similarity index 84%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.h
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.h
index 52886122688371cfcff51f82019e406883fc14ab..811b3119a84454afaf7b9b9bcac8ae13305c9baf 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.h
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.cpp b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.cpp
similarity index 88%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.cpp
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.cpp
index ac605a5698b767c26be9d2f8dee046640f5eaf39..2014fb07fa87c08664e00b28e888c77d7a3e6171 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.cpp
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,20 +35,16 @@
 #include <stdlib.h>
 #include <string.h>
 #include "objMeshGPUDeformer_coarseToFine_fbo.h"
-#ifndef __APPLE__
-#  include "GL/glext.h"
-#else
-#  include "OpenGL/glext.h"
-#endif
 
-#if defined(linux)
+#include "glh_extensions.h"
+
+#if defined(linux) || defined(__linux__)
   GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei, GLuint *);
   GLAPI void APIENTRY glBindFramebufferEXT (GLenum, GLuint);
   GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum, GLenum, GLenum, GLuint, GLint);
   GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum);
 #endif
 
-#include "glh_extensions.h"
 
 ObjMeshGPUDeformer_coarseToFine_fbo::~ObjMeshGPUDeformer_coarseToFine_fbo() {}
 
@@ -138,7 +138,7 @@ void ObjMeshGPUDeformer_coarseToFine_fbo::CheckFramebufferStatus()
 
 int ObjMeshGPUDeformer_coarseToFine_fbo::InitExtensions()
 {
-  #ifdef WIN32
+  #if defined(_WIN32) || defined(WIN32) 
     if (!glh_init_extensions("GL_EXT_framebuffer_object "
                            "GL_ARB_multitexture "))
     {
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.h b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.h
similarity index 79%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.h
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.h
index f8dc627769bc0f7abc5136dfe267344ac32102e1..6b36fc228fc0b36dd3689a8d53427e0a020bd64d 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.h
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_coarseToFine_fbo.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq.cpp b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq.cpp
similarity index 96%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq.cpp
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq.cpp
index e80ccf918a23c72890c766d1189d2f9d3d370984..022d3249bba00d675c84a2514710131da51fc7a9 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq.cpp
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -126,9 +130,14 @@ void ObjMeshGPUDeformer_uUq::Init(ObjMesh * mesh_, ObjMeshRender * meshRender_,
 
 ObjMeshGPUDeformer_uUq::~ObjMeshGPUDeformer_uUq()
 {
-  DeleteCGShaders();
-  if (Fragment_uUqProgram)
+  if (Fragment_uUqProgram) 
+  {
+    #ifndef __APPLE__
+      cgGLUnloadProgram(Fragment_uUqProgram);
+    #endif
     cgDestroyProgram(Fragment_uUqProgram);
+  }
+  DeleteCGShaders();
   DeleteRTT();
 }
 
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq.h b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq.h
similarity index 82%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq.h
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq.h
index 2c65ad2f456a0afa240d2991330d15b3bb036e1a..a5f962ca49f0fe0b41a932fb6df6ee27c4959bed 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq.h
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.cpp b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.cpp
similarity index 88%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.cpp
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.cpp
index ee3503097f700a165529194f0ace16f873f60d97..f302ea5ca64a307d4d9b08a8c7ecf2ef0f15e179 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.cpp
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,20 +35,16 @@
 #include <stdlib.h>
 #include <string.h>
 #include "objMeshGPUDeformer_uUq_fbo.h"
-#ifndef __APPLE__
-#  include "GL/glext.h"
-#else
-#  include "OpenGL/glext.h"
-#endif
 
-#if defined(linux)
+#include "glh_extensions.h"
+
+#if defined(linux) || defined(__linux__)
   GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei, GLuint *);
   GLAPI void APIENTRY glBindFramebufferEXT (GLenum, GLuint);
   GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum, GLenum, GLenum, GLuint, GLint);
   GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum);
 #endif
 
-#include "glh_extensions.h"
 
 ObjMeshGPUDeformer_uUq_fbo::~ObjMeshGPUDeformer_uUq_fbo() {}
 
@@ -140,7 +140,7 @@ void ObjMeshGPUDeformer_uUq_fbo::CheckFramebufferStatus()
 
 int ObjMeshGPUDeformer_uUq_fbo::InitExtensions()
 {
-  #ifdef WIN32
+  #if defined(_WIN32) || defined(WIN32) 
     if (!glh_init_extensions("GL_EXT_framebuffer_object "
                            "GL_ARB_multitexture "))
     {
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.h b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.h
similarity index 79%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.h
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.h
index aa2f0d7cbb254ea81a37bbb2c81c52ac7c738e2b..30a9163a4f8ab027adfdc85e3b215fabd4477391 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.h
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_fbo.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.cpp b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.cpp
similarity index 93%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.cpp
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.cpp
index da8f7c482c6f700b668298feb154fd1dd5fe80d7..37aca38f5a825e81d1263c5c1bbdd0498fd1c7d0 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.cpp
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -27,7 +31,7 @@
  *                                                                       *
  *************************************************************************/
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
 
 #include <string.h>
 #include <stdio.h>
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.h b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.h
similarity index 84%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.h
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.h
index aa8dd6b4ce46a0a829ff705568597552d8388efd..c269d910bf92166fb89e54846be7b6ae7ba13767 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.h
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_pbuffer.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,7 +36,7 @@
 #ifndef _OBJMESHGPUDEFORMER_UUQ_PBUFFER_H_
 #define _OBJMESHGPUDEFORMER_UUQ_PBUFFER_H_
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
 
 #ifdef _MSC_VER
   #pragma comment( lib, "cg.lib" )
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.cpp b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.cpp
similarity index 83%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.cpp
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.cpp
index 9a33c636819b8c8ce74ad2d0ab4537f87451d1f6..990d6eef0dff7c5b89d50902d1383f484bf71e66 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.cpp
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.h b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.h
similarity index 77%
rename from src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.h
rename to libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.h
index 6af107fa46c2208e1c84272e141f7472f88ff421..c98a5c144512864af0e22c6337dfd18c5d19c9b4 100644
--- a/src/libobjMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.h
+++ b/libraries/objMeshGPUDeformer/objMeshGPUDeformer_uUq_setLighting.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libobjMeshGPUDeformer/uUq-shaders.cpp b/libraries/objMeshGPUDeformer/uUq-shaders.cpp
similarity index 96%
rename from src/libobjMeshGPUDeformer/uUq-shaders.cpp
rename to libraries/objMeshGPUDeformer/uUq-shaders.cpp
index c28a5482317bce9b01db9164cd932e8a35e994c0..0e39602cc5c06503883436f96333627ddbd7e81d 100644
--- a/src/libobjMeshGPUDeformer/uUq-shaders.cpp
+++ b/libraries/objMeshGPUDeformer/uUq-shaders.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "objMeshGPUDeformer" library , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                                        2013 USC       *
+ *                                                        2018 USC       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libobjMeshGPUDeformer/vbo.cpp b/libraries/objMeshGPUDeformer/vbo.cpp
similarity index 55%
rename from src/libobjMeshGPUDeformer/vbo.cpp
rename to libraries/objMeshGPUDeformer/vbo.cpp
index a75ef1a8607605cd80fcd0af840940f4c01779e6..8af80767fa1f896915807b57afb66791aad87d01 100644
--- a/src/libobjMeshGPUDeformer/vbo.cpp
+++ b/libraries/objMeshGPUDeformer/vbo.cpp
@@ -1,14 +1,18 @@
 #include "vbo.h"
+// #include <iostream>
+// using namespace std;
+#if defined(_WIN32) || defined(WIN32)
 
-#ifdef WIN32
+#include "glh_extensions.h"
 
-PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL;
-PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL;
-PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL;
-PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL;
+//extern PFNGLBINDBUFFERARBPROC glBindBufferARB;
+//extern PFNGLBUFFERDATAARBPROC glBufferDataARB;
+//extern PFNGLGENBUFFERSARBPROC glGenBuffersARB;
+//extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB;
 
 bool InitializeVBOs(void)
 {
+  // cout << "Initializing VBOs (in Windows)." << endl;
   if (glBindBufferARB == NULL)
     glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
 
@@ -21,6 +25,8 @@ bool InitializeVBOs(void)
   if (glDeleteBuffersARB == NULL)
     glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
 
+  // cout << "glGenBuffersARB: " << glGenBuffersARB << endl;
+  // cout << "return " << (glBindBufferARB && glBufferDataARB && glGenBuffersARB && glDeleteBuffersARB) << endl;
   return (glBindBufferARB && glBufferDataARB && glGenBuffersARB && glDeleteBuffersARB);
 }
 
diff --git a/src/libobjMeshGPUDeformer/vbo.h b/libraries/objMeshGPUDeformer/vbo.h
similarity index 69%
rename from src/libobjMeshGPUDeformer/vbo.h
rename to libraries/objMeshGPUDeformer/vbo.h
index 4075ff11218cfc6673968108b6169a94393e3d3f..830b55f6e88764dad1b43a72c98771cdca635eed 100644
--- a/src/libobjMeshGPUDeformer/vbo.h
+++ b/libraries/objMeshGPUDeformer/vbo.h
@@ -5,7 +5,7 @@
 #ifndef _VBO_H_
 #define _VBO_H_
 
-#ifdef WIN32
+#if defined(_WIN32) || defined(WIN32)
 
 #include <windows.h>
 
@@ -21,10 +21,10 @@ bool InitializeVBOs(void);
 // === no user-callable code below ===
 
 // external function pointers 
-extern PFNGLBINDBUFFERARBPROC glBindBufferARB;
-extern PFNGLBUFFERDATAARBPROC glBufferDataARB;
-extern PFNGLGENBUFFERSARBPROC glGenBuffersARB;
-extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB;
+//extern PFNGLBINDBUFFERARBPROC glBindBufferARB;
+//extern PFNGLBUFFERDATAARBPROC glBufferDataARB;
+//extern PFNGLGENBUFFERSARBPROC glGenBuffersARB;
+//extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB;
 
 #else
   bool InitializeVBOs(void);
diff --git a/libraries/openGLHelper/fog.h b/libraries/openGLHelper/fog.h
new file mode 100644
index 0000000000000000000000000000000000000000..2ce6a2f569b4627d6ce4ed27023f2b32f523c875
--- /dev/null
+++ b/libraries/openGLHelper/fog.h
@@ -0,0 +1,64 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "openGLHelper" library , Copyright (C) 2018 USC                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef FOG_H
+#define FOG_H
+
+#include "openGL-headers.h"
+
+// a simple struct for rendering fog using openGL
+struct Fog
+{
+  Fog() : fogStart(10.0),  fogEnd(14.0), fogDensity(0.25) {}
+
+  // set openGL parameters for fog
+  inline void setParameters() const;
+
+  void enable() const { glEnable(GL_FOG); }
+
+  void disable() const { glDisable(GL_FOG); }
+
+  double fogStart, fogEnd, fogDensity;
+};
+
+
+inline void Fog::setParameters() const
+{
+  GLfloat fogColor[4] = {1.0, 1.0, 1.0, 1.0};
+  glFogfv(GL_FOG_COLOR, fogColor);
+  glFogf(GL_FOG_START, fogStart);
+  glFogf(GL_FOG_END, fogEnd);
+  glFogf(GL_FOG_DENSITY, fogDensity);
+  glFogi(GL_FOG_MODE, GL_LINEAR);
+}
+
+#endif
diff --git a/libraries/openGLHelper/handleRender.cpp b/libraries/openGLHelper/handleRender.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2a43f8c8f59c10264e30ec31574c7ae02825a897
--- /dev/null
+++ b/libraries/openGLHelper/handleRender.cpp
@@ -0,0 +1,235 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "openGLHelper" library , Copyright (C) 2018 USC                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "handleRender.h"
+#include "openGLHelper.h"
+
+
+//static int numArrowHeadVertices = 22;
+static double arrowHeadVertices[66]={0.057018,0.000000,-0.018526,0.048503,0.000000,-0.035239,0.035239,0.000000,-0.048503,0.018526,0.000000,-0.057018,0.000000,0.000000,-0.059953,-0.018526,0.000000,-0.057018,-0.035239,0.000000,-0.048503,-0.048503,0.000000,-0.035239,-0.057018,0.000000,-0.018526,-0.059953,0.000000,0.000000,-0.057018,0.000000,0.018526,-0.048503,0.000000,0.035239,-0.035239,0.000000,0.048503,-0.018526,0.000000,0.057018,-0.000000,0.000000,0.059953,0.018526,0.000000,0.057018,0.035239,0.000000,0.048503,0.048503,0.000000,0.035239,0.057018,0.000000,0.018526,0.059953,0.000000,0.000000,0.000000,0.281334,0.000000,-0.000000,0.000000,-0.000000};
+//static double arrowHeadFaceVertexNormals[147]={0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.930171, 0.208421, -0.302230, 0.791250, 0.208421, -0.574877, -0.000000, 1.000000, 0.000001, 0.574877, 0.208421, -0.791250, 0.302231, 0.208421, -0.930171, -0.000000, 0.208421, -0.978039, -0.302231, 0.208421, -0.930171, -0.574877, 0.208421, -0.791250, -0.791250, 0.208421, -0.574877, -0.930171, 0.208421, -0.302231, -0.978039, 0.208421, 0.000000, -0.930171, 0.208421, 0.302231, -0.791250, 0.208421, 0.574877, -0.574877, 0.208421, 0.791250, -0.302231, 0.208421, 0.930171, 0.000000, 0.208421, 0.978039, 0.302231, 0.208421, 0.930171, 0.574877, 0.208421, 0.791250, 0.791250, 0.208421, 0.574877, 0.930171, 0.208421, 0.302231, 0.978039, 0.208421, 0.000001, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, -1.000000, 0.000000};
+static int arrowHeadNumFaces = 40;
+static int arrowHeadFaceIndices[120]={17, 22, 18,18, 22, 19,19, 22, 20,20, 22, 1,1, 22, 2,1, 2, 21,2, 3, 21,3, 4, 21,4, 5, 21,5, 6, 21,6, 7, 21,7, 8, 21,8, 9, 21,9, 10, 21,10, 11, 21,11, 12, 21,12, 13, 21,13, 14, 21,14, 15, 21,15, 16, 21,16, 17, 21,17, 18, 21,18, 19, 21,19, 20, 21,20, 1, 21,17, 16, 22,16, 15, 22,15, 14, 22,14, 13, 22,22, 13, 12,7, 22, 8,12, 11, 22,11, 10, 22,10, 9, 22,22, 9, 8,2, 22, 3,7, 6, 22,6, 5, 22,5, 4, 22,22, 4, 3};
+//static int arrowHeadFaceVertexNormalIndices[120]={1,2,3,3,2,4,4,2,5,5,2,6,6,2,7,8,9,10,9,11,10,11,12,10,12,13,10,13,14,10,14,15,10,15,16,10,16,17,10,17,18,10,18,19,10,19,20,10,20,21,10,21,22,10,22,23,10,23,24,10,24,25,10,25,26,10,26,27,10,27,28,10,28,8,10,29,30,31,30,32,31,32,33,31,33,34,31,31,34,35,36,37,38,39,40,37,40,41,37,41,42,37,37,42,38,43,44,45,46,47,44,47,48,44,48,49,44,44,49,45};
+// one 3x3 rotation matrix per each axis
+static Mat3d arrowHeadRotationMatrices[3] = { Mat3d(0,1,0, -1,0,0,  0,0,1),
+                                              Mat3d(1,0,0,  0,1,0,  0,0,1),
+                                              Mat3d(-1,0,0, 0,0,-1, 0,1,0) };
+
+
+HandleRender::HandleRender(): lineWidth(3.0), axisLength(1.0), reverseDirection(false), arrowHeadScale(1.3), mouseOverMoveHandleAxis(-1)
+{
+  for(int i = 0; i < 3; i++)
+  {
+    moveHandleAxesLC[i] = moveHandleAxesUC[i] = Vec3d(0.);
+    showAxis[i] = true;
+  }
+}
+
+void HandleRender::resetMouseLocation()
+{
+  mouseOverMoveHandleAxis = -1;
+  showAxis[0] = showAxis[1] = showAxis[2] = true;
+}
+
+void HandleRender::dragHandle()
+{
+  if (mouseOverMoveHandleAxis != -1) // If the mouse cursor is over the handle
+  {
+    // it's only one axis selected, or all three axes selected
+    showAxis[0] = showAxis[1] = showAxis[2] = false;
+    if (mouseOverMoveHandleAxis == 0)
+      showAxis[0] = true;
+    else if (mouseOverMoveHandleAxis == 1)
+      showAxis[1] = true;
+    else if (mouseOverMoveHandleAxis == 2)
+      showAxis[2] = true;
+    else if (mouseOverMoveHandleAxis == 3)
+      showAxis[0] = showAxis[1] = showAxis[2] = true;
+  }
+}
+
+void HandleRender::releaseHandle()
+{
+  showAxis[0] = showAxis[1] = showAxis[2] = true;
+}
+
+void HandleRender::setMouseLocation(const Vec3d & mouseLocation)
+{
+  mouseOverMoveHandleAxis = -1;
+  bool overAxis[3];
+  overAxis[0] = overAxis[1] = overAxis[2] = false;
+  for(int axisIndex=0; axisIndex<3; axisIndex++)
+  {
+    //moveHandleAxesLC[axisIndex].print();// printf("\n");
+    //moveHandleAxesUC[axisIndex].print();// printf("\n");
+    overAxis[axisIndex] =
+           ((moveHandleAxesLC[axisIndex][0] < mouseLocation[0]) && (mouseLocation[0] < moveHandleAxesUC[axisIndex][0])
+        && (moveHandleAxesLC[axisIndex][1] < mouseLocation[1]) && (mouseLocation[1] < moveHandleAxesUC[axisIndex][1])
+        && (moveHandleAxesLC[axisIndex][2] < mouseLocation[2]) && (mouseLocation[2] < moveHandleAxesUC[axisIndex][2]));
+  }
+  if ((overAxis[0] && overAxis[1]) || (overAxis[0] && overAxis[2]) || (overAxis[1] && overAxis[2]))
+    mouseOverMoveHandleAxis = 3;
+  else if (overAxis[0])
+    mouseOverMoveHandleAxis = 0;
+  else if (overAxis[1])
+    mouseOverMoveHandleAxis = 1;
+  else if (overAxis[2])
+    mouseOverMoveHandleAxis = 2;
+
+//  if (mouseOverMoveHandleAxis > -1)
+//    cout << "axis " << mouseOverMoveHandleAxis << endl;
+}
+
+void HandleRender::render(const Vec3d & handlePosition, const Mat3d & rotation, int stencilValue)
+{
+  float previousLineWidth;
+  glGetFloatv(GL_LINE_WIDTH, &previousLineWidth);
+  //glEnable(GL_LIGHTING);
+  glDisable(GL_TEXTURE_2D);
+  glDisable(GL_LIGHTING);
+
+  //float lineAmbient[4] = {0.1, 0.1, 0.1, 1.0};
+  //float lineDiffuse[4];
+  //lineDiffuse[3] = 1.0;
+  //float lineSpecular[4] = {1.0, 1.0, 1.0, 1.0};
+  //float lineShininess = 1.0;
+  //glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, lineAmbient);
+  //glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, lineSpecular);
+  //glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, lineShininess);
+
+  Vec3d axisOffset[3];
+  Vec3d axisEndPoint[3];
+
+  glLineWidth(lineWidth);
+  // draw the handle axes
+  glBegin(GL_LINES);
+  for(int i = 0; i < 3; i++)
+  {
+    if (!showAxis[i])
+      continue;
+    //lineDiffuse[0] = lineDiffuse[1] = lineDiffuse[2] = 0.0;
+    //lineDiffuse[i] = 1.0;
+    //glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, lineDiffuse);
+    double vertexColor[3] = {0, 0, 0};
+    vertexColor[i] = 1.0;
+    glColor3f(vertexColor[0], vertexColor[1], vertexColor[2]);
+    glVertex3f(handlePosition[0], handlePosition[1], handlePosition[2]);
+
+    axisOffset[i] = Vec3d(0.);
+    axisOffset[i][i] = axisLength;
+    axisOffset[i] = rotation * axisOffset[i];
+
+    vertexColor[i] = 0.5;
+    glColor3f(vertexColor[0], vertexColor[1], vertexColor[2]);
+    axisEndPoint[i] = handlePosition + (reverseDirection ? -1.0 : 1.0) * axisOffset[i];
+    glVertex3f(axisEndPoint[i][0], axisEndPoint[i][1], axisEndPoint[i][2]);
+  }
+  glEnd();
+
+  // enhance the axis selectable area by hacking the stencil buffer
+  int previousStencilFunc = GL_ALWAYS;
+  int previousStencilValue = 0;
+  glGetIntegerv(GL_STENCIL_FUNC, &previousStencilFunc);
+  glGetIntegerv(GL_STENCIL_REF, &previousStencilValue);
+  glStencilFunc(GL_ALWAYS, stencilValue, ~(0u)); //always pass stencil test, stencil renference value is stencilValue (default 1)
+  glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+  glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+  glLineWidth(lineWidth*200.0);
+  glBegin(GL_LINES);
+  for(int i = 0; i < 3; i++)
+  {
+    if (!showAxis[i])
+      continue;
+    glVertex3f(handlePosition[0], handlePosition[1], handlePosition[2]);
+    glVertex3f(axisEndPoint[i][0], axisEndPoint[i][1], axisEndPoint[i][2]);
+  }
+  glEnd();
+  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+  glLineWidth(previousLineWidth);
+
+  // draw arrow head
+  glBegin(GL_TRIANGLES);
+  for(int axisIndex=0; axisIndex<3; axisIndex++)
+  {
+    if (!showAxis[axisIndex])
+      continue;
+
+    moveHandleAxesLC[axisIndex] = handlePosition - Vec3d(0.1 * arrowHeadScale);
+    moveHandleAxesUC[axisIndex] = handlePosition + Vec3d(0.1 * arrowHeadScale);
+
+    double scale = arrowHeadScale;
+    if ((mouseOverMoveHandleAxis == axisIndex) || (mouseOverMoveHandleAxis == 3))
+      scale *= 1.3;
+
+    for(int i=0; i<arrowHeadNumFaces; i++)
+    {
+
+      for(int v=0; v<3; v++)
+      {
+        int vIndex = arrowHeadFaceIndices[3*i+v] - 1;
+
+        double vertexColor[3] = {0, 0, 0};
+        vertexColor[axisIndex] = 1.0;
+        if (vIndex == 20)
+          vertexColor[axisIndex] = 0.0;
+        glColor3f(vertexColor[0], vertexColor[1], vertexColor[2]);
+
+        Mat3d arrowHeadR = rotation * arrowHeadRotationMatrices[axisIndex];
+
+        Vec3d vertexPosition = handlePosition + (reverseDirection ? -1.0 : 1.0) * (axisOffset[axisIndex] +
+            scale * (arrowHeadR * Vec3d(&arrowHeadVertices[3*vIndex])) );
+
+        if (vertexPosition[0] > moveHandleAxesUC[axisIndex][0])
+          moveHandleAxesUC[axisIndex][0] = vertexPosition[0];
+        if (vertexPosition[1] > moveHandleAxesUC[axisIndex][1])
+          moveHandleAxesUC[axisIndex][1] = vertexPosition[1];
+        if (vertexPosition[2] > moveHandleAxesUC[axisIndex][2])
+          moveHandleAxesUC[axisIndex][2] = vertexPosition[2];
+
+        if (vertexPosition[0] < moveHandleAxesLC[axisIndex][0])
+          moveHandleAxesLC[axisIndex][0] = vertexPosition[0];
+        if (vertexPosition[1] < moveHandleAxesLC[axisIndex][1])
+          moveHandleAxesLC[axisIndex][1] = vertexPosition[1];
+        if (vertexPosition[2] < moveHandleAxesLC[axisIndex][2])
+          moveHandleAxesLC[axisIndex][2] = vertexPosition[2];
+
+        //glNormal3f(vVertexNormal[0], vVertexNormal[1], vVertexNormal[2]);
+        glVertex3f(vertexPosition[0], vertexPosition[1], vertexPosition[2]);
+      }
+    }
+  }
+  glEnd();
+
+  glStencilFunc(previousStencilFunc, previousStencilValue, ~(0u));
+}
diff --git a/libraries/openGLHelper/handleRender.h b/libraries/openGLHelper/handleRender.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd11bdbf950e396d74a5bb6ec26927b3167eb387
--- /dev/null
+++ b/libraries/openGLHelper/handleRender.h
@@ -0,0 +1,91 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "openGLHelper" library , Copyright (C) 2018 USC                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef HANDLERENDER_H
+#define HANDLERENDER_H
+#include "minivector.h"
+
+
+// used to draw a handle with three arrows forming a coordinate system
+class HandleRender
+{
+public:
+  HandleRender();
+  virtual ~HandleRender() {}
+
+  // stencilValue: the stencil value used on the handle. Our code draws non-zero stencil values on rendered objects
+  // so that when the user clicks on the object we know where she clicks.
+  void render(const Vec3d & handlePosition, const Mat3d & rotation = Mat3d(1.0), int stencilValue = 1);
+
+  void setLineWidth(double lw) { lineWidth = lw; }
+  void setAxisLength(double al) { axisLength = al; }
+  void setHandleReversed(bool r) { reverseDirection = r; }
+  void setArrowHeadScale(double s) { arrowHeadScale = s; }
+
+  // mouse location is used to highlight which axis the mouse is on
+  // if mouseLocation is near the root of the axes, all three axes are highlighted
+  void setMouseLocation(const Vec3d & mouseLocation); 
+  // disable axis highlight
+  void resetMouseLocation();
+
+  // start handle dragging state
+  // when dragging the handle, only the selected axis (arrow) is rendered according to the mosue location
+  void dragHandle();
+  // end handle dragging state
+  // display all axes after the handle is released
+  void releaseHandle(); 
+
+  // whether the mouse is over a certain axis
+  bool isMouseOverAxis(int axis) const { return (mouseOverMoveHandleAxis == 3 || mouseOverMoveHandleAxis == axis); }
+  // whether the mouse is on the handle
+  bool isMouseOnHandle() const { return mouseOverMoveHandleAxis != -1; }
+
+protected:
+  bool showAxis[3];
+  double lineWidth;
+  double axisLength;
+  bool reverseDirection;
+  double arrowHeadScale;
+
+  Vec3d moveHandleAxesLC[3];
+  Vec3d moveHandleAxesUC[3];
+
+  // -1: not over any axis
+  //  0: over the x-axis
+  //  1: over the y-axis
+  //  2: over the z-axis
+  //  3: over all three axes
+  int mouseOverMoveHandleAxis;
+};
+
+
+#endif
diff --git a/libraries/openGLHelper/inputDevice.cpp b/libraries/openGLHelper/inputDevice.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ddc6bb607464a3fb7682e322f75da6e7526acaa5
--- /dev/null
+++ b/libraries/openGLHelper/inputDevice.cpp
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "openGLHelper" library , Copyright (C) 2018 USC                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "inputDevice.h"
+#include "openGLHelper.h"
+
+InputDevice::InputDevice()
+{
+  memset(mousePos, 0, sizeof(mousePos));
+  leftMouseButton = middleMouseButton = rightMouseButton = false;
+  shift = alt = ctrl = false;
+}
+
+void InputDevice::setButton(int button, int state)
+{
+  switch (button)
+  {
+    case GLUT_LEFT_BUTTON:
+      leftMouseButton = (state==GLUT_DOWN);
+    break;
+
+    case GLUT_MIDDLE_BUTTON:
+      middleMouseButton = (state==GLUT_DOWN);
+    break;
+
+    case GLUT_RIGHT_BUTTON:
+      rightMouseButton = (state==GLUT_DOWN);
+    break;
+  }
+
+  setModifiers();
+}
+
+void InputDevice::setModifiers()
+{
+  shift = (glutGetModifiers() == GLUT_ACTIVE_SHIFT);
+  alt = (glutGetModifiers() == GLUT_ACTIVE_ALT);
+  ctrl = (glutGetModifiers() == GLUT_ACTIVE_CTRL);
+}
diff --git a/libraries/openGLHelper/inputDevice.h b/libraries/openGLHelper/inputDevice.h
new file mode 100644
index 0000000000000000000000000000000000000000..a510679253de547dedb0cfd488ab6e31bb3b11a5
--- /dev/null
+++ b/libraries/openGLHelper/inputDevice.h
@@ -0,0 +1,69 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "openGLHelper" library , Copyright (C) 2018 USC                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef INPUTDEVICE_H
+#define INPUTDEVICE_H 
+
+// GLUT keyboard & mouse control
+class InputDevice
+{ 
+public:
+  InputDevice();
+  virtual ~InputDevice() {}
+
+  // called in mouseButtonActivity(int button, int state, int x, int y), mouseDrag(int x, int y) and mouseNoDrag(int x, int y), set mousePos
+  void setMousePos(int x, int y) { mousePos[0] = x; mousePos[1] = y; }
+
+  // called in mouseButtonActivity(int button, int state, int x, int y), set mouse button and shift/alt/ctrl
+  void setButton(int button, int state);
+
+  // called in SpecialKeysFunc, set shift/alt/ctrl
+  void setModifiers();
+
+  // get keyboard & mouse control
+  int getMousePosX() const { return mousePos[0]; }
+  int getMousePosY() const { return mousePos[1]; }
+  bool leftMouseButtonDown() const { return leftMouseButton; }
+  bool middleMouseButtonDown() const { return middleMouseButton; }
+  bool rightMouseButtonDown() const { return rightMouseButton; }
+  bool shiftPressed() const { return shift; }
+  bool altPressed() const { return alt; }
+  bool ctrlPressed() const { return ctrl; }
+
+protected:
+  int mousePos[2];
+  bool leftMouseButton, middleMouseButton, rightMouseButton;
+  bool shift, alt, ctrl;
+};
+
+
+#endif
diff --git a/src/libopenGLHelper/openGLHelper.cpp b/libraries/openGLHelper/openGLHelper.cpp
similarity index 58%
rename from src/libopenGLHelper/openGLHelper.cpp
rename to libraries/openGLHelper/openGLHelper.cpp
index 4971939125bd6e043d95498d0f83a570690b1bc8..b01b4b57aba3dbf7b5d7ec06ae901b8ee9e8bda5 100644
--- a/src/libopenGLHelper/openGLHelper.cpp
+++ b/libraries/openGLHelper/openGLHelper.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -27,15 +31,15 @@
  *************************************************************************/
 
 #include "openGLHelper.h"
-
-void *font = GLUT_BITMAP_9_BY_15;
-void *fonts[] =
-{
-  GLUT_BITMAP_9_BY_15,
-  GLUT_BITMAP_TIMES_ROMAN_10,
-  GLUT_BITMAP_TIMES_ROMAN_24
-};
-
+#include "macros.h"
+#include <iostream>
+using namespace std;
+//static void * fonts[] =
+//{
+//  GLUT_BITMAP_9_BY_15,
+//  GLUT_BITMAP_TIMES_ROMAN_10,
+//  GLUT_BITMAP_TIMES_ROMAN_24
+//};
 
 void UnitCube()
 {
@@ -183,18 +187,73 @@ void RenderSphere(float x, float y, float z)
   glPopMatrix();
 }
 
-void OutputText(int x, int y, char *string)
+void ChangeStateToRenderingText(int w, int h)
 {
-  int len, i;
-  glRasterPos2f(x, y);
-  len = (int) strlen(string);
-  for (i = 0; i < len; i++) 
-  {
+  glPushAttrib   ( GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_TRANSFORM_BIT | GL_LIGHTING_BIT ) ;
+  glDisable      ( GL_LIGHTING   ) ;
+  glDisable      ( GL_FOG        ) ;
+  glDisable      ( GL_TEXTURE_2D ) ;
+  glDisable      ( GL_DEPTH_TEST ) ;
+  glDisable      ( GL_CULL_FACE  ) ;
+  glEnable       ( GL_ALPHA_TEST ) ;
+  glEnable       ( GL_BLEND ) ;
+  glAlphaFunc    ( GL_GREATER, 0.1f ) ;
+  glBlendFunc    ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
+
+  glMatrixMode   ( GL_PROJECTION ) ;
+  glPushMatrix   () ;
+  glLoadIdentity () ;
+  gluOrtho2D     ( 0, w, 0, h ) ;
+  glMatrixMode   ( GL_MODELVIEW ) ;
+  glPushMatrix   () ;
+  glLoadIdentity () ;
+}
+
+void RestoreStateFromeRenderingText()
+{
+  glMatrixMode   ( GL_PROJECTION ) ;
+  glPopMatrix    () ;
+  glMatrixMode   ( GL_MODELVIEW ) ;
+  glPopMatrix    () ;
+  glPopAttrib    () ;
+}
+
+void OutputText(float X, float Y, const char *string, void * font)
+{
+  glRasterPos2f(X, Y);
+  for(size_t i = 0, len = strlen(string); i < len; i++) 
     glutBitmapCharacter(font, string[i]);
-  }
 }
 
-void PrintGLerror( char *msg )
+// void OutputText(float relx, float rely, int windowWidth, int windowHeight, const char * string, void * font)
+// {
+//   if(relx < 0)
+//     relx = windowWidth + relx;
+//   if(rely < 0)
+//     rely = windowHeight + rely;
+//   OutputText(-1 + 2.0 * relx / windowWidth, -1 + 2.0 * rely / windowHeight, string, font);
+// }
+
+void OutputStrokeText(float X, float Y, float fontSizeScale, const char * string, void * font)
+{
+  glPushMatrix();
+  glTranslatef(X, Y, -1);
+  glScalef(0.001 * fontSizeScale, 0.001 * fontSizeScale, 1);
+  for(size_t i = 0, len = strlen(string); i < len; i++) 
+    glutStrokeCharacter(font, string[i]);
+  glPopMatrix();
+}
+
+// void OutputStrokeText(float relx, float rely, int windowWidth, int windowHeight, float fontSizeScale, const char * string, void * font)
+// {
+//   if(relx < 0)
+//     relx = windowWidth + relx;
+//   if(rely < 0)
+//     rely = windowHeight + rely;
+//   OutputStrokeText(-1 + 2.0 * relx / windowWidth, -1 + 2.0 * rely / windowHeight, fontSizeScale, string, font);
+// }
+
+void PrintGLerror( const char *msg )
 {
  GLenum errCode;
  const GLubyte *errStr;
@@ -293,8 +352,8 @@ void DetermineCameraParameters(double centerX, double centerY, double centerZ, d
 }
 
 
-void DrawArrow( GLfloat px, GLfloat py, GLfloat pz,
-    GLfloat nx, GLfloat ny, GLfloat nz,
+void DrawArrow( float px, float py, float pz,
+    float nx, float ny, float nz,
     double arrowEndWidth, double arrowEndLength )
 {
   GLdouble normal[3], cross[3], zaxis[3];
@@ -306,7 +365,7 @@ void DrawArrow( GLfloat px, GLfloat py, GLfloat pz,
   double len = sqrt(nx*nx + ny*ny + nz*nz);
 
   if (len < 1E-6)
-	return;
+    return;
 
 
   glPushMatrix();
@@ -317,20 +376,20 @@ void DrawArrow( GLfloat px, GLfloat py, GLfloat pz,
     glEnd();
 
     // normalize the normal vector 
-	normal[0] /= len;
-	normal[1] /= len;
-	normal[2] /= len;
+    normal[0] /= len;
+    normal[1] /= len;
+    normal[2] /= len;
 
     // determine angle between z axis and normal 
     zaxis[0] = 0; zaxis[1] = 0; zaxis[2] = 1;
     angle = acos( zaxis[0]*normal[0] + zaxis[1]*normal[1] + zaxis[2]*normal[2] )/DegreesToRadians;
-         
+
     if ( angle != 0.0 ) 
-	{
+    {
       // find the axis of rotation 
       CROSSPRODUCT( zaxis[0], zaxis[1], zaxis[2], 
-		            normal[0], normal[1], normal[2], 
-					cross[0], cross[1], cross[2] );
+          normal[0], normal[1], normal[2],
+          cross[0], cross[1], cross[2] );
       glRotatef( angle, cross[0], cross[1], cross[2] );
     }
 
@@ -345,10 +404,23 @@ void DrawArrow( GLfloat px, GLfloat py, GLfloat pz,
   	  glScalef(2.0,2.0,2.0);
     #endif
 	*/
-    glutSolidCone( len*arrowEndWidth, len*arrowEndLength, 12, 1 );
+    glutSolidCone( len*arrowEndWidth, len*arrowEndLength, 24, 3 );
   glPopMatrix();
 }
 
+void DrawArrow(const Vec3d & p, const Vec3d & n,
+    double arrowEndWidth, double arrowEndLength)
+{
+  DrawArrow(p[0], p[1], p[2], n[0], n[1], n[2], arrowEndWidth, arrowEndLength);
+}
+
+void RenderLine(const Vec3d & v0, const Vec3d & v1)
+{
+  glBegin(GL_LINES);
+  Draw(v0, v1);
+  glEnd();
+}
+
 void JetColorMap(double x, double color[3])
 {
   double a; // alpha
@@ -445,7 +517,7 @@ void BuildSphereDisplayList(GLuint * solidSphereList, GLuint * wireSphereList)
   glEndList();
 } 
 
-char * DuplicateString(char * s)
+char * DuplicateString(const char * s)
 {
   // strdup sometimes causes problems, so we resort to this
   char * p = (char*) malloc (sizeof(char) * (strlen(s) + 1));
@@ -453,3 +525,142 @@ char * DuplicateString(char * s)
   return p;
 }
 
+
+void RenderAxes(double axisLength) 
+{
+  GLboolean lightingEnabled = glIsEnabled(GL_LIGHTING);
+  GLfloat color[4];
+  glGetFloatv(GL_CURRENT_COLOR, color);
+  glDisable(GL_LIGHTING);
+//  glEnable(GL_COLOR_MATERIAL);
+  glBegin(GL_LINES);
+  for (int i = 0; i<3; i++) 
+  {
+    float color[3] = { 0, 0, 0 };
+    color[i] = 1.0;
+    glColor3fv(color);
+    float vertex[3] = {0, 0, 0};
+    vertex[i] = axisLength;
+    glVertex3fv(vertex);
+    glVertex3f(0, 0, 0);
+  }
+  glEnd();
+  if (lightingEnabled)
+    glEnable(GL_LIGHTING);
+  glColor3fv(color);
+}
+
+void RenderLocator(const Vec3d & center, double r)
+{
+  GLboolean lightingEnabled = glIsEnabled(GL_LIGHTING);
+  GLfloat color[4];
+  glGetFloatv(GL_CURRENT_COLOR, color);
+  glDisable(GL_LIGHTING);
+
+  glBegin(GL_LINES);
+  glColor3f(1, 0.5, 0.5);
+  Draw(center + Vec3d(r, 0, 0), center - Vec3d(r, 0, 0));
+  glColor3f(0.5, 1, 0.5);
+  Draw(center + Vec3d(0, r, 0), center - Vec3d(0, r, 0));
+  glColor3f(0.5, 0.5, 1);
+  Draw(center + Vec3d(0, 0, r), center - Vec3d(0, 0, r));
+  glEnd();
+
+  if (lightingEnabled)
+    glEnable(GL_LIGHTING);
+  glColor3fv(color);
+}
+
+float readPixelDepthValue(int x, int y)
+{
+  GLint view[4];
+  glGetIntegerv (GL_VIEWPORT, view);
+
+  int winX = x;
+  int winY = view[1]+view[3]-1-y;
+
+  float zValue = 0.0f;
+  glReadPixels(winX,winY,1,1, GL_DEPTH_COMPONENT, GL_FLOAT, &zValue);
+  return zValue;
+}
+
+void unprojectPointFromScreen(int x, int y, double worldPosition[3], GLubyte * stencilValue, float * depthValue)
+{
+  GLdouble model[16], proj[16];
+  glGetDoublev (GL_MODELVIEW_MATRIX, model);
+  glGetDoublev (GL_PROJECTION_MATRIX, proj);
+
+  GLint view[4];
+  glGetIntegerv (GL_VIEWPORT, view);
+
+  int winX = x;
+  int winY = view[1]+view[3]-1-y;
+
+  float zValue = 0;
+  glReadPixels(winX,winY,1,1, GL_DEPTH_COMPONENT, GL_FLOAT, &zValue);
+  if (depthValue)
+    *depthValue = zValue;
+
+  if (stencilValue)
+  {
+    *stencilValue = 0;
+    glReadPixels(winX, winY, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilValue);
+  }
+
+  GLdouble worldX, worldY, worldZ;
+  gluUnProject(winX, winY, zValue, model, proj, view, &worldX, &worldY, &worldZ);
+  worldPosition[0] = worldX;
+  worldPosition[1] = worldY;
+  worldPosition[2] = worldZ;
+}
+
+void unprojectPointFromScreen(int x, int y, double depthValue, double worldPosition[3], GLubyte * stencilValue)
+{
+  GLdouble model[16], proj[16];
+  glGetDoublev (GL_MODELVIEW_MATRIX, model);
+  glGetDoublev (GL_PROJECTION_MATRIX, proj);
+
+  GLint view[4];
+  glGetIntegerv (GL_VIEWPORT, view);
+
+  int winX = x;
+  int winY = view[1]+view[3]-1-y;
+
+  if (stencilValue)
+  {
+    *stencilValue = 0;
+    glReadPixels(winX, winY, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilValue);
+  }
+
+  GLdouble worldX, worldY, worldZ;
+  gluUnProject(winX, winY, depthValue, model, proj, view, &worldX, &worldY, &worldZ);
+  worldPosition[0] = worldX;
+  worldPosition[1] = worldY;
+  worldPosition[2] = worldZ;
+}
+
+PolygonOffsetFillState::PolygonOffsetFillState(GLfloat inputFactor, GLfloat inputUnits, OffsetType type)
+{
+  glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &prevFactor);
+  glGetFloatv(GL_POLYGON_OFFSET_UNITS, &prevUnits);
+  
+  if (type == OT_ABSOLUTE) { glPolygonOffset(inputFactor, inputUnits); }
+  else { glPolygonOffset(prevFactor + inputFactor, prevUnits + inputUnits); } // relative
+  prevOffsetEnabled = glIsEnabled(GL_POLYGON_OFFSET_FILL);
+  glEnable(GL_POLYGON_OFFSET_FILL);
+}
+
+void PolygonOffsetFillState::restore()
+{
+  if (restored == true) return;
+  restored = true;
+  if (prevOffsetEnabled == false)
+    glDisable(GL_POLYGON_OFFSET_FILL);
+  glPolygonOffset(prevFactor, prevUnits);
+}
+
+void PolygonOffsetFillState::changeOffset(GLfloat inputFactor, GLfloat inputUnits, OffsetType type)
+{
+  if (type == OT_ABSOLUTE) { glPolygonOffset(inputFactor, inputUnits); }
+  else { glPolygonOffset(prevFactor + inputFactor, prevUnits + inputUnits); } // relative
+}
diff --git a/libraries/openGLHelper/openGLHelper.h b/libraries/openGLHelper/openGLHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..bec8935a81bd50bbd5c7e7b1eb7879a1d16fa37a
--- /dev/null
+++ b/libraries/openGLHelper/openGLHelper.h
@@ -0,0 +1,180 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Assorted OpenGL "helper" routines.
+*/
+
+#ifndef _OPENGLHELPER_H_
+#define _OPENGLHELPER_H_
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <float.h>
+#include <assert.h>
+#include <cassert>
+#include "openGL-headers.h"
+#include "minivector.h"
+
+// ============================= Rendering Texts =========================
+
+
+// this function saves some current openGL states and set up for rendering texts on 
+// window space: left: x=0, right: x = windowWidth, bottom: y = 0, top: y = windowHeight
+void ChangeStateToRenderingText(int windowWidth, int windowHeight);
+
+// restore states saved from ChangeStateToRenderingText()
+void RestoreStateFromeRenderingText();
+
+// helper class to set and restore states for text rendering
+class TextState
+{
+public:
+  TextState(int windowWidth, int windowHeight) { ChangeStateToRenderingText(windowWidth, windowHeight); }
+  ~TextState() { if (restored == false) RestoreStateFromeRenderingText(); }
+
+  void restore() { if (restored == false) { RestoreStateFromeRenderingText(); restored = true; } }
+protected:
+  bool restored = false;
+};
+
+// render text on screen using glutBitmapCharacter
+// call ChangeStateToRenderingText() first to set up. Alternatively, OpenGL modelview 
+// matrix need to be identy and projection matrix should be gluOrtho2D(0, w, 0, h) to 
+// show the text correctly
+// font size of bitmap characters cannot be controlled
+// color can be controlled by calling glColor3f beforehand                         ^ y=windowHeight
+// font can be one of                                                              |
+//  GLUT_BITMAP_9_BY_15,                                                           |
+//  GLUT_BITMAP_TIMES_ROMAN_10,                                      x=0  -------------------> x=windowWidth
+//  GLUT_BITMAP_TIMES_ROMAN_24,                                                    | 
+//  etc.                                                                           |
+// (X, Y): window space coordinates                                                | y=0
+void OutputText(float X, float Y, const char * string, void * font = GLUT_BITMAP_9_BY_15);
+// (relx, rely): shifted screen space coordinates in pixel units; origin is at the lower-left corner of the screen
+// input args windowWidth, windowHeight are needed to convert pixel units into original screen space coords.
+// if relx >= 0, relx starts at left edge of the window; otherwise, relx_p starts at right edge of the window
+// if rely >= 0, rely starts at bottom edge of the window; otherwise, rely_p starts at top edge of the window
+// void OutputTextRelative(float relx, float rely, int windowWidth, int windowHeight, const char * string, void * font = GLUT_BITMAP_9_BY_15);
+
+// same requirement as OutputText()
+// render text using stroke characters
+// font can be one of
+// GLUT_STROKE_ROMAN, and
+// GLUT_STROKE_MONO_ROMAN.
+// fontSizeScale: stroke characters can be scaled and their original sizes are huge compared to Bitmap characters. 
+// fontSizeScale=1 gives characters about three times larger than bitmap characters
+void OutputStrokeText(float X, float Y, float fontSizeScale, const char * string, void * font = GLUT_STROKE_ROMAN);
+// other parameters same as OutputText(float relx_p, float rely_p, int windowWidth, int windowHeight, const char * string, void * font)
+// void OutputStrokeText(float relx_p, float rely_p, int windowWidth, int windowHeight, float fontSizeScale, const char * string, void * font = GLUT_STROKE_ROMAN);
+
+// ============================= Rendering Shapes =========================
+
+// call glBegin()... glEnd() to render a unit cube / cube wireframe. No color specified
+void UnitCube();
+void UnitCubeWireframe();
+
+void RenderWireframeBox(double bmin[3], double bmax[3]);
+
+// draw an arrow starting from point p and end at point (p+n)
+// arrowEndWidth: relative base size of the arrow head
+// arrowEndLength: relative height of the arrow head
+void DrawArrow(float px, float py, float pz,
+    float nx, float ny, float nz,
+    double arrowEndWidth, double arrowEndLength);
+void DrawArrow(const Vec3d & p, const Vec3d & n,
+    double arrowEndWidth, double arrowEndLength);
+
+inline void Draw(const Vec3d & p) { glVertex3f(p[0], p[1], p[2]); }
+inline void Draw(const Vec3d & p0, const Vec3d & p1) { Draw(p0); Draw(p1); }
+inline void Draw(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2) { Draw(p0); Draw(p1); Draw(p2); }
+
+inline void DrawNormal(const Vec3d & p) { glNormal3f(p[0], p[1], p[2]); }
+
+// use glBegin(GL_LINES) to draw a line segment
+void RenderLine(const Vec3d & v0, const Vec3d & v1);
+
+
+void DetermineCameraParameters(double centerX, double centerY, double centerZ, double modelRadius, double * focusX, double * focusY, double * focusZ, double * cameraRadius, double * zNear, double * zFar);
+
+// convert scalar [0,1] to RGB color
+// values <0 or >1 are clamped
+void JetColorMap(double x, double color[3]);
+
+void RenderSphere(float x, float y, float z);
+void BuildSphereDisplayList(GLuint * solidSphereList, GLuint * wireSphereList);
+void TransparentSphere(GLuint solidSphereList, GLuint wireSphereList, double x, double y, double z, double radius);
+
+char * DuplicateString(const char * s);
+
+void PrintGLerror( const char *msg );
+
+void RenderAxes(double axisLength);
+
+// render a locator at center made with 3 line segments, each with different color and same length of 2.0 * radius
+void RenderLocator(const Vec3d & center, double radius = 1.0);
+
+// given (x,y) from GLUT mouseButtonActivity callback, get unprojected world position and optionally stencil value and depth value
+void unprojectPointFromScreen(int x, int y, double worldPosition[3], GLubyte * stencilValue = NULL, float * depthValue = NULL);
+
+float readPixelDepthValue(int x, int y);
+// given (x,y)  from GLUT mouseButtonActivity callback and depth value from readPixelDepthValue(x,y), 
+// get unprojected world position and optionally stencil value
+void unprojectPointFromScreen(int x, int y, double depthValue, double worldPosition[3], GLubyte * stencilValue = NULL);
+
+// manage OpenGL polygon offset fill state
+class PolygonOffsetFillState
+{
+public:
+  enum OffsetType
+  {
+    OT_RELATIVE,
+    OT_ABSOLUTE
+  };
+  PolygonOffsetFillState(GLfloat inputFactor, GLfloat inputUnits, OffsetType type = OT_RELATIVE);
+
+  ~PolygonOffsetFillState() { restore(); }
+
+  void changeOffset(GLfloat inputFactor, GLfloat inputUnits, OffsetType type = OT_RELATIVE);
+
+  void restore();
+protected:
+  bool restored = false;
+  GLfloat prevFactor = 0.0f;
+  GLfloat prevUnits = 0.0f;
+  bool prevOffsetEnabled = false;
+};
+
+#endif
+
diff --git a/src/libopenGLHelper/printBitmap.cpp b/libraries/openGLHelper/printBitmap.cpp
similarity index 75%
rename from src/libopenGLHelper/printBitmap.cpp
rename to libraries/openGLHelper/printBitmap.cpp
index a163b391624f2b7c28cd17761df4d46031052245..0068af7c1a568f40a6d51e652ada7f1f0147701f 100644
--- a/src/libopenGLHelper/printBitmap.cpp
+++ b/libraries/openGLHelper/printBitmap.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -27,8 +31,9 @@
  *************************************************************************/
 
 #include "printBitmap.h"
+#include "openGL-headers.h"
 
-void print_bitmap_string(float x, float y, float z, char * s)
+void print_bitmap_string(float x, float y, float z, const char * s)
 {
   glRasterPos3f(x,y,z);
   if (s && strlen(s)) 
diff --git a/src/libopenGLHelper/printBitmap.h b/libraries/openGLHelper/printBitmap.h
similarity index 75%
rename from src/libopenGLHelper/printBitmap.h
rename to libraries/openGLHelper/printBitmap.h
index dcc1126623fe424c5f6ac08ba0edfeb148921704..d85909ce6fc67302587c90fd1cc8002319d4441d 100644
--- a/src/libopenGLHelper/printBitmap.h
+++ b/libraries/openGLHelper/printBitmap.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,13 +40,7 @@
 #include <string.h>
 #include <float.h>
 
-#ifdef WIN32
-  #include "windows.h"
-#endif
-
-#include "openGL-headers.h"
-
-void print_bitmap_string(float x, float y, float z, char * s);
+void print_bitmap_string(float x, float y, float z, const char * s);
 void print_bitmap_integer(float x, float y, float z, int i);
 
 #endif
diff --git a/src/libopenGLHelper/saveScreenShot.cpp b/libraries/openGLHelper/saveScreenShot.cpp
similarity index 80%
rename from src/libopenGLHelper/saveScreenShot.cpp
rename to libraries/openGLHelper/saveScreenShot.cpp
index 2af95c04b96f0672e8a89df95e0b7d6484fb2631..e9911fa31bb6170c8649211bc596091270271552 100644
--- a/src/libopenGLHelper/saveScreenShot.cpp
+++ b/libraries/openGLHelper/saveScreenShot.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -34,7 +38,7 @@
 
 /* Write a screenshot to the specified filename */
 
-void Screenshot::SaveScreenshot(char * filename, ImageIO::fileFormatType fileFormat, int windowWidth, int windowHeight)
+void Screenshot::SaveScreenshot(const char * filename, ImageIO::fileFormatType fileFormat, int windowWidth, int windowHeight)
 {
   if (filename == NULL)
     return;
@@ -43,8 +47,8 @@ void Screenshot::SaveScreenshot(char * filename, ImageIO::fileFormatType fileFor
 
   unsigned char * pixels = (unsigned char*) malloc (sizeof(unsigned char) * 3 * windowWidth * windowHeight);
 
-  for (int i=windowHeight-1; i>=0; i--) 
-    glReadPixels(0, windowHeight-1-i, windowWidth, 1, GL_RGB, GL_UNSIGNED_BYTE, &pixels[3 * windowWidth * i]);
+  for (int i=0; i<windowHeight; i++)
+    glReadPixels(0, i, windowWidth, 1, GL_RGB, GL_UNSIGNED_BYTE, &pixels[3 * windowWidth * i]);
 
   ImageIO imageIO(windowWidth, windowHeight, 3, pixels);
   ImageIO::errorType errorCode = imageIO.save(filename, fileFormat);
@@ -57,7 +61,7 @@ void Screenshot::SaveScreenshot(char * filename, ImageIO::fileFormatType fileFor
   free(pixels);
 }
 
-void Screenshot::SaveStencilBuffer(char *filename, ImageIO::fileFormatType fileFormat, int windowWidth, int windowHeight, int rescale)
+void Screenshot::SaveStencilBuffer(const char *filename, ImageIO::fileFormatType fileFormat, int windowWidth, int windowHeight, int rescale)
 {
   if (filename == NULL)
     return;
diff --git a/libraries/openGLHelper/saveScreenShot.h b/libraries/openGLHelper/saveScreenShot.h
new file mode 100644
index 0000000000000000000000000000000000000000..e954d9c390ef2b4ad7b563c62c62fdde367ae445
--- /dev/null
+++ b/libraries/openGLHelper/saveScreenShot.h
@@ -0,0 +1,48 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _SAVESCREENSHOT_H_
+#define _SAVESCREENSHOT_H_
+
+#include "imageIO.h"
+
+/* Write a screenshot to the specified filename */
+
+class Screenshot
+{
+public:
+  static void SaveScreenshot(const char * filename, ImageIO::fileFormatType fileFormat, int windowWidth, int windowHeight);
+  static void SaveStencilBuffer(const char * filename, ImageIO::fileFormatType fileFormat, int windowWidth, int windowHeight, int rescale=0);
+};
+
+#endif
+
diff --git a/libraries/performanceCounter/frameRateLock.h b/libraries/performanceCounter/frameRateLock.h
new file mode 100644
index 0000000000000000000000000000000000000000..f407262b1b3b97d7098904527ef99dddfc231a00
--- /dev/null
+++ b/libraries/performanceCounter/frameRateLock.h
@@ -0,0 +1,66 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "performanceCounter" library , Copyright (C) 2018 USC                 *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "performanceCounter.h"
+#include <cassert>
+
+// used to lock frame-rate
+class FrameRateLock
+{
+public:
+  FrameRateLock(double targetFrameRate = 30.0) : frameRate(targetFrameRate), invFrameRate(1.0 / frameRate) { assert(frameRate > 0.0); }
+  virtual ~FrameRateLock() {}
+
+  // start internal counter
+  void start() { counter.StartCounter(); }
+
+  // called in openGL idle function, pause the code until target frame rate is reached
+  inline void lock();
+
+protected:
+  double frameRate, invFrameRate;
+  PerformanceCounter counter;
+};
+
+
+inline void FrameRateLock::lock()
+{
+  double elapsedTime = 0.0;
+  do
+  {
+    counter.StopCounter();
+    elapsedTime = counter.GetElapsedTime();
+  }
+  while (elapsedTime <= invFrameRate);
+
+  counter.StartCounter();
+}
diff --git a/libraries/performanceCounter/performanceCounter.h b/libraries/performanceCounter/performanceCounter.h
new file mode 100644
index 0000000000000000000000000000000000000000..a64d8f0f9b710b0d2ff94e6808766fbbb40dec21
--- /dev/null
+++ b/libraries/performanceCounter/performanceCounter.h
@@ -0,0 +1,159 @@
+/*
+
+* Copyright (c) 2008, Carnegie Mellon University
+* All rights reserved.
+*
+* Code author: Jernej Barbic
+* Research: Jernej Barbic, Doug L. James
+* Funding: NSF, Link Foundation
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of Carnegie Mellon University, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+=== A counter to measure code execution time. ===
+
+Designed for real-time system (e.g., real-time computer animation, haptics), 
+but useful in general. You can time arbitrary segments of your code.
+Same interface under Windows, Linux and Mac OS X.
+
+Under Linux/MAC OS X, accuracy is that of gettimeofday function,
+which gives time in seconds and microseconds.
+In practice, it has been accurate down to microsecond range.
+
+Under Windows, the counter uses the QueryPerformanceCounter Windows API call.
+Again, accuracy has been in the microsecond range in practice.
+
+Usage:
+Call StartCounter() before your code block.
+Call StopCounter() after your code block.
+Read the elapsed time using GetElapsedTime().
+
+Version: 1.0
+
+*/
+
+#ifndef _PERFORMANCECOUNTER_H_
+#define _PERFORMANCECOUNTER_H_
+
+/**************** LINUX/MAC OS X COUNTER *******************/
+
+#if (defined __unix__) || (defined __APPLE__)
+
+#include "stdlib.h"
+#include "sys/time.h"
+
+class PerformanceCounter
+{
+public:
+
+  PerformanceCounter() { StartCounter(); }
+
+  void StartCounter(); // call this before your code block
+  void StopCounter(); // call this after your code block
+
+  // read the elapsed time (units are seconds, accuracy is up to microseconds)
+  double GetElapsedTime();
+
+protected:
+  long startCountSec,stopCountSec,startCountMicroSec,stopCountMicroSec;
+};
+
+inline void PerformanceCounter::StartCounter()
+{
+  struct timeval tv;
+
+  gettimeofday(&tv,NULL);
+
+  startCountSec = tv.tv_sec;
+  startCountMicroSec = tv.tv_usec;
+  stopCountSec = startCountSec;
+  stopCountMicroSec = startCountMicroSec;
+}
+
+inline void PerformanceCounter::StopCounter()
+{
+  struct timeval tv;
+
+  gettimeofday(&tv,NULL);
+
+  stopCountSec = tv.tv_sec;
+  stopCountMicroSec = tv.tv_usec;
+}
+
+
+inline double PerformanceCounter::GetElapsedTime()
+{
+  double elapsedTime = 1.0 * (stopCountSec-startCountSec) + 1E-6 * (stopCountMicroSec - startCountMicroSec);
+  return elapsedTime;
+}
+
+#endif
+
+#if defined(_WIN32) || defined(WIN32)
+
+/**************** WINDOWS COUNTER *******************/
+
+#include <windows.h>
+
+class PerformanceCounter
+{
+public:
+
+  PerformanceCounter() 
+  {
+    // reset the counter frequency
+    QueryPerformanceFrequency(&timerFrequency);
+    StartCounter(); 
+  }
+
+  void StartCounter(); // call this before your code block
+  void StopCounter(); // call this after your code block
+
+  // read elapsed time (units are seconds, accuracy is up to microseconds)
+  double GetElapsedTime();
+
+protected:
+  LARGE_INTEGER timerFrequency;
+  LARGE_INTEGER startCount,stopCount;
+};
+
+inline void PerformanceCounter::StartCounter()
+{
+  QueryPerformanceCounter(&startCount);
+}
+
+inline void PerformanceCounter::StopCounter()
+{
+  QueryPerformanceCounter(&stopCount);
+}
+
+
+inline double PerformanceCounter::GetElapsedTime()
+{
+  return ((double)(stopCount.QuadPart - startCount.QuadPart))
+    / ((double)timerFrequency.QuadPart);
+}
+
+#endif
+#endif
+
diff --git a/libraries/performanceCounter/profiler.cpp b/libraries/performanceCounter/profiler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b6a30edd74ffdc68eed2bad51a93948d7e794c7
--- /dev/null
+++ b/libraries/performanceCounter/profiler.cpp
@@ -0,0 +1,294 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "performanceCounter" library , Copyright (C) 2018 USC                 *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "profiler.h"
+#include "matrixIO.h"
+#include <iomanip>
+#include <sstream>
+#include <cassert>
+#include <cstring>
+#include <algorithm>
+using namespace std;
+
+
+Profiler::Profiler()
+{
+  lastTimer = timer.end();
+}
+
+void Profiler::startTimer(const std::string & name)
+{
+  timer[name].start();
+  lastTimer = timer.find(name);
+}
+
+void Profiler::stopTimer(const std::string & name)
+{
+  if (timer.find(name) != timer.end())
+    timer[name].stop();
+}
+
+void Profiler::stopLastTimer()
+{
+  if (lastTimer != timer.end())
+    lastTimer->second.stop();
+}
+
+void Profiler::startExtraTimer(const std::string & name)
+{
+  extraTimer[name].start();
+}
+
+void Profiler::stopExtraTimer(const std::string & name)
+{
+  if (extraTimer.find(name) != extraTimer.end())
+    extraTimer[name].stop();
+}
+
+double Profiler::getTime(const std::string & name)
+{
+  if (timer.find(name) != timer.end())
+    return timer[name].getElapsedTime();
+  else
+    return 0.0;
+}
+
+std::string Profiler::toString()
+{
+  const string title = " PROFILING ";
+  const size_t titleLineLen = 30; // number of "=" in one side of ======= PROFILING =======
+  const size_t maxSize = titleLineLen * 2 + title.size();
+
+  map<string, double> times, extraTimes; // first record the timing on each timer
+  double overallTime = 0.0;
+  for(auto & p : timer)
+  {
+    double t = p.second.getElapsedTime();
+    times[p.first] = t;
+    overallTime += t;
+  }
+  for(auto & p: extraTimer) {
+    extraTimes[p.first] = p.second.getElapsedTime();
+  }
+
+  ostringstream os;
+  // print ======= PROFILING =======
+  for(size_t i = 0; i < titleLineLen; i++)
+    os << '=';
+  os << title;
+  for(size_t i = 0; i < titleLineLen; i++)
+    os << '=';
+  os << endl;
+
+  // print each timer
+  for(map<string, double>::iterator it = times.begin(); it != times.end(); it++)
+  {
+    ostringstream left, right;
+    // left                                 right
+    // <SECTION A>: 10.00 s                 5.00%
+    left << it->first << ": " << fixed << setprecision(2) << it->second << " s";
+    double per = it->second * 100 / overallTime;
+    right << fixed << setprecision(2) << per << "%";
+    string leftStr = left.str(), rightStr = right.str();
+
+    int numSpace = 0; // number of space character to connect left and right parts
+    if (leftStr.size() + rightStr.size() < maxSize)
+      numSpace = maxSize - leftStr.size() - rightStr.size();
+
+    os << leftStr;
+    for(int i = 0; i < numSpace; i++)
+      os << ' ';
+    os << rightStr << endl;
+  }
+  if (extraTimer.size() > 0) {
+    os << "extra ---\n";
+    for(auto & p : extraTimes) {
+      os << "  " << p.first << ": " << fixed << setprecision(2) << p.second << " s\n";
+    }
+  }
+  os << "Overall: " << overallTime << " s" << endl;
+
+  // print the ending =========================
+  for(size_t i = 0; i < maxSize; i++) os << '=';
+  os << endl;
+  return os.str();
+}
+
+void Profiler::clear()
+{
+  timer.clear();
+  lastTimer = timer.end();
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////
+
+IterationProfiler::IterationProfiler(int size, bool keep)
+{
+  assert(size > 0);
+  bufferSize = size;
+  numIterRecorded = 0;
+  keepHistory = keep;
+  numIterInHistory = 0;
+}
+
+void IterationProfiler::finishOneIteration()
+{
+  // add each stopWatch's time into averagingBuffer
+  // notice that we need to ensure that each stopWatch has a one-to-one relationship with the averagingBuffers
+  // because currently Profiler and IterationProfiler has no function to remove individual timers, 
+  //   we are sure that each averagingBuffer corresponds to one stopWatch
+  // but each stopWatch may not corresponds to an averagingBuffer
+  for(map<string, StopWatch>::iterator it = timer.begin(); it != timer.end(); it++)
+  {
+    double t = it->second.getElapsedTime();
+    it->second.reset();
+    map<string, ProfilingData>::iterator foundBuffer = profilingData.find(it->first);
+    if (foundBuffer == profilingData.end())
+    {
+      // because it's a new stopWatch, we create a new struct of ProfilingData.
+      foundBuffer = profilingData.insert(pair<string, ProfilingData>(it->first, ProfilingData(bufferSize))).first;
+      // since this profilingData is new and stores nothing on previous iterations, we should fill in previous timing of 0.0
+      for(int i = 0; i < numIterRecorded; i++)
+        foundBuffer->second.avgBuffer.addValue(0.0); // we only fill in numIterRecorded times
+      if (keepHistory)
+        foundBuffer->second.history.resize(numIterInHistory, 0.0); // if we keep history, we put all previous timing of 0.0 into history buffer
+    }
+    ProfilingData & data = foundBuffer->second;
+    data.avgBuffer.addValue(t);
+    if (data.maxTime < t)
+      data.maxTime = t;
+    if (keepHistory) // if we keep history, we add the new time
+      data.history.push_back(t);
+  }
+  if (numIterRecorded < bufferSize)
+    numIterRecorded++;
+
+  if (keepHistory)
+    numIterInHistory++;
+}
+
+void IterationProfiler::setAverageBufferSize(int newSize)
+{
+  assert(newSize > 0);
+  if (bufferSize == newSize)
+    return;
+
+  bufferSize = newSize;
+  for(map<string, ProfilingData>::iterator it = profilingData.begin(); it != profilingData.end(); it++)
+    it->second.avgBuffer.setBufferSize(bufferSize);
+  numIterRecorded = min(numIterRecorded, bufferSize);
+}
+
+void IterationProfiler::clear()
+{
+  profilingData.clear();
+  numIterRecorded = 0;
+  numIterInHistory = 0;
+  Profiler::clear();
+}
+
+std::string IterationProfiler::toString()
+{
+  const string title = " PROFILING ";
+  const size_t titleLineLen = 30;
+  const size_t maxSize = titleLineLen * 2 + title.size();
+
+  map<string, double> times; // first record the timing on each timer
+  double overallTime = 0.0;
+  for(map<string, ProfilingData>::iterator it = profilingData.begin(); it != profilingData.end(); it++)
+  {
+    double t = it->second.avgBuffer.getAverage() * 1000.0;  // unit to ms
+    if (t < 0.0) t = 0.0;
+    times[it->first] = t;
+    overallTime += t;
+  }
+
+
+  ostringstream os;
+
+  for(size_t i = 0; i < titleLineLen; i++)
+    os << '=';
+  os << title;
+  for(size_t i = 0; i < titleLineLen; i++)
+    os << '=';
+  os << endl;
+
+
+  
+  for(map<string, double>::iterator it = times.begin(); it != times.end(); it++)
+  {
+    ostringstream left, right;
+    double av = it->second;
+    left << it->first << ": " << av << " ms, max: " << (profilingData.find(it->first)->second.maxTime*1000.0) << " ms";
+    double per = av * 100 / overallTime;
+    right << fixed << setprecision(2) << per << "%";
+    string leftStr = left.str(), rightStr = right.str();
+
+    int numSpace = 0;
+    if (leftStr.size() + rightStr.size() < maxSize)
+      numSpace = maxSize - leftStr.size() - rightStr.size();
+    os << leftStr;
+    for(int i = 0; i < numSpace; i++)
+      os << ' ';
+    os << rightStr << endl;
+  }
+  os << "Overall: " << overallTime << " ms" << endl;
+
+  for(size_t i = 0; i < maxSize; i++)
+    os << '=';
+  os << endl;
+  return os.str();
+}
+
+bool IterationProfiler::saveHistory(const std::string & filename) const
+{
+  if (keepHistory == false)
+    return true;
+
+  map<string, ProfilingData>::const_iterator it = profilingData.begin();
+  if (it == profilingData.end())
+    return true;
+  int m = it->second.history.size();
+  int n = profilingData.size();
+
+  vector<double> matrix(m*n);
+  int index = 0;
+  for(map<string, ProfilingData>::const_iterator it = profilingData.begin(); it != profilingData.end(); it++)
+  {
+    memcpy(&matrix[m*index], it->second.history.data(), sizeof(double) * m);
+    index++;
+  }
+  int ret = WriteMatrixToDisk(filename.c_str(), m, n, &matrix[0]);
+  return ret == 0;
+}
diff --git a/libraries/performanceCounter/profiler.h b/libraries/performanceCounter/profiler.h
new file mode 100644
index 0000000000000000000000000000000000000000..850a8aa7666b3d264648ba0af41a638d55ddf037
--- /dev/null
+++ b/libraries/performanceCounter/profiler.h
@@ -0,0 +1,160 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "performanceCounter" library , Copyright (C) 2018 USC                 *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef PROFILER_H
+#define PROFILER_H
+
+#include "stopWatch.h"
+#include "averagingBuffer.h"
+#include <map>
+#include <string>
+
+// a profiling tool that can record timings in multiple parts of the code
+class Profiler
+{
+public:
+  Profiler();
+  virtual ~Profiler() {}
+
+  // start the timer on one section
+  // it does nothing if the timer has already started
+  void startTimer(const std::string & sectionName);
+
+  // stop the timer on one section
+  // it does nothing if there is no such section or the timer has already stopped
+  // when you stop the timer and start it again, new time is added onto the previous time measured on the last stopTimer call()
+  void stopTimer(const std::string & sectionName);
+  // stop the timer that started last
+  void stopLastTimer();
+
+  // return the elapsed time on one section
+  // return 0.0 if that section does not exist
+  double getTime(const std::string & sectionName);
+
+  void startExtraTimer(const std::string & sectionName);
+  void stopExtraTimer(const std::string & sectionName);
+
+  // clear all stored timers
+  void clear();
+
+  // return a string that writes the timing
+  // the format is:
+  // =============== PROFILING ================
+  // <SECTION A>: 10.00 s                 5.00%
+  // <SECTION B>: 20.00 s                10.00%
+  // <SECTION C>: 170.00 s               85.00%
+  // Overall: 200.00 s
+  // ==========================================
+  std::string toString();
+
+protected:
+  std::map<std::string, StopWatch> timer;
+  std::map<std::string, StopWatch>::iterator lastTimer;
+  std::map<std::string, StopWatch> extraTimer;
+};
+
+// a profiling tool that records timing on multiple sections in a iterative program
+class IterationProfiler : public Profiler
+{
+public:
+  IterationProfiler(int averageBufferSize = 20, bool keepHistory = false);
+  virtual ~IterationProfiler() {}
+
+  // tell the profiler that one iteration is finished
+  // calling this function will reset all timers and their time is stored in 
+  // corresponding averaging buffer and history buffer (if keepHistory is on) and maxTime is updated
+  virtual void finishOneIteration();
+  // set the number of recent iterations used to calculate average timing; default is 20
+  void setAverageBufferSize(int bufferSize);
+
+  // clear all stored data
+  void clear();
+
+  // return a string that writes the timing
+  // the format is:
+  // =============== PROFILING ================
+  // <SECTION A>: 10.00 ms                 5.00%
+  // <SECTION B>: 20.00 ms                10.00%
+  // <SECTION C>: 170.00 ms               85.00%
+  // Overall: 200.00 ms
+  // ==========================================
+  std::string toString();
+
+  bool saveHistory(const std::string & filename) const;
+
+protected:
+  int bufferSize;
+  struct ProfilingData
+  {
+    ProfilingData(int bufferSize) : avgBuffer(bufferSize), maxTime(0) {}
+    AveragingBuffer avgBuffer;
+    double maxTime;
+    std::vector<double> history;
+  };
+  std::map<std::string, ProfilingData> profilingData;
+
+  bool keepHistory;
+  int numIterInHistory;
+  int numIterRecorded;
+};
+
+// helper profiler measure timing on a section
+// by using destructor function, it stops the timer whenever it goes out of the code block
+
+class ProfilerSection
+{
+public:
+  ProfilerSection(Profiler * profiler, const std::string & sectionName) : p(profiler) { if (p) { name = sectionName; } }
+  virtual ~ProfilerSection() { stop(); }
+
+  void start() { if (p) p->startTimer(name); }
+  void stop() { if (p) p->stopTimer(name); }
+
+protected:
+  Profiler * p;
+  std::string name;
+};
+
+class ProfilerExtraSection
+{
+public:
+  ProfilerExtraSection(Profiler * profiler, const std::string & sectionName) : p(profiler) { if (p) { name = sectionName; } }
+  virtual ~ProfilerExtraSection() { stop(); }
+
+  void start() { if (p) p->startExtraTimer(name); }
+  void stop() { if (p) p->stopExtraTimer(name); }
+
+protected:
+  Profiler * p;
+  std::string name;
+};
+#endif
diff --git a/libraries/performanceCounter/stopWatch.h b/libraries/performanceCounter/stopWatch.h
new file mode 100644
index 0000000000000000000000000000000000000000..5574bcd3e43678a88002b0459f45057841e87f94
--- /dev/null
+++ b/libraries/performanceCounter/stopWatch.h
@@ -0,0 +1,202 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "performanceCounter" library , Copyright (C) 2018 USC                 *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef STOPWATCH_H
+#define STOPWATCH_H
+
+/*
+  === A stopwatch to measure code execution time. ===
+  Modified from performanceCounter.h
+
+  Designed for real-time system (e.g., real-time computer animation, haptics),
+  but useful in general. You can time arbitrary segments of your code.
+  Same interface under Windows, Linux and Mac OS X.
+
+  Under Linux/MAC OS X, accuracy is that of gettimeofday function,
+  which gives time in seconds and microseconds.
+  In practice, it has been accurate down to microsecond range.
+
+  Under Windows, the counter uses the QueryPerformanceCounter Windows API call.
+  Again, accuracy has been in the microsecond range in practice.
+*/
+
+#if defined(_WIN32) || defined(WIN32)
+#include <windows.h>
+#include <cstdlib>
+#include <cstdio>
+#else
+#include <cstdlib>
+#include <cstdio>
+#include "sys/time.h"
+#endif
+
+class StopWatch
+{
+public:
+  // initialize a stopped watch 
+  inline StopWatch();
+
+  // start counting
+  // have no effect if the watch has already started
+  inline void start();
+
+  // stop counting and store the elapsed time
+  // can be resumed by calling start() again
+  // have no effect if the watch has already stopped
+  inline void stop();
+
+  // stop the watch and reset internal timer
+  inline void reset();
+
+  // If the watch has stopped, return the stored time
+  // otherwise, return the stored time + the time from the recent start() to this getElapsedTime() call
+  inline double getElapsedTime();
+
+  inline bool hasStarted() const { return stopped == 0; }
+  inline bool hasStopped() const { return stopped; }
+
+protected:
+  inline double getTime();
+
+#if defined(_WIN32) || defined(WIN32)
+  LARGE_INTEGER timerFrequency;
+  LARGE_INTEGER startCount,stopCount;
+#else
+  long startCountSec,stopCountSec,startCountMicroSec,stopCountMicroSec;
+#endif
+  double savedTime;
+  int stopped;
+};
+
+// helper class for measuring the time of a scope
+class FunctionTimer
+{
+public:
+  FunctionTimer(StopWatch & w) : watch(&w) { watch->start(); }
+  ~FunctionTimer() { watch->stop(); }
+protected:
+  StopWatch * watch;
+};
+
+// helper class to print time of a function
+class ScopeTimeReporter
+{
+public:
+  ScopeTimeReporter(const char * scopeName) : scopeName(scopeName) { watch.start(); }
+  ~ScopeTimeReporter() { watch.stop(); printf("%s: %lfs\n", scopeName, watch.getElapsedTime());}
+protected:
+  const char * scopeName;
+  StopWatch watch;
+};
+
+#define REPORT_FUNCTION_TIME ScopeTimeReporter __function_timer(__func__)
+
+
+//////////////////////////////////////////////////////////
+//                   IMPLEMENTATION
+//////////////////////////////////////////////////////////
+
+inline StopWatch::StopWatch()
+{
+#if defined(_WIN32) || defined(WIN32)
+  QueryPerformanceFrequency(&timerFrequency);
+  startCount.QuadPart = stopCount.QuadPart = 0;
+#else
+  startCountSec = stopCountSec = startCountMicroSec = stopCountMicroSec = 0.0;
+#endif
+  savedTime = 0.0;
+  stopped = 1;
+}
+
+inline void StopWatch::start()
+{
+  if (stopped == 0)
+    return;
+  stopped = 0;
+
+#if defined(_WIN32) || defined(WIN32)
+  LARGE_INTEGER count;
+  QueryPerformanceCounter(&count);
+#else
+  struct timeval tv;
+  gettimeofday(&tv,NULL);
+#endif
+#if defined(_WIN32) || defined(WIN32)
+  startCount = count;
+#else
+  startCountSec = tv.tv_sec;
+  startCountMicroSec = tv.tv_usec;
+#endif
+}
+
+inline double StopWatch::getElapsedTime() 
+{
+  if (stopped)
+    return savedTime;
+
+  double currentTime = getTime();
+  return savedTime + currentTime;
+}
+
+inline void StopWatch::stop() 
+{
+  double currentTime = getTime();
+  if (stopped)
+    return;
+
+  stopped = 1;
+  savedTime += currentTime;
+}
+
+inline void StopWatch::reset()
+{
+  savedTime = 0.0;
+  stopped = 1;
+}
+
+inline double StopWatch::getTime() 
+{
+#if defined(_WIN32) || defined(WIN32)
+  QueryPerformanceCounter(&stopCount);
+  return ((double)(stopCount.QuadPart - startCount.QuadPart))
+        / ((double)timerFrequency.QuadPart);
+#else
+  struct timeval tv;
+  gettimeofday(&tv,NULL);
+  stopCountSec = tv.tv_sec;
+  stopCountMicroSec = tv.tv_usec;
+  return 1.0 * (stopCountSec-startCountSec) + 1E-6 * (stopCountMicroSec - startCountMicroSec);
+#endif
+}
+
+
+#endif /* STOPWATCH_H */
diff --git a/src/libpolarDecomposition/polarDecomposition.cpp b/libraries/polarDecomposition/polarDecomposition.cpp
similarity index 73%
rename from src/libpolarDecomposition/polarDecomposition.cpp
rename to libraries/polarDecomposition/polarDecomposition.cpp
index 1a3a791ef76fb06469ebb73f713e601ce5973de3..4f7045ee34e07b4ead0e058bd4670141df42c907 100644
--- a/src/libpolarDecomposition/polarDecomposition.cpp
+++ b/libraries/polarDecomposition/polarDecomposition.cpp
@@ -1,9 +1,8 @@
 #include <stdio.h>
 #include <math.h>
 #include "polarDecomposition.h"
+#include "mat3d.h"
 
-namespace vega
-{
 /*
   See polarDecomposition.h for license information.
 */
@@ -36,11 +35,12 @@ double PolarDecomposition::infNorm(const double * A)
 
 // Input: M (3x3 mtx)
 // Output: Q (3x3 rotation mtx), S (3x3 symmetric mtx)
-double PolarDecomposition::Compute(const double * M, double * Q, double * S, double tolerance)
+double PolarDecomposition::Compute(const double * M, double * Q, double * S, double tolerance, int forceRotation)
 {
   double Mk[9];
   double Ek[9];
   double det, M_oneNorm, M_infNorm, E_oneNorm;
+  int useSVD = 0;
 
   // Mk = M^T
   for(int i=0; i<3; i++)
@@ -62,6 +62,13 @@ double PolarDecomposition::Compute(const double * M, double * Q, double * S, dou
     crossProduct(&(Mk[0]), &(Mk[3]), &(MadjTk[6]));
 
     det = Mk[0] * MadjTk[0] + Mk[1] * MadjTk[1] + Mk[2] * MadjTk[2];
+
+    if ((det <= 1e-6) && forceRotation)
+    {
+      useSVD = 1;
+      break;
+    }
+
     if (det == 0.0) 
     {
       printf("Warning (polarDecomposition) : zero determinant encountered.\n");
@@ -71,7 +78,7 @@ double PolarDecomposition::Compute(const double * M, double * Q, double * S, dou
     double MadjT_one = oneNorm(MadjTk); 
     double MadjT_inf = infNorm(MadjTk);
 
-    double gamma = sqrt(sqrt((MadjT_one * MadjT_inf) / (M_oneNorm * M_infNorm)) / fabs(det));
+    double gamma = sqrt(sqrt((MadjT_one * MadjT_inf) / (M_oneNorm * M_infNorm * det * det)));
     double g1 = gamma * 0.5;
     double g2 = 0.5 / (gamma * det);
 
@@ -88,10 +95,27 @@ double PolarDecomposition::Compute(const double * M, double * Q, double * S, dou
   }
   while ( E_oneNorm > M_oneNorm * tolerance );
 
-  // Q = Mk^T 
-  for(int i=0; i<3; i++)
-    for(int j=0; j<3; j++)
-      Q[3*i+j] = Mk[3*j+i];
+  if (useSVD)
+  {
+    // use the SVD algorithm to compute Q
+    Mat3d Mm(M);
+    double modifiedSVD_singularValue_eps = tolerance;
+    Mat3d Um, Vm;
+    Vec3d Lambda;
+    int modifiedSVD = 1;
+    int code = SVD(Mm, Um, Lambda, Vm, modifiedSVD_singularValue_eps, modifiedSVD);
+    code = code + 1; // just to ignore compiler warning
+
+    Mat3d Qm = Um * trans(Vm);
+    Qm.convertToArray(Q);
+  }
+  else
+  {
+    // Q = Mk^T 
+    for(int i=0; i<3; i++)
+      for(int j=0; j<3; j++)
+        Q[3*i+j] = Mk[3*j+i];
+  }
 
   for(int i=0; i<3; i++)
     for(int j=0; j<3; j++)
@@ -102,11 +126,10 @@ double PolarDecomposition::Compute(const double * M, double * Q, double * S, dou
     }
     
   // S must be symmetric; enforce the symmetry
-  for (int i=0; i<3; i++) 
-    for (int j=i; j<3; j++)
-      S[3 * i + j] = S[3 * j + i] = 0.5 * (S[3 * i + j] + S[3 * j + i]);
+  S[1] = S[3] = 0.5 * (S[1] + S[3]);
+  S[2] = S[6] = 0.5 * (S[2] + S[6]);
+  S[5] = S[7] = 0.5 * (S[5] + S[7]);
 
-  return (det);
+  return det;
 }
 
-}
diff --git a/libraries/polarDecomposition/polarDecomposition.h b/libraries/polarDecomposition/polarDecomposition.h
new file mode 100644
index 0000000000000000000000000000000000000000..ac70bdad57acc475561aca526974be3eb0eca9d0
--- /dev/null
+++ b/libraries/polarDecomposition/polarDecomposition.h
@@ -0,0 +1,58 @@
+#ifndef _POLARDECOMPOSITION_H_
+#define _POLARDECOMPOSITION_H_
+
+/*
+  Polar decomposition of a general 3x3 matrix
+
+  This code uses the polar decomposition implementation provided as a companion to the book "Graphics Gems IV": 
+  Decompose.c 
+  Ken Shoemake, 1993 
+  Polar Decomposition of 3x3 matrix in 4x4, M = QS.  
+  The Graphics Gems IV implementation is available at: 
+  http://tog.acm.org/GraphicsGems/
+
+  The above website states that "All code here (on the GraphicsGems website) can be used without restrictions". It also lists the following EULA:
+  "EULA: The Graphics Gems code is copyright-protected. In other words, you cannot claim the text of the code as your own and resell it. Using the code is permitted in any program, product, or library, non-commercial or commercial. Giving credit is not required, though is a nice gesture. The code comes as-is, and if there are any flaws or problems with any Gems code, nobody involved with Gems - authors, editors, publishers, or webmasters - are to be held responsible. Basically, don't be a jerk, and remember that anything free comes with no guarantee."
+
+  Jernej Barbic made some adaptions to the polar decomposition code (wrap into a C++ class, some change in input/output format, etc.). 
+  He releases his adaptions of the polar decomposition code into the public domain, free of charge. The above EULA still applies, of course.
+*/
+
+class PolarDecomposition
+{
+public:
+
+  // Computes the Polar Decomposition of a general 3x3 matrix M.
+  // M = Q * S
+  // M is 3x3 input matrix
+  // Q is 3x3 orthogonal output matrix, Q Q^T = Q^T Q = I 
+  // S is 3x3 symmetric positive-definite output matrix
+  // Note: det(Q)=sgn(det(M)); this sign can be 1 or -1, depending on M
+  // if forceRotation is 1, the matrix Q will be a rotation, S will be symmetric, but not necessarily positive-definite
+  // M is not modified
+  // All matrices are row-major
+  static double Compute(const double * M, double * Q, double * S, double tolerance=1E-6, int forceRotation=0);
+
+protected:
+
+  // one-norm of a 3 x 3 matrix
+  static double oneNorm(const double * A);
+
+  // infinity-norm of a 3 x 3 matrix
+  static double infNorm(const double * A);
+
+  // a, b, c are 3-vectors
+  // compute cross product c = a x b
+  inline static void crossProduct(const double * a, const double * b, double * c);
+};
+
+// cross product: c = a x b
+inline void PolarDecomposition::crossProduct(const double * a, const double * b, double * c)
+{
+  c[0] = a[1] * b[2] - a[2] * b[1];
+  c[1] = a[2] * b[0] - a[0] * b[2];
+  c[2] = a[0] * b[1] - a[1] * b[0];
+}
+
+#endif
+
diff --git a/src/libpolarDecomposition/polarDecompositionGradient.cpp b/libraries/polarDecomposition/polarDecompositionGradient.cpp
similarity index 99%
rename from src/libpolarDecomposition/polarDecompositionGradient.cpp
rename to libraries/polarDecomposition/polarDecompositionGradient.cpp
index 1e1367b1881362463ae97d4efd6ed05f7a052bb4..7930042af29830d16239d81810f88c9799879325 100644
--- a/src/libpolarDecomposition/polarDecompositionGradient.cpp
+++ b/libraries/polarDecomposition/polarDecompositionGradient.cpp
@@ -53,8 +53,6 @@
 #include "matrixMultiplyMacros.h"
 #include "mat3d.h"
 
-namespace vega
-{
 void PolarDecompositionGradient::Compute(const double * M, const double * Q, const double * S, const double * MDot, double * omega, double * QDot, double * SDot, const double * MDotDot, double * omegaDot, double * QDotDot)
 {
   // compute omega = G^{-1} (2 * skew(Q^T * MDot)), where G = (tr(S)I - S) * Q^T
@@ -148,4 +146,3 @@ void PolarDecompositionGradient::Compute(const double * M, const double * Q, con
   }
 }
 
-}
diff --git a/libraries/polarDecomposition/polarDecompositionGradient.h b/libraries/polarDecomposition/polarDecompositionGradient.h
new file mode 100644
index 0000000000000000000000000000000000000000..f61a569c9e707df8fd0a849973f6e709158c7895
--- /dev/null
+++ b/libraries/polarDecomposition/polarDecompositionGradient.h
@@ -0,0 +1,90 @@
+/*
+
+* Copyright (c) 2011, Jernej Barbic, Yili Zhao, University of Southern California
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright
+*       notice, this list of conditions and the following disclaimer in the
+*       documentation and/or other materials provided with the distribution.
+*     * Neither the name of University of Southern California, nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY JERNEJ BARBIC, YILI ZHAO AND UNIVERSITY OF SOUTHERN CALIFORNIA 
+* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+* IN NO EVENT SHALL JERNEJ BARBIC, YILI ZHAO OR UNIVERSITY OF SOUTHERN CALIFORNIA BE 
+* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+/*
+  Gradient of polar decomposition of a general 3x3 matrix
+  Version 2.0
+
+  This code was used in the following publication:
+
+  Jernej Barbic, Yili Zhao:
+  Real-time Large-deformation Substructuring, ACM Transactions on Graphics (SIGGRAPH 2011), 30(4), Aug 2011
+
+  @article{Barbic:2011:RLS,
+    author =  {Jernej Barbi\v{c} and Yili Zhao},
+    journal = {ACM Trans. on Graphics (SIGGRAPH 2011)},
+    number =  "4",
+    title =   "Real-time Large-deformation Substructuring",
+    volume =  "30",
+    year =    "2011",
+    pages =   "91:1--91:7",
+  }
+
+  Authors of this code: Jernej Barbic, Yili Zhao
+*/
+
+#ifndef _POLARDECOMPOSITIONGRADIENT_H_
+#define _POLARDECOMPOSITIONGRADIENT_H_
+
+#include <stdlib.h>
+
+class PolarDecompositionGradient
+{
+public:
+  // computes the first and second *derivative* of the rotation matrix in polar decomposition
+  // also computes the first derivative of the symmetric matrix in polar decomposition
+  // for more details, please see the citation above
+
+  // Polar decomposition is:
+  // M = Q * S
+  // all matrices are 3x3, stored row major as a 9-vector of doubles
+
+  // Gradient computation instructions:
+  // inputs: M, Q, S, MDot, MDotDot (all 3x3 matrices)
+  // the routine assumes that the polar decomposition has been already performed (e.g., using polarDecomposition.h)
+  //   M: the input matrix (should be non-singular)
+  //   Q: the polar decomposition rotation (obtained, say, via polarDecomposition.h)
+  //   S: the polar decomposition symmetric matrix (obtained, say, via polarDecomposition.h)
+  //   MDot: the derivative of M
+  //   MDotDot: second derivative of M (only needed for omegaDot and QDotQDot, can be ommitted otherwise)
+  // outputs: omega, QDot, SDot, omegaDot, QDotDot (all 3x3 matrices, except omega and omegaDot which are 3-vectors)
+  //   omega: the rotational velocity of Q 
+  //   QDot: derivative of Q  
+  //   SDot: derivative of S
+  //   omegaDot: derivative of omega (only computed when MDotDot is provided, not referenced otherwise)   
+  //   QDotDot: second derivative of Q (only computed when MDotDot is provided and QDotDot is not NULL, not referenced otherwise)
+  // See also the included example driver.
+  static void Compute(const double * M, const double * Q, const double * S, const double * MDot, double * omega, double * QDot, double * SDot, const double * MDotDot=NULL, double * omegaDot=NULL, double * QDotDot = NULL);
+
+protected:
+
+};
+
+#endif
+
diff --git a/src/libpolarDecomposition/polarDecompositionGradientDriver.cpp b/libraries/polarDecomposition/polarDecompositionGradientDriver.cpp
similarity index 100%
rename from src/libpolarDecomposition/polarDecompositionGradientDriver.cpp
rename to libraries/polarDecomposition/polarDecompositionGradientDriver.cpp
diff --git a/src/libquaternion/example.cpp b/libraries/quaternion/example.cpp
similarity index 89%
rename from src/libquaternion/example.cpp
rename to libraries/quaternion/example.cpp
index f0b801cd22aa499cc81803a96cd0d1e2a0acbc4b..f1af69bdb7d19ea6360b1b1c691bf75780631343 100644
--- a/src/libquaternion/example.cpp
+++ b/libraries/quaternion/example.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "quaternion" library , Copyright (C) 2007 CMU                         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libquaternion/quaternion.cpp b/libraries/quaternion/quaternion.cpp
similarity index 84%
rename from src/libquaternion/quaternion.cpp
rename to libraries/quaternion/quaternion.cpp
index c59c9de3bd34a5675b64a682b17bcf5cb57f5bab..4948ddf1256f64058fcce4b5e7a36924ad76c19b 100644
--- a/src/libquaternion/quaternion.cpp
+++ b/libraries/quaternion/quaternion.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "quaternion" library , Copyright (C) 2007 CMU                         *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,8 +32,6 @@
 
 #include "quaternion.h"
 
-namespace vega
-{
 /*
   See David Baraff's SIGGRAPH course notes for the description of the method below:
   "An Introduction to Physically Based Modeling:
@@ -40,7 +42,7 @@ namespace vega
 //Transforms the given matrix (assumed orthogonal, specified 
 //in row-major order) into one of the two corresponding quaternions.
 template <typename real>
-Quaternion<real> Quaternion<real>::Matrix2Quaternion(real * R)
+Quaternion<real> Quaternion<real>::Matrix2Quaternion(const real R[9])
 {
 /* 
    Order of matrix elements is row-major:
@@ -107,7 +109,6 @@ Quaternion<real> Quaternion<real>::Matrix2Quaternion(real * R)
   return q;
 }
 
-template Quaternion<double> Quaternion<double>::Matrix2Quaternion(double * R);
-template Quaternion<float> Quaternion<float>::Matrix2Quaternion(float * R);
+template Quaternion<double> Quaternion<double>::Matrix2Quaternion(const double R[9]);
+template Quaternion<float> Quaternion<float>::Matrix2Quaternion(const float R[9]);
 
-}
diff --git a/libraries/quaternion/quaternion.h b/libraries/quaternion/quaternion.h
new file mode 100644
index 0000000000000000000000000000000000000000..3dcb4e5bbd44d415d464dfecea69d1fd9bf3bd6d
--- /dev/null
+++ b/libraries/quaternion/quaternion.h
@@ -0,0 +1,401 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "quaternion" library , Copyright (C) 2007 CMU                         *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _QUATERNION_H_
+#define _QUATERNION_H_
+
+/*
+  Quaternion C++ class.
+  This class implements quaternions and the commonly used 
+  algebraic operations on quaternions. The class is templated:
+  you can use either float or double precision.
+  Supports using quaternions to represent/manipulate rotations.
+
+  q = s + x * i + y * j + z * k
+
+  If using quaternions to represent rotations, q must be a unit quaternion. 
+    Then the following relates q to the corresponding rotation:
+      s = cos(angle/2)
+      (x,y,z) = sin(angle/2) * axis_of_rotation
+      (axis_of_rotation is unit length)
+
+  See also example.cpp .
+*/
+
+#include <stdio.h>
+#include <math.h>
+
+// forward declarations for the friend template
+template <typename real> class Quaternion;
+template <typename real> Quaternion<real> operator* (real alpha, Quaternion<real> q2);
+
+template <typename real>
+class Quaternion
+{
+public:
+
+  inline Quaternion(); // q = 0
+  inline Quaternion(real s, real x, real y, real z); // q = s + x * i + y * j + z * k
+  inline Quaternion(real s); // q = s + 0 * i + 0 * j + 0 * k;
+
+  // Makes the unit quaternion corresponding to a rotation around axis 'unitAxis' of angle 'angle'.
+  // Angle is in radians; unitAxis must be a unit 3D vector.
+  inline Quaternion(real angle, real unitAxis[3]); 
+
+  inline void Set(real s, real x, real y, real z); // sets quaternion to the new value
+
+  inline real Gets() const;
+  inline real Getx() const;
+  inline real Gety() const;
+  inline real Getz() const;
+
+  inline Quaternion operator+ (const Quaternion & q2) const; // q3 = q1+q2
+  inline Quaternion operator- (const Quaternion & q2) const; // q3 = q1-q2
+  inline Quaternion operator* (const Quaternion & q2) const; // q3 = q1 * q2
+  inline Quaternion operator/ (const Quaternion & q2) const; // q3 = q1 / q2
+
+  inline Quaternion conj(); // q2 = q1.conj()
+
+  inline Quaternion & operator= (const Quaternion & rhs); // q2 = q1;
+  inline Quaternion & operator= (real s); // sets quaternion equal to the scalar quaternion s
+  inline bool operator== (const Quaternion & rhs) const; // q2 == q1
+  inline bool operator!= (const Quaternion & rhs) const; // q2 != q1
+
+  void Normalize(); // q.Normalize() scales q such that it is unit size
+
+  inline void MoveToRightHalfSphere(); //  if scalar part (that is, 's') is negative, this will multiply the quaternion by -1
+  
+  inline real Norm2() const; // returns the squared norm of the quaternion, i.e. s*s + x*x + y*y + z*z
+  inline real Norm() const { return sqrt(Norm2()); }
+
+  // Transforms the quaternion to the corresponding rotation matrix.
+  // Quaternion is assumed to be a unit quaternion.
+  // R is a 3x3 orthogonal matrix and will be returned in row-major order.
+  inline void Quaternion2Matrix(real * R) const; 
+
+  // Transforms the given matrix (assumed orthogonal) into one of the two corresponding quaternions.
+  // Matrix is assumed to be in row-major order.
+  // There are two quaternions corresponding to a rotation (and they have opposite signs). You can't directly control which one you get, but you can force the real part to be non-negative by a subsequent call to MoveToRightHalfSphere() .
+  // This implementation follows David Baraff's SIGGRAPH course notes:
+  // http://www.cs.cmu.edu/~baraff/pbm/pbm.html
+  static Quaternion Matrix2Quaternion(const real R[9]);
+  
+  // Returns the angle of rotation (in radians), and the unit rotation axis corresponding to the quaternion.
+  // Assumes a unit quaternion (use Normalize() to remove any noise due to floating point errors).
+  // If s >= 0, the angle will be on the interval [0,pi] .
+  // If s < 0, the angle will be on the interval (pi,2pi]. To get a representation where the angle is on [0, pi], you can manually flip the sign of the returned unitAxis, and use the angle of 2pi-angle. Alternatively, you can use MoveToRightHalfSphere before calling GetRotation to ensure that you are always in the s >= 0 case.
+  inline void GetRotation(real * angle, real unitAxis[3]) const;
+
+  // Returns (x,y,z) = sin(theta/2) * axis, where
+  //   theta is the angle of rotation, theta is on [-pi,pi), and axis is the unit axis of rotation.
+  // Assumes a unit quaternion.
+  // Note: this routine is a bit exotic; I expect it to be not so widely used.
+  inline void GetSinExponential(real * x, real * y, real * z) const;
+
+  // Prints the quaternion to stdout.
+  inline void Print() const;
+
+  // Multiply quaternion with a scalar; e.g. q1 = alpha * q2;
+  friend Quaternion operator* (real alpha, const Quaternion & q2)
+  {
+    return Quaternion(alpha * q2.s, alpha * q2.x, alpha * q2.y, alpha * q2.z);
+  }
+
+  // Dot product of two quaternions
+  friend double dot(const Quaternion & q1, const Quaternion & q2) 
+  {
+    return q1.s * q2.s + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z;
+  }
+
+  // Negate a quaternion
+  friend Quaternion operator- (const Quaternion & q) 
+  {
+    return (-1) * q;
+  }
+
+  // Inverse quaternion
+  friend Quaternion inv(const Quaternion & q) 
+  {
+    Quaternion nq;
+    nq.s = q.s;
+    nq.x = -q.x;
+    nq.y = -q.y;
+    nq.z = -q.z;
+    return nq / q.Norm2();
+  }
+
+protected:
+  real s,x,y,z; 
+
+};
+
+template <typename real>
+inline Quaternion<real>::Quaternion()
+{
+  s = x = y = z = 0;
+}
+
+template <typename real>
+inline Quaternion<real>::Quaternion(real s_)
+{
+  s = s_;
+  x = y = z = 0;
+}
+
+template <typename real>
+inline Quaternion<real>::Quaternion(real angle, real unitAxis[3])
+{
+  s = cos(angle/2.0);
+  real sin2 = sin(angle/2.0);
+  x = sin2 * unitAxis[0];
+  y = sin2 * unitAxis[1];
+  z = sin2 * unitAxis[2];
+}
+
+template <typename real>
+inline Quaternion<real>::Quaternion(real s_, real x_, real y_, real z_)
+{
+  s = s_;
+  x = x_;
+  y = y_;
+  z = z_;
+}
+
+template <typename real>
+inline void Quaternion<real>::Set(real s_g, real x_g, real y_g, real z_g) // sets quaternion to the new value
+{
+  s = s_g;
+  x = x_g;
+  y = y_g;
+  z = z_g;
+}
+
+template <typename real>
+inline real Quaternion<real>::Gets() const { return s; }
+
+template <typename real>
+inline real Quaternion<real>::Getx() const { return x; } 
+
+template <typename real>
+inline real Quaternion<real>::Gety() const { return y; } 
+
+template <typename real>
+inline real Quaternion<real>::Getz() const { return z; }
+
+template <typename real>
+inline Quaternion<real> & Quaternion<real>::operator= (const Quaternion<real> & rhs)
+{
+  s = rhs.s;
+  x = rhs.x;
+  y = rhs.y;
+  z = rhs.z;
+
+  return *this;
+}
+
+template <typename real>
+inline Quaternion<real> & Quaternion<real>::operator= (real s_g)
+{
+  s = s_g;
+  x = 0;
+  y = 0;
+  z = 0;
+
+  return *this;
+}
+
+template <typename real>
+inline bool Quaternion<real>::operator== (const Quaternion<real> & rhs) const
+{
+  return ((s == rhs.s) && (x == rhs.x) &&
+          (y == rhs.y) && (z == rhs.z));
+}
+
+template <typename real>
+inline bool Quaternion<real>::operator!= (const Quaternion<real> & rhs) const
+{
+  return ((s != rhs.s) || (x != rhs.x) ||
+          (y != rhs.y) || (z != rhs.z));
+}
+
+template <typename real>
+inline void Quaternion<real>::Normalize()
+{
+  real invNorm;
+  invNorm = (real)1.0 / (real)sqrt(Norm2());
+
+  s *= invNorm;
+  x *= invNorm;
+  y *= invNorm;
+  z *= invNorm;
+
+}
+
+template <typename real>
+inline real Quaternion<real>::Norm2() const
+{ 
+  return (s*s + x*x + y*y + z*z); 
+}
+
+template <typename real>
+inline Quaternion<real> Quaternion<real>::operator+ (const Quaternion<real> & q2) const
+{
+  Quaternion<real> w(s + q2.s, x + q2.x, y + q2.y, z + q2.z);
+
+  return w;  
+}
+
+template <typename real>
+inline Quaternion<real> Quaternion<real>::operator- (const Quaternion<real> & q2) const
+{
+  Quaternion<real> w(s - q2.s, x - q2.x, y - q2.y, z - q2.z);
+  return w;  
+}
+
+template <typename real>
+inline Quaternion<real> Quaternion<real>::operator* (const Quaternion<real> & q2) const 
+{
+  Quaternion<real> w(
+        s * q2.s - x * q2.x - y    * q2.y - z * q2.z,
+        s * q2.x + q2.s * x + y    * q2.z - q2.y * z,
+        s * q2.y + q2.s * y + q2.x * z    - x    * q2.z,
+        s * q2.z + q2.s * z + x    * q2.y - q2.x * y);
+
+  return w;  
+}
+
+template <typename real>
+inline Quaternion<real> Quaternion<real>::operator/ (const Quaternion<real> & q2) const
+{
+  // compute invQ2 = q2^{-1}
+  Quaternion<real> invQ2; 
+  real invNorm2 = 1.0 / q2.Norm2();
+  invQ2.s = q2.s * invNorm2;
+  invQ2.x = -q2.x * invNorm2;
+  invQ2.y = -q2.y * invNorm2;
+  invQ2.z = -q2.z * invNorm2;
+
+  // result = *this * invQ2
+  return (*this * invQ2); 
+
+}
+
+template <typename real>
+inline Quaternion<real> Quaternion<real>::conj()
+{
+  Quaternion<real> w(s,-x,-y,-z);
+  return w;
+}
+
+// Transforms the quaternion to the corresponding rotation matrix.
+// Quaternion is assumed to be a unit quaternion.
+// R is a 3x3 orthogonal matrix and will be returned in row-major order.
+template <typename real>
+inline void Quaternion<real>::Quaternion2Matrix(real * R) const
+{
+  R[0] = 1 - 2*y*y - 2*z*z; R[1] = 2*x*y - 2*s*z;     R[2] = 2*x*z + 2*s*y;
+  R[3] = 2*x*y + 2*s*z;     R[4] = 1 - 2*x*x - 2*z*z; R[5] = 2*y*z - 2*s*x;
+  R[6] = 2*x*z - 2*s*y;     R[7] = 2*y*z + 2*s*x;     R[8] = 1 - 2*x*x - 2*y*y;
+}
+
+// Returns (x,y,z) = sin(theta/2) * axis, where
+//   theta is the angle of rotation, theta\in\{-pi,pi\}, and
+//   axis is the unit axis of rotation.
+template <typename real>
+inline void Quaternion<real>::GetSinExponential(real * sex, real * sey, real * sez)const
+{
+  if (s<0)
+  {
+    *sex = -x;
+    *sey = -y;
+    *sez = -z;
+  }
+  else
+  {
+    *sex = x;
+    *sey = y;
+    *sez = z;
+  }
+}
+
+template <typename real>
+inline void Quaternion<real>::GetRotation(real * angle, real unitAxis[3]) const
+{
+  if ((s >= ((real)1)) || (s <= (real)(-1)))
+  {
+    // identity; this check is necessary to avoid problems with acos if s is 1 + eps
+    *angle = 0;
+    unitAxis[0] = 1;
+    unitAxis[0] = 0;
+    unitAxis[0] = 0;
+    return;
+  }
+
+  *angle = 2.0 * acos(s);
+  real sin2 = x*x + y*y + z*z; //sin^2(*angle / 2.0)
+
+  if (sin2 == 0)
+  {
+    // identity rotation; angle is zero, any axis is equally good
+    unitAxis[0] = 1;
+    unitAxis[0] = 0;
+    unitAxis[0] = 0;
+  }
+  else
+  {
+    real inv = 1.0 / sqrt(sin2); // note: *angle / 2.0 is on [0,pi], so sin(*angle / 2.0) >= 0, and therefore the sign of sqrt can be safely taken positive
+    unitAxis[0] = x * inv;
+    unitAxis[1] = y * inv;
+    unitAxis[2] = z * inv;
+  }
+}
+
+template <typename real>
+inline void Quaternion<real>::MoveToRightHalfSphere()
+{
+  if (s<0)
+  {
+    s *= -1;
+    x *= -1;
+    y *= -1;
+    z *= -1;
+  }
+}
+
+template <typename real>
+inline void Quaternion<real>::Print() const
+{
+  printf("%f + %fi + %fj + %fk\n",s,x,y,z);
+}
+
+#endif
+
diff --git a/src/libreducedElasticForceModel/reducedLinearForceModel.cpp b/libraries/reducedElasticForceModel/reducedLinearForceModel.cpp
similarity index 81%
rename from src/libreducedElasticForceModel/reducedLinearForceModel.cpp
rename to libraries/reducedElasticForceModel/reducedLinearForceModel.cpp
index bc85a2078996e8b0ef0b560578e23c287c501238..bc5e58e5a86e6086c9d90309f5642d313333c9ab 100644
--- a/src/libreducedElasticForceModel/reducedLinearForceModel.cpp
+++ b/libraries/reducedElasticForceModel/reducedLinearForceModel.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedLinearForceModel.h b/libraries/reducedElasticForceModel/reducedLinearForceModel.h
similarity index 80%
rename from src/libreducedElasticForceModel/reducedLinearForceModel.h
rename to libraries/reducedElasticForceModel/reducedLinearForceModel.h
index 618c095a8eca5934e95d610ebd041878dcfdf327..cf2b903a3bd206dccb290188b86dbbb564b032a9 100644
--- a/src/libreducedElasticForceModel/reducedLinearForceModel.h
+++ b/libraries/reducedElasticForceModel/reducedLinearForceModel.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedLinearStVKForceModel.cpp b/libraries/reducedElasticForceModel/reducedLinearStVKForceModel.cpp
similarity index 82%
rename from src/libreducedElasticForceModel/reducedLinearStVKForceModel.cpp
rename to libraries/reducedElasticForceModel/reducedLinearStVKForceModel.cpp
index a07a2993cb784d5210facb2f1870382bdb529eb3..363a0ff31145f98419ec8fa89bb7844e12885446 100644
--- a/src/libreducedElasticForceModel/reducedLinearStVKForceModel.cpp
+++ b/libraries/reducedElasticForceModel/reducedLinearStVKForceModel.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedLinearStVKForceModel.h b/libraries/reducedElasticForceModel/reducedLinearStVKForceModel.h
similarity index 79%
rename from src/libreducedElasticForceModel/reducedLinearStVKForceModel.h
rename to libraries/reducedElasticForceModel/reducedLinearStVKForceModel.h
index 1f4c2be2c2d520f85be8b34ec4d36bcefe81db7e..3d18af680f6bad8cdb8a0f34a2bebe63483996dc 100644
--- a/src/libreducedElasticForceModel/reducedLinearStVKForceModel.h
+++ b/libraries/reducedElasticForceModel/reducedLinearStVKForceModel.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedMassSpringSystemForceModel.cpp b/libraries/reducedElasticForceModel/reducedMassSpringSystemForceModel.cpp
similarity index 89%
rename from src/libreducedElasticForceModel/reducedMassSpringSystemForceModel.cpp
rename to libraries/reducedElasticForceModel/reducedMassSpringSystemForceModel.cpp
index 87bcb5122c29368e37ca7abb820312f6cc3fb0bd..6c4b060132eec7ccc25b7287359a2b88c5c1459b 100644
--- a/src/libreducedElasticForceModel/reducedMassSpringSystemForceModel.cpp
+++ b/libraries/reducedElasticForceModel/reducedMassSpringSystemForceModel.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedMassSpringSystemForceModel.h b/libraries/reducedElasticForceModel/reducedMassSpringSystemForceModel.h
similarity index 83%
rename from src/libreducedElasticForceModel/reducedMassSpringSystemForceModel.h
rename to libraries/reducedElasticForceModel/reducedMassSpringSystemForceModel.h
index 83ffec72b7ba8e4fcf7e3a291edbc16af69035c4..cd199214417e82ec9e09e9b6f15dd90713ec3b9e 100644
--- a/src/libreducedElasticForceModel/reducedMassSpringSystemForceModel.h
+++ b/libraries/reducedElasticForceModel/reducedMassSpringSystemForceModel.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.cpp b/libraries/reducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.cpp
similarity index 88%
rename from src/libreducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.cpp
rename to libraries/reducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.cpp
index 53cbe429cd37bb27d5a30ec49e4f4262bdcc1460..4c2797d8448f6486ae492bb0049ad68ad9a23f25 100644
--- a/src/libreducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.cpp
+++ b/libraries/reducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.h b/libraries/reducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.h
similarity index 81%
rename from src/libreducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.h
rename to libraries/reducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.h
index 3d332e7a816b731b1040d6b7b625fd869c134af9..66892fa4e0ab9d440769bb6b2a93f838d5e0535c 100644
--- a/src/libreducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.h
+++ b/libraries/reducedElasticForceModel/reducedMassSpringSystemForceModelWithHessian.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedSpringForceModel.cpp b/libraries/reducedElasticForceModel/reducedSpringForceModel.cpp
similarity index 78%
rename from src/libreducedElasticForceModel/reducedSpringForceModel.cpp
rename to libraries/reducedElasticForceModel/reducedSpringForceModel.cpp
index 2a6c6872a929b0a78dd81bbb491348852bf919ff..522ea8ed6326410badfddecc733c432aafc60377 100644
--- a/src/libreducedElasticForceModel/reducedSpringForceModel.cpp
+++ b/libraries/reducedElasticForceModel/reducedSpringForceModel.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedSpringForceModel.h b/libraries/reducedElasticForceModel/reducedSpringForceModel.h
similarity index 79%
rename from src/libreducedElasticForceModel/reducedSpringForceModel.h
rename to libraries/reducedElasticForceModel/reducedSpringForceModel.h
index 2e6b1a40ef1c2404aef15ac5f91b352a3c807195..774e06f4b1e165abdb95cd26c52ea96107226238 100644
--- a/src/libreducedElasticForceModel/reducedSpringForceModel.h
+++ b/libraries/reducedElasticForceModel/reducedSpringForceModel.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedStVKForceModel.cpp b/libraries/reducedElasticForceModel/reducedStVKForceModel.cpp
similarity index 69%
rename from src/libreducedElasticForceModel/reducedStVKForceModel.cpp
rename to libraries/reducedElasticForceModel/reducedStVKForceModel.cpp
index 7f0e525e840dfe3e9aff0924810b0af9c3b9a025..8be4dec33d27c02763d7d9be4911a19b6fe1d8cc 100644
--- a/src/libreducedElasticForceModel/reducedStVKForceModel.cpp
+++ b/libraries/reducedElasticForceModel/reducedStVKForceModel.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,7 +33,7 @@
 
 #include "reducedStVKForceModel.h"
 
-ReducedStVKForceModel::ReducedStVKForceModel(StVKReducedInternalForces * stVKReducedInternalForces_, StVKReducedStiffnessMatrix * stVKStiffnessMatrix_): stVKReducedInternalForces(stVKReducedInternalForces_), stVKStiffnessMatrix(stVKStiffnessMatrix_), own_stVKStiffnessMatrix(false)
+ReducedStVKForceModel::ReducedStVKForceModel(StVKReducedInternalForces * stVKReducedInternalForces_, StVKReducedStiffnessMatrix * stVKStiffnessMatrix_): stVKReducedInternalForces(stVKReducedInternalForces_), stVKStiffnessMatrix(stVKStiffnessMatrix_), useScale(0), scale(1.0), own_stVKStiffnessMatrix(false)
 {
   r = stVKReducedInternalForces->Getr();
   if (stVKStiffnessMatrix == NULL)
@@ -42,16 +46,27 @@ ReducedStVKForceModel::ReducedStVKForceModel(StVKReducedInternalForces * stVKRed
 ReducedStVKForceModel::~ReducedStVKForceModel()
 {
   if (own_stVKStiffnessMatrix)
-    free(stVKStiffnessMatrix);
+    delete(stVKStiffnessMatrix);
 }
 
 void ReducedStVKForceModel::GetInternalForce(double * q, double * internalForces)
 {
-  stVKReducedInternalForces->Evaluate(q,internalForces);
+  stVKReducedInternalForces->Evaluate(q, internalForces);
+  if (useScale)
+  {
+    for(int i=0; i<r; i++)
+      internalForces[i] *= scale;
+  }
 }
 
 void ReducedStVKForceModel::GetTangentStiffnessMatrix(double * q, double * tangentStiffnessMatrix)
 {
-  stVKStiffnessMatrix->Evaluate(q,tangentStiffnessMatrix);
+  stVKStiffnessMatrix->Evaluate(q, tangentStiffnessMatrix);
+  if (useScale)
+  {
+    int r2 = r * r;
+    for(int i=0; i<r2; i++)
+      tangentStiffnessMatrix[i] *= scale;
+  }
 }
 
diff --git a/src/libreducedElasticForceModel/reducedStVKForceModel.h b/libraries/reducedElasticForceModel/reducedStVKForceModel.h
similarity index 79%
rename from src/libreducedElasticForceModel/reducedStVKForceModel.h
rename to libraries/reducedElasticForceModel/reducedStVKForceModel.h
index 2a9ba4d3be4f9e101c8d4319cd2ac8c0b2087f6d..738199e12876c0d22203df56654b9ff76102b4c8 100644
--- a/src/libreducedElasticForceModel/reducedStVKForceModel.h
+++ b/libraries/reducedElasticForceModel/reducedStVKForceModel.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -58,10 +62,17 @@ public:
   virtual void * GetReducedInternalForceClass() { return (void*)stVKReducedInternalForces; }
   virtual void * GetReducedStiffnessMatrixClass() { return (void*)stVKStiffnessMatrix; }
 
+  // set a scaling factor for forces and stiffness matrix
+  void SetScale(double scale_) { scale = scale_; }
+  void UseScale(int useScale_) { useScale = useScale_; } // default: do not use scale
+
 protected:
   StVKReducedInternalForces * stVKReducedInternalForces;
   StVKReducedStiffnessMatrix * stVKStiffnessMatrix;
 
+  int useScale;
+  double scale;
+
   bool own_stVKStiffnessMatrix;
 };
 
diff --git a/src/libreducedElasticForceModel/reducedStVKForceModelWithHessian.cpp b/libraries/reducedElasticForceModel/reducedStVKForceModelWithHessian.cpp
similarity index 78%
rename from src/libreducedElasticForceModel/reducedStVKForceModelWithHessian.cpp
rename to libraries/reducedElasticForceModel/reducedStVKForceModelWithHessian.cpp
index 47c4370ae0f4b03ec27876ad16dc67cbc8ea7297..b963a10e157b64998db7b961872126f6948c9995 100644
--- a/src/libreducedElasticForceModel/reducedStVKForceModelWithHessian.cpp
+++ b/libraries/reducedElasticForceModel/reducedStVKForceModelWithHessian.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,7 +40,7 @@ ReducedStVKForceModelWithHessian::ReducedStVKForceModelWithHessian(StVKReducedIn
 
 ReducedStVKForceModelWithHessian::~ReducedStVKForceModelWithHessian()
 {
-  free(stVKReducedHessianTensor);
+  delete(stVKReducedHessianTensor);
 }
 
 void ReducedStVKForceModelWithHessian::GetTangentHessianTensor(double * q, double * tangentHessianTensor)
diff --git a/src/libreducedElasticForceModel/reducedStVKForceModelWithHessian.h b/libraries/reducedElasticForceModel/reducedStVKForceModelWithHessian.h
similarity index 79%
rename from src/libreducedElasticForceModel/reducedStVKForceModelWithHessian.h
rename to libraries/reducedElasticForceModel/reducedStVKForceModelWithHessian.h
index e55c708993db3465073bae74127d23b0cf1e884d..db788ea4fc24d0aba1a5117ee4ca7f56cef8123b 100644
--- a/src/libreducedElasticForceModel/reducedStVKForceModelWithHessian.h
+++ b/libraries/reducedElasticForceModel/reducedStVKForceModelWithHessian.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedSubspaceStVKForceModel.cpp b/libraries/reducedElasticForceModel/reducedSubspaceStVKForceModel.cpp
similarity index 86%
rename from src/libreducedElasticForceModel/reducedSubspaceStVKForceModel.cpp
rename to libraries/reducedElasticForceModel/reducedSubspaceStVKForceModel.cpp
index ba0568cba57dd7df0dc3b97ba1a9764e883bf8d1..6f8d9e429a45ab873fd44f707acc121273957024 100644
--- a/src/libreducedElasticForceModel/reducedSubspaceStVKForceModel.cpp
+++ b/libraries/reducedElasticForceModel/reducedSubspaceStVKForceModel.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedElasticForceModel/reducedSubspaceStVKForceModel.h b/libraries/reducedElasticForceModel/reducedSubspaceStVKForceModel.h
similarity index 83%
rename from src/libreducedElasticForceModel/reducedSubspaceStVKForceModel.h
rename to libraries/reducedElasticForceModel/reducedSubspaceStVKForceModel.h
index 935771c858c93537100db70a4d96ee4ffff27441..49e6ab6b75e289c8a92b8338a8c2ca00d7e0b7a2 100644
--- a/src/libreducedElasticForceModel/reducedSubspaceStVKForceModel.h
+++ b/libraries/reducedElasticForceModel/reducedSubspaceStVKForceModel.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "elasticForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedForceModel/reducedForceModel.cpp b/libraries/reducedForceModel/reducedForceModel.cpp
similarity index 85%
rename from src/libreducedForceModel/reducedForceModel.cpp
rename to libraries/reducedForceModel/reducedForceModel.cpp
index f3c9931a844bc651cd9c7c921c2f95da75a4a827..8ad9cd0bd20c3c392dc40280397645415506e5f4 100644
--- a/src/libreducedForceModel/reducedForceModel.cpp
+++ b/libraries/reducedForceModel/reducedForceModel.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedForceModel/reducedForceModel.h b/libraries/reducedForceModel/reducedForceModel.h
similarity index 81%
rename from src/libreducedForceModel/reducedForceModel.h
rename to libraries/reducedForceModel/reducedForceModel.h
index db35231cc700c7b393d989e2401161b3deb1266a..a4e01cc96eb16f5f4a91f02d839b7d2f3c277019 100644
--- a/src/libreducedForceModel/reducedForceModel.h
+++ b/libraries/reducedForceModel/reducedForceModel.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedForceModel/reducedForceModelWithHessian.cpp b/libraries/reducedForceModel/reducedForceModelWithHessian.cpp
similarity index 87%
rename from src/libreducedForceModel/reducedForceModelWithHessian.cpp
rename to libraries/reducedForceModel/reducedForceModelWithHessian.cpp
index 53fe58eb6e9c715edc7605c750a164c2e2d0f80a..34a05966bb5e5d33d67d72fc4f6b58a36c1d7899 100644
--- a/src/libreducedForceModel/reducedForceModelWithHessian.cpp
+++ b/libraries/reducedForceModel/reducedForceModelWithHessian.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedForceModel/reducedForceModelWithHessian.h b/libraries/reducedForceModel/reducedForceModelWithHessian.h
similarity index 81%
rename from src/libreducedForceModel/reducedForceModelWithHessian.h
rename to libraries/reducedForceModel/reducedForceModelWithHessian.h
index 677ea6234b2e43ee4b3746046b273d129658a2ca..b9976d33fd925da521b7a8d77e90411e43418746 100644
--- a/src/libreducedForceModel/reducedForceModelWithHessian.h
+++ b/libraries/reducedForceModel/reducedForceModelWithHessian.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedForceModel" library , Copyright (C) 2007 CMU, 2009 MIT,       *
- *                                                       2013 USC        *
+ *                                                       2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libreducedStvk/StVKReducedHessianTensor.cpp b/libraries/reducedStvk/StVKReducedHessianTensor.cpp
similarity index 94%
rename from src/libreducedStvk/StVKReducedHessianTensor.cpp
rename to libraries/reducedStvk/StVKReducedHessianTensor.cpp
index a258f5db2f9b5c3900300de7f4c9c00b824f402a..45c6855d4749aed743b9944e94891162e5c39863 100644
--- a/src/libreducedStvk/StVKReducedHessianTensor.cpp
+++ b/libraries/reducedStvk/StVKReducedHessianTensor.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,9 +30,9 @@
  *                                                                       *
  *************************************************************************/
 
-#include "lapack-headers.h"
 #include "matrixMacros.h"
 #include "StVKReducedHessianTensor.h"
+#include "lapack-headers.h"
 
 StVKReducedHessianTensor::StVKReducedHessianTensor(StVKReducedStiffnessMatrix * stVKReducedStiffnessMatrix) : shallowCopy(0)
 {
@@ -352,7 +356,7 @@ double *Y, const int incY);
       dfq, 1);
 }
 
-int StVKReducedHessianTensor::Save(char * filename)
+int StVKReducedHessianTensor::Save(const char * filename)
 {
   FILE * fout = fopen(filename,"wb");
 
@@ -380,7 +384,7 @@ int StVKReducedHessianTensor::Save(char * filename)
 }
 
 
-StVKReducedHessianTensor::StVKReducedHessianTensor(char * filename)
+StVKReducedHessianTensor::StVKReducedHessianTensor(const char * filename)
 {
   FILE * fin = fopen(filename,"rb");
 
diff --git a/src/libreducedStvk/StVKReducedHessianTensor.h b/libraries/reducedStvk/StVKReducedHessianTensor.h
similarity index 89%
rename from src/libreducedStvk/StVKReducedHessianTensor.h
rename to libraries/reducedStvk/StVKReducedHessianTensor.h
index c534a5ad71601cc65c5193c9bf0e3fd4d7345ba9..4945b31bae2e4470fd52db1ad05e30a0ad6bfbb3 100644
--- a/src/libreducedStvk/StVKReducedHessianTensor.h
+++ b/libraries/reducedStvk/StVKReducedHessianTensor.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedStVK" library , Copyright (C) 2007 CMU, 2009 MIT              *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -48,12 +52,12 @@ public:
   StVKReducedHessianTensor(StVKReducedStiffnessMatrix * stVKReducedStiffnessMatrix);
 
   // load the coefficients from file
-  StVKReducedHessianTensor(char * filename);
+  StVKReducedHessianTensor(const char * filename);
 
   ~StVKReducedHessianTensor();
 
   // saves coefficients out, in binary format
-  int Save(char * filename);
+  int Save(const char * filename);
 
   // make a buffer that you can then pass it to the "Evaluate" routine
   void MakeRoomForTensor(double ** Hq);
@@ -86,7 +90,6 @@ public:
 
   // makes shallow copies of all pointers, except those initialized by InitBuffers
   // use this if you want to Evaluate two or more identical models (i.e., two copies of an object) in parallel (to ensure thread safety)
-  // you do not need to use this if you are Evaluating a single model in parallel (e.g., using the MT derived class)
   StVKReducedHessianTensor * ShallowClone();
 
 protected:
diff --git a/src/libreducedStvk/StVKReducedInternalForces.cpp b/libraries/reducedStvk/StVKReducedInternalForces.cpp
similarity index 87%
rename from src/libreducedStvk/StVKReducedInternalForces.cpp
rename to libraries/reducedStvk/StVKReducedInternalForces.cpp
index e506428f1ba857af86c54978ebf13c66a0e42f1c..effac0a2dda3ffef7a3c133ca2bc30dca7c2c00e 100644
--- a/src/libreducedStvk/StVKReducedInternalForces.cpp
+++ b/libraries/reducedStvk/StVKReducedInternalForces.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,17 +30,21 @@
  *                                                                       *
  *************************************************************************/
 
-#include "lapack-headers.h"
 #include "matrixIO.h"
-#if (defined(WIN32) || defined(linux)) && defined(__INTEL_MKL__)
-  #include "mkl_service.h"
-#endif
 #include "matrixMacros.h"
 #include "matrixProjection.h"
 #include "StVKReducedInternalForces.h"
 #include "volumetricMeshENuMaterial.h"
+#include "lapack-headers.h"
+#if defined(_WIN32) || defined(WIN32) || defined(linux) || defined(__linux__)
+  #include "mkl_service.h"
+#endif
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+using namespace std;
 
-StVKReducedInternalForces::StVKReducedInternalForces(int r, double * U, VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, int initOnly, bool addGravity_, double g_, int verbose_): precomputedIntegrals(precomputedABCDIntegrals), unitReducedGravityForce(NULL), reducedGravityForce(NULL), addGravity(addGravity_), g(g_), useSingleThread(0), shallowCopy(0), verbose(verbose_)
+StVKReducedInternalForces::StVKReducedInternalForces(int r, double * U, VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity_, double g_, int verbose_): precomputedIntegrals(precomputedABCDIntegrals), unitReducedGravityForce(NULL), reducedGravityForce(NULL), addGravity(addGravity_), g(g_), useSingleThread(0), shallowCopy(0), verbose(verbose_)
 {
   int numElements = volumetricMesh->getNumElements();
   lambdaLame = (double*) malloc (sizeof(double) * numElements);
@@ -57,14 +65,253 @@ StVKReducedInternalForces::StVKReducedInternalForces(int r, double * U, Volumetr
   }
 
   InitComputation(r, U, volumetricMesh);
-  if (!initOnly)
+
+  #ifdef USE_TBB
+    tbb::enumerable_thread_specific<vector<double>> threadLocalData(r * (linearSize + quadraticSize + cubicSize), 0.0);
+    tbb::parallel_for(tbb::blocked_range<int>(0, volumetricMesh->getNumElements()), [&](const tbb::blocked_range<int> & rng)
+    {
+      auto & localVector = threadLocalData.local();
+      double * target[3] = { &localVector[0], &localVector[r*linearSize], &localVector[r*(linearSize + quadraticSize)] };
+      tbb::this_task_arena::isolate([&]
+      {
+        ProcessElements(rng.begin(), rng.end(), target);
+      });
+    }, tbb::static_partitioner());
+
+    for(const auto & localVector : threadLocalData)
+    {
+      const double * sourceLinear = &localVector[0];
+      for(int j=0; j<r*linearSize; j++)
+        linearCoef_[j] += sourceLinear[j];
+
+      const double * sourceQuadratic = &localVector[r * linearSize];
+      for(int j=0; j<r*quadraticSize; j++)
+        quadraticCoef_[j] += sourceQuadratic[j];
+
+      const double * sourceCubic = &localVector[r * (linearSize + quadraticSize)];
+      for(int j=0; j<r*cubicSize; j++)
+        cubicCoef_[j] += sourceCubic[j];
+    }
+  #else
     ProcessElements(0, volumetricMesh->getNumElements());
+  #endif
   InitGravity();
 }
 
+StVKReducedInternalForces::StVKReducedInternalForces(const char * filename, int rTarget, int bigEndianMachine, int verbose_) : unitReducedGravityForce(NULL), reducedGravityForce(NULL), addGravity(false), g(9.81), useSingleThread(0), shallowCopy(0), verbose(verbose_)
+{
+  FILE * fin = fopen(filename, "rb");
+  if (!fin)
+  {
+    printf("Error: could not read from the input cubic polynomial file.\n");
+    throw 1;
+  }
+
+  LoadFromStream(fin, rTarget, bigEndianMachine);
+  fclose(fin);
+}
+
+StVKReducedInternalForces::StVKReducedInternalForces(FILE * fin, int rTarget, int bigEndianMachine, int verbose_) : unitReducedGravityForce(NULL), reducedGravityForce(NULL), addGravity(false), g(9.81), useSingleThread(0), shallowCopy(0), verbose(verbose_)
+{
+  LoadFromStream(fin, rTarget, bigEndianMachine); 
+}
+
+int StVKReducedInternalForces::LoadFromStream(FILE * fin, int rTarget, int bigEndianMachine) 
+{
+  if (verbose)
+    printf("Loading polynomials assuming little endian machine: %s.", (!bigEndianMachine) ? "TRUE" : "FALSE");
+
+  int header[4];
+
+  if ((int)(fread(header, sizeof(int), 4, fin)) < 4)
+  {
+    printf("Error: couldn't read from input cubic polynomial file.\n");
+    throw 1;
+  }
+  
+  r = header[0];
+
+  int buffer;
+  if (bigEndianMachine)
+  {
+    little2big(&r, &buffer, sizeof(int));
+    r = buffer;
+  }
+
+  if (rTarget > r)
+  {
+    printf("Error: the input cubic polynomial file has r=%d, but you requested %d > %d.\n", r, rTarget, r);
+    throw 2;
+  }
+
+  // first read in the coefficients as if all modes requested
+  if (verbose)
+    printf(" r=%d\n", r);
+  
+  r2 = r * r;
+
+  linearSize = header[1];
+
+  if (bigEndianMachine)
+  {
+    little2big(&linearSize, &buffer, sizeof(int));
+    linearSize = buffer;
+  }
+
+  quadraticSize = header[2];
+
+  if (bigEndianMachine)
+  {
+    little2big(&quadraticSize, &buffer, sizeof(int));
+    quadraticSize = buffer;
+  }
+
+  cubicSize = header[3];
+
+  if (bigEndianMachine)
+  {
+    little2big(&cubicSize, &buffer, sizeof(int));
+    cubicSize = buffer;
+  }
+
+  linearCoef_ = (double*) malloc (sizeof(double) * r * linearSize);
+
+  if ((int)(fread(linearCoef_,sizeof(double),r*linearSize,fin)) < r*linearSize)
+  {
+    printf("Error: couldn't read from input cubic polynomial file.\n");
+    throw 1;
+  }
+
+  double bufferd;
+  if (bigEndianMachine)
+  {
+    for(int i=0; i<r*linearSize; i++)
+    {
+      little2big(&linearCoef_[i], &bufferd, sizeof(double));
+      linearCoef_[i] = bufferd;
+    }
+  }
+
+  quadraticCoef_ = (double*) malloc (sizeof(double) * r * quadraticSize);
+
+  if ((int)(fread(quadraticCoef_,sizeof(double),r*quadraticSize,fin)) < r*quadraticSize)
+  {
+    printf("Error: couldn't read from input cubic polynomial file.\n");
+    throw 1;
+  }
+
+  if (bigEndianMachine)
+  {
+    for(int i=0; i<r*quadraticSize; i++)
+    {
+      little2big(&quadraticCoef_[i], &bufferd, sizeof(double));
+      quadraticCoef_[i] = bufferd;
+    }
+  }
+
+  cubicCoef_ = (double*) malloc (sizeof(double) * r * cubicSize);
+
+  if ((int)(fread(cubicCoef_,sizeof(double),r*cubicSize,fin)) < r*cubicSize)
+  {
+    printf("Error: couldn't read from input cubic polynomial file.\n");
+    throw 1;
+  }
+
+  if (bigEndianMachine)
+  {
+    for(int i=0; i<r*cubicSize; i++)
+    {
+      little2big(&cubicCoef_[i], &bufferd, sizeof(double));
+      cubicCoef_[i] = bufferd;
+    }
+  }
+
+  if (rTarget >= 0)
+  {
+    int linearSizeTarget, quadraticSizeTarget, cubicSizeTarget;
+    GetSizes(rTarget, &linearSizeTarget, &quadraticSizeTarget, &cubicSizeTarget);
+
+    double * linearCoefTemp_ = 
+      (double*) malloc (sizeof(double) * rTarget * linearSizeTarget);
+
+    double * quadraticCoefTemp_ = 
+      (double*) malloc (sizeof(double) * rTarget * quadraticSizeTarget);
+
+    double * cubicCoefTemp_ = 
+      (double*) malloc (sizeof(double) * rTarget * cubicSizeTarget);
+
+    for(int output=0; output<rTarget; output++)
+      for(int i=0; i<rTarget; i++)
+      {
+        SetSizes(rTarget);
+        int positionTarget = linearCoefPos(output, i); 
+        SetSizes(r);
+        int position = linearCoefPos(output, i); 
+        linearCoefTemp_[positionTarget] = linearCoef_[position];
+      }
+ 
+    for(int output=0; output<rTarget; output++)
+      for(int i=0; i<rTarget; i++)
+        for(int j=i; j<rTarget; j++)
+        {
+          SetSizes(rTarget);
+          int positionTarget = quadraticCoefPos(output, i, j); 
+          SetSizes(r);
+          int position = quadraticCoefPos(output, i, j); 
+          quadraticCoefTemp_[positionTarget] = quadraticCoef_[position];
+        }
+
+    for(int output=0; output<rTarget; output++)
+      for(int i=0; i<rTarget; i++)
+        for(int j=i; j<rTarget; j++)
+          for(int k=j; k<rTarget; k++)
+          {
+            SetSizes(rTarget);
+            int positionTarget = cubicCoefPos(output, i, j, k); 
+            SetSizes(r);
+            int position = cubicCoefPos(output, i, j, k); 
+            cubicCoefTemp_[positionTarget] = cubicCoef_[position];
+          }
+
+    r = rTarget;
+    SetSizes(r);
+
+    free(linearCoef_);
+    free(quadraticCoef_);
+    free(cubicCoef_);
+
+    linearCoef_ = linearCoefTemp_;
+    quadraticCoef_ = quadraticCoefTemp_;
+    cubicCoef_ = cubicCoefTemp_;
+  }
+
+  volumetricMesh = NULL;
+  U = NULL;
+  reducedGravityForce = NULL;
+  precomputedIntegrals = NULL;
+  numElementVertices = 0;
+  lambdaLame = NULL;
+  muLame = NULL;
+
+  InitBuffers();
+
+  addGravity = false;
+
+  useSingleThread = 0;
+  shallowCopy = 0;
+  g=9.81; 
+
+  return 0;
+}
+
 StVKReducedInternalForces::~StVKReducedInternalForces()
 {
-  if (!shallowCopy)
+  if (shallowCopy >= 1)
+  {
+    if (shallowCopy == 2)
+      free(reducedGravityForce);
+  }
+  else
   {
     free(unitReducedGravityForce);
     free(reducedGravityForce);
@@ -92,7 +339,7 @@ void StVKReducedInternalForces::InitGravity(VolumetricMesh * volumetricMesh_, do
   if ((mesh == NULL) || (UB ==NULL))
   {
     printf("Error: cannot init gravity. Mesh or basis is not specified.\n");
-    exit(1);
+    return;
   }
 
   if (reducedGravityForce == NULL)
@@ -191,7 +438,10 @@ void StVKReducedInternalForces::ProcessElements(int startElement, int endElement
     if (verbose >= 1)
     {
       if (el % 100 == 1)
-        printf("%d ",el); fflush(NULL);
+      {
+        printf("%d ",el); 
+        fflush(NULL);
+      }
     }
 
     double lambda = lambdaLame[el];
@@ -237,8 +487,6 @@ void StVKReducedInternalForces::ProcessElements(int startElement, int endElement
   for(int c=0; c<numElementVertices; c++)
     forceBuffer[c] = (double*) calloc (3*r2,sizeof(double));
 
-  memset(quadraticCoef_, 0, sizeof(double) * r * quadraticSize);
-
   int * vertices = (int*) malloc (sizeof(int) * numElementVertices);
 
   for(int el=startElement; el < endElement; el++)
@@ -248,7 +496,10 @@ void StVKReducedInternalForces::ProcessElements(int startElement, int endElement
     if (verbose >= 1)
     {
       if (el % 100 == 1)
-        printf("%d ",el); fflush(NULL);
+      {
+        printf("%d ",el); 
+        fflush(NULL);
+      }
     }
 
     double lambda = lambdaLame[el];
@@ -355,7 +606,10 @@ void StVKReducedInternalForces::ProcessElements(int startElement, int endElement
     if (verbose >= 1)
     {
       if ((el % 50 == 1) || ((r > 30) && (el % 25 == 1)))
-        printf("%d ",el); fflush(NULL);
+      {
+        printf("%d ",el); 
+        fflush(NULL);
+      }
     }
 
     double lambda = lambdaLame[el];
@@ -414,9 +668,6 @@ void StVKReducedInternalForces::ProcessElements(int startElement, int endElement
     } // over a
   }
 
-  for(int i=0; i < r*cubicSize; i++)
-    cubicCoef_[i] = 0.0;
-
   // unpack
   for(int i=0; i<r; i++)
     for(int j=0; j<r; j++)
@@ -441,7 +692,7 @@ void StVKReducedInternalForces::ProcessElements(int startElement, int endElement
   precomputedIntegrals->ReleaseElementIterator(elIter);
 }
 
-void StVKReducedInternalForces::TestPolynomial(double * q, StVKInternalForces * stVKInternalForces, char * filenameU)
+void StVKReducedInternalForces::TestPolynomial(double * q, StVKInternalForces * stVKInternalForces, const char * filenameU)
 {
   double * fqPoly = (double*) malloc (sizeof(double) * r);
   double * fqDirect = (double*) malloc (sizeof(double) * r);
@@ -497,13 +748,21 @@ void StVKReducedInternalForces::TestPolynomial(double * q, StVKInternalForces *
   free(fqPoly);
 }
 
-int StVKReducedInternalForces::Save(char * filename)
+int StVKReducedInternalForces::Save(const char * filename)
 {
   FILE * fout = fopen(filename,"wb");
-
   if (!fout)
     return 1;
 
+  Save(fout);
+
+  fclose(fout);
+
+  return 0;
+}
+
+int StVKReducedInternalForces::Save(FILE * fout)
+{
   if ((int)(fwrite(&r,sizeof(int),1,fout)) < 1)
     return 1;
 
@@ -525,8 +784,6 @@ int StVKReducedInternalForces::Save(char * filename)
   if ((int)(fwrite(cubicCoef_,sizeof(double),r*cubicSize,fout)) < r*cubicSize)
     return 1;
 
-  fclose(fout);
-
   return 0;
 }
 
@@ -537,7 +794,7 @@ void StVKReducedInternalForces::SetSizes(int rTarget)
   GetSizes(r, &linearSize, &quadraticSize, &cubicSize);
 }
 
-int StVKReducedInternalForces::GetrFromFile(char * filename)
+int StVKReducedInternalForces::GetrFromFile(const char * filename)
 {
   FILE * fin = fopen(filename,"rb");
 
@@ -572,202 +829,6 @@ void StVKReducedInternalForces::little2big(void * input, void * output, int numB
   }
 }
 
-StVKReducedInternalForces::StVKReducedInternalForces(char * filename, int rTarget, int bigEndianMachine, int verbose_) : verbose(verbose_)
-{
-  if (verbose)
-    printf("Loading polynomials assuming little endian machine: %s.", (!bigEndianMachine) ? "TRUE" : "FALSE");
-
-  FILE * fin = fopen(filename,"rb");
-
-  if (!fin)
-  {
-    printf("Error: couldn't read from input cubic polynomial file.\n");
-    throw 1;
-  }
-
-  if ((int)(fread(&r,sizeof(int),1,fin)) < 1)
-  {
-    printf("Error: couldn't read from input cubic polynomial file.\n");
-    throw 1;
-  }
-  int buffer;
-  if (bigEndianMachine)
-  {
-    little2big(&r, &buffer, sizeof(int));
-    r = buffer;
-  }
-
-  if (rTarget > r)
-  {
-    printf("Error: the input cubic polynomial file has r=%d, but you requested %d > %d.\n", r, rTarget, r);
-    throw 2;
-  }
-
-  // first read in the coefficients as if all modes requested
-  if (verbose)
-    printf(" r=%d\n", r);
-  
-  r2 = r * r;
-
-  if ((int)(fread(&linearSize,sizeof(int),1,fin)) < 1)
-  {
-    printf("Error: couldn't read from input cubic polynomial file.\n");
-    throw 1;
-  }
-
-  if (bigEndianMachine)
-  {
-    little2big(&linearSize, &buffer, sizeof(int));
-    linearSize = buffer;
-  }
-
-  if ((int)(fread(&quadraticSize,sizeof(int),1,fin)) < 1)
-  {
-    printf("Error: couldn't read from input cubic polynomial file.\n");
-    throw 1;
-  }
-
-  if (bigEndianMachine)
-  {
-    little2big(&quadraticSize, &buffer, sizeof(int));
-    quadraticSize = buffer;
-  }
-
-  if ((int)(fread(&cubicSize,sizeof(int),1,fin)) < 1)
-  {
-    printf("Error: couldn't read from input cubic polynomial file.\n");
-    throw 1;
-  }
-
-  if (bigEndianMachine)
-  {
-    little2big(&cubicSize, &buffer, sizeof(int));
-    cubicSize = buffer;
-  }
-
-  linearCoef_ = (double*) malloc (sizeof(double) * r * linearSize);
-
-  if ((int)(fread(linearCoef_,sizeof(double),r*linearSize,fin)) < r*linearSize)
-  {
-    printf("Error: couldn't read from input cubic polynomial file.\n");
-    throw 1;
-  }
-
-  double bufferd;
-  if (bigEndianMachine)
-  {
-    for(int i=0; i<r*linearSize; i++)
-    {
-      little2big(&linearCoef_[i], &bufferd, sizeof(double));
-      linearCoef_[i] = bufferd;
-    }
-  }
-
-  quadraticCoef_ = (double*) malloc (sizeof(double) * r * quadraticSize);
-
-  if ((int)(fread(quadraticCoef_,sizeof(double),r*quadraticSize,fin)) < r*quadraticSize)
-  {
-    printf("Error: couldn't read from input cubic polynomial file.\n");
-    throw 1;
-  }
-
-  if (bigEndianMachine)
-  {
-    for(int i=0; i<r*quadraticSize; i++)
-    {
-      little2big(&quadraticCoef_[i], &bufferd, sizeof(double));
-      quadraticCoef_[i] = bufferd;
-    }
-  }
-
-  cubicCoef_ = (double*) malloc (sizeof(double) * r * cubicSize);
-
-  if ((int)(fread(cubicCoef_,sizeof(double),r*cubicSize,fin)) < r*cubicSize)
-  {
-    printf("Error: couldn't read from input cubic polynomial file.\n");
-    throw 1;
-  }
-
-  if (bigEndianMachine)
-  {
-    for(int i=0; i<r*cubicSize; i++)
-    {
-      little2big(&cubicCoef_[i], &bufferd, sizeof(double));
-      cubicCoef_[i] = bufferd;
-    }
-  }
-
-  fclose(fin);
-
-  if (rTarget >= 0)
-  {
-    int linearSizeTarget, quadraticSizeTarget, cubicSizeTarget;
-    GetSizes(rTarget, &linearSizeTarget, &quadraticSizeTarget, &cubicSizeTarget);
-
-    double * linearCoefTemp_ = 
-      (double*) malloc (sizeof(double) * rTarget * linearSizeTarget);
-
-    double * quadraticCoefTemp_ = 
-      (double*) malloc (sizeof(double) * rTarget * quadraticSizeTarget);
-
-    double * cubicCoefTemp_ = 
-      (double*) malloc (sizeof(double) * rTarget * cubicSizeTarget);
-
-    for(int output=0; output<rTarget; output++)
-      for(int i=0; i<rTarget; i++)
-      {
-        SetSizes(rTarget);
-        int positionTarget = linearCoefPos(output, i); 
-        SetSizes(r);
-        int position = linearCoefPos(output, i); 
-        linearCoefTemp_[positionTarget] = linearCoef_[position];
-      }
- 
-    for(int output=0; output<rTarget; output++)
-      for(int i=0; i<rTarget; i++)
-        for(int j=i; j<rTarget; j++)
-        {
-          SetSizes(rTarget);
-          int positionTarget = quadraticCoefPos(output, i, j); 
-          SetSizes(r);
-          int position = quadraticCoefPos(output, i, j); 
-          quadraticCoefTemp_[positionTarget] = quadraticCoef_[position];
-        }
-
-    for(int output=0; output<rTarget; output++)
-      for(int i=0; i<rTarget; i++)
-        for(int j=i; j<rTarget; j++)
-          for(int k=j; k<rTarget; k++)
-          {
-            SetSizes(rTarget);
-            int positionTarget = cubicCoefPos(output, i, j, k); 
-            SetSizes(r);
-            int position = cubicCoefPos(output, i, j, k); 
-            cubicCoefTemp_[positionTarget] = cubicCoef_[position];
-          }
-
-    r = rTarget;
-    SetSizes(r);
-
-    free(linearCoef_);
-    free(quadraticCoef_);
-    free(cubicCoef_);
-
-    linearCoef_ = linearCoefTemp_;
-    quadraticCoef_ = quadraticCoefTemp_;
-    cubicCoef_ = cubicCoefTemp_;
-  }
-
-  volumetricMesh = NULL;
-  U = NULL;
-  reducedGravityForce = NULL;
-  precomputedIntegrals = NULL;
-  numElementVertices = 0;
-
-  InitBuffers();
-
-  addGravity = false;
-}
 
 void StVKReducedInternalForces::Evaluate(double * q, double * fq)
 {
@@ -810,7 +871,7 @@ void StVKReducedInternalForces::Evaluate(double * q, double * fq)
 
   if (useSingleThread)
   {
-    #if (defined(WIN32) || defined(linux)) && defined(__INTEL_MKL__)
+    #if defined(_WIN32) || defined(WIN32) || defined(linux) || defined(__linux__)
       mkl_max_threads = mkl_get_max_threads();
       mkl_dynamic = mkl_get_dynamic();
       mkl_set_num_threads(1);
@@ -881,7 +942,7 @@ void StVKReducedInternalForces::Evaluate(double * q, double * fq)
 
   if (useSingleThread)
   {
-    #if (defined(WIN32) || defined(linux)) && defined(__INTEL_MKL__)
+    #if defined(_WIN32) || defined(WIN32) || defined(linux) || defined(__linux__)
       mkl_set_num_threads(mkl_max_threads);
       mkl_set_dynamic(mkl_dynamic);
     #elif defined(__APPLE__)
@@ -1259,7 +1320,7 @@ void StVKReducedInternalForces::PrintCubicCoefficients()
         }
 }
 
-void StVKReducedInternalForces::UseSingleThread(int useSingleThread_)
+void StVKReducedInternalForces::UseSingleThreadInEvaluation(int useSingleThread_)
 {
   useSingleThread = useSingleThread_;
 }
@@ -1286,14 +1347,52 @@ void StVKReducedInternalForces::Scale(double scalingFactor)
     cubicCoef_[i] *= scalingFactor;
 }
 
-StVKReducedInternalForces * StVKReducedInternalForces::ShallowClone()
+StVKReducedInternalForces * StVKReducedInternalForces::ShallowClone(int deepCloneGravityBuffer)
 {
   StVKReducedInternalForces * output = new StVKReducedInternalForces(*this); // invoke default copy constructor
   output->shallowCopy = 1;
   output->InitBuffers();
+  if (deepCloneGravityBuffer)
+  {
+    output->reducedGravityForce = NULL;
+    output->shallowCopy = 2;
+  }
   return output;
 }
 
+int StVKReducedInternalForces::SaveEmptyCub(const char * filename)
+{
+  FILE * fout = fopen(filename, "wb");
+  if (!fout)
+    return 1;
+
+  SaveEmptyCub(fout);
+
+  fclose(fout);
+  return 0;
+}
+
+int StVKReducedInternalForces::SaveEmptyCub(FILE * fout)
+{
+  int r = 0;
+  if ((int)(fwrite(&r,sizeof(int),1,fout)) < 1)
+    return 1;
+
+  int linearSize = 0;
+  if ((int)(fwrite(&linearSize,sizeof(int),1,fout)) < 1)
+    return 1;
+
+  int quadraticSize = 0;
+  if ((int)(fwrite(&quadraticSize,sizeof(int),1,fout)) < 1)
+    return 1;
+
+  int cubicSize = 0;
+  if ((int)(fwrite(&cubicSize,sizeof(int),1,fout)) < 1)
+    return 1;
+
+  return 0;
+}
+
 /*
   // the cached version of Evaluate... performs slowly than the straight version
   // build products q_i * q_j
diff --git a/src/libreducedStvk/StVKReducedInternalForces.h b/libraries/reducedStvk/StVKReducedInternalForces.h
similarity index 85%
rename from src/libreducedStvk/StVKReducedInternalForces.h
rename to libraries/reducedStvk/StVKReducedInternalForces.h
index 31b4f2ce20105434bc66a29bac04377a8d4ddaca..11f61ecfd6819cf88848a6e5e239ec72aec5e8b8 100644
--- a/src/libreducedStvk/StVKReducedInternalForces.h
+++ b/libraries/reducedStvk/StVKReducedInternalForces.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -54,22 +58,21 @@ public:
   // r : dimension of the basis
   // U : the basis matrix (it must be of size 3*n x r, where n is the number of vertices in the volumetric mesh)
   // volumetricMesh, precomputedABCDIntegrals: same meaning as in StVKInternalForces.h
-  // initOnly:
-  //   0: normal mode (default, recommended)
-  //   1: do not actually run the computation (advanced low-level mode; must then manually call ProcessElements)
   // note: this computation can last for several minutes, depending on r and n
-  StVKReducedInternalForces(int r, double * U, VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, int initOnly=0, bool addGravity=false, double g=9.81, int verbose=1);
+  StVKReducedInternalForces(int r, double * U, VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity=false, double g=9.81, int verbose=1);
 
   // load the previously computed coefficients from a file 
   // if r>=0 is specified, only up to first r modes will be used; if r=-1 (default), all modes will be used
   // bigEndianMachine allows you to load little endian data (e.g. PC, Mac Intel) on a big endian machine (e.g. Mac PowerPC); default: 0 (no conversion)
-  StVKReducedInternalForces(char * filename, int r=-1, int bigEndianMachine=0, int verbose=1);
+  StVKReducedInternalForces(const char * filename, int r=-1, int bigEndianMachine=0, int verbose=1);
+  StVKReducedInternalForces(FILE * fin, int r=-1, int bigEndianMachine=0, int verbose=1); // read from binary stream
 
   ~StVKReducedInternalForces();
 
   // saves coefficients to a disk file (in binary format, the convention is to use the .cub file extension)
-  int Save(char * filename);
-
+  int Save(const char * filename);
+  int Save(FILE * fout); // saves to stream
+  
   // evaluates the reduced internal forces for the given configuration q, result is written into fq (which must be a pre-allocated vector of length r)
   // see also the f_int(x) comment in StVKInternalForces.h on the sign of fq; the comment applies here too
   void Evaluate(double * q, double * fq);
@@ -81,7 +84,7 @@ public:
 
   inline int Getr() { return r; }
   // report r from the given polynomial binary file 
-  static int GetrFromFile(char * filename);
+  static int GetrFromFile(const char * filename);
 
   void Scale(double scalingFactor); // scales all coefficients with the given scaling factor; i.e., linearly and uniformly scale the stiffness of the model; frequency spectrum will scale linearly by sqrt(scalingFactor)
 
@@ -101,7 +104,7 @@ public:
 
   // compares the value of the polynomial with evaluation via U^T f(Uq)
   // prints out the two values (they should be equal)
-  void TestPolynomial(double * q, StVKInternalForces * stVKInternalForces, char * modalMatrixFilename);
+  void TestPolynomial(double * q, StVKInternalForces * stVKInternalForces, const char * modalMatrixFilename);
 
   // sorts three integers in ascending order
   static void tripleSort(int & a, int & b, int & c);
@@ -119,10 +122,10 @@ public:
   double MaxAbsCubicCoefficient();
   double AverageAbsCubicCoefficient();
 
-  static inline int GetLinearSize(int r) { return r; }
-  static inline int GetQuadraticSize(int r) { return r*(r+1)/2; }
-  static inline int GetCubicSize(int r) { return r * (r+1) * (r+2) / 6; }
-  static inline int GetTotalFileSize(int r) { return r * (GetLinearSize(r) + GetQuadraticSize(r) + GetCubicSize(r)) + 4; }
+  static inline int GetLinearSize(int r) { return r; } // number of double-precision entries
+  static inline int GetQuadraticSize(int r) { return r*(r+1)/2; } // number of double-precision entries
+  static inline int GetCubicSize(int r) { return r * (r+1) * (r+2) / 6; } // number of double-precision entries
+  static inline int GetTotalFileSize(int r) { return r * (GetLinearSize(r) + GetQuadraticSize(r) + GetCubicSize(r)) * sizeof(double) + 4 * sizeof(int); } // in bytes
 
   inline int GetLinearSize() { return GetLinearSize(r); }
   inline int GetQuadraticSize() { return GetQuadraticSize(r); }
@@ -133,15 +136,15 @@ public:
   inline double * GetQuadraticTermsBuffer() { return quadraticCoef_; }
   inline double * GetCubicTermsBuffer() { return cubicCoef_; }
 
-  // computes the contributions from voxels startElements to endElement-1
-  void ProcessElements(int startElement, int endElement, double ** target=NULL);
-
-  void UseSingleThread(int useSingleThread);
+  void UseSingleThreadInEvaluation(int useSingleThread_);
 
   // makes shallow copies of all pointers, except those initialized by InitBuffers
   // use this if you want to Evaluate two or more identical models (i.e., two copies of an object) in parallel (to ensure thread safety)
-  // you do not need to use this if you are Evaluating a single model in parallel (e.g., using the MT derived class)
-  StVKReducedInternalForces * ShallowClone();
+  StVKReducedInternalForces * ShallowClone(int deepCloneGravityBuffer=0);
+
+  // saves a model with r=0
+  static int SaveEmptyCub(const char * filename);
+  static int SaveEmptyCub(FILE * fout);
 
 protected:
 
@@ -201,6 +204,9 @@ protected:
 
   void InitComputation(int r, double * U, VolumetricMesh * volumetricMesh);
 
+  // computes the contributions from voxels startElements to endElement-1
+  void ProcessElements(int startElement, int endElement, double ** target=NULL);
+
   void GetSizes(int r, int * linearSize, int * quadraticSize, int * cubicSize);
   void SetSizes(int r);
 
@@ -236,6 +242,8 @@ protected:
   int shallowCopy;
 
   int verbose;
+
+  int LoadFromStream(FILE * fin, int rTarget, int bigEndianMachine);
 };
 
 inline int StVKReducedInternalForces::cubicCoefPos(int index, int i, int j, int k)
diff --git a/libraries/reducedStvk/StVKReducedInternalForcesMultiLoad.cpp b/libraries/reducedStvk/StVKReducedInternalForcesMultiLoad.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7970cf75c83e2f736faaa0833eace7ade589497d
--- /dev/null
+++ b/libraries/reducedStvk/StVKReducedInternalForcesMultiLoad.cpp
@@ -0,0 +1,91 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Load/save many internal force models from one file.
+*/
+
+#include "StVKReducedInternalForcesMultiLoad.h"
+
+int StVKReducedInternalForcesMultiLoad(const char * filename, int * numModels, StVKReducedInternalForces *** stVKReducedInternalForces, int verbose)
+{
+  FILE * fin = fopen(filename, "rb");
+  if (!fin)
+  {
+    printf("Error: unable to access file %s.\n", filename);
+    return 1;
+  }
+
+  if ((int)(fread(numModels,sizeof(int),1,fin)) < 1)
+  {
+    printf("Error: couldn't read from the input cubic polynomial multifile.\n");
+    return 1;
+  }
+
+  *stVKReducedInternalForces = (StVKReducedInternalForces**) malloc (sizeof(StVKReducedInternalForces*) * *numModels);
+  for(int i=0; i<*numModels; i++)
+  {
+    int rTarget = -1;
+    int bigEndianMachine = 0;
+    (*stVKReducedInternalForces)[i] = new StVKReducedInternalForces(fin, rTarget, bigEndianMachine, verbose);
+  }
+
+  fclose(fin);
+
+  return 0;
+}
+
+int StVKReducedInternalForcesMultiSave(const char * filename, int numModels, StVKReducedInternalForces ** stVKReducedInternalForces, int verbose)
+{
+  FILE * fout = fopen(filename, "wb");
+  if (!fout)
+  {
+    printf("Error: unable to access file %s.\n", filename);
+    return 1;
+  }
+
+  if ((int)(fwrite(&numModels,sizeof(int),1,fout)) < 1)
+    return 1;
+  
+  for(int i=0; i<numModels; i++)
+  {
+    if (stVKReducedInternalForces[i] != NULL)
+      stVKReducedInternalForces[i]->Save(fout);
+    else
+      StVKReducedInternalForces::SaveEmptyCub(fout);
+  }
+
+  fclose(fout);
+
+  return 0;
+}
+
diff --git a/libraries/reducedStvk/StVKReducedInternalForcesMultiLoad.h b/libraries/reducedStvk/StVKReducedInternalForcesMultiLoad.h
new file mode 100644
index 0000000000000000000000000000000000000000..ffed1a820317918f1d5a632555858c0b8a17363e
--- /dev/null
+++ b/libraries/reducedStvk/StVKReducedInternalForcesMultiLoad.h
@@ -0,0 +1,46 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Load/save many internal force models from one file.
+*/
+
+#ifndef _STVKREDUCEDINTERNALFORCESMULTILOAD_H_
+#define _STVKREDUCEDINTERNALFORCESMULTILOAD_H_
+
+#include "StVKReducedInternalForces.h"
+
+int StVKReducedInternalForcesMultiLoad(const char * filename, int * numModels, StVKReducedInternalForces *** stVKReducedInternalForces, int verbose=0);
+int StVKReducedInternalForcesMultiSave(const char * filename, int numModels, StVKReducedInternalForces ** stVKReducedInternalForces, int verbose=0);
+
+#endif
+
diff --git a/src/libreducedStvk/StVKReducedStiffnessMatrix.cpp b/libraries/reducedStvk/StVKReducedStiffnessMatrix.cpp
similarity index 94%
rename from src/libreducedStvk/StVKReducedStiffnessMatrix.cpp
rename to libraries/reducedStvk/StVKReducedStiffnessMatrix.cpp
index 3af20da080e991613e7800c0f5d03c07c3937f9b..c05fc308f8cc2a3104adf5dfaa56ed5958237206 100644
--- a/src/libreducedStvk/StVKReducedStiffnessMatrix.cpp
+++ b/libraries/reducedStvk/StVKReducedStiffnessMatrix.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,12 +30,12 @@
  *                                                                       *
  *************************************************************************/
 
+#include "matrixMacros.h"
+#include "StVKReducedStiffnessMatrix.h"
 #include "lapack-headers.h"
-#if (defined(WIN32) || defined(linux)) && defined(__INTEL_MKL__)
+#if defined(_WIN32) || defined(WIN32) || defined(linux) || defined(__linux__)
   #include "mkl_service.h"
 #endif
-#include "matrixMacros.h"
-#include "StVKReducedStiffnessMatrix.h"
 
 StVKReducedStiffnessMatrix::~StVKReducedStiffnessMatrix()
 {
@@ -238,7 +242,7 @@ void StVKReducedStiffnessMatrix::Evaluate(double * q, double * Rq)
 
   if (useSingleThread)
   {
-    #if (defined(WIN32) || defined(linux)) && defined(__INTEL_MKL__)
+    #if defined(_WIN32) || defined(WIN32) || defined(linux) || defined(__linux__)
       mkl_max_threads = mkl_get_max_threads();
       mkl_dynamic = mkl_get_dynamic();
       mkl_set_num_threads(1);
@@ -298,7 +302,7 @@ void StVKReducedStiffnessMatrix::Evaluate(double * q, double * Rq)
 
   if (useSingleThread)
   {
-    #if (defined(WIN32) || defined(linux)) && defined(__INTEL_MKL__)
+    #if defined(_WIN32) || defined(WIN32) || defined(linux) || defined(__linux__)
       mkl_set_num_threads(mkl_max_threads);
       mkl_set_dynamic(mkl_dynamic);
     #elif defined(__APPLE__)
@@ -464,7 +468,7 @@ void StVKReducedStiffnessMatrix::EvaluateLinear(double * q, double * Rq)
 
 }
 
-int StVKReducedStiffnessMatrix::Save(char * filename)
+int StVKReducedStiffnessMatrix::Save(const char * filename)
 {
   FILE * fout = fopen(filename,"wb");
 
@@ -495,7 +499,7 @@ int StVKReducedStiffnessMatrix::Save(char * filename)
 }
 
 
-StVKReducedStiffnessMatrix::StVKReducedStiffnessMatrix(char * filename)
+StVKReducedStiffnessMatrix::StVKReducedStiffnessMatrix(const char * filename)
 {
   FILE * fin = fopen(filename,"rb");
 
diff --git a/src/libreducedStvk/StVKReducedStiffnessMatrix.h b/libraries/reducedStvk/StVKReducedStiffnessMatrix.h
similarity index 90%
rename from src/libreducedStvk/StVKReducedStiffnessMatrix.h
rename to libraries/reducedStvk/StVKReducedStiffnessMatrix.h
index e3c4dd632c510181b9606b1a359dd3126ed581cd..8e300857c5b75e13822169a629b74a3fce4def03 100644
--- a/src/libreducedStvk/StVKReducedStiffnessMatrix.h
+++ b/libraries/reducedStvk/StVKReducedStiffnessMatrix.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -58,12 +62,12 @@ public:
   StVKReducedStiffnessMatrix(StVKReducedInternalForces * stVKReducedInternalForces, int verbose=1);
 
   // load the coefficients from a file
-  StVKReducedStiffnessMatrix(char * filename);
+  StVKReducedStiffnessMatrix(const char * filename);
 
   ~StVKReducedStiffnessMatrix();
 
   // saves coefficients (in binary format; the convention is to use the .sti file extension)
-  int Save(char * filename);
+  int Save(const char * filename);
 
   // evaluates the stiffness matrix for the given configuration q, result is written into Kq (must be a pre-allocated r x r matrix)
   // Kq will be symmetric; the routine returns all the r*r entries of the matrix, as opposed to just the upper triangle
@@ -95,7 +99,6 @@ public:
 
   // makes shallow copies of all pointers, except those initialized by InitBuffers
   // use this if you want to Evaluate two or more identical models (i.e., two copies of an object) in parallel (to ensure thread safety)
-  // you do not need to use this if you are Evaluating a single model in parallel (e.g., using the MT derived class)
   StVKReducedStiffnessMatrix * ShallowClone();
 
 protected:
diff --git a/src/librenderVolumetricMesh/renderVolumetricMesh.cpp b/libraries/renderVolumetricMesh/renderVolumetricMesh.cpp
similarity index 73%
rename from src/librenderVolumetricMesh/renderVolumetricMesh.cpp
rename to libraries/renderVolumetricMesh/renderVolumetricMesh.cpp
index ddb6b8e86a5a232d3bce6f4391f3cc23b66efe68..edcfa0883dc2296a42f3c0f5da032d2997f65ff8 100644
--- a/src/librenderVolumetricMesh/renderVolumetricMesh.cpp
+++ b/libraries/renderVolumetricMesh/renderVolumetricMesh.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "renderVolumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT,    *
- *                                                          2013 USC     *
+ *                                                          2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,17 +32,14 @@
  *************************************************************************/
 
 #include <float.h>
-using namespace std;
-
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
 #include "openGL-headers.h"
+#include "printBitmap.h"
+#include "openGLHelper.h"
 #include "renderVolumetricMesh.h"
 #include "volumetricMeshENuMaterial.h"
 #include "cubicMesh.h"
 #include "tetMesh.h"
+using namespace std;
 
 // controls how material groups are rendered
 #define RENDERVOLUMETRICMESH_RENDERINGMODE_FLAT 0
@@ -47,6 +48,12 @@ using namespace std;
 
 RenderVolumetricMesh::RenderVolumetricMesh()
 {
+  maxE = DBL_MAX;
+  maxnu = 0.5;
+  maxDensity = DBL_MAX;
+  minE = 0;
+  minnu = 0;
+  minDensity = 0;
   renderingMode = RENDERVOLUMETRICMESH_RENDERINGMODE_DISCRETECOLORS;
 }
 
@@ -104,10 +111,10 @@ void RenderVolumetricMesh::DetermineMaxMin(VolumetricMesh * volumetricMesh)
 
 void RenderVolumetricMesh::RenderTet(VolumetricMesh * volumetricMesh, int el, int wireframe)
 {
-  Vec3d v0 = *(volumetricMesh->getVertex(el,0));
-  Vec3d v1 = *(volumetricMesh->getVertex(el,1));
-  Vec3d v2 = *(volumetricMesh->getVertex(el,2));
-  Vec3d v3 = *(volumetricMesh->getVertex(el,3));
+  Vec3d v0 = volumetricMesh->getVertex(el,0);
+  Vec3d v1 = volumetricMesh->getVertex(el,1);
+  Vec3d v2 = volumetricMesh->getVertex(el,2);
+  Vec3d v3 = volumetricMesh->getVertex(el,3);
 
   #define RENDERVTX(i) glVertex3f(v##i[0], v##i[1], v##i[2]);
   if (wireframe)
@@ -154,16 +161,24 @@ void RenderVolumetricMesh::RenderTet(VolumetricMesh * volumetricMesh, int el, in
   }
 }
 
+void RenderVolumetricMesh::RenderElement(VolumetricMesh * volumetricMesh, int el, int wireframe)
+{
+  if (volumetricMesh->getElementType() == CubicMesh::elementType())
+    RenderCube(volumetricMesh, el, wireframe);
+  else if (volumetricMesh->getElementType() == TetMesh::elementType())
+    RenderTet(volumetricMesh, el, wireframe);
+}
+
 void RenderVolumetricMesh::RenderCube(VolumetricMesh * volumetricMesh, int el, int wireframe)
 {
   // move to vertex 0
   glPushMatrix();
-  Vec3d v0 = *(volumetricMesh->getVertex(el,0));
+  Vec3d v0 = volumetricMesh->getVertex(el,0);
   glTranslated(v0[0],v0[1],v0[2]);
 
-  Vec3d v1 = *(volumetricMesh->getVertex(el,1));
-  Vec3d v3 = *(volumetricMesh->getVertex(el,3));
-  Vec3d v4 = *(volumetricMesh->getVertex(el,4));
+  Vec3d v1 = volumetricMesh->getVertex(el,1);
+  Vec3d v3 = volumetricMesh->getVertex(el,3);
+  Vec3d v4 = volumetricMesh->getVertex(el,4);
     
   Vec3d axisX = norm(v1-v0);
   Vec3d axisY = norm(v3-v0);
@@ -189,66 +204,6 @@ void RenderVolumetricMesh::RenderCube(VolumetricMesh * volumetricMesh, int el, i
   glPopMatrix();
 }
 
-void RenderVolumetricMesh::JetColorMap(double x, double color[3])
-{
-  double a; // alpha
-
-  if (x < 0)
-  {
-    color[0] = 0;
-    color[1] = 0;
-    color[2] = 0;
-    return;
-  } 
-  else if (x < 0.125) 
-  {
-    a = x / 0.125;
-    color[0] = 0;
-    color[1] = 0;
-    color[2] = 0.5 + 0.5 * a;
-    return;
-  }
-  else if (x < 0.375) 
-  {
-    a = (x - 0.125) / 0.25;
-    color[0] = 0;
-    color[1] = a;
-    color[2] = 1;
-    return;
-  }
-  else if (x < 0.625) 
-  {         
-    a = (x - 0.375) / 0.25;
-    color[0] = a;
-    color[1] = 1;
-    color[2] = 1 - a;
-    return;
-  }     
-  else if (x < 0.875) 
-  {
-    a = (x - 0.625) / 0.25;
-    color[0] = 1;
-    color[1] = 1 - a;
-    color[2] = 0;
-    return;
-  }     
-  else if (x <= 1.0) 
-  {
-    a = (x - 0.875) / 0.125;
-    color[0] = 1 - 0.5 * a;
-    color[1] = 0;
-    color[2] = 0;
-    return;
-  }
-  else
-  {
-    color[0] = 1;
-    color[1] = 1;
-    color[2] = 1;
-    return;
-  }
-}
-
 void RenderVolumetricMesh::Render(VolumetricMesh * volumetricMesh, int wireframe, double * u)
 {
   glDisable(GL_LIGHTING);
@@ -262,29 +217,21 @@ void RenderVolumetricMesh::Render(VolumetricMesh * volumetricMesh, int wireframe
   if (renderingMode == RENDERVOLUMETRICMESH_RENDERINGMODE_DISCRETECOLORS)
   {
     if (volumetricMesh->getNumMaterials() == 0)
-    {
       printf("Error: discrete color rendering mode in renderVolumetricMesh called with zero materials.\n");
-    }
 
     map<int,int> actualMaterials;
     for(int ss=0; ss < volumetricMesh->getNumRegions(); ss++)
     {
       int materialIndex = volumetricMesh->getRegion(ss)->getMaterialIndex();
       int setIndex = volumetricMesh->getRegion(ss)->getSetIndex();
-      VolumetricMesh::Set * elementSet = volumetricMesh->getSet(setIndex);
+      const VolumetricMesh::Set * elementSet = volumetricMesh->getSet(setIndex);
       int numElements = elementSet->getNumElements();
 
       map<int,int> :: iterator iter = actualMaterials.find(materialIndex);
-      if (iter == actualMaterials.end())
-      {
-        // new material
+      if (iter == actualMaterials.end()) // new material
         actualMaterials.insert(make_pair(materialIndex, numElements));
-      }
-      else
-      {
-        // existing material
+      else // existing material
         iter->second += numElements;
-      }
     }
     int numActualMaterials = (int)actualMaterials.size();
 
@@ -304,7 +251,7 @@ void RenderVolumetricMesh::Render(VolumetricMesh * volumetricMesh, int wireframe
     double multiplicator = (numActualMaterials == 1 ? 1.0 : 1.0 / (numActualMaterials - 1));
     for(int ss=0; ss < volumetricMesh->getNumRegions(); ss++)
     {
-      VolumetricMesh::Region * region = volumetricMesh->getRegion(ss);
+      const VolumetricMesh::Region * region = volumetricMesh->getRegion(ss);
 
       int materialIndex = region->getMaterialIndex(); 
       double color[3];
@@ -315,7 +262,7 @@ void RenderVolumetricMesh::Render(VolumetricMesh * volumetricMesh, int wireframe
       color[2] = gray;
 
       int setIndex = region->getSetIndex(); 
-      VolumetricMesh::Set * elementSet = volumetricMesh->getSet(setIndex);
+      const VolumetricMesh::Set * elementSet = volumetricMesh->getSet(setIndex);
       set<int> elements;
       elementSet->getElements(elements);
 
@@ -338,9 +285,9 @@ void RenderVolumetricMesh::Render(VolumetricMesh * volumetricMesh, int wireframe
         }
         else
         {
-          #define VER(j) (*volumetricMesh->getVertex(el,j))[0] + u[3*volumetricMesh->getVertexIndex(el, j)+0],\
-		         (*volumetricMesh->getVertex(el,j))[1] + u[3*volumetricMesh->getVertexIndex(el, j)+1],\
-		         (*volumetricMesh->getVertex(el,j))[2] + u[3*volumetricMesh->getVertexIndex(el, j)+2]
+          #define VER(j) volumetricMesh->getVertex(el,j)[0] + u[3*volumetricMesh->getVertexIndex(el, j)+0],\
+		         volumetricMesh->getVertex(el,j)[1] + u[3*volumetricMesh->getVertexIndex(el, j)+1],\
+		         volumetricMesh->getVertex(el,j)[2] + u[3*volumetricMesh->getVertexIndex(el, j)+2]
           if (wireframe)
           {
             if (meshType == 1)
@@ -428,9 +375,9 @@ void RenderVolumetricMesh::Render(VolumetricMesh * volumetricMesh, int wireframe
       }
       else
       {
-        #define VERA(j) (*volumetricMesh->getVertex(i,j))[0] + u[3*volumetricMesh->getVertexIndex(i, j)+0],\
-                        (*volumetricMesh->getVertex(i,j))[1] + u[3*volumetricMesh->getVertexIndex(i, j)+1],\
-	                (*volumetricMesh->getVertex(i,j))[2] + u[3*volumetricMesh->getVertexIndex(i, j)+2]
+        #define VERA(j) volumetricMesh->getVertex(i,j)[0] + u[3*volumetricMesh->getVertexIndex(i, j)+0],\
+                        volumetricMesh->getVertex(i,j)[1] + u[3*volumetricMesh->getVertexIndex(i, j)+1],\
+	                volumetricMesh->getVertex(i,j)[2] + u[3*volumetricMesh->getVertexIndex(i, j)+2]
 
         if (wireframe)
         {
@@ -473,9 +420,9 @@ void RenderVolumetricMesh::RenderWireframeDeformation(VolumetricMesh * volumetri
 void RenderVolumetricMesh::RenderVertexDeformed(VolumetricMesh * volumetricMesh, int ver, double * U)
 {
   glBegin(GL_POINTS);
-    glVertex3f((*volumetricMesh->getVertex(ver))[0]+U[3*(ver)+0],
-	       (*volumetricMesh->getVertex(ver))[1]+U[3*(ver)+1],
-               (*volumetricMesh->getVertex(ver))[2]+U[3*(ver)+2]);
+    glVertex3f(volumetricMesh->getVertex(ver)[0]+U[3*(ver)+0],
+	       volumetricMesh->getVertex(ver)[1]+U[3*(ver)+1],
+               volumetricMesh->getVertex(ver)[2]+U[3*(ver)+2]);
   glEnd();
 }
 
@@ -486,8 +433,8 @@ void RenderVolumetricMesh::RenderVertices(VolumetricMesh * volumetricMesh)
   glBegin(GL_POINTS);
   for (i=0; i < volumetricMesh->getNumVertices(); i++)
   {
-     Vec3d * vertex = volumetricMesh->getVertex(i);
-     glVertex3f((*vertex)[0],(*vertex)[1],(*vertex)[2]);
+     Vec3d vertex = volumetricMesh->getVertex(i);
+     glVertex3f(vertex[0],vertex[1],vertex[2]);
   }
   glEnd();
 }
@@ -500,8 +447,8 @@ void RenderVolumetricMesh::RenderVertices
   std::set<int> :: iterator iter;
   for(iter = vertices->begin(); iter != vertices->end(); iter++)
   {
-    Vec3d * vertex = volumetricMesh->getVertex(*iter + offset);
-    glVertex3f((*vertex)[0],(*vertex)[1],(*vertex)[2]);
+    Vec3d vertex = volumetricMesh->getVertex(*iter + offset);
+    glVertex3f(vertex[0],vertex[1],vertex[2]);
   }
   glEnd();
 }
@@ -513,8 +460,8 @@ void RenderVolumetricMesh::RenderVertices(VolumetricMesh * volumetricMesh,
   int offset = (oneIndexed ? -1 : 0);
   for (int i=0; i < numVertices; i++)
   {
-    Vec3d * vertex = volumetricMesh->getVertex(vertices[i] + offset);
-    glVertex3f((*vertex)[0],(*vertex)[1],(*vertex)[2]);
+    Vec3d vertex = volumetricMesh->getVertex(vertices[i] + offset);
+    glVertex3f(vertex[0],vertex[1],vertex[2]);
   }
   glEnd();
 }
@@ -525,8 +472,8 @@ void RenderVolumetricMesh::SelectRenderVertices(VolumetricMesh * volumetricMesh)
   {
     glLoadName(i+1);
     glBegin(GL_POINTS);
-      Vec3d * vertex = volumetricMesh->getVertex(i);
-      glVertex3f((*vertex)[0],(*vertex)[1],(*vertex)[2]);
+      Vec3d vertex = volumetricMesh->getVertex(i);
+      glVertex3f(vertex[0],vertex[1],vertex[2]);
     glEnd();
   }
 }
@@ -536,8 +483,8 @@ void RenderVolumetricMesh::DrawSelectedPoints(VolumetricMesh * volumetricMesh, i
   glBegin(GL_POINTS);
   for (int i=0; i < numSelectedVertices; i++)
   {
-    Vec3d * vertex = volumetricMesh->getVertex(selectedVertices[i]-1);
-    glVertex3f((*vertex)[0],(*vertex)[1],(*vertex)[2]);
+    Vec3d vertex = volumetricMesh->getVertex(selectedVertices[i]-1);
+    glVertex3f(vertex[0],vertex[1],vertex[2]);
   }
   glEnd();
 }
@@ -549,8 +496,8 @@ void RenderVolumetricMesh::DrawUnselectedPoints(VolumetricMesh * volumetricMesh,
   {
     if (selectionArray[i+1] != 0)
       continue;
-    Vec3d * vertex = volumetricMesh->getVertex(i);
-    glVertex3f((*vertex)[0],(*vertex)[1],(*vertex)[2]);
+    Vec3d vertex = volumetricMesh->getVertex(i);
+    glVertex3f(vertex[0],vertex[1],vertex[2]);
   }
   glEnd();
 }
@@ -607,122 +554,20 @@ void RenderVolumetricMesh::RenderVertexLabels(VolumetricMesh * volumetricMesh, i
   // labels are printed out in the range 1... , not 0...
   for (int i=start; i< end; i++)
   {
-    Vec3d * vertex = volumetricMesh->getVertex(i);
-    print_bitmap_integer((*vertex)[0],(*vertex)[1],(*vertex)[2],i+1);
+    Vec3d vertex = volumetricMesh->getVertex(i);
+    print_bitmap_integer(vertex[0],vertex[1],vertex[2],i+1);
   }
 }
 
-void RenderVolumetricMesh::UnitCube()
-{
-  glBegin(GL_TRIANGLES);
-
-  glNormal3f(0,-1,0);  
-
-  glVertex3f(0,0,0); // front
-  glVertex3f(1,0,0);
-  glVertex3f(1,0,1);
-
-  glVertex3f(0,0,0);
-  glVertex3f(1,0,1);
-  glVertex3f(0,0,1);
-
-  glNormal3f(0,1,0);  
-
-  glVertex3f(0,1,0); // back
-  glVertex3f(1,1,1);
-  glVertex3f(1,1,0);
-
-  glVertex3f(0,1,1);
-  glVertex3f(1,1,1);
-  glVertex3f(0,1,0);
-
-  glNormal3f(1,0,0);  
-
-  glVertex3f(1,0,0); // right
-  glVertex3f(1,1,0);
-  glVertex3f(1,1,1);
-
-  glVertex3f(1,0,0);
-  glVertex3f(1,1,1);
-  glVertex3f(1,0,1);
-
-  glNormal3f(-1,0,0);  
-
-  glVertex3f(0,0,0); // left
-  glVertex3f(0,1,1);
-  glVertex3f(0,1,0);
-
-  glVertex3f(0,0,1);
-  glVertex3f(0,1,1);
-  glVertex3f(0,0,0);
-
-  glNormal3f(0,0,1);  
-
-  glVertex3f(0,0,1); // top
-  glVertex3f(1,0,1);
-  glVertex3f(1,1,1);
-
-  glVertex3f(0,0,1);
-  glVertex3f(1,1,1);
-  glVertex3f(0,1,1);
-
-  glNormal3f(0,0,-1);  
-
-  glVertex3f(0,0,0); // bottom
-  glVertex3f(1,1,0);
-  glVertex3f(1,0,0);
-
-  glVertex3f(0,1,0);
-  glVertex3f(1,1,0);
-  glVertex3f(0,0,0);
-
-  glEnd();
-}
-
-void RenderVolumetricMesh::UnitCubeWireframe()
-{
-  glBegin(GL_LINES); 
-    glVertex3f(0,0,0);
-    glVertex3f(1,0,0);
-    glVertex3f(0,1,0);
-    glVertex3f(1,1,0);
-    glVertex3f(0,0,0);
-    glVertex3f(0,1,0);
-    glVertex3f(1,0,0);
-    glVertex3f(1,1,0);
-
-    glVertex3f(0,0,1);
-    glVertex3f(1,0,1);
-    glVertex3f(0,1,1);
-    glVertex3f(1,1,1);
-    glVertex3f(0,0,1);
-    glVertex3f(0,1,1);
-    glVertex3f(1,0,1);
-    glVertex3f(1,1,1);
-
-    glVertex3f(0,0,0);
-    glVertex3f(0,0,1);
-
-    glVertex3f(0,1,0);
-    glVertex3f(0,1,1);
-
-    glVertex3f(1,0,0);
-    glVertex3f(1,0,1);
-
-    glVertex3f(1,1,0);
-    glVertex3f(1,1,1);
-  glEnd();
-}
-
 void RenderVolumetricMesh::CubeDeformable(double u0x,double u0y,double u0z,
-					double u1x,double u1y,double u1z,
-					double u2x,double u2y,double u2z,
-					double u3x,double u3y,double u3z,
-					double u4x,double u4y,double u4z,
-					double u5x,double u5y,double u5z,
-					double u6x,double u6y,double u6z,
-					double u7x,double u7y,double u7z
-					)
+    double u1x,double u1y,double u1z,
+    double u2x,double u2y,double u2z,
+    double u3x,double u3y,double u3z,
+    double u4x,double u4y,double u4z,
+    double u5x,double u5y,double u5z,
+    double u6x,double u6y,double u6z,
+    double u7x,double u7y,double u7z
+)
 {
   glBegin(GL_TRIANGLES);
 
@@ -790,14 +635,14 @@ void RenderVolumetricMesh::CubeDeformable(double u0x,double u0y,double u0z,
 }
 
 void RenderVolumetricMesh::CubeWireframeDeformable(double u0x,double u0y,double u0z,
-    			double u1x,double u1y,double u1z,
-			double u2x,double u2y,double u2z,
-			double u3x,double u3y,double u3z,
-			double u4x,double u4y,double u4z,
-			double u5x,double u5y,double u5z,
-			double u6x,double u6y,double u6z,
-			double u7x,double u7y,double u7z
-					)
+    double u1x,double u1y,double u1z,
+    double u2x,double u2y,double u2z,
+    double u3x,double u3y,double u3z,
+    double u4x,double u4y,double u4z,
+    double u5x,double u5y,double u5z,
+    double u6x,double u6y,double u6z,
+    double u7x,double u7y,double u7z
+)
 {
   glBegin(GL_LINES); 
     glVertex3f(u0x,u0y,u0z);
@@ -833,10 +678,10 @@ void RenderVolumetricMesh::CubeWireframeDeformable(double u0x,double u0y,double
 }
 
 void RenderVolumetricMesh::TetDeformable(double u0x,double u0y,double u0z,
-					 double u1x,double u1y,double u1z,
-					 double u2x,double u2y,double u2z,
-					 double u3x,double u3y,double u3z
-					)
+    double u1x,double u1y,double u1z,
+    double u2x,double u2y,double u2z,
+    double u3x,double u3y,double u3z
+)
 {
   #define RENDERVTXALT(i) glVertex3f(u##i##x, u##i##y, u##i##z);
   glBegin(GL_TRIANGLES);
@@ -859,10 +704,10 @@ void RenderVolumetricMesh::TetDeformable(double u0x,double u0y,double u0z,
 }
 
 void RenderVolumetricMesh::TetWireframeDeformable(double u0x,double u0y,double u0z,
-					 double u1x,double u1y,double u1z,
-					 double u2x,double u2y,double u2z,
-					 double u3x,double u3y,double u3z
-					)
+    double u1x,double u1y,double u1z,
+    double u2x,double u2y,double u2z,
+    double u3x,double u3y,double u3z
+)
 {
   glBegin(GL_LINES);
     RENDERVTXALT(0);
diff --git a/src/librenderVolumetricMesh/renderVolumetricMesh.h b/libraries/renderVolumetricMesh/renderVolumetricMesh.h
similarity index 88%
rename from src/librenderVolumetricMesh/renderVolumetricMesh.h
rename to libraries/renderVolumetricMesh/renderVolumetricMesh.h
index 771591ec54b5e1849aad1a8e908900fd6748f20e..479d33b5fe86923e19f04eea5dc2caee63db54e0 100644
--- a/src/librenderVolumetricMesh/renderVolumetricMesh.h
+++ b/libraries/renderVolumetricMesh/renderVolumetricMesh.h
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "renderVolumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT,    *
- *                                                          2013 USC     *
+ *                                                          2018 USC     *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,7 +35,6 @@
 #define _RENDER_VOLUMETRIC_MESH_
 
 #include <set>
-#include "printBitmap.h"
 #include "volumetricMesh.h"
 
 class RenderVolumetricMesh
@@ -43,6 +46,8 @@ public:
   void RenderWireframe(VolumetricMesh * volumetricMesh);
   void RenderSolidAndWireframe(VolumetricMesh * volumetricMesh);
 
+  void RenderElement(VolumetricMesh * volumetricMesh, int el, int wireframe=0);
+
   void RenderVertices(VolumetricMesh * volumetricMesh);
   void RenderVertices(VolumetricMesh * volumetricMesh, int * vertices, int numVertices, bool oneIndexed=true);
   void RenderVertices(VolumetricMesh * volumetricMesh, std::set<int> * vertices, bool oneIndexed=true);
@@ -64,9 +69,6 @@ public:
   void SetGradedRenderingMode(VolumetricMesh * volumetricMesh); // colored graded, according to numerical values of E, nu, density
   void SetDiscreteRenderingMode(); // different material groups colored with distinct colors (default)
 
-  static void UnitCube();
-  static void UnitCubeWireframe();
-
 protected:
   double maxE;
   double maxnu;
@@ -113,7 +115,6 @@ protected:
   void RenderCube(VolumetricMesh * volumetricMesh, int el, int wireframe=0);
   void RenderTet(VolumetricMesh * volumetricMesh, int el, int wireframe=0);
 
-  void JetColorMap(double x, double color[3]);
   void DetermineMaxMin(VolumetricMesh * volumetricMesh);
 };
 
diff --git a/src/librigidBodyDynamics/example.cpp b/libraries/rigidBodyDynamics/example.cpp
similarity index 100%
rename from src/librigidBodyDynamics/example.cpp
rename to libraries/rigidBodyDynamics/example.cpp
diff --git a/src/librigidBodyDynamics/rigidBody.cpp b/libraries/rigidBodyDynamics/rigidBody.cpp
similarity index 94%
rename from src/librigidBodyDynamics/rigidBody.cpp
rename to libraries/rigidBodyDynamics/rigidBody.cpp
index 00f0eb890219ab8f45d51def6c993c062a1f1553..a48d75c0b07547246066f2e9a9e6dd0908838602 100644
--- a/src/librigidBodyDynamics/rigidBody.cpp
+++ b/libraries/rigidBodyDynamics/rigidBody.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "rigidBodyDynamics" library , Copyright (C) 2007 CMU                  *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,8 +32,6 @@
 
 #include "rigidBody.h"
 
-namespace vega
-{
 RigidBody::RigidBody(double mass, double inertiaTensorAtRestX, double inertiaTensorAtRestY, double inertiaTensorAtRestZ)
 {
   this->mass=mass;
@@ -230,4 +232,3 @@ void RigidBody::SetRotationalDamping(double rotationalDampingCoef)
   this->rotationalDampingCoef = rotationalDampingCoef;
 }
 
-}
diff --git a/libraries/rigidBodyDynamics/rigidBody.h b/libraries/rigidBodyDynamics/rigidBody.h
new file mode 100644
index 0000000000000000000000000000000000000000..f1096a63fc7c38d318575ae5375b01f7364d2102
--- /dev/null
+++ b/libraries/rigidBodyDynamics/rigidBody.h
@@ -0,0 +1,490 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "rigidBodyDynamics" library , Copyright (C) 2007 CMU                  *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _RIGIDBODY_H_
+#define _RIGIDBODY_H_
+
+/*
+
+Rigid body dynamics
+============================================
+
+Classes 'RigidBody' and 'RigidBody_GeneralTensor' 
+implement 6-DOF rigid dynamics of a single rigid body, as explained,
+for example, in:
+   
+David Baraff:
+"An Introduction to Physically Based Modeling:
+ Rigid Body Simulation I: Unconstrained Rigid Body Dynamics"
+(SIGGRAPH 97 Course Notes)
+http://www.cs.cmu.edu/~baraff/pbm/pbm.html
+
+In other words, these two classes allow you to simulate the 
+motion of a single rigid body, under any specified (potentially
+time-varying) external forces and torques. Arbitrary tensors 
+of inertia are supported.  The solution is computed by numerically 
+timestepping the ordinary differential equations of rigid body 
+motion, derived from the Newton's 2nd law, and conservation of 
+linear momentum and angular momentum.
+
+For example, ballistic motion can be simulated
+if gravity is used as the external force.
+Objects bouncing off the ground/impacting other objects
+can be simulated if you combine
+'RigidBody' (or 'RigidBody_GeneralTensor')
+with a collision detection algorithm that provides
+the contact external (penalty-based) forces. 
+Impulse simulations are not directly supported; however
+the API does allow you to instantenously change 
+linear velocity/angular velocity, provided you compute
+the velocity changes yourself.
+
+Why are there two classes 
+('RigidBody' and 'RigidBody_GeneralTensor')? 
+============================================================
+
+Use 'RigidBody' class if the inertia tensor around the 
+center of mass, **in the rest** configuration, with respect to the 
+world coordinate axes, is diagonal. This doesn't mean that the inertia 
+tensor around the center of mass will be diagonal for arbitrary object 
+rotations, the assumption is only that the tensor **at rest** be diagonal. 
+  
+Use 'RigidBody_GeneralTensor' class for a general 3x3 matrix 
+inertia tensor in the rest configuration.
+
+Both 'RigidBody' and 'RigidBody_GeneralTensor' correctly treat 
+the inertia tensor as non-diagonal for non-identity rotations. 
+
+The reason for making 'RigidBody' a separate class is that
+time-stepping is slightly faster for 'RigidBody' since the 
+internal update of the current inertia tensor can be made simpler.
+
+
+How is the position/orientation of a rigid body defined in this class?
+======================================================================
+
+The current configuration of the rigid body is specified by the
+current world coordinate position of the center of mass 
+(this can be retrieved/set via the 'Position' vector),
+and by specifying the current rotation of the object around the center of mass.
+(this can be retrieved/set via the 'Rotation' matrix).
+One way to visualize this is to first position the object's center of mass to
+position 'Position', then rotate the object around the center of mass
+by the rotation matrix 'Rotation'.
+
+Here is an alternative explanation:
+Suppose the body is initially positioned such that the center of mass
+coincides with the origin of the world coordinate system and suppose
+the location of some material point on the object is given by a
+3-dim vector X, in the world coordinate system. 
+Then, the current location of this material point, again in the world
+coordinate system, equals: 
+Position + Rotation * X,
+where Position is the current location of the center of mass, and
+Rotation is the current rotation of the object around its center of mass.
+
+The routines in this class timestep Position and Rotation according
+to your provided external forces, using the explicit Euler integrator.
+They do not provide any code to
+actually display the rigid body on the screen. For that, you need
+to use a 3D graphics library, such as OpenGL.
+
+How to use the classes?
+=======================
+
+You must:
+1. Specify object rigid body parameters (in the constructor): 
+  1.1. mass, and
+  1.2. inertia tensor in the rest configuration. This tensor is a 3x3 matrix and must be given with respect to the world-coordinate axes, and around the center of mass.
+2. Provide external forces and torques at every timestep. 
+3. Call the explicit Euler integrator routine to time-step the dynamics to the next time step
+
+At any timestep, you can query object position/rotation, velocity/angular velocity, linear/angular momentum, current inertia tensor (with respect to world-coordinate axes), etc.
+
+See also example.cpp.
+
+
+Misc
+====
+
+Code version: 1.0
+
+All matrices are stored in row-major format:
+
+    [ A[0] A[1] A[2] ]
+A = [ A[3] A[4] A[5] ]
+    [ A[6] A[7] A[8] ]
+
+Dependencies: the Quaternion C++ class 
+
+Things to do: 
+Implement a higher order integrator, such as Runge-Kutta 2nd order 
+or 4th order. Add support for impulses.
+
+*/
+
+#include <string.h>
+#include "quaternion.h"
+
+class RigidBody
+{
+public:
+
+  // inertia tensor parameter refers to the following coordinate system (and with body's rotation = identity)
+  //   origin: center of mass
+  //   axes: aligned with world-coordinate-system axes
+  // constructor will set position and rotation to zero/identity, use SetPosition/Rotation to place the object wherever you want
+  // assumes diagonal inertia tensor at rest (and given by diag(inertiaTensorAtRestX,inertiaTensorAtRestY,inertiaTensorAtRestZ))
+  RigidBody(double mass, double inertiaTensorAtRestX, double inertiaTensorAtRestY, double inertiaTensorAtRestZ); 
+  virtual ~RigidBody() {};
+
+  // === query current state ===
+
+  inline void GetPosition(double * x, double * y, double * z);
+  inline void GetPointPosition(double X, double Y, double Z, double * x, double * y, double * z); // retrieve current world-coordinate position of a point which has world-coordinates (X,Y,Z) when center of mass is at the world-coordinate origin, and rotation is identity
+  inline void GetRotation(double * R);
+  inline Quaternion<double> GetQuaternion(); // returns the quaternion corresponding to the current rotation
+  inline void GetVelocity(double * vx, double * vy, double * vz);
+  inline void GetAngularVelocity(double * wx, double * wy, double * wz);
+  inline void GetAngularMomentum(double * amx, double * amy, double * amz);
+  inline double GetMass(); // returns the mass
+  inline void GetInertiaTensor(double * IT); // returns the current inertia tensor (row-major 3x3 matrix)
+  void TransformToLocal(double * globalVector, double * localVector); // transform a world-coordinate vector to the frame of the rigid object
+  void TransformToGlobal(double * localVector, double * globalVector); // transform a world-coordinate vector to the frame of the rigid object
+
+  // === set current state ===
+
+  // NOTE: all of the following routines of course also update the internal simulation state as necessary, e.g. if you change angular velocity, angular momentum will also be updated
+  inline void SetPosition(double x, double y, double z); // updates the position of the center of mass
+    // updates the position of the center of mass, such that point with material coordinates (X,Y,Z) (in object's coordinate system) ends up at world-coordinate position (posx,posy,posz)
+  inline void SetPosition(double X, double Y, double Z, double posx, double posy, double posz); 
+  inline void SetRotation(double R[9]); // updates the rotation
+  inline void SetRotation(Quaternion<double> q); // updates the rotation
+  inline void SetVelocity(double vx, double vy, double vz); 
+  inline void SetAngularVelocity(double wx, double wy, double wz); 
+  inline void SetAngularMomentum(double amx, double amy, double amz); 
+  inline void ResetBodyToRest(); // resets position to the origin, rotation to identity, and linear and angular velocities to zero
+  void SetToAnotherBody(RigidBody & anotherBody); // set position, velocity, rotation and angular velocity to that of "anotherBody"
+
+  // === prescribe external forces/torques ===
+
+  // all the forces and torques remain active (for all subsequent timesteps) until explicitly changed; use ResetWrenches() to reset to zero or Set* to set to a new value
+  // the torques refer to the coordinate system where the origin is at the center of mass, and where the axes are aligned with the world coordinate axes 
+  // (and not with the body's axes!!)
+  // you need to set the forces and torques separately; setting the forces doesn't set any torques
+  inline void ResetExternalForce(); // does not reset torques
+  inline void SetExternalForce(double fx, double fy, double fz); // sets the current total external force
+  inline void AddExternalForce(double fx, double fy, double fz); // force is added to whatever force already previously specified
+  inline void ResetExternalTorque(); // does not reset forces
+  inline void SetExternalTorque(double tx, double ty, double tz); // sets the current total external torque
+  inline void AddExternalTorque(double tx, double ty, double tz); // torque is added to whatever torques already previously specified
+  inline void ResetWrenches() { ResetExternalForce(); ResetExternalTorque(); }
+
+  // this helper function computes torque arising from a single force f acting at world-coordinate position forcePos
+  // NOTE: you must still call one of the above functions to actually set the torque,  i.e. call (Set/Add)ExternalTorque(tx,ty,tz) after computing the torque via ComputeTorque
+  inline void ComputeTorque(double forcePosx, double forcePosy, double forcePosz, 
+	                    double fx, double fy, double fz,
+	                    double * tx, double * ty, double * tz);
+
+  // upVector must be normalized
+  inline void AddGravity(double g=9.81, double upVectorx = 0.0, double upVectory = 1.0, double upVectorz = 0.0);
+  void SetLinearDamping(double linearDampingCoef); // force = - linearDampingCoef * velocity (default: 0.0)
+  void SetRotationalDamping(double rotationalDampingCoef); // torque = - dampingCoef * angularVelocity (default: 0.0)
+
+  // === timestepping functions ===
+
+  // performs one step of Euler integration, i.e. advances solution
+  // from t to t + timestep
+  void EulerStep(double timestep); 
+  void EulerStepSymplectic(double timestep); 
+  void RK2(double timestep); // unimplemented
+
+protected:
+
+  // computes the inertia tensor (given the current rotation matrix)
+  virtual void ComputeInertiaTensor(double intertiaTensor[9]);
+
+  // computes the angular velocity from the angular momentum
+  // via equation w = (R * I_body^{-1} * R^T) L
+  // using the current rotation matrix 
+  virtual void ComputeAngularVelocity();
+
+  // computes angular momentum from the angular velocity, using the equation L = I * w
+  // assumes that I and w have been pre-set to the correct values
+  void ComputeAngularMomentum();
+
+  double positionX, positionY, positionZ; // world-coordinate position of center
+  double velocityX, velocityY, velocityZ; // world-coordinate position of center
+
+  double R[9]; // orientation matrix
+ 
+  double mass;
+
+  double externalForceX, externalForceY, externalForceZ;
+  double externalTorqueX, externalTorqueY, externalTorqueZ;
+
+  double angularVelocityX, angularVelocityY, angularVelocityZ;
+  double angularMomentumX, angularMomentumY, angularMomentumZ;
+  
+  Quaternion<double> q;
+
+  double linearDampingCoef;
+  double rotationalDampingCoef;
+  
+  // inertia tensor are with respect to the world coordinate system
+  double inertiaTensorAtRestX, inertiaTensorAtRestY, inertiaTensorAtRestZ; // the diagonal components of inertiaTensorAtRest
+  double inverseInertiaTensorAtRestX, inverseInertiaTensorAtRestY, inverseInertiaTensorAtRestZ; // the diagonal components of inverseInertiaTensorAtRest
+
+  void Invert3x3Matrix(double * A, double * AInv);
+};
+
+
+/************* INLINED FUNCTIONS **********************************/
+
+
+inline void RigidBody::ResetExternalForce()
+{
+  externalForceX = externalForceY = externalForceZ = 0;
+}
+
+inline void RigidBody::ResetExternalTorque()
+{
+  externalTorqueX = externalTorqueY = externalTorqueZ = 0;
+}
+
+
+inline void RigidBody::SetPosition(double x, double y, double z)
+{
+  positionX = x;
+  positionY = y;
+  positionZ = z;
+}
+
+
+inline void RigidBody::SetRotation(double R[9]) 
+{
+  memcpy(this->R,R,sizeof(double)*9);
+
+  // update the quaternion
+  q = Quaternion<double>::Matrix2Quaternion(R);
+  ComputeAngularMomentum();
+}
+
+inline void RigidBody::SetRotation(Quaternion<double> q)
+{
+  this->q = q;
+  q.Quaternion2Matrix(R);
+  ComputeAngularMomentum();
+}
+
+
+inline void RigidBody::SetExternalForce(double fx, double fy, double fz)
+{
+  externalForceX = fx;
+  externalForceY = fy;
+  externalForceZ = fz;
+}
+
+inline void RigidBody::AddExternalForce(double fx, double fy, double fz)
+{
+  externalForceX += fx;
+  externalForceY += fy;
+  externalForceZ += fz;
+}
+
+inline void RigidBody::SetExternalTorque(double tx, double ty, double tz)
+{
+  externalTorqueX = tx;
+  externalTorqueY = ty;
+  externalTorqueZ = tz;
+}
+
+inline void RigidBody::AddExternalTorque(double tx, double ty, double tz)
+{
+  externalTorqueX += tx;
+  externalTorqueY += ty;
+  externalTorqueZ += tz;
+}
+
+inline void RigidBody::ComputeTorque
+  (double forcePosx, double forcePosy, double forcePosz,
+   double fx, double fy, double fz,
+   double * tx, double * ty, double * tz)
+{
+  double rx,ry,rz;
+
+  // compute the torque handle
+  rx = forcePosx - positionX;
+  ry = forcePosy - positionY;
+  rz = forcePosz - positionZ;
+
+  // t = r x f
+  *tx = ry * fz - fy * rz;
+  *ty = fx * rz - rx * fz;
+  *tz = rx * fy - fx * ry;
+}
+
+inline void RigidBody::AddGravity(double g, double upVectorx, double upVectory, double upVectorz)
+{
+  double force = mass * g;
+
+  externalForceX -= upVectorx * force;
+  externalForceY -= upVectory * force;
+  externalForceZ -= upVectorz * force;
+}
+
+// computes angular momentum, using the equation L = I * w
+// assumes that I and w have been pre-set to the correct values
+inline void RigidBody::ComputeAngularMomentum()
+{
+  double inertiaTensor[9];
+  ComputeInertiaTensor(inertiaTensor);
+  angularMomentumX = inertiaTensor[0] * angularVelocityX + inertiaTensor[1] * angularVelocityY + inertiaTensor[2] * angularVelocityZ;
+  angularMomentumY = inertiaTensor[3] * angularVelocityX + inertiaTensor[4] * angularVelocityY + inertiaTensor[5] * angularVelocityZ;
+  angularMomentumZ = inertiaTensor[6] * angularVelocityX + inertiaTensor[7] * angularVelocityY + inertiaTensor[8] * angularVelocityZ;
+}
+
+inline void RigidBody::SetVelocity(double vx, double vy, double vz)
+{
+  velocityX = vx;
+  velocityY = vy;
+  velocityZ = vz;
+}
+
+inline void RigidBody::SetAngularVelocity(double wx, double wy, double wz)
+{
+  angularVelocityX = wx;
+  angularVelocityY = wy;  
+  angularVelocityZ = wz;
+  ComputeAngularMomentum();
+}
+
+inline void RigidBody::SetAngularMomentum(double amx, double amy, double amz)
+{
+  angularMomentumX = amx;
+  angularMomentumY = amy;
+  angularMomentumZ = amz;
+}
+
+inline void RigidBody::GetAngularVelocity(double * wx, double * wy, double * wz)
+{
+  *wx = angularVelocityX;
+  *wy = angularVelocityY;
+  *wz = angularVelocityZ;
+}
+
+inline void RigidBody::GetAngularMomentum(double * amx, double * amy, double * amz)
+{
+  *amx = angularMomentumX;
+  *amy = angularMomentumY;
+  *amz = angularMomentumZ;
+}
+
+
+inline void RigidBody::GetPosition(double * x, double * y, double * z)
+{
+  *x = positionX;
+  *y = positionY;
+  *z = positionZ;
+}
+
+inline void RigidBody::GetPointPosition(double X, double Y, double Z, double * x, double * y, double * z)
+{
+  *x = positionX + R[0] * X + R[1] * Y + R[2] * Z;
+  *y = positionY + R[3] * X + R[4] * Y + R[5] * Z;
+  *z = positionZ + R[6] * X + R[7] * Y + R[8] * Z;
+}
+
+inline void RigidBody::GetRotation(double * R_copy)
+{
+  R_copy[0] = R[0];
+  R_copy[1] = R[1];
+  R_copy[2] = R[2];
+  R_copy[3] = R[3];
+  R_copy[4] = R[4];
+  R_copy[5] = R[5];
+  R_copy[6] = R[6];
+  R_copy[7] = R[7];
+  R_copy[8] = R[8];
+}
+
+inline void RigidBody::GetVelocity(double * vx, double * vy, double * vz)
+{
+  *vx = velocityX;
+  *vy = velocityY;
+  *vz = velocityZ;
+}
+
+inline Quaternion<double> RigidBody::GetQuaternion() // returns the current rotation quaternion
+{
+  return q;
+}
+
+
+inline void RigidBody::SetPosition(double X, double Y, double Z, double posx, double posy, double posz)
+{
+  // (posx,posy,posz) = (x,y,z) + R (X,Y,Z)
+  // (x,y,z) = (posx,posy,posz) - R (X,Y,Z)
+  double x,y,z;
+  x = posx - R[0] * X - R[1] * Y - R[2] * Z;
+  y = posy - R[3] * X - R[4] * Y - R[5] * Z;
+  z = posz - R[6] * X - R[7] * Y - R[8] * Z;
+
+  SetPosition(x,y,z);
+}
+
+inline double RigidBody::GetMass()
+{
+  return mass;
+}
+
+inline void RigidBody::GetInertiaTensor(double * IT)
+{
+  ComputeInertiaTensor(IT);
+}
+
+inline void RigidBody::ResetBodyToRest()
+{
+  positionX = positionY = positionZ = 0;
+  velocityX = velocityY = velocityZ = 0;
+
+  memset(R,0,sizeof(double)*9);
+  R[0] = 1; R[4] = 1; R[8] = 1;
+
+  q = Quaternion<double>(1);
+
+  angularVelocityX = angularVelocityY = angularVelocityZ = 0;
+  angularMomentumX = angularMomentumY = angularMomentumZ = 0;
+}
+
+#endif
+
diff --git a/src/librigidBodyDynamics/rigidBody_generalTensor.cpp b/libraries/rigidBodyDynamics/rigidBody_generalTensor.cpp
similarity index 94%
rename from src/librigidBodyDynamics/rigidBody_generalTensor.cpp
rename to libraries/rigidBodyDynamics/rigidBody_generalTensor.cpp
index cfc8a2d117757db23f2cbc82b6bc36a8c6c1853c..ca84c905fb61892d84ca48434d6ded8e4fb476d9 100644
--- a/src/librigidBodyDynamics/rigidBody_generalTensor.cpp
+++ b/libraries/rigidBodyDynamics/rigidBody_generalTensor.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "rigidBodyDynamics" library , Copyright (C) 2007 CMU                  *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include "rigidBody_generalTensor.h"
 #include "matrixMultiplyMacros.h"
 
-namespace vega
-{
 RigidBody_GeneralTensor::RigidBody_GeneralTensor(double mass, double inertiaTensorAtRest[9]):
   RigidBody(mass,inertiaTensorAtRest[0],inertiaTensorAtRest[4],inertiaTensorAtRest[8])
 { 
@@ -168,4 +170,3 @@ void RigidBody_GeneralTensor::GetInverseInertiaTensorAtRest(double * inverseIner
   MATRIX_SET3X3(inverseInertiaTensorAtRest_, inverseInertiaTensorAtRest);
 }
 
-}
diff --git a/libraries/rigidBodyDynamics/rigidBody_generalTensor.h b/libraries/rigidBodyDynamics/rigidBody_generalTensor.h
new file mode 100644
index 0000000000000000000000000000000000000000..8914c00588f907bca05b3a1330f7a0abbc7d689a
--- /dev/null
+++ b/libraries/rigidBodyDynamics/rigidBody_generalTensor.h
@@ -0,0 +1,83 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "rigidBodyDynamics" library , Copyright (C) 2007 CMU                  *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _RIGIDBODY_GENERAL_TENSOR_H_
+#define _RIGIDBODY_GENERAL_TENSOR_H_
+
+/*
+  Classes 'RigidBody' and 'RigidBody_GeneralTensor' 
+  implement 6-DOF rigid dynamics of a single rigid body, as presented in:
+   
+  David Baraff:
+  "An Introduction to Physically Based Modeling:
+   Rigid Body Simulation I: Unconstrained Rigid Body Dynamics"
+  (SIGGRAPH 97 Course Notes)
+  http://www.cs.cmu.edu/~baraff/pbm/pbm.html
+
+  See rigidBody.h for more detailed usage.
+*/
+
+#include "rigidBody.h"
+
+class RigidBody_GeneralTensor : public RigidBody
+{
+public:
+
+  // inertia tensor at rest should be the world-coordinate-system inertia tensor 
+  //   when position=(0,0,0), and rotation=identity
+  // constructor will set position and rotation to zero/identity
+  RigidBody_GeneralTensor(double mass, double inertiaTensorAtRest[9]);
+
+  // modify the body inertia tensor (i.e., tensor in the frame of reference of the object)
+  // if updateAngularVelocity is set, angular velocity will be updated so that angular momentum stays constant
+  void SetInertiaTensor(double inertiaTensorAtRest[9], int updateAngularVelocity=0);
+
+  void GetInverseInertiaTensor(double * inverseInertiaTensor);
+  void GetInverseInertiaTensorAtRest(double * inverseInertiaTensorAtRest);
+protected:
+
+  // computes the inertia tensor (given the current rotation matrix); dense inertia tensor at rest
+  virtual void ComputeInertiaTensor(double inertiaTensor[9]);
+
+  // computes the angular velocity from the angular momentum
+  // via equation w = (R * I_body^{-1} * R^T) L
+  // using the current rotation matrix 
+  virtual void ComputeAngularVelocity();
+  
+  // inertia tensor are with respect to the world coordinate system
+  double inertiaTensorAtRest[9]; // the inertia tensor when position=(0,0,0) and R=I
+  double inverseInertiaTensorAtRest[9]; 
+
+};
+
+#endif
+
diff --git a/libraries/sceneObject/sceneGroundPlane.cpp b/libraries/sceneObject/sceneGroundPlane.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..466628894b6b11d2d34bd28174cd0ea69c7aa96d
--- /dev/null
+++ b/libraries/sceneObject/sceneGroundPlane.cpp
@@ -0,0 +1,194 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "sceneGroundPlane.h"
+#include "openGL-headers.h"
+#include "imageIO.h"
+#include <cstring>
+using namespace std;
+
+
+SceneGroundPlane::SceneGroundPlane(const char * groundPlaneString, const char * groundPlaneTextureFilename)
+{
+  groundPlaneHeight = 0;
+  lightPos = Vec3d(0,0,100);
+  groundPlaneSize = 50;
+  color[0] = color[1] = color[2] = 1.0f;
+  ambientFactor = diffuseFactor = specularFactor = 1.0f;
+  shininess = 120.0f;
+
+  displayListGround = 0;
+  displayListGroundWithTexture = 0;
+  planeResolution = 100;
+  groundTexName = 0;
+
+  int ret = sscanf(groundPlaneString,"%lf,%lf,%lf,%lf,%lf,r%f,g%f,b%f,a%f,d%f,s%f,sh%f", &groundPlaneHeight,
+      &lightPos[0], &lightPos[1], &lightPos[2],
+      &groundPlaneSize, &color[0], &color[1], &color[2], &ambientFactor, &diffuseFactor, &specularFactor, &shininess);
+  if (ret != 12)
+    throw 1;
+
+  displayListGround = glGenLists(1);
+  glNewList(displayListGround, GL_COMPILE);
+  buildDisplayList();
+  glEndList();
+
+
+  if (groundPlaneTextureFilename && strlen(groundPlaneTextureFilename) > 0)
+  {
+    ImageIO groundTexture;
+    ImageIO::fileFormatType type;
+    if (groundTexture.load(groundPlaneTextureFilename, &type) != ImageIO::OK)
+    {
+      cout << "Error reading image " << groundPlaneTextureFilename << "." << endl;
+      throw 2;
+    }
+
+    // == create ground texture ==
+    glGenTextures(1, &groundTexName);
+    glBindTexture(GL_TEXTURE_2D, groundTexName);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // repeat pattern in s
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // repeat pattern in t
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB,
+            groundTexture.getWidth(), groundTexture.getHeight(), GL_RGB,
+            GL_UNSIGNED_BYTE, groundTexture.getPixels());
+
+    //glTexImage2D(GL_TEXTURE_2D, 0 , GL_RGB,
+    //groundTexture->nx, groundTexture->ny, 0,
+    //GL_RGB, GL_UNSIGNED_BYTE, groundTexture->pix);
+
+    displayListGroundWithTexture = glGenLists(1);
+    glNewList(displayListGroundWithTexture, GL_COMPILE);
+    buildTexturedDisplayList();
+    glEndList();
+  }
+}
+
+SceneGroundPlane::~SceneGroundPlane()
+{
+  if (displayListGround != 0)
+    glDeleteLists(displayListGround, 1);
+  if (displayListGroundWithTexture != 0)
+  {
+    glDeleteTextures(1, &groundTexName);
+    glDeleteLists(displayListGroundWithTexture, 1);
+  }
+}
+
+void SceneGroundPlane::buildDisplayList()
+{
+  glPushAttrib(GL_ENABLE_BIT|GL_LIGHTING_BIT|GL_POLYGON_BIT);
+  glEnable(GL_POLYGON_OFFSET_FILL);
+  glEnable(GL_LIGHTING);
+  glPolygonOffset(1.0,1.0);
+
+  float blendFactor = 1.0f;
+  float planeAmbient[4]  = {  ambientFactor * color[0],  ambientFactor * color[1],  ambientFactor * color[2], blendFactor};
+  float planeDiffuse[4]  = {  diffuseFactor * color[0],  diffuseFactor * color[1],  diffuseFactor * color[2], blendFactor};
+  float planeSpecular[4] = { specularFactor * color[0], specularFactor * color[1], specularFactor * color[2], blendFactor};
+  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, planeAmbient);
+  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, planeDiffuse);
+  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, planeSpecular);
+  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
+
+  glNormal3f(0,1,0);
+
+  glBegin(GL_QUADS);
+  double planeIncrement = groundPlaneSize / planeResolution;
+  for(int i=0; i<planeResolution; i++)
+    for(int j=0; j<planeResolution; j++)
+    {
+      glVertex3f(-groundPlaneSize/2 + i * planeIncrement, groundPlaneHeight, -groundPlaneSize/2 + j * planeIncrement);
+      glVertex3f(-groundPlaneSize/2 + i * planeIncrement, groundPlaneHeight, -groundPlaneSize/2 + (j+1) * planeIncrement);
+      glVertex3f(-groundPlaneSize/2 + (i+1) * planeIncrement, groundPlaneHeight, -groundPlaneSize/2 + (j+1) * planeIncrement);
+      glVertex3f(-groundPlaneSize/2 + (i+1) * planeIncrement, groundPlaneHeight, -groundPlaneSize/2 + j * planeIncrement);
+    }
+  glEnd();
+
+  glPopAttrib();
+}
+
+void SceneGroundPlane::buildTexturedDisplayList()
+{
+  glPushAttrib(GL_ENABLE_BIT);
+  glEnable(GL_TEXTURE_2D);
+  glBindTexture(GL_TEXTURE_2D, groundTexName);
+  glNormal3f(0,1,0);
+  const double wrapFactor = 8.0;
+  double planeIncrement = groundPlaneSize / planeResolution;
+
+  glBegin(GL_QUADS);
+  for(int i=0; i<planeResolution; i++)
+    for(int j=0; j<planeResolution; j++)
+    {
+      glTexCoord2f(wrapFactor * i / planeResolution, wrapFactor * j / planeResolution);
+      glVertex3f(-groundPlaneSize/2 + i * planeIncrement, groundPlaneHeight, -groundPlaneSize/2 + j * planeIncrement);
+
+      glTexCoord2f(wrapFactor * (i+1) / planeResolution, wrapFactor * j / planeResolution);
+      glVertex3f(-groundPlaneSize/2 + (i+1) * planeIncrement, groundPlaneHeight, -groundPlaneSize/2 + j * planeIncrement);
+
+      glTexCoord2f(wrapFactor * (i+1) / planeResolution, wrapFactor * (j+1) / planeResolution);
+      glVertex3f(-groundPlaneSize/2 + (i+1) * planeIncrement, groundPlaneHeight, -groundPlaneSize/2 + (j+1) * planeIncrement);
+
+      glTexCoord2f(wrapFactor * i / planeResolution, wrapFactor * (j+1) / planeResolution);
+      glVertex3f(-groundPlaneSize/2 + i * planeIncrement, groundPlaneHeight, -groundPlaneSize/2 + (j+1) * planeIncrement);
+    }
+  glEnd();
+
+  glPopAttrib();
+}
+
+void SceneGroundPlane::renderShadow(SceneObject * object)
+{
+  GLboolean enabled = GL_FALSE;
+  glGetBooleanv(GL_TEXTURE_2D, &enabled);
+  glDisable(GL_TEXTURE_2D);
+//  double ground[4] = {0, 1, 0, -groundPlaneHeight - 0.01};
+  double ground[4] = {0, 1, 0, -groundPlaneHeight};
+  double light[4] = {lightPos[0], lightPos[1], lightPos[2], 1};
+  object->RenderShadow(ground, light);
+  if (enabled)
+    glEnable(GL_TEXTURE_2D);
+}
+
+void SceneGroundPlane::render()
+{
+  if (displayListGroundWithTexture == 0)
+    glCallList(displayListGround);
+  else
+    glCallList(displayListGroundWithTexture);
+}
diff --git a/libraries/sceneObject/sceneGroundPlane.h b/libraries/sceneObject/sceneGroundPlane.h
new file mode 100644
index 0000000000000000000000000000000000000000..2901fe0de2464cf545e1065f745db208e76705bd
--- /dev/null
+++ b/libraries/sceneObject/sceneGroundPlane.h
@@ -0,0 +1,74 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef SCENEGROUNDPLANE_H
+#define SCENEGROUNDPLANE_H
+
+#include <cstddef>
+#include "sceneObject.h"
+
+// render ground plane and shadow for SceneObject
+
+class SceneGroundPlane
+{
+public:
+  // groundPlaneString format:
+  // "<ground height>,<light position X,Y,Z>,<ground size>,r<red>,g<green>,b<blue>,a<ambient factor>,d<diffuse factor>,s<specular factor>,sh<shininess>"
+  // where <red>,<green>,<blue> is the ground color, ranging from 0.0 to 1.0
+  //       <ambient/diffuse/specular factor> scales the ground color for ambient/diffuse/specular component, ranging from 0.0 to 1.0
+  // groundPlaneTextureFilename: input texture; if it's NULL, use the color specified in groundPlaneString
+  SceneGroundPlane(const char * groundPlaneString, const char * groundPlaneTextureFilename = NULL);
+  virtual ~SceneGroundPlane();
+
+  void renderShadow(SceneObject * object);
+
+  void render();
+
+protected:
+  void buildDisplayList();
+  void buildTexturedDisplayList();
+
+  double groundPlaneHeight;
+  Vec3d lightPos;
+  double groundPlaneSize;
+  float color[3];
+  float ambientFactor, diffuseFactor, specularFactor;
+  float shininess;
+  GLuint displayListGround;
+  GLuint displayListGroundWithTexture;
+  int planeResolution;
+  unsigned int groundTexName;
+};
+
+
+
+#endif
diff --git a/src/libsceneObject/sceneObject.cpp b/libraries/sceneObject/sceneObject.cpp
similarity index 78%
rename from src/libsceneObject/sceneObject.cpp
rename to libraries/sceneObject/sceneObject.cpp
index 33ab9d2998603c2f06bc09015a5b93cd5df8b89a..b247eb2a807023478dd147f05857c2fcac2c99f7 100644
--- a/src/libsceneObject/sceneObject.cpp
+++ b/libraries/sceneObject/sceneObject.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,23 +37,28 @@
 #include <float.h>
 #include "sceneObject.h"
 #include "objMeshRender.h"
-#include "objMeshEncode.h"
 
-SceneObject::SceneObject(char * filename):
+SceneObject::SceneObject(const char * filename):
   mesh(NULL), meshRender(NULL), displayList(0), displayListExists(false), displayListEdges(0), displayListEdgesExists(false)
 { 
-  int verbose = 0;
-  mesh = new ObjMesh(filename, verbose);
+  mesh = new ObjMesh(filename);
 
-  int encStart = strlen(filename) - 4;
-  if ((encStart > 0) && (strcmp(&filename[encStart], ".enc") == 0))
-  {
-    // must decode
-    printf("Decoding mesh.\n");
-    objMeshDecode(mesh);
-    printf("Decoded mesh.\n");
-  }
+  Construct();
+}
 
+SceneObject::SceneObject(ObjMesh * objMesh, bool deepCopy_):
+  deepCopy(deepCopy_), mesh(NULL), meshRender(NULL), displayList(0), displayListExists(false), displayListEdges(0), displayListEdgesExists(false)
+{ 
+  if (deepCopy)
+    mesh = new ObjMesh(*objMesh);
+  else
+    mesh = objMesh;
+
+  Construct();
+}
+
+void SceneObject::Construct()
+{
   meshRender = new ObjMeshRender(mesh);
   if (meshRender->numTextures() > 0)
     hasTextures_ = true;
@@ -65,8 +74,9 @@ SceneObject::SceneObject(char * filename):
 SceneObject::~SceneObject()
 {
   PurgeDisplayList();
-  delete mesh;
-  delete meshRender;
+  if (deepCopy)
+    delete(mesh);
+  delete(meshRender);
 }
 
 void SceneObject::SetMaterialAlpha(double alpha)
@@ -88,13 +98,13 @@ void SceneObject::PurgeDisplayList()
 void SceneObject::BuildDisplayList()
 {
   GLenum errorCode;
-  const GLubyte * errorString;
   
   errorCode = glGetError();
   if (errorCode != GL_NO_ERROR)
   {
-    errorString = gluErrorString(errorCode);
-    printf("OpenGL Error (start of BuildDisplayList): %s\n", errorString);
+    //const GLubyte * errorString;
+    //errorString = gluErrorString(errorCode);
+    //printf("OpenGL Error (start of BuildDisplayList): %s\n", errorString);
   }
 
   if (displayListExists)
@@ -112,13 +122,14 @@ void SceneObject::BuildDisplayList()
   errorCode = glGetError();
   if (errorCode != GL_NO_ERROR)
   {
-    errorString = gluErrorString(errorCode);
-    printf("OpenGL Error (end of BuildDisplayList): %s\n", errorString);
+    //errorString = gluErrorString(errorCode);
+    //const GLubyte * errorString;
+    //printf("OpenGL Error (end of BuildDisplayList): %s\n", errorString);
   }
 }
 
 // assumes pre-existing face normals
-// second parameter is treshold angle for hard edges
+// second parameter is threshold angle for hard edges
 void SceneObject::BuildVertexNormals(double thresholdAngle)
 {
   //do stuff with structure
@@ -229,9 +240,24 @@ void SceneObject::RenderFacesAndEdges()
   meshRender->render(OBJMESHRENDER_TRIANGLES | OBJMESHRENDER_EDGES, renderMode);
 }
 
-void SceneObject::RenderEdgesInGroup(char * groupName)
+void SceneObject::RenderEdgesInGroup(const char * groupName)
 {
-  meshRender->renderGroupEdges(groupName);
+  RenderEdgesInGroup(mesh->getGroupIndex(groupName));
+}
+
+void SceneObject::RenderEdgesInGroup(int groupIndex)
+{
+  meshRender->render(OBJMESHRENDER_EDGES, renderMode, groupIndex);
+}
+
+void SceneObject::RenderGroup(const char * groupName)
+{
+  RenderGroup(mesh->getGroupIndex(groupName));
+}
+
+void SceneObject::RenderGroup(int groupIndex)
+{
+  meshRender->render(OBJMESHRENDER_TRIANGLES, renderMode, groupIndex);
 }
 
 void SceneObject::RenderVertices(int numVertices, int * vertexList)
@@ -270,10 +296,10 @@ int SceneObject::GetClosestVertex(Vec3d & queryPos, double * distance, double *
 }
 
 // highlights vertex i, i=0,1,2,...,n-1
-void SceneObject::HighlightVertex(int i)
+void SceneObject::HighlightVertex(int i, const Vec3d & color, double pointSize) const
 {
-  glColor3f(0,1,0);
-  glPointSize(8.0);
+  glColor3f(color[0],color[1],color[2]);
+  glPointSize(pointSize);
 
   Vec3d pos = mesh->getPosition(i);
 
@@ -282,9 +308,36 @@ void SceneObject::HighlightVertex(int i)
   glEnd();
 }
 
+void SceneObject::EnableCustomColor()
+{
+  renderMode = renderMode | OBJMESHRENDER_CUSTOMCOLOR;
+}
+
+void SceneObject::DisableCustomColor()
+{
+  renderMode = renderMode & (~OBJMESHRENDER_CUSTOMCOLOR);
+}
+
+void SceneObject::SetCustomColor(const std::vector<Vec3d> & colors)
+{
+  meshRender->setCustomColors(colors);
+}
+
+void SceneObject::EnableFlatFaces()
+{
+  renderMode = renderMode & (~OBJMESHRENDER_SMOOTH);
+  renderMode = renderMode | OBJMESHRENDER_FLAT;
+}
+
+void SceneObject::DisableFlatFaces()
+{
+  renderMode = renderMode & (~OBJMESHRENDER_FLAT);
+  renderMode = renderMode | OBJMESHRENDER_SMOOTH;
+}
+
 bool SceneObject::AreTexturesEnabled()
 {
-  return ((renderMode && OBJMESHRENDER_TEXTURE) != 0);
+  return ((renderMode & OBJMESHRENDER_TEXTURE) != 0);
 }
 
 void SceneObject::EnableTextures()
@@ -378,7 +431,7 @@ void SceneObject::ExportMeshGeometry(int * numVertices, double ** vertices, int
   mesh->exportGeometry(numVertices, vertices, numTriangles, triangles, NULL, NULL);
 }
 
-void SceneObject::PrintBitmapString(float x, float y, float z, char* s)
+void SceneObject::PrintBitmapString(float x, float y, float z, const char* s)
 {
   glRasterPos3f(x,y,z);
   if (s && strlen(s)) 
@@ -391,10 +444,10 @@ void SceneObject::PrintBitmapString(float x, float y, float z, char* s)
   }
 }
 
-void SceneObject::PrintBitmapInteger(float x, float y, float z, long i)
+void SceneObject::PrintBitmapInteger(float x, float y, float z, int i)
 {
   char s[200];
-  sprintf(s,"%ld",i);
+  sprintf(s,"%d",i);
   PrintBitmapString(x,y,z,s);
 }
 
diff --git a/src/libsceneObject/sceneObject.h b/libraries/sceneObject/sceneObject.h
similarity index 68%
rename from src/libsceneObject/sceneObject.h
rename to libraries/sceneObject/sceneObject.h
index 857ec9ac42558ff5ea71e7546e3e417118728de8..7f21851dca0c451623821052865756aa7000e5a0 100644
--- a/src/libsceneObject/sceneObject.h
+++ b/libraries/sceneObject/sceneObject.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -34,9 +38,7 @@
 #ifndef _SCENEOBJECT_H_
 #define _SCENEOBJECT_H_
 
-#ifdef WIN32
-  #include <windows.h>
-#endif
+#include "openGL-headers.h"
 
 #include "macros.h"
 #include "objMesh.h"
@@ -53,23 +55,30 @@ public:
   enum TextureTransparencyType {USETEXTURETRANSPARENCY, NOTEXTURETRANSPARENCY}; // enables 2-pass rendering for textures with transparencies
 
   // create a static scene object, by loading it from an Alias Wavefront OBJ file
-  SceneObject(char * filename);
+  SceneObject(const char * filename);
+  SceneObject(ObjMesh * objMesh, bool deepCopy = true);
   virtual ~SceneObject();
 
   // ==== render ====
 
+  // if has displayList, calls displayList 
+  // otherwise uses ObjMeshRender to render triangles with renderMode
   virtual void Render();
   virtual void RenderVertices();
   virtual void RenderEdges(); 
   virtual void RenderNormals();
   virtual void RenderFacesAndEdges();
 
+  // only call glVertex3f to render the vertex position
   virtual void RenderVertex(int vertex);
   virtual void RenderVertices(int numVertices, int * vertexList); // 0-indexed vertices
   virtual void RenderVertices_Selection();
 
   virtual void RenderShadow(double ground[4], double light[4]);
-  void RenderEdgesInGroup(char * groupName); // renders only the edges in the given group
+  virtual void RenderGroup(const char * groupName);
+  virtual void RenderGroup(int groupIndex);
+  virtual void RenderEdgesInGroup(const char * groupName); // renders only the edges in the given group
+  virtual void RenderEdgesInGroup(int groupIndex);
 
   // ==== display lists ====
 
@@ -81,6 +90,7 @@ public:
   inline int Getn() { return n; }
   inline int GetNumVertices() { return n; }
   inline int GetNumFaces() { return mesh->getNumFaces(); }
+  inline const Vec3d & GetVertexPosition(int index) const { return mesh->getPosition(index); }
   inline ObjMesh * GetMesh() { return mesh; }
   inline ObjMeshRender * GetMeshRender() { return meshRender; }
   inline int GetRenderMode() { return renderMode; }
@@ -90,7 +100,7 @@ public:
   // compute mesh centroid and smallest enclosing radius
   void ComputeMeshGeometricParameters(Vec3d * centroid, double * radius);
   // export mesh data
-  void ExportMeshGeometry(int * numVertices, double ** vertices, int * numTriangles, int ** triangles);
+  void ExportMeshGeometry(int * numVertices, double ** vertices, int * numTriangles=NULL, int ** triangles=NULL);
 
   // finds the closest vertex using an exhaustive search
   // returns distance in "distance", if distance is not NULL
@@ -101,8 +111,15 @@ public:
 
   // lightingModulation determines whether to multiply or replace the object color with the texture color
   // mipmap determines whether to use mipmapping for texture rendering
-  // texturePool and updatePool should normally be set to NULL and 0. These options exist so that you can share the same texture across multiple meshes. If "texturePool" is not NULL, the loader will search for the texture in the pool of textures, Only if not found, it will load the texture, otherwise, it will soft-link it. If "updatePool" is 1, the loader will add a soft-link to any unique textures discovered in this object to the texturePool.
-  int SetUpTextures(LightingModulationType lightingModulation=MODULATE, MipmapType mipmap=USEMIPMAP, AnisotropicFilteringType anisotropicFiltering=USEANISOTROPICFILTERING, TextureTransparencyType=NOTEXTURETRANSPARENCY, std::vector<ObjMeshRender::Texture*> * texturePool=NULL, int updatePool=0); // you must call this **after** OpenGL has been initialized!!!
+  // texturePool and updatePool should normally be set to NULL and 0.
+  // These options exist so that you can share the same texture across multiple meshes.
+  // If "texturePool" is not NULL, the loader will search for the texture in the pool of textures,
+  // Only if not found, it will load the texture, otherwise, it will soft-link it.
+  // If "updatePool" is 1, the loader will add a soft-link to any unique textures discovered in this object to the texturePool.
+  // you must call this **after** OpenGL has been initialized!!!
+  int SetUpTextures(LightingModulationType lightingModulation=MODULATE, MipmapType mipmap=USEMIPMAP,
+      AnisotropicFilteringType anisotropicFiltering=USEANISOTROPICFILTERING, TextureTransparencyType=NOTEXTURETRANSPARENCY,
+      std::vector<ObjMeshRender::Texture*> * texturePool=NULL, int updatePool=0);
   void EnableTextures();
   void DisableTextures();
   bool AreTexturesEnabled();
@@ -115,7 +132,8 @@ public:
   void BuildFaceNormals();
   void BuildNeighboringStructure();  // must be called before the vertex-normal functions below
 
-  // second parameter is treshold angle for hard edges:
+  // second parameter is treshold angle for hard edges: 
+  // if the normals of two nearby faces form an angle that's larger than thresholdAngle, it's considered as a hard edge
   void BuildVertexNormals(double thresholdAngle=85.0); // assumes pre-existing face normals
   void BuildNormals(double thresholdAngle=85.0); // builds both face and vertex normals
   void BuildNormalsFancy(double thresholdAngle=85.0);  // rebuilds facet normals + calls vertex-per-triangle normal update
@@ -128,7 +146,18 @@ public:
   void ShowPointLabels();
   // shows point labels for points from k to l
   void ShowPointLabels(int k, int l);
-  void HighlightVertex(int i); // same as RenderVertex, except it always renders in green color and with point size 8.0
+  // renders vertex i in with given color and point size.
+  // default: green color and with point size 8.0
+  void HighlightVertex(int i, const Vec3d & color = Vec3d(0.0,1.0,0.0), double pointSize = 8.0) const;
+
+  // ==== custom vetex color ====
+  void EnableCustomColor();
+  void DisableCustomColor();
+  void SetCustomColor(const std::vector<Vec3d> & colors); // specific color for every mesh vertex
+
+  // ==== render flat faces ====
+  void EnableFlatFaces();
+  void DisableFlatFaces();
 
   // model matrix for the shadow
   static void SetShadowingModelviewMatrix(double ground[4], double light[4]);
@@ -137,10 +166,12 @@ public:
   virtual void TransformRigidly(double * centerOfMass, double * R);
 
 protected:
+  void Construct();
   int n;
   unsigned int renderMode;
   TextureTransparencyType textureTransparency;
 
+  bool deepCopy;
   ObjMesh * mesh;
   ObjMeshRender * meshRender;
   GLuint displayList;
@@ -149,8 +180,8 @@ protected:
   GLuint displayListEdges;
   bool displayListEdgesExists;
 
-  void PrintBitmapString(float x,float y, float z, char* s);
-  void PrintBitmapInteger(float x,float y, float z,long i);
+  void PrintBitmapString(float x,float y, float z, const char* s);
+  void PrintBitmapInteger(float x,float y, float z, int i);
 
   bool hasTextures_;
 };
diff --git a/src/libsceneObject/sceneObject6DOF.cpp b/libraries/sceneObject/sceneObject6DOF.cpp
similarity index 79%
rename from src/libsceneObject/sceneObject6DOF.cpp
rename to libraries/sceneObject/sceneObject6DOF.cpp
index 6ea98fa9af6fe47ed1ac367dcc3c3ef3ce68ee7d..13f57560fd564446ef48be39039a315ecceede53 100644
--- a/src/libsceneObject/sceneObject6DOF.cpp
+++ b/libraries/sceneObject/sceneObject6DOF.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,11 +37,22 @@
 #include <float.h>
 #include "sceneObject6DOF.h"
 
-SceneObject6DOF::SceneObject6DOF(char * filenameOBJ): SceneObjectWithRestPosition(filenameOBJ), scale(1.0) 
+SceneObject6DOF::SceneObject6DOF(const char * filenameOBJ): SceneObjectWithRestPosition(filenameOBJ), scale(1.0) 
 {
-  memset(centerOfMass,0,sizeof(double)*3);
-  memset(R,0,sizeof(double)*9);
+  Construct();
+}
+
+SceneObject6DOF::SceneObject6DOF(ObjMesh * objMesh, bool deepCopy): SceneObjectWithRestPosition(objMesh, deepCopy), scale(1.0) 
+{
+  Construct();
+}
+
+void SceneObject6DOF::Construct()
+{
+  memset(centerOfMass, 0, sizeof(double) * 3);
+  memset(R, 0, sizeof(double) * 9);
   R[0] = 1; R[4] = 1; R[8] = 1;
+  rotation = 1;
 }
 
 SceneObject6DOF::~SceneObject6DOF()
@@ -224,22 +239,38 @@ void SceneObject6DOF::RenderLocalFrame(double axesSize)
 
 void SceneObject6DOF::TransformToLocal(double * globalVector, double * localVector)
 {
-  // global = R * local + pos
-  // local = R^{-1} * (global - pos)
-
-  // R = s * Rot  
-  // R^{-1} = 1 / s Rot^T = 1 / s^2 * R
-
   double temp[3] = { globalVector[0] - centerOfMass[0], globalVector[1] - centerOfMass[1], globalVector[2] - centerOfMass[2] };
 
-  localVector[0] = R[0] * temp[0] + R[3] * temp[1] + R[6] * temp[2];
-  localVector[1] = R[1] * temp[0] + R[4] * temp[1] + R[7] * temp[2];
-  localVector[2] = R[2] * temp[0] + R[5] * temp[1] + R[8] * temp[2];
-
-  double invScale2 = 1.0 / (scale * scale);
-  localVector[0] *= invScale2;
-  localVector[1] *= invScale2;
-  localVector[2] *= invScale2;
+  if (rotation != 0)
+  {
+    // global = R * local + pos
+    // local = R^{-1} * (global - pos)
+
+    // we know that R = s * Rot, so we can invert R as follows:  
+    // R^{-1} = 1 / s Rot^T = 1 / s^2 * R
+
+    localVector[0] = R[0] * temp[0] + R[3] * temp[1] + R[6] * temp[2];
+    localVector[1] = R[1] * temp[0] + R[4] * temp[1] + R[7] * temp[2];
+    localVector[2] = R[2] * temp[0] + R[5] * temp[1] + R[8] * temp[2];
+    
+    double invScale2 = 1.0 / (scale * scale);
+    localVector[0] *= invScale2;
+    localVector[1] *= invScale2;
+    localVector[2] *= invScale2;
+  }
+  else
+  {
+    // global = R * local + pos
+    // local = R^{-1} * (global - pos)
+    // R is not necessarily a rotation
+
+    double RInv[9];
+    inverse3x3(R, RInv);
+
+    localVector[0] = RInv[0] * temp[0] + RInv[1] * temp[1] + RInv[2] * temp[2];
+    localVector[1] = RInv[3] * temp[0] + RInv[4] * temp[1] + RInv[5] * temp[2];
+    localVector[2] = RInv[6] * temp[0] + RInv[7] * temp[1] + RInv[8] * temp[2];
+  }
 }
 
 void SceneObject6DOF::TransformToGlobal(double * localVector, double * globalVector)
diff --git a/src/libsceneObject/sceneObject6DOF.h b/libraries/sceneObject/sceneObject6DOF.h
similarity index 76%
rename from src/libsceneObject/sceneObject6DOF.h
rename to libraries/sceneObject/sceneObject6DOF.h
index fbd74203e165b866294b0234a568b053fc5ea977..f195050428b6b88cacb31c5b158c9a2ccc95f8e5 100644
--- a/src/libsceneObject/sceneObject6DOF.h
+++ b/libraries/sceneObject/sceneObject6DOF.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,7 +44,8 @@ class SceneObject6DOF : public virtual SceneObjectWithRestPosition
 {
 public:
 
-  SceneObject6DOF(char * filenameOBJ); 
+  SceneObject6DOF(const char * filenameOBJ); 
+  SceneObject6DOF(ObjMesh * objMesh, bool deepCopy = true); 
   virtual ~SceneObject6DOF();
 
   inline void SetRigidBodyParameters(double * centerOfMass, double * R);
@@ -61,16 +66,20 @@ public:
 
   void SetOpenGLModelviewMatrix(); // sets the OpenGL modelview matrix to the current frame of the object
 
-  void SetScale(double scale); // to support scaling non-rigid transformations
   void TransformToLocal(double * globalVector, double * localVector); // transform a world-coordinate vector to the frame of the rigid object
   void TransformToGlobal(double * localVector, double * globalVector); // transform a vector from the frame of the rigid object to global world-coordinate
+  inline void SetRotationMode(int rotation); // to support non-rigid transformations in "TransformToLocal"; default: rotatation=1; call this function with rotation=0 if the matrix R is not a rotation, in order to get the correct results for "TransformToLocal"
+  void SetScale(double scale); // to accelerate "TransformToLocal" for transformations of the form R = scale * Rot, where Rot is a rotation; if R is in this format, you can set the scale (and set rotation=1 via "SetRotationMode" above), which increases the speed of "TransformToLocal"
+  double GetScale() const { return scale; }
 
   int GetClosestVertex(Vec3d & queryPos, double * distance, double * auxVertexBuffer);
 
 protected:
+  void Construct();
   double centerOfMass[3];
   double R[9];
   double scale;
+  int rotation;
 };
 
 inline void SceneObject6DOF::SetRigidBodyParameters(double * centerOfMass_, double * R_)
@@ -125,5 +134,10 @@ inline void SceneObject6DOF::GetRigidBodyParameters(double * centerOfMass_, doub
   R_[8] = R[8];
 }
 
+inline void SceneObject6DOF::SetRotationMode(int rotation_)
+{
+  rotation = rotation_;
+}
+
 #endif
 
diff --git a/src/libsceneObject/sceneObjectDeformable.cpp b/libraries/sceneObject/sceneObjectDeformable.cpp
similarity index 70%
rename from src/libsceneObject/sceneObjectDeformable.cpp
rename to libraries/sceneObject/sceneObjectDeformable.cpp
index 6b8fd5b90aafbef0b55edf76647f5ff20fc4ec4d..d70a1ebc40b3bd152b60e29723c44d945538539d 100644
--- a/src/libsceneObject/sceneObjectDeformable.cpp
+++ b/libraries/sceneObject/sceneObjectDeformable.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,11 +36,16 @@
 #include <string.h>
 #include "sceneObjectDeformable.h"
 
-SceneObjectDeformable::SceneObjectDeformable(char * filenameOBJ):
+SceneObjectDeformable::SceneObjectDeformable(const char * filenameOBJ):
    SceneObjectWithRestPosition(filenameOBJ) 
 {
 }
 
+SceneObjectDeformable::SceneObjectDeformable(ObjMesh * objMesh, bool deepCopy):
+   SceneObjectWithRestPosition(objMesh, deepCopy) 
+{
+}
+
 SceneObjectDeformable::~SceneObjectDeformable()
 {
 }
@@ -47,7 +56,7 @@ void SceneObjectDeformable::ResetDeformationToRest()
     mesh->setPosition(i, Vec3d(restPosition[3 * i + 0], restPosition[3 * i + 1], restPosition[3 * i + 2]));
 }
 
-void SceneObjectDeformable::AddVertexDeformations(double * u)
+void SceneObjectDeformable::AddVertexDeformations(const double * u)
 {
   for(int i = 0; i < n; i++)
   {
@@ -55,7 +64,7 @@ void SceneObjectDeformable::AddVertexDeformations(double * u)
   }
 }
 
-void SceneObjectDeformable::SetVertexDeformations(double * u)
+void SceneObjectDeformable::SetVertexDeformations(const double * u)
 {
   for(int i = 0; i < n; i++)
   {
@@ -63,12 +72,12 @@ void SceneObjectDeformable::SetVertexDeformations(double * u)
   }
 }
 
-void SceneObjectDeformable::SetVertexDeformations(float * u)
+void SceneObjectDeformable::SetVertexDeformations(const float * u)
 {
   // set the deformations
   for(int i = 0; i < n; i++)
   {
-    mesh->setPosition(i, mesh->getPosition(i) + Vec3d(restPosition[3 * i + 0] + u[3 * i + 0], restPosition[3 * i + 1] + u[3 * i + 1], restPosition[3 * i + 2] + u[3 * i + 2]));
+    mesh->setPosition(i, Vec3d(restPosition[3 * i + 0] + u[3 * i + 0], restPosition[3 * i + 1] + u[3 * i + 1], restPosition[3 * i + 2] + u[3 * i + 2]));
   }
 }
 
diff --git a/src/libsceneObject/sceneObjectDeformable.h b/libraries/sceneObject/sceneObjectDeformable.h
similarity index 72%
rename from src/libsceneObject/sceneObjectDeformable.h
rename to libraries/sceneObject/sceneObjectDeformable.h
index fe705dda61617fda88430405e6a43b46d35ed945..970a8c324791ca5b50007bc2d6263082b838d99b 100644
--- a/src/libsceneObject/sceneObjectDeformable.h
+++ b/libraries/sceneObject/sceneObjectDeformable.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,21 +44,24 @@
 class SceneObjectDeformable : public virtual SceneObjectWithRestPosition
 {
 public:
-  SceneObjectDeformable(char * filenameOBJ);
+  SceneObjectDeformable(const char * filenameOBJ);
+  SceneObjectDeformable(ObjMesh * objMesh, bool deepCopy = true);
   virtual ~SceneObjectDeformable();
 
   // sets the current dynamic vertex positions to the rest position + specified deformation
-  void SetVertexDeformations(double * u);
-  void SetVertexDeformations(float * u);
+  void SetVertexDeformations(const double * u);
+  void SetVertexDeformations(const float * u);
 
   // adds deformations to current dynamicPosition of the vertices
-  void AddVertexDeformations(double * u);
+  void AddVertexDeformations(const double * u);
 
   void ResetDeformationToRest();
 
   inline void GetSingleVertexRestPosition(int vertex, double * x, double * y, double * z);
   inline void SetSingleVertexRestPosition(int vertex, double x, double y, double z);
   inline void GetSingleVertexPositionFromBuffer(int vertex, double * x, double * y, double * z);
+  inline const Vec3d & GetSingleVertexPositionFromBuffer(int vertex) const;
+  inline Vec3d GetSingleVertexRestPosition(int vertex) const;
 
   virtual void SetLighting(Lighting * lighting);
 
@@ -84,5 +91,15 @@ inline void SceneObjectDeformable::GetSingleVertexPositionFromBuffer(int vertex,
   *z = pos[2];
 }
 
+inline const Vec3d& SceneObjectDeformable::GetSingleVertexPositionFromBuffer(int vertex) const
+{
+  return mesh->getPosition(vertex);
+}
+
+inline Vec3d SceneObjectDeformable::GetSingleVertexRestPosition(int vertex) const
+{
+  return Vec3d(&restPosition[3*vertex]);
+}
+
 #endif
 
diff --git a/src/libsceneObject/sceneObjectDeformable6DOF.cpp b/libraries/sceneObject/sceneObjectDeformable6DOF.cpp
similarity index 75%
rename from src/libsceneObject/sceneObjectDeformable6DOF.cpp
rename to libraries/sceneObject/sceneObjectDeformable6DOF.cpp
index 19f354e421bc1c2247f632ae44fe41ec72c55162..4795e3af2b532e52e4873ddd322beaf626809d4b 100644
--- a/src/libsceneObject/sceneObjectDeformable6DOF.cpp
+++ b/libraries/sceneObject/sceneObjectDeformable6DOF.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,7 +37,11 @@
 #include <float.h>
 #include "sceneObjectDeformable6DOF.h"
 
-SceneObjectDeformable6DOF::SceneObjectDeformable6DOF(char * filenameOBJ): SceneObjectWithRestPosition(filenameOBJ), SceneObjectDeformable(filenameOBJ), SceneObject6DOF(filenameOBJ)
+SceneObjectDeformable6DOF::SceneObjectDeformable6DOF(const char * filenameOBJ): SceneObjectWithRestPosition(filenameOBJ), SceneObjectDeformable(filenameOBJ), SceneObject6DOF(filenameOBJ)
+{
+}
+
+SceneObjectDeformable6DOF::SceneObjectDeformable6DOF(ObjMesh * objMesh, bool deepCopy): SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectDeformable(objMesh, deepCopy), SceneObject6DOF(objMesh, deepCopy)
 {
 }
 
diff --git a/src/libsceneObject/sceneObjectDeformable6DOF.h b/libraries/sceneObject/sceneObjectDeformable6DOF.h
similarity index 79%
rename from src/libsceneObject/sceneObjectDeformable6DOF.h
rename to libraries/sceneObject/sceneObjectDeformable6DOF.h
index 1e1142acc26acd1eb3b6d56ef3265e19a96480e9..2602d80b902fa756b0ecc68b698e75abecb87020 100644
--- a/src/libsceneObject/sceneObjectDeformable6DOF.h
+++ b/libraries/sceneObject/sceneObjectDeformable6DOF.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,7 +40,8 @@ class SceneObjectDeformable6DOF : public virtual SceneObjectDeformable, public S
 {
 public:
 
-  SceneObjectDeformable6DOF(char * filenameOBJ); 
+  SceneObjectDeformable6DOF(const char * filenameOBJ); 
+  SceneObjectDeformable6DOF(ObjMesh * objMesh, bool deepCopy = true); 
   virtual ~SceneObjectDeformable6DOF();
 
   virtual void GetSingleVertexPosition(int vertex, double * x, double * y, double * z);
diff --git a/src/libsceneObject/sceneObjectDeformableGPU.cpp b/libraries/sceneObject/sceneObjectDeformableGPU.cpp
similarity index 81%
rename from src/libsceneObject/sceneObjectDeformableGPU.cpp
rename to libraries/sceneObject/sceneObjectDeformableGPU.cpp
index d269f31a3410a1a8801614d52a428124d746fb49..c0c4a1e9cc896fc068a03d3bb69c2cd5ce6006a9 100644
--- a/src/libsceneObject/sceneObjectDeformableGPU.cpp
+++ b/libraries/sceneObject/sceneObjectDeformableGPU.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,7 +32,7 @@
 
 #include "sceneObjectDeformableGPU.h"
 
-SceneObjectDeformableGPU::SceneObjectDeformableGPU(char * filenameOBJ) : SceneObjectWithRestPosition(filenameOBJ), SceneObjectDeformable(filenameOBJ)
+SceneObjectDeformableGPU::SceneObjectDeformableGPU(const char * filenameOBJ) : SceneObjectWithRestPosition(filenameOBJ), SceneObjectDeformable(filenameOBJ)
 {
   try
   {
diff --git a/src/libsceneObject/sceneObjectDeformableGPU.h b/libraries/sceneObject/sceneObjectDeformableGPU.h
similarity index 81%
rename from src/libsceneObject/sceneObjectDeformableGPU.h
rename to libraries/sceneObject/sceneObjectDeformableGPU.h
index 76ca83a67f01dd3d3a77640b1bd97a30c6a70fe6..a809f7c7d092b830d6617d521317e17f5e71df9c 100644
--- a/src/libsceneObject/sceneObjectDeformableGPU.h
+++ b/libraries/sceneObject/sceneObjectDeformableGPU.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -43,7 +47,7 @@
 class SceneObjectDeformableGPU : public virtual SceneObjectDeformable
 {
 public:
-  SceneObjectDeformableGPU(char * filenameOBJ);
+  SceneObjectDeformableGPU(const char * filenameOBJ);
   virtual ~SceneObjectDeformableGPU();
 
   void SetInterpolation(int numCoarseVertices, int numElementVertices, int * vertices, double * weights);
diff --git a/src/libsceneObject/sceneObjectDeformableGPU6DOF.cpp b/libraries/sceneObject/sceneObjectDeformableGPU6DOF.cpp
similarity index 84%
rename from src/libsceneObject/sceneObjectDeformableGPU6DOF.cpp
rename to libraries/sceneObject/sceneObjectDeformableGPU6DOF.cpp
index a4594698eed034a2405c2ce41842a19204700713..f3b6570e948c96efb87a9fd88a68b02701a63b6a 100644
--- a/src/libsceneObject/sceneObjectDeformableGPU6DOF.cpp
+++ b/libraries/sceneObject/sceneObjectDeformableGPU6DOF.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,7 +32,7 @@
 
 #include "sceneObjectDeformableGPU6DOF.h"
 
-SceneObjectDeformableGPU6DOF::SceneObjectDeformableGPU6DOF(char * filenameOBJ): SceneObjectWithRestPosition(filenameOBJ), SceneObjectDeformable(filenameOBJ), SceneObjectDeformableGPU(filenameOBJ), SceneObjectDeformable6DOF(filenameOBJ)
+SceneObjectDeformableGPU6DOF::SceneObjectDeformableGPU6DOF(const char * filenameOBJ): SceneObjectWithRestPosition(filenameOBJ), SceneObjectDeformable(filenameOBJ), SceneObjectDeformableGPU(filenameOBJ), SceneObjectDeformable6DOF(filenameOBJ)
 {
 }
 
diff --git a/src/libsceneObject/sceneObjectDeformableGPU6DOF.h b/libraries/sceneObject/sceneObjectDeformableGPU6DOF.h
similarity index 79%
rename from src/libsceneObject/sceneObjectDeformableGPU6DOF.h
rename to libraries/sceneObject/sceneObjectDeformableGPU6DOF.h
index 2c09ac70d94ae87b4d73ba15aefa738c4e63c141..cecb738c7c16ad1ad7741af7f8970ba02c0bcb86 100644
--- a/src/libsceneObject/sceneObjectDeformableGPU6DOF.h
+++ b/libraries/sceneObject/sceneObjectDeformableGPU6DOF.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,7 +40,7 @@ class SceneObjectDeformableGPU6DOF : public SceneObjectDeformableGPU, public Sce
 {
 public:
 
-  SceneObjectDeformableGPU6DOF(char * filenameOBJ);
+  SceneObjectDeformableGPU6DOF(const char * filenameOBJ);
   virtual ~SceneObjectDeformableGPU6DOF();
 
   virtual void Render();
diff --git a/src/libsceneObject/sceneObjectWithRestPosition.cpp b/libraries/sceneObject/sceneObjectWithRestPosition.cpp
similarity index 77%
rename from src/libsceneObject/sceneObjectWithRestPosition.cpp
rename to libraries/sceneObject/sceneObjectWithRestPosition.cpp
index 684a35d4cc72e99ef4efb00fe657df028ef7ae65..4011f2f979152a195d9907d1e1df1141cb9fd761 100644
--- a/src/libsceneObject/sceneObjectWithRestPosition.cpp
+++ b/libraries/sceneObject/sceneObjectWithRestPosition.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,7 +34,17 @@
 #include <stdlib.h>
 #include "sceneObjectWithRestPosition.h"
 
-SceneObjectWithRestPosition::SceneObjectWithRestPosition(char * filename): SceneObject(filename) 
+SceneObjectWithRestPosition::SceneObjectWithRestPosition(const char * filename): SceneObject(filename) 
+{
+  Construct();
+}
+
+SceneObjectWithRestPosition::SceneObjectWithRestPosition(ObjMesh * objMesh, bool deepCopy): SceneObject(objMesh, deepCopy) 
+{
+  Construct();
+}
+
+void SceneObjectWithRestPosition::Construct()
 {
   restPosition = (double*) malloc (sizeof(double) * 3 * n);
   for(int i = 0; i < n; i++)
diff --git a/src/libsceneObject/sceneObjectWithRestPosition.h b/libraries/sceneObject/sceneObjectWithRestPosition.h
similarity index 75%
rename from src/libsceneObject/sceneObjectWithRestPosition.h
rename to libraries/sceneObject/sceneObjectWithRestPosition.h
index 4509bf7c42642e3bb4336245657c2319c72b79b2..7c8f02f3e57d18e2c5618fabbeeb092379b69c5c 100644
--- a/src/libsceneObject/sceneObjectWithRestPosition.h
+++ b/libraries/sceneObject/sceneObjectWithRestPosition.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -34,7 +38,8 @@
 class SceneObjectWithRestPosition: public SceneObject
 {
 public:
-  SceneObjectWithRestPosition(char * filename);
+  SceneObjectWithRestPosition(const char * filename);
+  SceneObjectWithRestPosition(ObjMesh * objMesh, bool deepCopy = true);
   virtual ~SceneObjectWithRestPosition();
 
   void GetVertexRestPositions(float * buffer);
@@ -44,6 +49,7 @@ public:
   virtual void TransformRigidly(double * centerOfMass, double * R);
 
 protected:
+  void Construct();
   double * restPosition;
 };
 
diff --git a/src/libsceneObjectReduced/sceneObjectReduced.cpp b/libraries/sceneObjectReduced/sceneObjectReduced.cpp
similarity index 64%
rename from src/libsceneObjectReduced/sceneObjectReduced.cpp
rename to libraries/sceneObjectReduced/sceneObjectReduced.cpp
index b430e96541f6cdda349d04df519cf1820306e815..508a6ecdac73155d58cf488a2eee26f44b6a72f5 100644
--- a/src/libsceneObjectReduced/sceneObjectReduced.cpp
+++ b/libraries/sceneObjectReduced/sceneObjectReduced.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,7 +37,17 @@
 #include <float.h>
 #include "sceneObjectReduced.h"
 
-SceneObjectReduced::SceneObjectReduced(char * filenameOBJ, ModalMatrix * modalMatrix): SceneObjectWithRestPosition(filenameOBJ), SceneObjectDeformable(filenameOBJ) 
+SceneObjectReduced::SceneObjectReduced(const char * filenameOBJ, ModalMatrix * modalMatrix): SceneObjectWithRestPosition(filenameOBJ), SceneObjectDeformable(filenameOBJ) 
+{
+  Construct(modalMatrix);
+}
+
+SceneObjectReduced::SceneObjectReduced(ObjMesh * objMesh, ModalMatrix * modalMatrix, bool deepCopy): SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectDeformable(objMesh, deepCopy) 
+{
+  Construct(modalMatrix);
+}
+
+void SceneObjectReduced::Construct(ModalMatrix * modalMatrix)
 {
   this->modalMatrix = modalMatrix;
   r = modalMatrix->Getr();
@@ -49,3 +63,10 @@ void SceneObjectReduced::SetZeroq()
 {
   memset(q, 0, sizeof(double) * r);
 }
+
+void SceneObjectReduced::Scaleq(double scale)
+{
+  for(int i=0; i<r; i++)
+    q[i] *= scale;
+}
+
diff --git a/src/libsceneObjectReduced/sceneObjectReduced.h b/libraries/sceneObjectReduced/sceneObjectReduced.h
similarity index 80%
rename from src/libsceneObjectReduced/sceneObjectReduced.h
rename to libraries/sceneObjectReduced/sceneObjectReduced.h
index e78e48ca9f7cf958ff83e6abd6dc4c630c1c0857..646fea47faaaf7b2e40883bd689901c9f4e57df4 100644
--- a/src/libsceneObjectReduced/sceneObjectReduced.h
+++ b/libraries/sceneObjectReduced/sceneObjectReduced.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -49,7 +53,8 @@ class SceneObjectReduced : public SceneObjectDeformable
 {
 public:
 
-  SceneObjectReduced(char * filenameOBJ, ModalMatrix * modalMatrix);
+  SceneObjectReduced(const char * filenameOBJ, ModalMatrix * modalMatrix);
+  SceneObjectReduced(ObjMesh * objMesh, ModalMatrix * modalMatrix, bool deepCopy = true);
   virtual ~SceneObjectReduced();
 
   inline int Getr() { return r; }
@@ -68,9 +73,11 @@ public:
   void SetZeroq();
   double * Getqp() { return q; }
   virtual void Setq(double * q) = 0; // copies q into internal state
+  void Scaleq(double scale); // scales the current q
   virtual void Compute_uUq() = 0;
 
 protected:
+  void Construct(ModalMatrix * modalMatrix);
 
   double * q;
   int r;
diff --git a/src/libsceneObjectReduced/sceneObjectReduced6DOF.cpp b/libraries/sceneObjectReduced/sceneObjectReduced6DOF.cpp
similarity index 67%
rename from src/libsceneObjectReduced/sceneObjectReduced6DOF.cpp
rename to libraries/sceneObjectReduced/sceneObjectReduced6DOF.cpp
index ec75606877e9c1702151811b0fb824e602a15221..7830a51eabfd2e7b0a24bb41badd16626ea3f450 100644
--- a/src/libsceneObjectReduced/sceneObjectReduced6DOF.cpp
+++ b/libraries/sceneObjectReduced/sceneObjectReduced6DOF.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,11 +32,18 @@
 
 #include "sceneObjectReduced6DOF.h"
 
-SceneObjectReduced6DOF::SceneObjectReduced6DOF(char * filenameOBJ, ModalMatrix * modalMatrix): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix), SceneObject6DOF(filenameOBJ)
+SceneObjectReduced6DOF::SceneObjectReduced6DOF(const char * filenameOBJ, ModalMatrix * modalMatrix): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix), SceneObject6DOF(filenameOBJ), qvel(NULL)
 {
 }
 
-SceneObjectReduced6DOF::~SceneObjectReduced6DOF() {}
+SceneObjectReduced6DOF::SceneObjectReduced6DOF(ObjMesh * objMesh, ModalMatrix * modalMatrix, bool deepCopy): SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectReduced(objMesh, modalMatrix, deepCopy), SceneObject6DOF(objMesh, deepCopy), qvel(NULL)
+{
+}
+
+SceneObjectReduced6DOF::~SceneObjectReduced6DOF() 
+{
+  free(qvel);
+}
 
 void SceneObjectReduced6DOF::GetSingleVertexPosition(int vertex, double * x, double * y, double * z)
 {
@@ -49,7 +60,7 @@ void SceneObjectReduced6DOF::GetSingleVertexPosition(int vertex, double * x, dou
   *z = R[6] * x0 + R[7] * y0 + R[8] * z0 + centerOfMass[2];
 }
 
-void SceneObjectReduced6DOF::GetSingleVertexVelocity(int vertex, double * objectVel, double * objectAngVel, double * qvel, double * velx, double * vely, double * velz)
+void SceneObjectReduced6DOF::GetSingleVertexVelocity(int vertex, double * objectVel, double * objectAngVel, double * velx, double * vely, double * velz)
 {
   SceneObject6DOF::GetSingleVertexVelocity(vertex, objectVel, objectAngVel, velx, vely, velz);
 
@@ -60,3 +71,12 @@ void SceneObjectReduced6DOF::GetSingleVertexVelocity(int vertex, double * object
   *velz += R[6] * localDefoVel[0] + R[7] * localDefoVel[1] + R[8] * localDefoVel[2];
 }
 
+void SceneObjectReduced6DOF::Setqvel(double * qvel_)
+{
+  int r = modalMatrix->Getr();
+  if (qvel == NULL)
+    qvel = (double*) malloc (sizeof(double) * r);
+  memcpy(qvel, qvel_, sizeof(double) * r);
+}
+
+
diff --git a/src/libsceneObjectReduced/sceneObjectReduced6DOF.h b/libraries/sceneObjectReduced/sceneObjectReduced6DOF.h
similarity index 73%
rename from src/libsceneObjectReduced/sceneObjectReduced6DOF.h
rename to libraries/sceneObjectReduced/sceneObjectReduced6DOF.h
index ee95a86b43dd4c99b418523c295aba9233cf9abd..0bc6aeba3ac1e9fcd4048be86d0fe3ff27b2d1a1 100644
--- a/src/libsceneObjectReduced/sceneObjectReduced6DOF.h
+++ b/libraries/sceneObjectReduced/sceneObjectReduced6DOF.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,11 +40,12 @@ class SceneObjectReduced6DOF : public virtual SceneObjectReduced, public SceneOb
 {
 public:
 
-  SceneObjectReduced6DOF(char * filenameOBJ, ModalMatrix * modalMatrix); 
+  SceneObjectReduced6DOF(const char * filenameOBJ, ModalMatrix * modalMatrix); 
+  SceneObjectReduced6DOF(ObjMesh * objMesh, ModalMatrix * modalMatrix, bool deepCopy = true); 
   virtual ~SceneObjectReduced6DOF();
 
   virtual void GetSingleVertexPosition(int vertex, double * x, double * y, double * z);
-  virtual void GetSingleVertexVelocity(int vertex, double * objectVel, double * objectAngVel, double * qvel, double * velx, double * vely, double * velz);
+  virtual void GetSingleVertexVelocity(int vertex, double * objectVel, double * objectAngVel, double * velx, double * vely, double * velz);
 
   virtual void Render() = 0;
   virtual void RenderVertices() = 0;
@@ -49,7 +54,10 @@ public:
   virtual void RenderVertices(int numVertices, int * vertexList) = 0;
   virtual void RenderShadow(double ground[4], double light[4]) = 0;
 
+  virtual void Setqvel(double * qvel); // copies qvel into internal state
+
 protected:
+  double * qvel;
 };
 
 #endif
diff --git a/src/libsceneObjectReduced/sceneObjectReducedCPU.cpp b/libraries/sceneObjectReduced/sceneObjectReducedCPU.cpp
similarity index 71%
rename from src/libsceneObjectReduced/sceneObjectReducedCPU.cpp
rename to libraries/sceneObjectReduced/sceneObjectReducedCPU.cpp
index 6a3f25219ca44345bd396cc0e18116ec6242a904..b908711993458ba37ccabab2493a052a7f787255 100644
--- a/src/libsceneObjectReduced/sceneObjectReducedCPU.cpp
+++ b/libraries/sceneObjectReduced/sceneObjectReducedCPU.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,7 +37,17 @@
 #include <float.h>
 #include "sceneObjectReducedCPU.h"
 
-SceneObjectReducedCPU::SceneObjectReducedCPU(char * filenameOBJ, ModalMatrix * modalMatrix): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix) 
+SceneObjectReducedCPU::SceneObjectReducedCPU(const char * filenameOBJ, ModalMatrix * modalMatrix): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix) 
+{
+  Construct(modalMatrix);
+}
+
+SceneObjectReducedCPU::SceneObjectReducedCPU(ObjMesh * objMesh, ModalMatrix * modalMatrix, bool deepCopy): SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectReduced(objMesh, modalMatrix, deepCopy) 
+{
+  Construct(modalMatrix);
+}
+
+void SceneObjectReducedCPU::Construct(ModalMatrix * modalMatrix)
 {
   if (n != modalMatrix->Getn())
   {
diff --git a/src/libsceneObjectReduced/sceneObjectReducedCPU.h b/libraries/sceneObjectReduced/sceneObjectReducedCPU.h
similarity index 75%
rename from src/libsceneObjectReduced/sceneObjectReducedCPU.h
rename to libraries/sceneObjectReduced/sceneObjectReducedCPU.h
index 4d4b5697af0d24564c75e3f6d039ee91b045df4f..abdd782aefd00e490b72ba2d91e8be7d5f3ba295 100644
--- a/src/libsceneObjectReduced/sceneObjectReducedCPU.h
+++ b/libraries/sceneObjectReduced/sceneObjectReducedCPU.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,7 +44,8 @@ class SceneObjectReducedCPU : virtual public SceneObjectReduced
 {
 public:
 
-  SceneObjectReducedCPU(char * filenameOBJ, ModalMatrix * modalMatrix);
+  SceneObjectReducedCPU(const char * filenameOBJ, ModalMatrix * modalMatrix);
+  SceneObjectReducedCPU(ObjMesh * objMesh, ModalMatrix * modalMatrix, bool deepCopy = true);
 
   void Getq(double * q);
   void Setq(double * q);
@@ -52,6 +57,7 @@ public:
   virtual int GetClosestVertex(Vec3d & queryPos, double * distance=NULL, double * auxVertexBuffer=NULL);
 
 protected:
+  void Construct(ModalMatrix * modalMatrix);
   double * u;
 };
 
diff --git a/src/libsceneObjectReduced/sceneObjectReducedCPU6DOF.cpp b/libraries/sceneObjectReduced/sceneObjectReducedCPU6DOF.cpp
similarity index 79%
rename from src/libsceneObjectReduced/sceneObjectReducedCPU6DOF.cpp
rename to libraries/sceneObjectReduced/sceneObjectReducedCPU6DOF.cpp
index 65fdd38f9fb1d2eda44e2371adcae8f88a445129..78c144a93d55afb1c101ff72b577b81ded8e1f21 100644
--- a/src/libsceneObjectReduced/sceneObjectReducedCPU6DOF.cpp
+++ b/libraries/sceneObjectReduced/sceneObjectReducedCPU6DOF.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,7 +32,11 @@
 
 #include "sceneObjectReducedCPU6DOF.h"
 
-SceneObjectReducedCPU6DOF::SceneObjectReducedCPU6DOF(char * filenameOBJ, ModalMatrix * modalMatrix): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix), SceneObjectReducedCPU(filenameOBJ, modalMatrix), SceneObjectReduced6DOF(filenameOBJ, modalMatrix)
+SceneObjectReducedCPU6DOF::SceneObjectReducedCPU6DOF(const char * filenameOBJ, ModalMatrix * modalMatrix): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix), SceneObjectReducedCPU(filenameOBJ, modalMatrix), SceneObjectReduced6DOF(filenameOBJ, modalMatrix)
+{
+}
+
+SceneObjectReducedCPU6DOF::SceneObjectReducedCPU6DOF(ObjMesh * objMesh, ModalMatrix * modalMatrix, bool deepCopy): SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectReduced(objMesh, modalMatrix, deepCopy), SceneObjectReducedCPU(objMesh, modalMatrix, deepCopy), SceneObjectReduced6DOF(objMesh, modalMatrix, deepCopy)
 {
 }
 
diff --git a/src/libsceneObjectReduced/sceneObjectReducedCPU6DOF.h b/libraries/sceneObjectReduced/sceneObjectReducedCPU6DOF.h
similarity index 76%
rename from src/libsceneObjectReduced/sceneObjectReducedCPU6DOF.h
rename to libraries/sceneObjectReduced/sceneObjectReducedCPU6DOF.h
index c99885733eaf781ae69ea3b7fa6ece3d6a0cde7b..036e60d705bc1194358714686e051799fbd43d26 100644
--- a/src/libsceneObjectReduced/sceneObjectReducedCPU6DOF.h
+++ b/libraries/sceneObjectReduced/sceneObjectReducedCPU6DOF.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -35,8 +39,8 @@
 class SceneObjectReducedCPU6DOF : public SceneObjectReducedCPU, public SceneObjectReduced6DOF
 {
 public:
-
-  SceneObjectReducedCPU6DOF(char * filenameOBJ, ModalMatrix * modalMatrix); 
+  SceneObjectReducedCPU6DOF(const char * filenameOBJ, ModalMatrix * modalMatrix); 
+  SceneObjectReducedCPU6DOF(ObjMesh * objMesh, ModalMatrix * modalMatrix, bool deepCopy = true); 
   virtual ~SceneObjectReducedCPU6DOF();
 
   virtual void Render();
diff --git a/src/libsceneObjectReduced/sceneObjectReducedGPU.cpp b/libraries/sceneObjectReduced/sceneObjectReducedGPU.cpp
similarity index 75%
rename from src/libsceneObjectReduced/sceneObjectReducedGPU.cpp
rename to libraries/sceneObjectReduced/sceneObjectReducedGPU.cpp
index cf5dc6dea12175a188db85b8dbb809d3620d90d4..7d8556ee969f75689014e46b587610c0baed1d57 100644
--- a/src/libsceneObjectReduced/sceneObjectReducedGPU.cpp
+++ b/libraries/sceneObjectReduced/sceneObjectReducedGPU.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,9 +37,21 @@
 #include <float.h>
 #include "sceneObjectReducedGPU.h"
 
-SceneObjectReducedGPU::SceneObjectReducedGPU(char * filenameOBJ, 
+SceneObjectReducedGPU::SceneObjectReducedGPU(const char * filenameOBJ, 
       ModalMatrix * modalMatrix, int GPUMethod):
       SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix)
+{
+  Construct(GPUMethod);
+}
+
+SceneObjectReducedGPU::SceneObjectReducedGPU(ObjMesh * objMesh, 
+      ModalMatrix * modalMatrix, int GPUMethod, bool deepCopy):
+      SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectReduced(objMesh, modalMatrix, deepCopy)
+{
+  Construct(GPUMethod);
+}
+
+void SceneObjectReducedGPU::Construct(int GPUMethod)
 {
   try
   {
@@ -47,7 +63,7 @@ SceneObjectReducedGPU::SceneObjectReducedGPU(char * filenameOBJ,
     }
     else if (GPUMethod == 1)
     {
-      #ifdef WIN32
+      #if defined(_WIN32) || defined(WIN32)
         render_uUq = new ObjMeshGPUDeformer_uUq_pbuffer();
         render_uUq->Init(mesh, meshRender, r, modalMatrix->GetMatrix(), renderMode);
       #else
@@ -69,9 +85,7 @@ SceneObjectReducedGPU::SceneObjectReducedGPU(char * filenameOBJ,
   }
 }
 
-SceneObjectReducedGPU::SceneObjectReducedGPU(char * filenameOBJ, 
-      ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod):
-      SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix)
+void SceneObjectReducedGPU::Construct(SceneObjectReducedGPU * cloningSource, int GPUMethod)
 {
   try
   {
@@ -82,7 +96,7 @@ SceneObjectReducedGPU::SceneObjectReducedGPU(char * filenameOBJ,
     }
     else if (GPUMethod == 1)
     {
-      #ifdef WIN32
+      #if defined(_WIN32) || defined(WIN32)
         render_uUq = new ObjMeshGPUDeformer_uUq_pbuffer();
         render_uUq->Clone(cloningSource->render_uUq);
       #else
@@ -104,6 +118,20 @@ SceneObjectReducedGPU::SceneObjectReducedGPU(char * filenameOBJ,
   }
 }
 
+SceneObjectReducedGPU::SceneObjectReducedGPU(const char * filenameOBJ, 
+      ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod):
+      SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix)
+{
+  Construct(cloningSource, GPUMethod);
+}
+
+SceneObjectReducedGPU::SceneObjectReducedGPU(ObjMesh * objMesh, 
+      ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod, bool deepCopy):
+      SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectReduced(objMesh, modalMatrix, deepCopy)
+{
+  Construct(cloningSource, GPUMethod);
+}
+
 SceneObjectReducedGPU::~SceneObjectReducedGPU()
 {
   delete(render_uUq);
diff --git a/src/libsceneObjectReduced/sceneObjectReducedGPU.h b/libraries/sceneObjectReduced/sceneObjectReducedGPU.h
similarity index 73%
rename from src/libsceneObjectReduced/sceneObjectReducedGPU.h
rename to libraries/sceneObjectReduced/sceneObjectReducedGPU.h
index 29244801ec8d38823b85c358841a8b1887e1c9f7..1092b25eb720a9aa14069d31ce844c98fd8ea630 100644
--- a/src/libsceneObjectReduced/sceneObjectReducedGPU.h
+++ b/libraries/sceneObjectReduced/sceneObjectReducedGPU.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -46,8 +50,12 @@ public:
   // GPUMethod: 
   // 0: framebuffer object
   // 1: pbuffer (Windows only)
-  SceneObjectReducedGPU(char * filenameOBJ, ModalMatrix * modalMatrix, int GPUMethod = 0);
-  SceneObjectReducedGPU(char * filenameOBJ, ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod = 0);
+  SceneObjectReducedGPU(const char * filenameOBJ, ModalMatrix * modalMatrix, int GPUMethod = 0);
+  SceneObjectReducedGPU(ObjMesh * objMesh, ModalMatrix * modalMatrix, int GPUMethod = 0, bool deepCopy = true);
+
+  SceneObjectReducedGPU(const char * filenameOBJ, ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod = 0);
+  SceneObjectReducedGPU(ObjMesh * objMesh, ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod = 0, bool deepCopy = true);
+
   virtual ~SceneObjectReducedGPU();
 
   void Getq(double * q);
@@ -70,6 +78,8 @@ public:
   virtual int GetClosestVertex(Vec3d & queryPos, double * distance, double * auxVertexBuffer);
 
 protected:
+  void Construct(int GPUMethod);
+  void Construct(SceneObjectReducedGPU * cloningSource, int GPUMethod);
 
   ObjMeshGPUDeformer_uUq * render_uUq;
   ObjMeshGPUDeformer_uUq_setLighting setGPULighting;
diff --git a/src/libsceneObjectReduced/sceneObjectReducedGPU6DOF.cpp b/libraries/sceneObjectReduced/sceneObjectReducedGPU6DOF.cpp
similarity index 67%
rename from src/libsceneObjectReduced/sceneObjectReducedGPU6DOF.cpp
rename to libraries/sceneObjectReduced/sceneObjectReducedGPU6DOF.cpp
index 65dca74283bc33a814adf9645e773f4cd59b87ed..61eb023107f9bb94e156593c57d3be1620f53ffd 100644
--- a/src/libsceneObjectReduced/sceneObjectReducedGPU6DOF.cpp
+++ b/libraries/sceneObjectReduced/sceneObjectReducedGPU6DOF.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,15 +32,24 @@
 
 #include "sceneObjectReducedGPU6DOF.h"
 
-SceneObjectReducedGPU6DOF::SceneObjectReducedGPU6DOF(char * filenameOBJ, ModalMatrix * modalMatrix, int GPUMethod): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix), SceneObjectReducedGPU(filenameOBJ, modalMatrix, GPUMethod), SceneObjectReduced6DOF(filenameOBJ, modalMatrix) 
+SceneObjectReducedGPU6DOF::SceneObjectReducedGPU6DOF(const char * filenameOBJ, ModalMatrix * modalMatrix, int GPUMethod): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix), SceneObjectReducedGPU(filenameOBJ, modalMatrix, GPUMethod), SceneObjectReduced6DOF(filenameOBJ, modalMatrix) 
 {
 }
 
-SceneObjectReducedGPU6DOF::SceneObjectReducedGPU6DOF(char * filenameOBJ, ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix), SceneObjectReducedGPU(filenameOBJ, modalMatrix, cloningSource, GPUMethod), SceneObjectReduced6DOF(filenameOBJ, modalMatrix) 
+SceneObjectReducedGPU6DOF::SceneObjectReducedGPU6DOF(const char * filenameOBJ, ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod): SceneObjectWithRestPosition(filenameOBJ), SceneObjectReduced(filenameOBJ, modalMatrix), SceneObjectReducedGPU(filenameOBJ, modalMatrix, cloningSource, GPUMethod), SceneObjectReduced6DOF(filenameOBJ, modalMatrix) 
 {
 }
 
-SceneObjectReducedGPU6DOF::~SceneObjectReducedGPU6DOF() {}
+SceneObjectReducedGPU6DOF::SceneObjectReducedGPU6DOF(ObjMesh * objMesh, ModalMatrix * modalMatrix, int GPUMethod, bool deepCopy): SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectReduced(objMesh, modalMatrix, deepCopy), SceneObjectReducedGPU(objMesh, modalMatrix, GPUMethod, deepCopy), SceneObjectReduced6DOF(objMesh, modalMatrix, deepCopy){
+}
+
+SceneObjectReducedGPU6DOF::SceneObjectReducedGPU6DOF(ObjMesh * objMesh, ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod, bool deepCopy): SceneObjectWithRestPosition(objMesh, deepCopy), SceneObjectReduced(objMesh, modalMatrix, deepCopy), SceneObjectReducedGPU(objMesh, modalMatrix, cloningSource, GPUMethod, deepCopy), SceneObjectReduced6DOF(objMesh, modalMatrix, deepCopy) 
+{
+}
+
+SceneObjectReducedGPU6DOF::~SceneObjectReducedGPU6DOF() 
+{
+}
 
 void SceneObjectReducedGPU6DOF::Render()
 {
diff --git a/src/libsceneObjectReduced/sceneObjectReducedGPU6DOF.h b/libraries/sceneObjectReduced/sceneObjectReducedGPU6DOF.h
similarity index 69%
rename from src/libsceneObjectReduced/sceneObjectReducedGPU6DOF.h
rename to libraries/sceneObjectReduced/sceneObjectReducedGPU6DOF.h
index 72e59e4b8b84b600dd381df2599200adaa98fafb..426897dc8e7126106657c3db569ef367a11f03a3 100644
--- a/src/libsceneObjectReduced/sceneObjectReducedGPU6DOF.h
+++ b/libraries/sceneObjectReduced/sceneObjectReducedGPU6DOF.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC    *
+ * "sceneObject" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC    *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,9 +40,13 @@ class SceneObjectReducedGPU6DOF : public SceneObjectReducedGPU, public SceneObje
 {
 public:
 
-  SceneObjectReducedGPU6DOF(char * filenameOBJ, ModalMatrix * modalMatrix, int GPUMethod = 0); 
-  SceneObjectReducedGPU6DOF(char * filenameOBJ, ModalMatrix * modalMatrix,
-    SceneObjectReducedGPU * cloningSource, int GPUMethod = 0);
+  SceneObjectReducedGPU6DOF(const char * filenameOBJ, ModalMatrix * modalMatrix, int GPUMethod = 0); 
+  SceneObjectReducedGPU6DOF(const char * filenameOBJ, ModalMatrix * modalMatrix, SceneObjectReducedGPU * cloningSource, int GPUMethod = 0);
+  
+  SceneObjectReducedGPU6DOF(ObjMesh * objMesh, ModalMatrix * modalMatrix, int GPUMethod = 0, bool deepCopy = true); 
+  SceneObjectReducedGPU6DOF(ObjMesh * objMesh, ModalMatrix * modalMatrix,
+    SceneObjectReducedGPU * cloningSource, int GPUMethod = 0, bool deepCopy = true);
+
   virtual ~SceneObjectReducedGPU6DOF();
 
   virtual void Render();
diff --git a/libraries/shapeEdit/arapDeformer.cpp b/libraries/shapeEdit/arapDeformer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3db483429f4cc2bb1b93ea1a277ea6b776946afc
--- /dev/null
+++ b/libraries/shapeEdit/arapDeformer.cpp
@@ -0,0 +1,681 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "shapeEdit" library , Copyright (C) 2018 USC                          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Koki Nagano, Yijing Li, Jernej Barbic        *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "arapDeformer.h"
+#include <float.h>
+#include "vec3d.h"
+//#include "matrix.h"
+#include "polarDecomposition.h"
+#include "objMeshOrientable.h"
+#include "triKey.h"
+#include "tetKey.h"
+#include "rectKey.h"
+#include "geometryQuery.h"
+#include "generateLaplacian.h"
+#include "triple.h"
+#include "basicAlgorithms.h"
+#include <algorithm>
+#include <iostream>
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#else
+  #include "range.h"
+#endif
+//#include "matrixBLAS.h"
+using namespace std;
+
+#define USE_DIRICHLET
+
+// ARAP Energy: E = \sum_i \sum_j\in N(i) wij | (pi'-pj') - Ri (pi-pj) |^2,
+//                  i: vtx index, j: nbr index in i's neighborhood N(i), pi: rest position of vtx i, pi': deformed position of i
+//                  Ri: the current rotation around vtx i
+//                  wij = wji = 0.5 * \sum_k cot(angle_ikj), the weight on edge (i,j) is the 0.5 * sum of each angle /_ikj which
+//                  opposites the edge (i,j) on a triangle (i,j,k)
+// partial E/ partial pi' = 4 \sum_j\in N(i) wij ( (pi'-pj') - 0.5 * (Ri+Rj)(pi-pj)
+//                        = 4 \sum_j wij [ (pi'-pj') - \sum_j 0.5 * wij (Ri+Rj)(pi-pj) ]
+// To set pE = 0, we have: L p = b, where
+//               L_ij = wij is a laplacian matrix, b_i = \sum_j 0.5 * wij (Ri+Rj)(pi-pj)
+
+ARAPModel::ARAPModel(const ObjMesh * objMesh)
+{
+  n = objMesh->getNumVertices();
+  for(int i = 0; i < n; i++)
+    restVertices.push_back(objMesh->getPosition(i));
+
+  L = generateLaplacian(objMesh, true);
+  assert(L->HasInfOrNaN() == false);
+  W = nullptr;
+  initialize();
+}
+
+ARAPModel::ARAPModel(const TetMesh * tetMesh, const double * vtxWeights)
+{
+  n = tetMesh->getNumVertices();
+  for(int i = 0; i < n; i++)
+    restVertices.push_back(tetMesh->getVertex(i));
+
+  SparseMatrixOutline LOutline(n), WOutline(n);
+  const double min_angle = acos(0.9999);
+  const double max_angle = acos(-0.9999);
+  vector<UTriKey> visitedFaces;
+  for(int ele = 0; ele < tetMesh->getNumElements(); ele++)
+  {
+    OTetKey tet(tetMesh->getVertexIndices(ele));
+    for(size_t j = 0; j < 4; j++)
+    {
+      UTriKey uface = tet.uFaceKey(j);
+      visitedFaces.push_back(uface);
+    }
+  }
+  sortAndDeduplicate(visitedFaces);
+
+#ifdef USE_TBB
+  tbb::enumerable_thread_specific<vector<triple<int,int,double>>> threadLocalWeightBuffer;
+  tbb::parallel_for(tbb::blocked_range<int>(0, visitedFaces.size()), [&](const tbb::blocked_range<int> & rng)
+  {
+    auto & weightBuffer = threadLocalWeightBuffer.local();
+#else
+    vector<triple<int,int,double>> weightBuffer;
+    Range<int> rng(0, visitedFaces.size());
+#endif
+    for(int face = rng.begin(); face != rng.end(); ++face)
+    {
+      UTriKey oface = visitedFaces[face];
+      assert(oface[0] != oface[1] && oface[1] != oface[2] && oface[2] != oface[0]);
+      for(int k = 0; k < 3; k++)
+      {
+        int v0 = oface[k];
+        int v1 = oface[(k+1)%3];
+        int v2 = oface[(k+2)%3];
+        Vec3d p0 = restVertices[v0];
+        Vec3d p1 = restVertices[v1];
+        Vec3d p2 = restVertices[v2];
+
+        //compute 0.5 * cot (angle(p1p0p2))
+        double w = 0.5 / tan(clamp(getTriangleAngleRobust(p0, p1, p2), min_angle, max_angle));
+        // cout << "computing vtx " << v0 << " " << v1 << " " << v2 << " " << w << endl;
+        //clamp
+        if (w < 0) w = 0;
+
+        // formula from the paper: wij = 0.5 (cot alpha_ij + cot beta_ij)
+        // alpha_ij and beta_ij are the angles opposite of the mesh edge (i, j)
+
+        weightBuffer.emplace_back(v1, v2, w);
+      }
+    }
+#ifdef USE_TBB
+  }, tbb::static_partitioner());
+
+  for(const auto & weightBuffer : threadLocalWeightBuffer)
+#endif
+  {
+    for(const auto & t : weightBuffer)
+    {
+      int v1 = t.first, v2 = t.second;
+      double w = t.third;
+      WOutline.AddEntry(v1, v2, w);
+      WOutline.AddEntry(v2, v1, w);
+      if (vtxWeights) // default vtxWeight is 1.0 for each vtx.
+        w *= (vtxWeights[v1] + vtxWeights[v2]) / 2.0;
+      LOutline.AddEntry(v1, v2, -w);
+      LOutline.AddEntry(v2, v1, -w);
+      LOutline.AddEntry(v1, v1, w);
+      LOutline.AddEntry(v2, v2, w);
+    }
+  }
+
+  //we have to do this to ensure every row has at least one element
+  //otherwise the solver will complain
+  for(int row = 0; row < n; row++)
+    LOutline.AddEntry(row, row, 0);
+
+  L = new SparseMatrix(&LOutline);
+  W = new SparseMatrix(&WOutline);
+
+//  W->Save("W.sparse",0);
+  initialize();
+}
+
+ARAPModel::ARAPModel(const CubicMesh * cubicMesh, const double * vtxWeights)
+{
+  n = cubicMesh->getNumVertices();
+  for(int i = 0; i < n; i++)
+    restVertices.push_back(cubicMesh->getVertex(i));
+
+  SparseMatrixOutline LOutline(n), WOutline(n);
+
+  // face vtx indices
+  int faceVtx[6][4] = {{0,3,2,1}, {4,5,6,7}, {0,1,5,4}, {3,7,6,2}, {1,2,6,5}, {0,4,7,3}};
+
+  set<URectKey> visitedFaces;
+  for(int ele = 0; ele < cubicMesh->getNumElements(); ele++)
+  {
+    for(int f = 0; f < 6; f++)
+    {
+      int v[4];
+      v[0] = cubicMesh->getVertexIndex(ele, faceVtx[f][0]);
+      v[1] = cubicMesh->getVertexIndex(ele, faceVtx[f][1]);
+      v[2] = cubicMesh->getVertexIndex(ele, faceVtx[f][2]);
+      v[3] = cubicMesh->getVertexIndex(ele, faceVtx[f][3]);
+      URectKey key(v[0],v[1],v[2],v[3]);
+      if (visitedFaces.find(key) != visitedFaces.end())
+        continue;
+      visitedFaces.insert(key);
+
+      for(int i = 0; i < 4; i++)
+      {
+        int j = (i+1)%4;
+        double w = 0.5;
+        WOutline.AddEntry(v[i], v[j], w);
+        WOutline.AddEntry(v[j], v[i], w);
+
+        if (vtxWeights) // default vtxWeight is 1.0 for each vtx.
+          w *= (vtxWeights[v[i]] + vtxWeights[v[j]]) / 2.0;
+
+        LOutline.AddEntry(v[i], v[j], -w);
+        LOutline.AddEntry(v[j], v[i], -w);
+        LOutline.AddEntry(v[i], v[i], w);
+        LOutline.AddEntry(v[j], v[j], w);
+      }
+    }
+  }
+  //we have to do this to ensure every row has at least one element
+  //otherwise the solver will complain
+  for(int row = 0; row < n; row++)
+    LOutline.AddEntry(row, row, 0);
+
+  L = new SparseMatrix(&LOutline);
+  W = new SparseMatrix(&WOutline);
+
+//  W->Save("W.sparse",0);
+  initialize();
+}
+
+void ARAPModel::initialize()
+{
+  R.resize(n, Mat3d(1.0));
+  vector<double> p0(n); //Lp = L * p0
+  //LP stores the multiplication of L and p. By using Lp, we can rewrite the original formula in terms of u not p
+  for(int i = 0; i < 3; i++)
+  {
+    Lp[i].resize(n);
+    for(int j = 0; j < n; j++)
+      p0[j] = restVertices[j][i];
+    L->MultiplyVector(&p0[0],&Lp[i][0]);
+  }
+}
+
+ARAPModel::~ARAPModel()
+{
+  delete L;
+  delete W;
+}
+
+void ARAPModel::updateRotations(const double * disp)
+{
+  double Si[9], U[9], Ri[9];
+
+  for(int vtxIdx = 0; vtxIdx < n; vtxIdx++)
+  {
+    Vec3d pos_i_def, pos_j_def, pos_i_rest, pos_j_rest;
+    pos_i_rest = restVertices[vtxIdx];
+    pos_i_def = pos_i_rest + (disp + vtxIdx*3);
+
+    int rowLength = L->GetRowLength(vtxIdx);
+
+    memset(Si, 0, sizeof(double) * 9);
+
+    for(int col = 0; col < rowLength; col++)
+    {
+      int neighbor = L->GetColumnIndex(vtxIdx, col);
+      if (neighbor == vtxIdx) continue;
+      pos_j_rest = restVertices[neighbor];
+      pos_j_def = pos_j_rest + (disp + neighbor*3);
+
+      double w = -1 * (L->GetEntry(vtxIdx, col));
+      Vec3d e_rest = pos_i_rest - pos_j_rest;
+      Vec3d e_def = pos_i_def - pos_j_def;
+
+      //w_ij * eij * (eij_deformed)T
+      Mat3d cov = w * tensorProduct(e_rest, e_def);
+      cov.addToArray(Si);
+    }
+
+    double tolerance = 1E-6;
+    int forceRotation = 1;
+
+    PolarDecomposition::Compute(Si, Ri, U, tolerance, forceRotation);
+
+    Mat3d RiM(Ri);
+    R[vtxIdx] = trans(RiM);
+  }
+}
+
+
+void ARAPModel::buildPosRHS(std::vector<double> rhs[3])
+{
+  for(int i = 0; i < 3; i++)
+    rhs[i].assign(n, 0.0);
+
+  for(int vtxIdx = 0; vtxIdx < n; vtxIdx++)
+  {
+    int rowLength = L->GetRowLength(vtxIdx);
+    Vec3d pos_i = restVertices[vtxIdx];
+    // iterate all the neighbors of current vertex
+    for(int entryIndex = 0; entryIndex < rowLength; entryIndex++)
+    {
+      // w_ij
+      double w = -1 * (L->GetEntry(vtxIdx, entryIndex)); // L stores negative wij
+      int nbrIdx = L->GetColumnIndex(vtxIdx, entryIndex);
+      if (nbrIdx == vtxIdx)
+        continue;
+
+      // R_i + R_j
+      Mat3d Rsum = R[vtxIdx] + R[nbrIdx];
+      Vec3d pos_j = restVertices[nbrIdx];
+      // p_i - p_j
+      Vec3d pos_diff = pos_i - pos_j;
+      // (w_ij / 2) * (R_i + R_j) * (p_i - p_j)
+      Vec3d res = (w / 2) * Rsum * pos_diff;
+
+      //seperate the dofs
+      for(int d = 0; d < 3; d++)
+        rhs[d][vtxIdx] += res[d];
+    }
+  }
+
+//  vector<double> nrhs[3];
+//  for(int d = 0; d < 3; d++)
+//    nrhs[d].resize(n);
+//  for(int i = 0; i < n; i++)
+//    buildOneVertexRHS(i, nrhs);
+//
+//  for(int i = 0; i < n; i++)
+//    nrhs[1][i] -= rhs[1][i];
+}
+
+void ARAPModel::buildDispRHS(std::vector<double> rhs[3])
+{
+  buildPosRHS(rhs);
+  for(int d = 0; d < 3; d++)
+    for(int i = 0; i < n; i++)
+      rhs[d][i] -= Lp[d][i];
+}
+
+void ARAPModel::buildPosRHSOnOneRotation(int vtxIdx, std::vector<double> rhs[3])
+{
+  int rowLength = L->GetRowLength(vtxIdx);
+  Vec3d pos_i = restVertices[vtxIdx];
+  // iterate all the neighbors of current vertex
+  for(int entryIndex = 0; entryIndex < rowLength; entryIndex++)
+  {
+    // w_ij
+    double w = -1 * (L->GetEntry(vtxIdx, entryIndex)); // L stores negative wij
+    int nbrIdx = L->GetColumnIndex(vtxIdx, entryIndex);
+    if (nbrIdx == vtxIdx)
+      continue;
+
+    Vec3d pos_j = restVertices[nbrIdx];
+
+    // p_i - p_j
+    Vec3d pos_diff = pos_i - pos_j;
+
+    // (w_ij / 2) * R_i * (p_i - p_j)
+    Vec3d res = (w / 2) * R[vtxIdx] * pos_diff;
+
+    //seperate the dofs
+    for(int d = 0; d < 3; d++)
+    {
+      rhs[d][vtxIdx] += res[d];
+      rhs[d][nbrIdx] -= res[d];
+    }
+  }
+}
+
+void ARAPModel::buildEnergyHessian(SparseMatrix ** sparseMatrix) const
+{
+  SparseMatrixOutline outline(3*n);
+
+  outline.AddBlockMatrix(0,0, L, 1., 3);
+  *sparseMatrix = new SparseMatrix(&outline);
+}
+
+// E = \sum_i \sum_j\in N(i) wij | (pi'-pj') - Ri (pi-pj) |^2,
+// partial E/ partial pi' = 4 \sum_j\in N(i) wij ( (pi'-pj') - 0.5 * (Ri+Rj)(pi-pj) )
+void ARAPModel::getEnergyAndGradient(const double * disp, double * energy, double * gradient)
+{
+  if (gradient)
+    memset(gradient, 0, n * 3);
+  updateRotations(disp);
+
+  if (energy)
+    *energy = 0.;
+
+  for(int vtxIdx = 0; vtxIdx < n; vtxIdx++)
+  {
+    int rowLength = L->GetRowLength(vtxIdx);
+    Vec3d resPi = restVertices[vtxIdx];
+    Vec3d defPi = resPi + Vec3d(disp + 3*vtxIdx);
+    // iterate all the neighbors of current vertex
+    for(int j = 0; j < rowLength; j++)
+    {
+      // w_ij
+      double w = -1 * (L->GetEntry(vtxIdx, j)); // L stores negative wij
+      int nbrIdx = L->GetColumnIndex(vtxIdx, j);
+      if (nbrIdx == vtxIdx)
+        continue;
+      Vec3d resPj = restVertices[nbrIdx];
+      Vec3d defPj = resPj + Vec3d(disp + 3*nbrIdx);
+
+      // p_i - p_j
+      Vec3d resDif = resPi - resPj;
+      Vec3d defDif = defPi - defPj;
+
+      // (R_i) * (p_i - p_j)
+      Vec3d rotResDif = R[vtxIdx] * resDif;
+
+      if (energy)
+        *energy += w * len2(defDif - rotResDif);
+
+      if (gradient)
+      {
+        Vec3d g = defDif - 0.5 * rotResDif;
+        g *= 4 * w;
+        g.addToArray(gradient + 3 * vtxIdx);
+      }
+    }
+  }
+}
+
+void ARAPModel::setRotations(const Mat3d * rotations)
+{
+  for(int i = 0; i < n; i++)
+    R[i] = rotations[i];
+//  memcpy(R.data(), rotations, sizeof(Mat3d) * n);
+}
+
+void ARAPModel::assembleDimension(const std::vector<double> input[3], double * output)
+{
+  int n = input[0].size();
+  for(int i = 0; i < n; i++)
+  {
+    output[3 * i + 0] = input[0][i];
+    output[3 * i + 1] = input[1][i];
+    output[3 * i + 2] = input[2][i];
+  }
+}
+
+ARAPDeformer::ARAPDeformer(const ObjMesh * objMesh, int numFixedVertices_, const int * fixedVertices_, int numThreads_) :
+    arapModel(objMesh), numThreads(numThreads_)
+{
+  //assert(objMesh->isTriangularMesh());
+  n = objMesh->getNumVertices();
+  n3 = 3*n;
+
+  fixedVertices.resize(numFixedVertices_);
+  memcpy(fixedVertices.data(), fixedVertices_, sizeof(int) * numFixedVertices_);
+  fixedVerticesSet.insert(fixedVertices.begin(), fixedVertices.end());
+}
+
+ARAPDeformer::ARAPDeformer(const TetMesh * tetMesh, int numFixedVertices_, const int * fixedVertices_, int numThreads_) :
+    arapModel(tetMesh), numThreads(numThreads_)
+{
+  n = tetMesh->getNumVertices();
+  n3 = 3*n;
+
+  fixedVertices.resize(numFixedVertices_);
+  memcpy(fixedVertices.data(), fixedVertices_, sizeof(int) * numFixedVertices_);
+  fixedVerticesSet.insert(fixedVertices.begin(), fixedVertices.end());
+}
+
+ARAPDeformer::ARAPDeformer(const CubicMesh * cubicMesh, int numFixedVertices_, const int * fixedVertices_, int numThreads_) :
+    arapModel(cubicMesh), numThreads(numThreads_)
+{
+  n = cubicMesh->getNumVertices();
+  n3 = 3*n;
+
+  fixedVertices.resize(numFixedVertices_);
+  memcpy(fixedVertices.data(), fixedVertices_, sizeof(int) * numFixedVertices_);
+  fixedVerticesSet.insert(fixedVertices.begin(), fixedVertices.end());
+}
+
+ARAPDeformer::~ARAPDeformer()
+{
+  delete solver[0];
+  delete solver[1];
+  delete solver[2];
+}
+
+bool ARAPDeformer::checkHandles(const std::map<int, Vec3d> & newHandles)
+{
+  bool rebuild = false;
+  if (newHandles.size() != handles.size())
+    rebuild = true;
+  else
+  {
+    // checking handle vertex indices and updating handle displacements
+    map<int, Vec3d>::iterator oldIt = handles.begin();
+    map<int, Vec3d>::const_iterator newIt = newHandles.begin();
+    for(; newIt != newHandles.end(); oldIt++, newIt++)
+      if (oldIt->first != newIt->first)
+      {
+        rebuild = true;
+        break;
+      }
+      else
+      {
+        oldIt->second = newIt->second;
+        //cout << "move handle " << oldIt->first  << " to " << oldIt->second << endl;
+      }
+  }
+  return rebuild;
+}
+
+void ARAPDeformer::updateHandles(const std::map<int, Vec3d> & newHandles)
+{
+  bool rebuild = checkHandles(newHandles);
+  if (rebuild)
+    rebuildSolver(newHandles);
+}
+
+void ARAPDeformer::rebuildSolver(const std::map<int, Vec3d> & newHandles)
+{
+  for(int i = 0; i < 3; i++)
+    delete (solver[i]);
+  handles = newHandles;
+
+  // no constraints, so return
+  if (newHandles.size() == 0)
+  {
+    for(int i = 0; i < 3; i++)
+      solver[i] = nullptr;
+    return;
+  }
+
+  cout << "Build ARAPDeformer solver with " << handles.size() << " handles" << endl;
+
+  // form constraints
+#ifdef USE_DIRICHLET
+  set<int> constrainedVertices(fixedVertices.begin(), fixedVertices.end());
+  for(map<int, Vec3d>::const_iterator it = newHandles.begin(); it != newHandles.end(); it++)
+  {
+    constrainedVertices.insert(it->first);
+    assert(it->first < n);
+  }
+  vector<int> constrainedVerticesVec(constrainedVertices.begin(), constrainedVertices.end());
+  for(size_t i = 0; i < constrainedVerticesVec.size(); i++)
+    assert(constrainedVerticesVec[i] >= 0 && constrainedVerticesVec[i] < n);
+
+  for(int i = 0; i < 3; i++)
+  {
+    //reconstruct the solver and do Cholesky decomposition
+    int updatable = 0, addDirichlet = 1;
+    solver[i] = new LagrangeMultiplierSolver(arapModel.getL(), nullptr, nullptr, constrainedVerticesVec.size(), constrainedVerticesVec.data(),
+        numThreads, updatable, addDirichlet);
+  }
+#else
+  SparseMatrixOutline JOutline(newHandles.size());
+  int row = 0;
+  for(map<int, Vec3d>::const_iterator it = newHandles.begin(); it != newHandles.end(); it++)
+  {
+    JOutline.AddEntry(row, it->first, constrainedScale);
+    row++;
+  }
+  JOutline.AddEntry(0, n-1, 0);
+  SparseMatrix J(&JOutline);
+
+  for(int i = 0; i < 3; i++)
+  {
+    //reconstruct the solver and do Cholesky decomposition
+    solver[i] = new LagrangeMultiplierSolver(L, &J, nullptr, fixedVertices.size(), fixedVertices.data(), numThreads);
+  }
+#endif
+}
+
+void ARAPDeformer::deform(const double * dispLast, double * disp, double epsilon, unsigned int maxIteration)
+{
+  if (handles.size() == 0)
+  {
+    memset(disp, 0, sizeof(double) * 3 * n);
+    return;
+  }
+
+  double ep2 = epsilon * epsilon;
+
+
+  vector<double> buffer(n3);
+  memcpy(buffer.data(), dispLast, sizeof(double) * n3);
+
+  double error2 = 0;
+  unsigned int iter = 0;
+  double dispLastLen2 = 0;
+  do
+  {
+    dispLastLen2 = 0;
+    for(int i = 0; i < n3; i++)
+      dispLastLen2 += buffer[i] * buffer[i];
+
+    deformOneIter(&buffer[0], disp);
+    error2 = 0;
+    for(int i = 0; i < n3; i++)
+    {
+      error2 += (buffer[i] - disp[i]) * (buffer[i] - disp[i]);
+    }
+    cout << sqrt(error2 / dispLastLen2) << " ";
+    iter++;
+    if (iter >= maxIteration)
+      break;
+    memcpy(buffer.data(), disp, sizeof(double) * n3);
+  } while(error2 / dispLastLen2 > ep2);
+  cout << iter << " " << endl;
+}
+
+void ARAPDeformer::deformOneIter(const double * dispLast, double * disp)
+{
+  if (handles.size() == 0) //if there is no constraints, set to rest configuration
+  {
+    memset(disp, 0, sizeof(double) * 3 * n);
+    return;
+  }
+
+  arapModel.updateRotations(dispLast);
+  optimizePositions(disp);
+}
+
+void ARAPDeformer::deformOneIter(double * disp)
+{
+  deformOneIter(disp, disp);
+}
+
+void ARAPDeformer::optimizePositions(double * disp)
+{
+  vector<double> rhs[3], x[3];
+  for(int i = 0; i < 3; i++)
+  {
+    rhs[i].resize(n);
+    x[i].resize(n);
+  }
+
+  arapModel.buildDispRHS(rhs);
+
+  for(std::map<int, Vec3d>::iterator it = handles.begin(); it != handles.end(); it++)
+  {
+    Vec3d disp = it->second;
+
+    int vtx = it->first;
+    for(int d = 0; d < 3; d++)
+      x[d][vtx] = disp[d];
+  }
+
+  // 3 solves
+  for(int i = 0; i < 3; i++)
+    solver[i]->SolveLinearSystem(&x[i][0], &rhs[i][0]);
+
+  arapModel.assembleDimension(x, disp);
+}
+
+void ARAPDeformer::setNumThreads(int threads)
+{
+  if (solver[0])
+    for(int i = 0; i < 3; i++)
+      solver[i]->SetNumThreads(threads);
+}
+
+//void ARAPDeformer::setConstraintMatrix(const SparseMatrix * C[3], const SparseMatrix * B[3])
+//{
+//  vector<int> oldFixedDOFs(solver[0]->GetFixedDOFs(), solver[0]->GetFixedDOFs() + solver[0]->GetNumFixedDOFs());
+//
+//  if (C[0] == nullptr)
+//    consRhs.clear();
+//  else
+//    consRhs.assign(C[0]->GetNumRows(), 0.0);
+//  for(int i = 0; i < 3; i++)
+//  {
+//    assert((C[i] == nullptr && consRhs.size() == 0) || (C[i] && C[i]->GetNumRows() == (int)consRhs.size()));
+//    //reconstruct the solver and do Cholesky decomposition
+//    int updatable = 0, addDirichlet = 1;
+//    solver[i] = new LagrangeMultiplierSolver(L, C[i], B[i], oldFixedDOFs.size(), oldFixedDOFs.data(),
+//        numThreads, updatable, addDirichlet);
+//  }
+//}
+//
+//void ARAPDeformer::setConstraintRhs(const double * constraintRhs)
+//{
+//  memcpy(consRhs.data(), constraintRhs, consRhs.size() * sizeof(double));
+//}
+//
+//void ARAPDeformer::clearConstraints()
+//{
+//  vector<const SparseMatrix *> C(3, nullptr);
+//  setConstraintMatrix(&C[0]);
+//}
+
diff --git a/libraries/shapeEdit/arapDeformer.h b/libraries/shapeEdit/arapDeformer.h
new file mode 100644
index 0000000000000000000000000000000000000000..6a963556c8ca10281b7fe3758f6a16defdea7d9f
--- /dev/null
+++ b/libraries/shapeEdit/arapDeformer.h
@@ -0,0 +1,150 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "shapeEdit" library , Copyright (C) 2018 USC                          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Koki Nagano, Yijing Li, Jernej Barbic        *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef ARAPDEFORMER_H
+#define ARAPDEFORMER_H
+
+#include "sparseMatrix.h"
+#include "objMesh.h"
+#include "LagrangeMultiplierSolver.h"
+#include <set>
+#include <map>
+#include <vector>
+#include "tetMesh.h"
+#include "cubicMesh.h"
+
+/*
+  As-rigid-as-possible model for obj meshes, tet meshes and cubic meshes.
+
+  1. For obj meshes, this class implements the model for fast geometric shape deformation described in the following paper:
+
+    Olga Sorkine and Marc Alexa: As-Rigid-As-Possible Surface Modeling
+    In Proc. of Eurographics Symposium on Geometry Processing (2007), Vol. 4, p. 30
+
+  2. For tet and cubic meshes, the class implements the extension for volumetric meshes described in the same paper.
+*/
+
+class ARAPModel
+{
+public:
+  ARAPModel(const ObjMesh * objMesh);
+  ARAPModel(const TetMesh * tetMesh, const double * vtxWeights = nullptr);
+  ARAPModel(const CubicMesh * tetMesh, const double * vtxWeights = nullptr);
+  virtual ~ARAPModel();
+
+  void updateRotations(const double * disp);
+
+  void buildDispRHS(std::vector<double> rhs[3]);
+  void buildPosRHS(std::vector<double> rhs[3]);
+
+  const SparseMatrix * getL() const { return L; }
+  const SparseMatrix * getW() const { return W; }
+  const Vec3d & getRestVertex(int i) const { return restVertices[i]; }
+  const std::vector<Vec3d> & getRestVertices() const { return restVertices; }
+
+  const Mat3d & getRotation(int i) const { return R[i]; }
+  const std::vector<Mat3d> & getRotations() const { return R; }
+
+  void setRotations(const Mat3d * rotations);
+
+  // we omit the derivative of rotation Ri, so energy hessian is always the same: a weighted Laplacian matrix
+  // the hessian is of size 3*#vtx
+  void buildEnergyHessian(SparseMatrix ** sparseMatrix) const;
+
+  // compute energy and its gradient; the derivative of rotation is omitted in calculation
+  // either energy or gradient can be nullptr, dim is of size 3*#vtx
+  void getEnergyAndGradient(const double * disp, double * energy, double * gradient = nullptr);
+
+  void buildPosRHSOnOneRotation(int vtxIdx, std::vector<double> rhs[3]);
+
+  static void assembleDimension(const std::vector<double> input[3], double * output);
+
+protected:
+  void initialize();
+
+  int n;
+  SparseMatrix * L;
+  SparseMatrix * W; // W is the wij matrix
+  std::vector<Vec3d> restVertices;
+  std::vector<double> Lp[3];
+  std::vector<Mat3d> R;
+};
+
+
+class ARAPDeformer
+{
+public:
+  ARAPDeformer(const ObjMesh * objMesh, int numFixedVertices, const int * fixedVertices, int numThreads);
+  ARAPDeformer(const TetMesh * tetMesh, int numFixedVertices, const int * fixedVertices, int numThreads);
+  ARAPDeformer(const CubicMesh * tetMesh, int numFixedVertices, const int * fixedVertices, int numThreads);
+  virtual ~ARAPDeformer();
+
+  void setNumThreads(int threads);
+
+  virtual void updateHandles(const std::map<int, Vec3d> & newHandles);
+
+  // do one iteration of rotation and position optimization, return a rest shape (disp = 0) if no constraints
+  void deformOneIter(double * disp); //disp serves as input and output
+  virtual void deformOneIter(const double * dispLast, double * disp);
+
+  // do rotation and position optimization until relative error < epsilon or maxIteration is reached
+  void deform(const double * dispLast, double * disp, double epsilon, unsigned int maxIteration);
+
+  const SparseMatrix * getL() const { return arapModel.getL(); }
+
+protected:
+  // update handle displacements. return true if handle vertex indices don't match
+  bool checkHandles(const std::map<int, Vec3d> & newHandles);
+
+  void rebuildSolver(const std::map<int, Vec3d> & newHandles);
+
+  //Solve 3 linear systems (n * n)
+  //call Lagrange multiplier solver SolveLinearSystem()
+  //store the position to sceneObjectDeformable (it uses displacement. instead of position)
+  void optimizePositions(double * disp);
+
+  ARAPModel arapModel;
+
+  std::map<int, Vec3d> handles;
+
+  std::vector<int> fixedVertices;
+  std::set<int> fixedVerticesSet;
+
+  int numThreads;
+
+  //per-edge Laplacian matrix: n * n
+
+  LagrangeMultiplierSolver * solver[3] = {nullptr, nullptr, nullptr};
+  int n, n3;
+};
+
+#endif
diff --git a/libraries/shapeEdit/generateLaplacian.cpp b/libraries/shapeEdit/generateLaplacian.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..135f7ecd785aa440258c45bf8f8795c8f33e4a91
--- /dev/null
+++ b/libraries/shapeEdit/generateLaplacian.cpp
@@ -0,0 +1,324 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "shapeEdit" library , Copyright (C) 2018 USC                          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Koki Nagano, Yijing Li, Jernej Barbic        *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <iostream>
+#include <vector>
+#include <set>
+#include <cassert>
+#include <limits>
+#include "generateLaplacian.h"
+#include "tetMeshManifold.h"
+#include "matrixBLAS.h"
+#include "triMeshGeo.h"
+#include "geometryQuery.h"
+#include "basicAlgorithms.h"
+#include "containerHelper.h"
+using namespace std;
+
+// the direction that is perpendicular to the line (start, dir) and go from the line to an external point p
+static Vec3d getNormalOfLine(Vec3d start, Vec3d dir, Vec3d p)
+{
+  dir.normalize();
+  double projOnLine = dot(p - start, dir);
+  Vec3d closestPoint = start + (projOnLine / len2(dir)) * dir;
+  Vec3d disp = p - closestPoint;
+  disp.normalize();
+  return disp;
+}
+
+static void addVolumetricLaplacianTerm(const TetMesh * tetMesh, int vi, int vj, int op0, int op1,
+    SparseMatrixOutline & outline, int lineID)
+{
+  set<int> as;
+  as.insert(vi);
+  as.insert(vj);
+  as.insert(op0);
+  as.insert(op1);
+  assert(as.size() == 4);
+
+  Vec3d pi = tetMesh->getVertex(vi);
+  Vec3d pj = tetMesh->getVertex(vj);
+  Vec3d p0 = tetMesh->getVertex(op0);
+  Vec3d p1 = tetMesh->getVertex(op1);
+  Vec3d dir = p1 - p0;
+  double lij = len(dir);
+  Vec3d ni = getNormalOfLine(p0, dir, pi);
+  Vec3d nj = getNormalOfLine(p0, dir, pj);
+  double cosij = dot(ni, nj);
+  //  cout << vi << " " << vj << " angle: " << acos(cosij) * 180 / M_PI << endl;
+  double sinij = sqrt(1 - cosij * cosij);
+  double cotij = cosij / sinij;
+  assert(Vec3d::isNaN(cotij) == false);
+  double w = lij * cotij / 6.;
+
+  //if (lineID == vj && w < 0)
+  //  w = 0;
+
+  outline.AddEntry(lineID, vi, -w);
+  outline.AddEntry(lineID, vj, w);
+}
+
+SparseMatrix * generateLaplacian(const TetMesh * tetMesh, bool LinearlyPrecise)
+{
+  int anotherIndex[4][3] = { {1,2,3}, {0,2,3}, {0,1,3}, {0,1,2} };
+  int oppositeIndex[4][3][2] = { { {2,3}, {1,3}, {1,2} }, { {2,3}, {0,3}, {0,2} }, { {1,3}, {0,3}, {0,1} }, { {1,2}, {0,2}, {0,1} } };
+  int n = tetMesh->getNumVertices();
+  int t = tetMesh->getNumElements();
+  SparseMatrixOutline outline(n);
+  for(int e = 0; e < t; e++)
+    for(int i = 0; i < 4; i++)
+    {
+      int vi = tetMesh->getVertexIndex(e, i);
+      for(int j = 0; j < 3; j++)
+      {
+        int vj = tetMesh->getVertexIndex(e, anotherIndex[i][j]);
+        assert(vi != vj);
+        int op0 = tetMesh->getVertexIndex(e, oppositeIndex[i][j][0]);
+        int op1 = tetMesh->getVertexIndex(e, oppositeIndex[i][j][1]);
+        addVolumetricLaplacianTerm(tetMesh, vi, vj, op0, op1, outline, vi);
+      }
+    }
+
+  if (LinearlyPrecise)
+  {
+    TetMeshManifold m;
+    for(int i = 0; i < t; i++)
+      m.add(tetMesh->getVertexIndices(i), i);
+    const map<OTriKey, TetMeshManifold::Tetrahedron*> & surface = m.getSurfaceMap();
+    const TetMeshManifold::TriMap & triMap = m.getTriMap();
+    int triangleOtherIndex[3][2] = { {1,2}, {0,2}, {0,1} };
+    for(map<OTriKey, TetMeshManifold::Tetrahedron *>::const_iterator it = surface.begin(); it != surface.end(); it++)
+    {
+      const OTriKey & tri = it->first;
+      //cout << "tri: " << tri[0] << " " << tri[1] << " " << tri[2] << endl;
+      UTriKey utri(tri.indices());
+      TetMeshManifold::TriCIter triIter = triMap.find(utri);
+      const TetMeshManifold::Tetrahedron * tet = triIter->second->getTet(0);
+      assert(triIter->second->getTet(1) == NULL);
+      int vf = tet->getOppositeVtx(triIter->second);
+      assert(vf >= 0);
+      for(int i = 0; i < 3; i++)
+      {
+        int vi = tri[i];
+        assert(vi != vf);
+        for(int j = 0; j < 3; j++)
+        {
+          int vj = tri[j];
+          assert(vj != vf);
+          int op0 = tri[triangleOtherIndex[j][0]];
+          int op1 = tri[triangleOtherIndex[j][1]];
+          addVolumetricLaplacianTerm(tetMesh, vf, vj, op0, op1, outline, vi);
+        }
+      }
+    }
+  }
+
+  return new SparseMatrix(&outline);
+}
+
+// compute cotangent for angle aob
+static double computeCotangent(const ObjMesh * objMesh, int va, int vo, int vb)
+{
+  // clamp cos values to remove the effect of nearly degenerate triangles
+  const double min_angle = 0.015;
+  const double max_angle = M_PI - min_angle;
+
+  Vec3d pa = objMesh->getPosition(va);
+  Vec3d pb = objMesh->getPosition(vb);
+  Vec3d po = objMesh->getPosition(vo);
+
+  double angle = getTriangleAngleRobust(po, pa, pb);
+  angle = clamp(angle, min_angle, max_angle);
+  double tanAngle = tan(angle);
+  assert(tanAngle != 0.0);
+  assert(tanAngle != numeric_limits<double>::infinity());
+  assert(tanAngle != - numeric_limits<double>::infinity());
+  return 1.0 / tanAngle;
+}
+
+SparseMatrix * generateLaplacian(const ObjMesh * objMesh, bool clampCotangent)
+{
+  bool triangulated = false;
+  if (!objMesh->isTriangularMesh())
+  {
+    ObjMesh * triMesh = new ObjMesh(*objMesh);
+    triMesh->triangulate();
+    objMesh = triMesh;
+    triangulated = true;
+  }
+
+  //assert(objMesh->isTriangularMesh());
+  int n = objMesh->getNumVertices();
+
+  //add entries to the
+  SparseMatrixOutline outline(n);
+
+  //ObjMesh * objMesh = sceneObjectDeformable->GetMesh();
+
+  int pi0[] = { 2, 0, 1 };
+  int pi1[] = { 1, 2, 0 };
+  int pi2[] = { 0, 1, 2 };
+  //w_ij = w_ji = 1/2 [ cot(alpha_ij) + cot(beta_ij) ],   alpha_ij, beta_ij are angles opposite of the edge (i,j)
+  //typedef pair<int, int> Edge;
+  for(size_t i = 0; i < objMesh->getNumGroups(); i++)
+  {
+    const ObjMesh::Group* g = objMesh->getGroupHandle(i);
+    for(size_t j = 0; j < g->getNumFaces(); j++)
+    {
+      const ObjMesh::Face * face = g->getFaceHandle(j);
+      //TODO: add a function to compute face angle
+      assert(face->getVertexHandle(0) != face->getVertexHandle(1) && face->getVertexHandle(1) != face->getVertexHandle(2)
+          && face->getVertexHandle(2) != face->getVertexHandle(0));
+      for(int k = 0; k < 3; k++)
+      {
+        unsigned int v0 = face->getVertexHandle(pi0[k])->getPositionIndex();
+        unsigned int v1 = face->getVertexHandle(pi1[k])->getPositionIndex();
+        unsigned int v2 = face->getVertexHandle(pi2[k])->getPositionIndex();
+
+        double w = 0.5 * computeCotangent(objMesh, v0, v2, v1);
+
+        if (clampCotangent && w < 0) //clamp
+          w = 0.0;
+        assert(v0 != v1);
+        outline.AddEntry(v0, v1, -w);
+        outline.AddEntry(v1, v0, -w);
+
+        outline.AddEntry(v0, v0, w);
+        outline.AddEntry(v1, v1, w);
+      }
+    }
+  }
+
+  //we have to do this to ensure every row has at least one element
+  //otherwise the solver will complain
+  for(int row = 0; row < n; row++)
+    outline.AddEntry(row, row, 0);
+
+
+  // NOTE: the following commented algorithm only works on 2D. So it's disabled
+
+//  if (linearlyPrecise)
+//  {
+//    typedef pair<int, int> edge;
+//#define SORTED_EDGE(a, b) ((a) > (b) ? edge((b),(a)) : edge((a),(b)))
+//    map<edge, std::vector<const ObjMesh::Face*> > faceMap;
+//
+//    for(size_t i = 0; i < objMesh->getNumGroups(); i++)
+//    {
+//      const ObjMesh::Group* g = objMesh->getGroupHandle(i);
+//      for(size_t j = 0; j < g->getNumFaces(); j++)
+//      {
+//        const ObjMesh::Face * face = g->getFaceHandle(j);
+//        for(int k = 0; k < 3; k++)
+//        {
+//          unsigned int v0 = face->getVertexHandle(pi0[k])->getPositionIndex();
+//          unsigned int v1 = face->getVertexHandle(pi1[k])->getPositionIndex();
+//          edge e = SORTED_EDGE(v0, v1);
+//          faceMap[e].push_back(face);
+//          assert(faceMap[e].size() <= 2);
+//        }
+//      }
+//    }
+//    // find all boundary edges
+//    int numBoundaryEdges = 0;
+//    for(map<edge, std::vector<const ObjMesh::Face*> >::iterator it = faceMap.begin(); it != faceMap.end(); it++)
+//    {
+//      edge e = it->first;
+//      if (it->second.size() == 2) // skip non-boundary edges
+//        continue;
+//      numBoundaryEdges++;
+//      const ObjMesh::Face* face = it->second[0];
+//
+//      for(int i = 0; i < 2; i++)
+//      {
+//        int vi = (i == 0 ? e.first : e.second);
+//        int vj = (i == 1 ? e.first : e.second);
+//        int vk = vi;
+//        for(int k = 0; k < 3; k++)
+//        {
+//          vk = face->getVertexHandle(k)->getPositionIndex();
+//          if (vk != vi && vk != vj)
+//            break;
+//        }
+//        assert(vk != vi);
+//
+//        double w = 0.5 * computeCotangent(objMesh, vi, vk, vj);
+//        if (clampCotangent && w < 0) //clamp
+//          w = 0.0;
+//
+//        outline.AddEntry(vi, vk, -w);
+//        outline.AddEntry(vi, vi, w);
+//
+//        outline.AddEntry(vj, vk, -w);
+//        outline.AddEntry(vj, vj, w);
+//      }
+//    }
+//    cout << "numBoundaryEdges: " << numBoundaryEdges << endl;
+//  }
+
+  if (triangulated)
+    delete objMesh;
+
+  return new SparseMatrix(&outline);
+}
+
+// return ||A 1_n||_2
+double testConstantPrecision(const SparseMatrix * A)
+{
+  int n = A->GetNumRows();
+  vector<double> ones(n, 1.0);
+  vector<double> result(n, 0.0);
+  A->MultiplyVector(&ones[0], &result[0]);
+  return EuclideanNorm(n, &result[0]);
+}
+
+// return ||A \bar{V}||_2, \bar{V} is a nx3 matrix storing rest positions of the mesh
+double testLinearPrecision(const SparseMatrix * A, const VolumetricMesh * mesh)
+{
+  int n = A->GetNumRows();
+  assert(n == mesh->getNumVertices());
+  vector<double> V(3*n, 0.0);
+  vector<double> result(3*n, 0.0);
+  for(int i = 0; i < n; i++)
+  {
+    const Vec3d & v = mesh->getVertex(i);
+    for(int j = 0; j < 3; j++)
+      V[i + j * n] = v[j];
+  }
+
+  for(int i = 0; i < 3; i++)
+  {
+    A->MultiplyVector(&V[i*n], &result[i*n]);
+  }
+  return EuclideanNorm(3*n, &result[0]);
+}
+
diff --git a/libraries/shapeEdit/generateLaplacian.h b/libraries/shapeEdit/generateLaplacian.h
new file mode 100644
index 0000000000000000000000000000000000000000..379df753e60be296b9ce5f6104b968e0f0697f67
--- /dev/null
+++ b/libraries/shapeEdit/generateLaplacian.h
@@ -0,0 +1,54 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "shapeEdit" library , Copyright (C) 2018 USC                          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Koki Nagano, Yijing Li, Jernej Barbic        *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef GENERATELAPLACIAN_H
+#define GENERATELAPLACIAN_H
+
+#include "sparseMatrix.h"
+#include "tetMesh.h"
+#include "objMesh.h"
+
+// generate Laplacian matrix for tet mesh [Wang 2015 Linear Subspace Design for Real-Time Shape Deformation]
+SparseMatrix * generateLaplacian(const TetMesh * tetMesh, bool linearlyPrecise);
+
+// generate Laplacian matrix for obj mesh [Sorkine 2007 ARAP]
+// if clampCotangent == true, clamp the cotangent weights in the Lapalcian matrix to be >= 0
+SparseMatrix * generateLaplacian(const ObjMesh * objMesh, bool clampCotangent);
+
+// return ||A 1_n||_F
+double testConstantPrecision(const SparseMatrix * A);
+
+// return ||A \bar{V}||_F, \bar{V} is a nx3 matrix storing rest positions of the mesh
+double testLinearPrecision(const SparseMatrix * A, const VolumetricMesh * mesh);
+//double testLinearPrecision(const SparseMatrix * A, const ObjMesh * mesh);
+
+#endif
diff --git a/src/libsparseMatrix/example.cpp b/libraries/sparseMatrix/example.cpp
similarity index 92%
rename from src/libsparseMatrix/example.cpp
rename to libraries/sparseMatrix/example.cpp
index b8d1748dfafe13f81ee961fca8a4959c7ecbe80b..d4c88f069e28fd3e98c5c7436568b3e9f39c309a 100644
--- a/src/libsparseMatrix/example.cpp
+++ b/libraries/sparseMatrix/example.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseMatrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseMatrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/libsparseMatrix/sparseMatrix.cpp b/libraries/sparseMatrix/sparseMatrix.cpp
similarity index 72%
rename from src/libsparseMatrix/sparseMatrix.cpp
rename to libraries/sparseMatrix/sparseMatrix.cpp
index f5eb4c1686950a837c26d108c1d6b93892dd5e9d..29d86c9006d5a45aa9da6bd6174d1dc4064b4938 100644
--- a/src/libsparseMatrix/sparseMatrix.cpp
+++ b/libraries/sparseMatrix/sparseMatrix.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseMatrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseMatrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,11 +34,13 @@
 #include <stdio.h>
 #include <string.h>
 #include <math.h>
+#include <cmath>
+#include <cassert>
+#include <limits>
 #include "sparseMatrix.h"
+#include "constrainedDOFs.h"
 using namespace std;
 
-namespace vega
-{
 SparseMatrixOutline::SparseMatrixOutline(int numRows_): numRows(numRows_)
 {
   Allocate();
@@ -62,7 +68,7 @@ SparseMatrixOutline::SparseMatrixOutline(int numRows_, double diagonal): numRows
   }
 }
 
-SparseMatrixOutline::SparseMatrixOutline(int numRows_, double * diagonal): numRows(numRows_)
+SparseMatrixOutline::SparseMatrixOutline(int numRows_, const double * diagonal): numRows(numRows_)
 {
   Allocate();
   pair<int,double> entry;
@@ -74,14 +80,14 @@ SparseMatrixOutline::SparseMatrixOutline(int numRows_, double * diagonal): numRo
   }
 }
 
-SparseMatrixOutline::SparseMatrixOutline(char * filename, int expand)
+SparseMatrixOutline::SparseMatrixOutline(const char * filename, int expand)
 {
   if (expand <= 0)
   {
     printf("Error: invalid expand factor %d in SparseMatrixOutline constructor.\n", expand);
     throw 1;
   }
-  
+
   FILE * inputMatrix = fopen(filename,"r");
   if (!inputMatrix)
   {
@@ -89,7 +95,7 @@ SparseMatrixOutline::SparseMatrixOutline(char * filename, int expand)
     throw 2;
   }
 
-  // read input size 
+  // read input size
   int m1, n1;
   if (fscanf(inputMatrix, "%d\n%d\n", &m1, &n1) < 2)
   {
@@ -120,22 +126,24 @@ void SparseMatrixOutline::Allocate()
 {
   // allocate empty datastructure for row entries
   columnEntries.clear();
-  map<int,double> emptyMap;
-  for (int i=0; i<numRows; i++)
-    columnEntries.push_back(emptyMap);
+  columnEntries.resize(numRows);
 }
 
 void SparseMatrixOutline::IncreaseNumRows(int numAddedRows)
 {
-  map<int,double> emptyMap;
-  for(int i=0; i<numAddedRows; i++)
-    columnEntries.push_back(emptyMap);
-
   numRows += numAddedRows;
+  columnEntries.resize(numRows);
+}
+
+void SparseMatrixOutline::Initialize(int numRows)
+{
+  this->numRows = numRows;
+  columnEntries.assign(numRows, map<int,double>());
 }
 
 void SparseMatrixOutline::AddEntry(int i, int j, double value)
 {
+  assert((size_t)i < columnEntries.size());
   map<int,double>::iterator pos = columnEntries[i].find(j);
   if (pos != columnEntries[i].end())
     pos->second += value;
@@ -146,16 +154,37 @@ void SparseMatrixOutline::AddEntry(int i, int j, double value)
   }
 }
 
+// remove the entry in row i, column j
+void SparseMatrixOutline::RemoveEntry(int i, int j)
+{
+  assert((size_t)i < columnEntries.size());
+  columnEntries[i].erase(j);
+}
+
 // add a block matrix, starting at row i, and column j 
-void SparseMatrixOutline::AddBlockMatrix(int iStart, int jStart, const SparseMatrix * block, double scalarFactor)
+void SparseMatrixOutline::AddBlockMatrix(int iStart, int jStart, const SparseMatrix * block, double scalarFactor, int expand)
 {
   int nBlock = block->GetNumRows();
   for(int i=0; i<nBlock; i++)
-  {
-    int rowLength = block->GetRowLength(i);
-    for(int j=0; j<rowLength; j++)
-      AddEntry(iStart + i, jStart + block->GetColumnIndex(i,j), scalarFactor * block->GetEntry(i,j));
-  }
+    for(int j=0; j<block->GetRowLength(i); j++)
+    {
+      if(expand <= 1)
+        AddEntry(iStart + i, jStart + block->GetColumnIndex(i,j), scalarFactor * block->GetEntry(i,j));
+      else
+      {
+        int columnIndex = block->GetColumnIndex(i,j);
+        double entry = scalarFactor * block->GetEntry(i,j);
+        for(int e = 0; e < expand; e++)
+          AddEntry(iStart + expand * i + e, jStart + expand * columnIndex + e, entry);
+      }
+    }
+}
+
+void SparseMatrixOutline::AddBlockMatrix(int iStart, int jStart, const SparseMatrixOutline & block, double scalarFactor)
+{
+  for(int i=0; i<block.numRows; i++)
+    for(map<int,double>::const_iterator it = block.columnEntries[i].begin(); it != block.columnEntries[i].end(); it++)
+      AddEntry(iStart + i, jStart + it->first, scalarFactor * it->second);
 }
 
 void SparseMatrixOutline::MultiplyRow(int row, double scalar)
@@ -164,13 +193,20 @@ void SparseMatrixOutline::MultiplyRow(int row, double scalar)
     iter->second *= scalar;
 }
 
-void SparseMatrixOutline::AddBlock3x3Entry(int i, int j, double * matrix3x3)
+void SparseMatrixOutline::AddBlock3x3Entry(int i, int j, const double * matrix3x3)
 {
   for(int k=0; k<3; k++)
     for(int l=0; l<3; l++)
       AddEntry(3*i+k,3*j+l,matrix3x3[3*k+l]);
 }
 
+void SparseMatrixOutline::AddBlock3x3Entry(int i, int j, double value)
+{
+  for(int k=0; k<3; k++)
+    for(int l=0; l<3; l++)
+      AddEntry(3*i+k,3*j+l,value);
+}
+
 double SparseMatrixOutline::GetEntry(int i, int j) const
 {
   map<int,double>::const_iterator pos = columnEntries[i].find(j);
@@ -186,15 +222,15 @@ int SparseMatrixOutline::GetNumColumns() const
   for(int i=0; i<numRows; i++)
   {
     map<int,double>::const_iterator j1;
-    // traverse all row entries 
-    for(j1=columnEntries[i].begin(); j1 != columnEntries[i].end(); j1++) 
+    // traverse all row entries
+    for(j1=columnEntries[i].begin(); j1 != columnEntries[i].end(); j1++)
       if (j1->first > numColumns)
         numColumns = j1->first;
   }
   return numColumns + 1;
 }
 
-int SparseMatrixOutline::Save(char * filename, int oneIndexed) const
+int SparseMatrixOutline::Save(const char * filename, int oneIndexed) const
 {
   FILE * fout = fopen(filename, "w");
   if (!fout)
@@ -204,10 +240,9 @@ int SparseMatrixOutline::Save(char * filename, int oneIndexed) const
   for(int i=0; i<numRows; i++)
   {
     map<int,double>::const_iterator j1;
-    // traverse all row entries 
-    for(j1=columnEntries[i].begin(); j1 != columnEntries[i].end(); ++j1) 
+    // traverse all row entries
+    for(j1=columnEntries[i].begin(); j1 != columnEntries[i].end(); ++j1)
       fprintf(fout,"%d %d %.15f\n",i,j1->first + oneIndexed, j1->second + oneIndexed);
-
   }
   fclose(fout);
 
@@ -228,11 +263,11 @@ int SparseMatrixOutline::GetNumEntries() const
 {
   int num = 0;
   for(int i=0; i<numRows; i++)
-    num += columnEntries[i].size();
+    num += (int)(columnEntries[i].size());
   return num;
 }
 
-SparseMatrix::SparseMatrix(char * filename)
+SparseMatrix::SparseMatrix(const char * filename)
 {
   SparseMatrixOutline sparseMatrixOutline(filename);
   InitFromOutline(&sparseMatrixOutline);
@@ -251,7 +286,7 @@ void SparseMatrix::InitFromOutline(SparseMatrixOutline * sparseMatrixOutline)
 
   for(int i=0; i<numRows; i++)
   {
-    rowLength[i] = sparseMatrixOutline->columnEntries[i].size();
+    rowLength[i] = (int)(sparseMatrixOutline->columnEntries[i].size());
     columnIndices[i] = (int*) malloc (sizeof(int) * rowLength[i]);
     columnEntries[i] = (double*) malloc (sizeof(double) * rowLength[i]);
 
@@ -270,6 +305,65 @@ void SparseMatrix::InitFromOutline(SparseMatrixOutline * sparseMatrixOutline)
   }
 }
 
+// construct matrix by specifying all entries: number of rows, length of each row, indices of columns of non-zero entries in each row, values of non-zero entries in each row
+SparseMatrix::SparseMatrix(int numRows_, int * rowLength_, int ** columnIndices_, double ** columnEntries_, int shallowCopy) : numRows(numRows_)
+{
+  if (shallowCopy)
+  {
+    rowLength = rowLength_;
+    columnIndices = columnIndices_;
+    columnEntries = columnEntries_;
+    numSubMatrixIDs = 0;
+    subMatrixIndices = NULL;
+    subMatrixIndexLengths = NULL;
+    subMatrixStartRow = NULL;
+    subMatrixNumRows = NULL;
+    superMatrixIndices = NULL;
+    superRows = NULL;
+    diagonalIndices = NULL;
+    transposedIndices = NULL;
+    return;
+  }
+
+  Allocate();
+
+  for (int i = 0; i<numRows; i++)
+  {
+    rowLength[i] = rowLength_[i];
+    columnIndices[i] = (int*) malloc(sizeof(int) * rowLength[i]);
+    columnEntries[i] = (double*) malloc(sizeof(double) * rowLength[i]);
+    memcpy(columnIndices[i], columnIndices_[i], sizeof(int) * rowLength[i]);
+    memcpy(columnEntries[i], columnEntries_[i], sizeof(double) * rowLength[i]);
+  }
+}
+
+SparseMatrix::SparseMatrix(int numSubMatrices, const SparseMatrix ** subMatrices, const int * numColumns)
+{
+  numRows = 0;
+  for(int i = 0; i < numSubMatrices; i++)
+    numRows += subMatrices[i]->numRows;
+
+  Allocate();
+
+  for(int matid = 0, rowStart = 0, colStart = 0; matid < numSubMatrices; matid++)
+  {
+    const SparseMatrix * mat = subMatrices[matid];
+    memcpy(rowLength+rowStart, mat->rowLength, sizeof(int) * mat->numRows);
+
+    for(int i = 0; i < mat->numRows; i++)
+    {
+      int ri = rowStart + i;
+      int length = rowLength[ri];
+      columnIndices[ri] = (int*) malloc(sizeof(int) * length);
+      columnEntries[ri] = (double*) malloc(sizeof(double) * length);
+      memcpy(columnEntries[ri], mat->columnEntries[i], sizeof(double) * length);
+      for(int j = 0; j < length; j++)
+        columnIndices[ri][j] = mat->columnIndices[i][j] + colStart;
+    }
+    rowStart += mat->numRows;
+    colStart += numColumns[matid];
+  }
+}
 // allocator
 void SparseMatrix::Allocate()
 {
@@ -280,6 +374,8 @@ void SparseMatrix::Allocate()
   numSubMatrixIDs = 0;
   subMatrixIndices = NULL;
   subMatrixIndexLengths = NULL;
+  subMatrixStartRow = NULL;
+  subMatrixNumRows = NULL;
   superMatrixIndices = NULL;
   superRows = NULL;
   diagonalIndices = NULL;
@@ -316,8 +412,18 @@ SparseMatrix::~SparseMatrix()
   FreeTranspositionIndices();
 }
 
+void SparseMatrix::FreeAuxiliaryData()
+{
+  FreeDiagonalIndices();
+  FreeTranspositionIndices();
+  if (subMatrixIndices != NULL)
+    for(int i=numSubMatrixIDs-1; i>=0; i--)
+      FreeSubMatrixIndices(i);
+  FreeSuperMatrixIndices();
+}
+
 // copy constructor
-SparseMatrix::SparseMatrix(const SparseMatrix & source) 
+SparseMatrix::SparseMatrix(const SparseMatrix & source)
 {
   //printf("Copy constructor\n");fflush(NULL);
   numRows = source.GetNumRows();
@@ -340,15 +446,19 @@ SparseMatrix::SparseMatrix(const SparseMatrix & source)
     }
   }
 
-  subMatrixIndices = NULL; 
+  subMatrixIndices = NULL;
   subMatrixIndexLengths = NULL;
+  subMatrixStartRow = NULL;
+  subMatrixNumRows = NULL;
   numSubMatrixIDs = source.numSubMatrixIDs;
   if (source.subMatrixIndices != NULL)
   {
     subMatrixIndices = (int***) malloc (sizeof(int**) * numSubMatrixIDs);
-    memcpy(subMatrixIndices, source.subMatrixIndices, sizeof(int**) * numSubMatrixIDs);
     subMatrixIndexLengths = (int**) malloc (sizeof(int*) * numSubMatrixIDs);
-    memcpy(subMatrixIndexLengths, source.subMatrixIndexLengths, sizeof(int*) * numSubMatrixIDs);
+    subMatrixStartRow = (int*) malloc(sizeof(int) * numSubMatrixIDs);
+    subMatrixNumRows = (int*) malloc(sizeof(int) * numSubMatrixIDs);
+    memcpy(subMatrixStartRow, source.subMatrixStartRow, sizeof(int) * numSubMatrixIDs);
+    memcpy(subMatrixNumRows, source.subMatrixNumRows, sizeof(int) * numSubMatrixIDs);
 
     for(int matrixID=0; matrixID < numSubMatrixIDs; matrixID++)
     {
@@ -362,14 +472,12 @@ SparseMatrix::SparseMatrix(const SparseMatrix & source)
       subMatrixIndices[matrixID] = (int**) malloc(sizeof(int*) * numRows);
       subMatrixIndexLengths[matrixID] = (int*) malloc(sizeof(int) * numRows);
 
-      for(int i=0; i<numRows; i++)
+      for(int i=0; i<subMatrixNumRows[matrixID]; i++)
       {
         subMatrixIndexLengths[matrixID][i] = source.subMatrixIndexLengths[matrixID][i];
         subMatrixIndices[matrixID][i] = (int*) malloc(sizeof(int) * subMatrixIndexLengths[matrixID][i]);
         for(int j=0; j < subMatrixIndexLengths[matrixID][i]; j++)
-        {
           subMatrixIndices[matrixID][i][j] = source.subMatrixIndices[matrixID][i][j];
-        }
       }
     }
   }
@@ -391,7 +499,7 @@ SparseMatrix::SparseMatrix(const SparseMatrix & source)
     }
   }
 
-  diagonalIndices = NULL; 
+  diagonalIndices = NULL;
   if (source.diagonalIndices != NULL)
   {
     diagonalIndices = (int*) malloc (sizeof(int) * numRows);
@@ -415,7 +523,7 @@ void SparseMatrix::MultiplyVector(int startRow, int endRow, const double * vecto
 {
   for(int i=startRow; i<endRow; i++)
   {
-    result[i-startRow] = 0;
+    result[i-startRow] = 0.0;
     for(int j=0; j < rowLength[i]; j++)
       result[i-startRow] += vector[columnIndices[i][j]] * columnEntries[i][j];
   }
@@ -425,7 +533,7 @@ void SparseMatrix::MultiplyVector(const double * vector, double * result) const
 {
   for(int i=0; i<numRows; i++)
   {
-    result[i] = 0;
+    result[i] = 0.0;
     for(int j=0; j < rowLength[i]; j++)
       result[i] += vector[columnIndices[i][j]] * columnEntries[i][j];
   }
@@ -471,7 +579,7 @@ void SparseMatrix::MultiplyMatrixAdd(int numDenseRows, int numDenseColumns, cons
     MultiplyVectorAdd(&denseMatrix[numDenseRows * column], &result[numRows * column]);
 }
 
-// result = A * trans(denseMatrix) 
+// result = A * trans(denseMatrix)
 // trans(denseMatrix) is a dense matrix with 'numDenseColumns' columns, result is a numRows x numDenseColumns dense matrix
 void SparseMatrix::MultiplyMatrixTranspose(int numDenseColumns, const double * denseMatrix, double * result) const
 {
@@ -498,9 +606,9 @@ double SparseMatrix::QuadraticForm(const double * vector) const
         result += 2.0 * columnEntries[i][j] * vector[i] * vector[index];
     }
   }
-  
+
   return result;
-} 
+}
 
 void SparseMatrix::NormalizeVector(double * vector) const
 {
@@ -545,7 +653,7 @@ SparseMatrix & SparseMatrix::operator*=(const double alpha)
 }
 
 SparseMatrix & SparseMatrix::operator+=(const SparseMatrix & mat2)
-{   
+{
   for(int i=0; i<numRows; i++)
     for(int j=0; j < rowLength[i]; j++)
       columnEntries[i][j] += mat2.columnEntries[i][j];
@@ -553,10 +661,10 @@ SparseMatrix & SparseMatrix::operator+=(const SparseMatrix & mat2)
 }
 
 SparseMatrix & SparseMatrix::operator-=(const SparseMatrix & mat2)
-{  
+{
   for(int i=0; i<numRows; i++)
     for(int j=0; j < rowLength[i]; j++)
-      columnEntries[i][j] -= mat2.columnEntries[i][j]; 
+      columnEntries[i][j] -= mat2.columnEntries[i][j];
   return *this;
 }
 
@@ -571,6 +679,47 @@ SparseMatrix & SparseMatrix::operator=(const SparseMatrix & source)
   return *this;
 }
 
+bool SparseMatrix::operator==(const SparseMatrix & mat2) const
+{
+  if(numRows != mat2.numRows)
+    return false;
+  for(int i = 0; i < numRows; i++)
+  {
+    if(rowLength[i] != mat2.rowLength[i])
+      return false;
+    for(int j = 0; j < rowLength[i]; j++)
+    {
+      if(columnIndices[i][j] != mat2.columnIndices[i][j])
+        return false;
+      if(columnEntries[i][j] != mat2.columnEntries[i][j])
+        return false;
+    }
+  }
+  return true;
+}
+
+bool SparseMatrix::operator!=(const SparseMatrix & mat2) const
+{
+  return !(*this == mat2);
+}
+
+bool SparseMatrix::SameStructure(const SparseMatrix & mat2) const
+{
+  if(numRows != mat2.numRows) 
+    return false;
+  for(int i = 0; i < numRows; i++)
+  {
+    if(rowLength[i] != mat2.rowLength[i])
+      return false;
+    for(int j = 0; j < rowLength[i]; j++)
+    {
+      if(columnIndices[i][j] != mat2.columnIndices[i][j])
+        return false;
+    }
+  }
+  return true;
+}
+
 void SparseMatrix::ScalarMultiply(const double alpha, SparseMatrix * dest)
 {
   if (dest == NULL)
@@ -632,9 +781,9 @@ void SparseMatrix::Print(int sparsePrint) const
         index++;
         printf("%f,",0.0);
       }
-    
+
       printf("\n");
-    } 
+    }
   }
 }
 
@@ -662,9 +811,10 @@ void SparseMatrix::BuildDiagonalIndices()
 void SparseMatrix::FreeDiagonalIndices()
 {
   free(diagonalIndices);
+  diagonalIndices = nullptr;
 }
 
-void SparseMatrix::GetDiagonal(double * diagonal)
+void SparseMatrix::GetDiagonal(double * diagonal) const
 {
   if (diagonalIndices != NULL)
   {
@@ -758,7 +908,7 @@ void SparseMatrix::MakeLinearDataArray(double * data) const
       data[count] = columnEntries[i][j];
       count++;
     }
-  }   
+  }
 }
 
 void SparseMatrix::MakeLinearRowIndexArray(double * indices) const
@@ -771,7 +921,7 @@ void SparseMatrix::MakeLinearRowIndexArray(double * indices) const
       indices[count] = i;
       count++;
     }
-  }   
+  }
 }
 
 void SparseMatrix::MakeLinearColumnIndexArray(double * indices) const
@@ -784,7 +934,7 @@ void SparseMatrix::MakeLinearColumnIndexArray(double * indices) const
       indices[count] = columnIndices[i][j];
       count++;
     }
-  }   
+  }
 }
 
 void SparseMatrix::MakeLinearRowIndexArray(int * indices) const
@@ -797,7 +947,7 @@ void SparseMatrix::MakeLinearRowIndexArray(int * indices) const
       indices[count] = i;
       count++;
     }
-  }   
+  }
 }
 
 void SparseMatrix::MakeLinearColumnIndexArray(int * indices) const
@@ -810,7 +960,7 @@ void SparseMatrix::MakeLinearColumnIndexArray(int * indices) const
       indices[count] = columnIndices[i][j];
       count++;
     }
-  }   
+  }
 }
 
 void SparseMatrix::FreeTranspositionIndices()
@@ -840,39 +990,39 @@ void SparseMatrix::BuildTranspositionIndices()
     {
       transposedIndices[i][j] = buffer[columnIndices[i][j]];
       buffer[columnIndices[i][j]]++;
-    }   
-  }  
+    }
+  }
 
   free(buffer);
 }
 
 double SparseMatrix::SkewSymmetricCheck()
 {
-  double maxEntry = 0;  
+  double maxEntry = 0;
 
-  BuildTranspositionIndices();  
+  BuildTranspositionIndices();
 
-  for(int i=0; i<numRows; i++)  
-  {    
-    for(int j=0; j<GetRowLength(i); j++)    
-    {      
-      double entry1 = GetEntry(i, j);      
-      int tindex = TransposedIndex(i, j);      
-      double entry2 = GetEntry(GetColumnIndex(i,j), tindex);      
+  for(int i=0; i<numRows; i++)
+  {
+    for(int j=0; j<GetRowLength(i); j++)
+    {
+      double entry1 = GetEntry(i, j);
+      int tindex = TransposedIndex(i, j);
+      double entry2 = GetEntry(GetColumnIndex(i,j), tindex);
 
-      // entry1 + entry2 should be zero          
+      // entry1 + entry2 should be zero
       if (fabs(entry1 + entry2) > maxEntry)
         maxEntry = fabs(entry1 + entry2);
-    }  
-  }  
-
-  FreeTranspositionIndices();
+    }
+  }
 
   return maxEntry;
 }
 
 void SparseMatrix::SymmetrizeMatrix()
 {
+  BuildTranspositionIndices();
+
   for(int i=0; i<numRows; i++)
   {
     for(int j=0; j<rowLength[i]; j++)
@@ -880,7 +1030,7 @@ void SparseMatrix::SymmetrizeMatrix()
       int jAbs = columnIndices[i][j];
 
       if (jAbs >= i)
-        break; 
+        break;
 
       // copy elt (jAbs,i) into position (i,jAbs)
       columnEntries[i][j] = columnEntries[jAbs][TransposedIndex(i,j)];
@@ -900,7 +1050,7 @@ double SparseMatrix::GetMaxAbsEntry() const
         max = el;
     }
   }
-  
+
   return max;
 }
 
@@ -915,81 +1065,81 @@ double SparseMatrix::GetRowNorm2(int row) const
   return norm2;
 }
 
-// solve M * x = b
-// ASSUMES the sparse matrix is diagonal !!!
-void SparseMatrix::DiagonalSolve(double * rhs) const
+bool SparseMatrix::HasInf() const
 {
-  for(int i=0; i<numRows; i++)
-    rhs[i] /= columnEntries[i][0]; // the diagonal element
+  double posInf = std::numeric_limits<double>::infinity();
+  double negInf = -posInf;
+  for(int i = 0; i < numRows; i++)
+    for(int j = 0; j < rowLength[i]; j++)
+      if (columnEntries[i][j] == posInf || columnEntries[i][j] == negInf) return true;
+  
+  return false;
 }
 
-void SparseMatrix::BuildRenumberingVector(int nConstrained, int nSuper, int numFixedDOFs, int * fixedDOFs, int ** superDOFs, int oneIndexed)
+bool SparseMatrix::HasNaN() const
 {
-  // superRows[i] is the row index in the super matrix corresponsing to row i of constrained matrix
-  (*superDOFs) = (int*) malloc (sizeof(int) * nConstrained);
-  int constrainedDOF = 0;
-  int superDOF = 0;
-  for(int i=0; i<numFixedDOFs; i++)
-  {
-    int nextSuperDOF = fixedDOFs[i];
-    nextSuperDOF -= oneIndexed;
-    if ( (nextSuperDOF >= nSuper) || (nextSuperDOF < 0) )
-    {
-      printf("Error: invalid fixed super DOF %d specified.\n", nextSuperDOF);
-      exit(1);
-    }
+  for(int i = 0; i < numRows; i++)
+    for(int j = 0; j < rowLength[i]; j++)
+      if (isnan(columnEntries[i][j])) return true;
 
-    while (superDOF < nextSuperDOF)
-    {
-      if (constrainedDOF >= nConstrained)
-      {
-        printf("Error: too many DOFs specified.\n");
-        exit(1);
-      }
-      (*superDOFs)[constrainedDOF] = superDOF; 
-      constrainedDOF++;
-      superDOF++;
-    }
+  return false;
+}
 
-    superDOF++; // skip the deselected DOF
-  }
-  while (superDOF < nSuper)
-  {
-    if (constrainedDOF >= nConstrained)
-    {
-      printf("Error: too many DOFs specified.\n");
-      exit(1);
-    }
-    (*superDOFs)[constrainedDOF] = superDOF; 
+bool SparseMatrix::HasInfOrNaN() const
+{
+  return HasInf() || HasNaN();
+}
 
-    constrainedDOF++;
-    superDOF++;
-  }
+
+// solve M * x = b
+// ASSUMES the sparse matrix is diagonal !!!
+void SparseMatrix::DiagonalSolve(double * rhs) const
+{
+  for(int i=0; i<numRows; i++)
+    rhs[i] /= columnEntries[i][0]; // the diagonal element
 }
 
-void SparseMatrix::BuildSuperMatrixIndices(int numFixedRowColumns, int * fixedRowColumns, SparseMatrix * superMatrix, int oneIndexed)
+void SparseMatrix::BuildSuperMatrixIndices(int numFixedRowColumns, const int * fixedRowColumns, const SparseMatrix * superMatrix, int oneIndexed)
 {
-  BuildSuperMatrixIndices(numFixedRowColumns, fixedRowColumns, numFixedRowColumns, fixedRowColumns, superMatrix, oneIndexed); 
+  BuildSuperMatrixIndices(numFixedRowColumns, fixedRowColumns, numFixedRowColumns, fixedRowColumns, superMatrix, oneIndexed);
 }
 
-void SparseMatrix::BuildSuperMatrixIndices(int numFixedRows, int * fixedRows, int numFixedColumns, int * fixedColumns, SparseMatrix * superMatrix, int oneIndexed)
+void SparseMatrix::BuildSuperMatrixIndices(int numFixedRows, const int * fixedRows, int numFixedColumns, const int * fixedColumns, const SparseMatrix * superMatrix, int oneIndexed)
 {
   int numSuperColumns = superMatrix->GetNumColumns();
-  int numColumns = numSuperColumns - numFixedColumns;
- 
-  if ((numRows + numFixedRows != superMatrix->numRows) || (GetNumColumns() + numFixedColumns > numSuperColumns) )
+  int numCurrentColumns = GetNumColumns();
+
+  // note that there's no restriction on the relationship between numSuperColumns, numCurrentColumns and numFixedColumns,
+  // because SparseMatrix does not store #columns of its represented full matrix
+  // if a superMatrix has the structure: (0,0), (0,9), fixing column 9 creates a subMatrix of (0,0), 
+  //   its numCurrentColumns = 1, numCurrentColumns + numFixedColumns = 2 < numSuperColumns = 10
+  // if a superMatrix has the structure: (0,0), (0,1) but it actually represents a full matrix of size 1x10, 
+  //   fixing column 1-9 creates a subMatrix of (0,0), but numCurrentColumns + numFixedColumns = 10 > numSuperColumns = 2 
+  if (numRows + numFixedRows != superMatrix->numRows)
   {
     printf("Error in BuildSuperMatrixIndices: number of constrained DOFs does not match the size of the two matrices.\n");
     printf("num rows: %d num fixed rows in super matrix: %d num rows in super matrix: %d\n", numRows, numFixedRows, superMatrix->numRows);
-    printf("num columns: %d num fixed columns in super matrix: %d num columns in super matrix: %d\n", numColumns, numFixedColumns, numSuperColumns);
-    exit(1);
+    assert(numRows + numFixedRows == superMatrix->numRows);
+  }
+
+  // remove entries in fixedColumns that are larger than the #column in superMatrix
+  for(int i = numFixedColumns-1; i >= 0; i--)
+  {
+    if (fixedColumns[i] - oneIndexed >= numSuperColumns)
+      numFixedColumns--;
+    else
+      break;
   }
+  int numColumns = numSuperColumns - numFixedColumns;
+  assert(numCurrentColumns <= numColumns);
 
+  FreeSuperMatrixIndices(); // free previous indices
   // build row renumbering function:
-  BuildRenumberingVector(numRows, superMatrix->numRows, numFixedRows, fixedRows, &superRows, oneIndexed);
+  superRows = (int*) malloc (sizeof(int) * numRows); // represents a mapping: row index in this matrix -> row index in super matrix
+  ConstrainedDOFs::FindFreeDOFs(superMatrix->numRows, superRows, numFixedRows, fixedRows, oneIndexed);
   // build column renumbering function:
-  int * superColumns_;
-  BuildRenumberingVector(numColumns, numSuperColumns, numFixedColumns, fixedColumns, &superColumns_, oneIndexed);
+  int * superColumns_ = (int*) malloc (sizeof(int) * numColumns); // column index in this matix -> column index in super matrix
+  ConstrainedDOFs::FindFreeDOFs(numSuperColumns, superColumns_, numFixedColumns, fixedColumns, oneIndexed);
 
   // superRows[i] is the row index in the super matrix corresponsing to row i of constrained matrix
   // superColumns_[i] is the dense column index in the super matrix corresponsing to the dense column i of constrained matrix
@@ -999,77 +1149,124 @@ void SparseMatrix::BuildSuperMatrixIndices(int numFixedRows, int * fixedRows, in
   for(int i=0; i < numRows; i++)
   {
     superMatrixIndices[i] = (int*) malloc (sizeof(int) *  rowLength[i]);
-    for(int j=0; j < rowLength[i]; j++)
+    int iConstrained = i;
+    int nextjSuper = 0;
+    int iSuper = superRows[iConstrained];
+    int iSuperRowLength = superMatrix->rowLength[iSuper];
+    const int * iSuperColumnIndices = superMatrix->columnIndices[iSuper];
+    for(int j=0; j < rowLength[i]; j++) // loops over one dense row of the constrained matrix (this matrix)
     {
-      int iConstrained = i;
-      int jConstrainedDense = columnIndices[iConstrained][j];
-      int iSuper = superRows[iConstrained];
-      int jSuperDense = superColumns_[jConstrainedDense];
-      int jSuper = superMatrix->GetInverseIndex(iSuper, jSuperDense);
-      if (jSuper < 0)
+      int jConstrainedDense = columnIndices[iConstrained][j]; // column of one entry (iConstrained, j->jConstrainedDense)
+      int jSuperDense = superColumns_[jConstrainedDense];     // corresponding column in super matrix: jConstrainedDense -> jSuperDense
+      for(; nextjSuper < iSuperRowLength; nextjSuper++)       // looking for the entry in super matrix: (iSuper, nextjSuper->jSuperDense)
+      	if (iSuperColumnIndices[nextjSuper] == jSuperDense)
+      	  break;
+      if (nextjSuper == iSuperRowLength) // we cannot find the corresponding entry in super matrix
       {
-        printf("Error in BuildSuperMatrixIndices: failed to compute inverse index.\n");
-        printf("i=%d j=%d iConstrained=%d jConstrainedDense=%d iSuper=%d jSuperDense=%d jSuper=%d\n", i, j, iConstrained, jConstrainedDense, iSuper, jSuperDense, jSuper);
+        printf("Error in BuildSuperMatrixIndices: failed to find the index.\n");
+        printf("i=%d j=%d iConstrained=%d jConstrainedDense=%d iSuper=%d jSuperDense=%d\n", i, j, iConstrained, jConstrainedDense, iSuper, jSuperDense);
         fflush(NULL);
-        exit(1);
       }
-      superMatrixIndices[i][j] = jSuper;
+      assert(nextjSuper < iSuperRowLength);
+      superMatrixIndices[i][j] = nextjSuper;
+      nextjSuper++;
     }
-  } 
+  }
 
   free(superColumns_);
 }
 
-void SparseMatrix::AssignSuperMatrix(SparseMatrix * superMatrix)
+void SparseMatrix::AssignSuperMatrix(const SparseMatrix & superMatrix)
 {
   for(int i=0; i<numRows; i++)
   {
-    double * row = superMatrix->columnEntries[superRows[i]];
+    double * row = superMatrix.columnEntries[superRows[i]];
     int * indices = superMatrixIndices[i];
     for(int j=0; j < rowLength[i]; j++)
       columnEntries[i][j] = row[indices[j]];
   }
 }
 
-void SparseMatrix::BuildSubMatrixIndices(SparseMatrix & mat2, int subMatrixID)
+void SparseMatrix::FreeSuperMatrixIndices()
+{
+  if (superRows != NULL)
+  {
+    for(int i=0; i<numRows; i++)
+      free(superMatrixIndices[i]);
+    free(superMatrixIndices);
+    free(superRows);
+    superMatrixIndices = NULL;
+    superRows = NULL;
+  }
+}
+
+void SparseMatrix::BuildSubMatrixIndices(const SparseMatrix & submatrix, int subMatrixID, int startRow, int startColumn)
 {
   if (subMatrixID >= numSubMatrixIDs)
   {
     subMatrixIndices = (int***) realloc (subMatrixIndices, sizeof(int**) * (subMatrixID + 1));
     subMatrixIndexLengths = (int**) realloc (subMatrixIndexLengths, sizeof(int*) * (subMatrixID + 1));
+    subMatrixStartRow = (int*) realloc(subMatrixStartRow, sizeof(int) * (subMatrixID + 1));
+    subMatrixNumRows = (int*) realloc(subMatrixNumRows, sizeof(int) * (subMatrixID + 1));
     for(int i=numSubMatrixIDs; i <= subMatrixID; i++)
     {
       subMatrixIndices[i] = NULL;
       subMatrixIndexLengths[i] = NULL;
+      subMatrixStartRow[i] = 0;
+      subMatrixNumRows[i] = 0;
     }
     numSubMatrixIDs = subMatrixID + 1;
   }
-
-  if ((subMatrixIndices[subMatrixID] != NULL) || (subMatrixIndexLengths[subMatrixID] != NULL))
+  else if ((subMatrixIndices[subMatrixID] != NULL) || (subMatrixIndexLengths[subMatrixID] != NULL))
   {
+    for (int i = 0; i < subMatrixNumRows[subMatrixID]; i++)
+      free(subMatrixIndices[subMatrixID][i]);
     free(subMatrixIndices[subMatrixID]);
     free(subMatrixIndexLengths[subMatrixID]);
     subMatrixIndices[subMatrixID] = NULL;
     subMatrixIndexLengths[subMatrixID] = NULL;
+    subMatrixStartRow[subMatrixID] = 0;
+    subMatrixNumRows[subMatrixID] = 0;
     //printf("Warning: old submatrix indices (matrixID %d) have not been de-allocated.\n", subMatrixID);
   }
 
-  subMatrixIndices[subMatrixID] = (int**) malloc (sizeof(int*) * numRows);
-  subMatrixIndexLengths[subMatrixID] = (int*) malloc (sizeof(int) * numRows);
+  subMatrixStartRow[subMatrixID] = startRow;
+  subMatrixNumRows[subMatrixID] = submatrix.numRows;
+  subMatrixIndices[subMatrixID] = (int**) malloc (sizeof(int*) * subMatrixNumRows[subMatrixID]);
+  subMatrixIndexLengths[subMatrixID] = (int*) malloc (sizeof(int) * subMatrixNumRows[subMatrixID]);
 
-  for(int i=0; i<numRows; i++)
+  int endRow = startRow + subMatrixNumRows[subMatrixID];
+  if(endRow > numRows)
   {
-    subMatrixIndices[subMatrixID][i] = (int*) malloc (sizeof(int) * mat2.rowLength[i]);
-    subMatrixIndexLengths[subMatrixID][i] = mat2.rowLength[i];
-    int * indices = mat2.columnIndices[i];
-    for(int j=0; j < mat2.rowLength[i]; j++)
+    printf("Error (BuildSubMatrixIndices): given submatrix placed at startRow %d exceeds the length of this matrix: %d\n", startRow, numRows);
+    exit(1);
+  }
+
+  for(int i=0; i<subMatrixNumRows[subMatrixID]; i++)
+  {
+    //begin at the startRow, find correspondence to subMatrix at each row
+    subMatrixIndices[subMatrixID][i] = (int*) malloc (sizeof(int) * submatrix.rowLength[i]);
+    subMatrixIndexLengths[subMatrixID][i] = submatrix.rowLength[i];
+    int * indices = submatrix.columnIndices[i];
+
+    // this assumes that the column indices are sorted (which they always are)
+    int col = 0;
+    int * colIndices = columnIndices[startRow + i];
+
+    for(int j=0; j < submatrix.rowLength[i]; j++)
     {
+      int targetColumn = startColumn + indices[j];
+      while (colIndices[col] < targetColumn)
+        col++;
+
+      subMatrixIndices[subMatrixID][i][j] = (colIndices[col] == targetColumn) ? col : -1;
+
       // finds the position in row i of element with column index jDense
       // int inverseIndex(int i, int jDense);
-      subMatrixIndices[subMatrixID][i][j] = GetInverseIndex(i,indices[j]);
+      //subMatrixIndices[subMatrixID][i][j] = GetInverseIndex(startRow + i, startColumn + indices[j]);
       if (subMatrixIndices[subMatrixID][i][j] == -1)
       {
-        printf("Error (BuildSubMatrixIndices): given matrix is not a submatrix of this matrix.\n");
+        printf("Error (BuildSubMatrixIndices): given matrix is not a submatrix of this matrix. The following index does not exist in this matrix: (%d,%d)\n", startRow + i, startColumn + indices[j]);
         exit(1);
       }
     }
@@ -1086,10 +1283,14 @@ void SparseMatrix::FreeSubMatrixIndices(int subMatrixID)
 
   if (subMatrixIndices[subMatrixID] != NULL)
   {
+    for(int i=0; i<subMatrixNumRows[subMatrixID]; i++)
+      free(subMatrixIndices[subMatrixID][i]);
     free(subMatrixIndices[subMatrixID]);
     free(subMatrixIndexLengths[subMatrixID]);
     subMatrixIndices[subMatrixID] = NULL;
     subMatrixIndexLengths[subMatrixID] = NULL;
+    subMatrixStartRow[subMatrixID] = 0;
+    subMatrixNumRows[subMatrixID] = 0;
   }
 
   // check if this was the largest index
@@ -1100,6 +1301,8 @@ void SparseMatrix::FreeSubMatrixIndices(int subMatrixID)
       numSubMatrixIDs = i + 1;
       subMatrixIndices = (int***) realloc (subMatrixIndices, sizeof(int**) * numSubMatrixIDs);
       subMatrixIndexLengths = (int**) realloc (subMatrixIndexLengths, sizeof(int*) * numSubMatrixIDs);
+      subMatrixStartRow = (int*) realloc (subMatrixStartRow, sizeof(int) * numSubMatrixIDs);
+      subMatrixNumRows = (int*) realloc (subMatrixNumRows, sizeof(int) * numSubMatrixIDs);
       break;
     }
 
@@ -1110,18 +1313,45 @@ void SparseMatrix::FreeSubMatrixIndices(int subMatrixID)
   {
     free(subMatrixIndices);
     free(subMatrixIndexLengths);
+    free(subMatrixStartRow);
+    free(subMatrixNumRows);
     subMatrixIndices = NULL;
     subMatrixIndexLengths = NULL;
+    subMatrixStartRow = NULL;
+    subMatrixNumRows = NULL;
   }
 }
 
-SparseMatrix & SparseMatrix::AddSubMatrix(double factor, SparseMatrix & mat2, int subMatrixID)
+void SparseMatrix::AssignSubMatrix(const SparseMatrix & submatrix, int subMatrixID)
 {
-  for(int i=0; i<numRows; i++)
+  int startRow = subMatrixStartRow[subMatrixID];
+  for(int i=0; i<subMatrixNumRows[subMatrixID]; i++)
   {
     int * indices = subMatrixIndices[subMatrixID][i];
-    for(int j=0; j < mat2.rowLength[i]; j++)
-      columnEntries[i][indices[j]] += factor * mat2.columnEntries[i][j];
+    for(int j=0; j < submatrix.rowLength[i]; j++)
+      columnEntries[startRow + i][indices[j]] = submatrix.columnEntries[i][j];
+  }
+}
+
+void SparseMatrix::AssignToSubMatrix(SparseMatrix & submatrix, int subMatrixID) const
+{
+  int startRow = subMatrixStartRow[subMatrixID];
+  for(int i=0; i<subMatrixNumRows[subMatrixID]; i++)
+  {
+    int * indices = subMatrixIndices[subMatrixID][i];
+    for(int j=0; j < submatrix.rowLength[i]; j++)
+      submatrix.columnEntries[i][j] = columnEntries[startRow + i][indices[j]];
+  }
+}
+
+SparseMatrix & SparseMatrix::AddSubMatrix(double factor, SparseMatrix & submatrix, int subMatrixID)
+{
+  int startRow = subMatrixStartRow[subMatrixID];
+  for(int i=0; i<subMatrixNumRows[subMatrixID]; i++)
+  {
+    int * indices = subMatrixIndices[subMatrixID][i];
+    for(int j=0; j < submatrix.rowLength[i]; j++)
+      columnEntries[startRow + i][indices[j]] += factor * submatrix.columnEntries[i][j];
   }
 
   return *this;
@@ -1172,11 +1402,11 @@ int SparseMatrix::GenerateNAGFormat(double * a, int * irow, int * icol, int * is
       }
     }
   }
-  
+
   istr[numRows] = num;
 
   return num;
-} 
+}
 
 void SparseMatrix::GenerateCompressedRowMajorFormat(double * a, int * ia, int * ja, int upperTriangleOnly, int oneIndexed) const
 {
@@ -1192,9 +1422,9 @@ void SparseMatrix::GenerateCompressedRowMajorFormat(double * a, int * ia, int *
       if ((!upperTriangleOnly) || (columnIndices[row][j] >= row))
       {
         if (a != NULL)
-          a[count] = columnEntries[row][j];         
+          a[count] = columnEntries[row][j];
         if (ja != NULL)
-          ja[count] = columnIndices[row][j] + oneIndexed; 
+          ja[count] = columnIndices[row][j] + oneIndexed;
         count++;
       }
     }
@@ -1218,9 +1448,9 @@ void SparseMatrix::GenerateCompressedRowMajorFormat_four_array(double * values,
       if ((!upperTriangleOnly) || (columnIndices[row][j] >= row))
       {
         if (values != NULL)
-          values[count] = columnEntries[row][j];         
+          values[count] = columnEntries[row][j];
         if (columns != NULL)
-          columns[count] = columnIndices[row][j] + oneIndexed; 
+          columns[count] = columnIndices[row][j] + oneIndexed;
         count++;
       }
     }
@@ -1230,7 +1460,7 @@ void SparseMatrix::GenerateCompressedRowMajorFormat_four_array(double * values,
   }
 }
 
-int SparseMatrix::Save(char * filename, int oneIndexed) const
+int SparseMatrix::Save(const char * filename, int oneIndexed) const
 {
   FILE * fout = fopen(filename,"w");
   if (!fout)
@@ -1241,7 +1471,7 @@ int SparseMatrix::Save(char * filename, int oneIndexed) const
   {
     for(int j=0; j < rowLength[i]; j++)
     {
-      int index = columnIndices[i][j]; 
+      int index = columnIndices[i][j];
       double entry = columnEntries[i][j];
       fprintf(fout,"%d %d %.15G\n",i + oneIndexed, index + oneIndexed, entry);
     }
@@ -1251,7 +1481,7 @@ int SparseMatrix::Save(char * filename, int oneIndexed) const
   return 0;
 }
 
-int SparseMatrix::SaveToMatlabFormat(char * filename) const
+int SparseMatrix::SaveToMatlabFormat(const char * filename) const
 {
   FILE * fout = fopen(filename,"w");
   if (!fout)
@@ -1261,7 +1491,7 @@ int SparseMatrix::SaveToMatlabFormat(char * filename) const
   {
     for(int j=0; j < rowLength[i]; j++)
     {
-      int index = columnIndices[i][j]; 
+      int index = columnIndices[i][j];
       double entry = columnEntries[i][j];
       fprintf(fout,"%d %d %.15G\n",i + 1, index + 1, entry);
     }
@@ -1273,6 +1503,7 @@ int SparseMatrix::SaveToMatlabFormat(char * filename) const
 
 void SparseMatrix::RemoveRowColumn(int index)
 {
+  FreeAuxiliaryData();
   // remove row 'index'
   free(columnEntries[index]);
   free(columnIndices[index]);
@@ -1298,7 +1529,7 @@ void SparseMatrix::RemoveRowColumn(int index)
         {
           columnIndices[i][k] = columnIndices[i][k+1];
           columnEntries[i][k] = columnEntries[i][k+1];
-        } 
+        }
         rowLength[i]--;
       }
     }
@@ -1306,25 +1537,26 @@ void SparseMatrix::RemoveRowColumn(int index)
     // decrease indices for DOFs above index
     for(int j=0; j<rowLength[i]; j++)
     {
-      if(columnIndices[i][j] > index)     
+      if(columnIndices[i][j] > index)
       {
         // decrease index
         columnIndices[i][j]--;
       }
-    }   
+    }
   }
 
   numRows--;
 }
 
-void SparseMatrix::RemoveRowsColumnsSlow(int numRemovedRowsColumns, int * removedRowsColumns, int oneIndexed)
+void SparseMatrix::RemoveRowsColumnsSlow(int numRemovedRowsColumns, const int * removedRowsColumns, int oneIndexed)
 {
   for(int i=0; i<numRemovedRowsColumns; i++)
     RemoveRowColumn(removedRowsColumns[i]-i-oneIndexed);
 }
 
-void SparseMatrix::RemoveRowsColumns(int numRemovedRowsColumns, int * removedRowsColumns, int oneIndexed)
+void SparseMatrix::RemoveRowsColumns(int numRemovedRowsColumns, const int * removedRowsColumns, int oneIndexed)
 {
+  FreeAuxiliaryData();
   // the removed dofs must be pre-sorted
   // build a map from old dofs to new ones
   vector<int> oldToNew(numRows);
@@ -1354,8 +1586,8 @@ void SparseMatrix::RemoveRowsColumns(int numRemovedRowsColumns, int * removedRow
   {
     if (oldToNew[sourceRow] == -1)
     {
-      free(columnIndices[sourceRow]);    
-      free(columnEntries[sourceRow]);    
+      free(columnIndices[sourceRow]);
+      free(columnEntries[sourceRow]);
       continue;
     }
 
@@ -1382,12 +1614,13 @@ void SparseMatrix::RemoveRowsColumns(int numRemovedRowsColumns, int * removedRow
 
   numRows -= numRemovedRowsColumns;
   columnEntries = (double**) realloc(columnEntries, sizeof(double*) * numRows);
-  columnIndices = (int**) realloc(columnIndices, sizeof(double*) * numRows);
+  columnIndices = (int**) realloc(columnIndices, sizeof(int*) * numRows);
   rowLength = (int*) realloc(rowLength, sizeof(int) * numRows);
 }
 
 void SparseMatrix::RemoveColumn(int index)
 {
+  FreeAuxiliaryData();
   // remove column 'index'
   for(int i=0; i<numRows; i++)
   {
@@ -1402,7 +1635,7 @@ void SparseMatrix::RemoveColumn(int index)
         {
           columnIndices[i][k] = columnIndices[i][k+1];
           columnEntries[i][k] = columnEntries[i][k+1];
-        } 
+        }
         rowLength[i]--;
       }
     }
@@ -1410,17 +1643,18 @@ void SparseMatrix::RemoveColumn(int index)
     // decrease indices for DOFs above index
     for(int j=0; j<rowLength[i]; j++)
     {
-      if(columnIndices[i][j] > index)     
+      if(columnIndices[i][j] > index)
       {
         // decrease index
         columnIndices[i][j]--;
       }
-    }   
+    }
   }
 }
 
-void SparseMatrix::RemoveColumns(int numRemovedColumns, int * removedColumns, int oneIndexed)
+void SparseMatrix::RemoveColumns(int numRemovedColumns, const int * removedColumns, int oneIndexed)
 {
+  FreeAuxiliaryData();
   // the removed dofs must be pre-sorted
   // build a map from old dofs to new ones
   int numColumns = GetNumColumns();
@@ -1476,7 +1710,7 @@ void SparseMatrix::RemoveColumns(int numRemovedColumns, int * removedColumns, in
   }
 }
 
-void SparseMatrix::RemoveColumnsSlow(int numColumns, int * columns, int oneIndexed)
+void SparseMatrix::RemoveColumnsSlow(int numColumns, const int * columns, int oneIndexed)
 {
   for(int i=0; i<numColumns; i++)
     RemoveColumn(columns[i]-i-oneIndexed);
@@ -1484,6 +1718,7 @@ void SparseMatrix::RemoveColumnsSlow(int numColumns, int * columns, int oneIndex
 
 void SparseMatrix::RemoveRow(int index)
 {
+  FreeAuxiliaryData();
   // remove row 'index'
   free(columnEntries[index]);
   free(columnIndices[index]);
@@ -1498,14 +1733,15 @@ void SparseMatrix::RemoveRow(int index)
   numRows--;
 }
 
-void SparseMatrix::RemoveRowsSlow(int numRows, int * rows, int oneIndexed)
+void SparseMatrix::RemoveRowsSlow(int numRows, const int * rows, int oneIndexed)
 {
   for(int i=0; i<numRows; i++)
     RemoveRow(rows[i]-i-oneIndexed);
 }
 
-void SparseMatrix::RemoveRows(int numRemovedRows, int * removedRows, int oneIndexed)
+void SparseMatrix::RemoveRows(int numRemovedRows, const int * removedRows, int oneIndexed)
 {
+  FreeAuxiliaryData();
   // the removed dofs must be pre-sorted
   // build a map from old dofs to new ones
   vector<int> oldToNew(numRows);
@@ -1535,8 +1771,8 @@ void SparseMatrix::RemoveRows(int numRemovedRows, int * removedRows, int oneInde
   {
     if (oldToNew[sourceRow] == -1)
     {
-      free(columnIndices[sourceRow]);    
-      free(columnEntries[sourceRow]);    
+      free(columnIndices[sourceRow]);
+      free(columnEntries[sourceRow]);
       continue;
     }
 
@@ -1555,23 +1791,30 @@ void SparseMatrix::RemoveRows(int numRemovedRows, int * removedRows, int oneInde
 double SparseMatrix::GetInfinityNorm() const
 {
   double norm = 0.0;
-
   for(int i=0; i<numRows; i++)
   {
     double absRowSum = 0;
-
     for(int j=0; j<rowLength[i]; j++)
-    {
       absRowSum += fabs(columnEntries[i][j]);
-    }
-
     if (absRowSum > norm)
       norm = absRowSum;
   }
-
   return norm;
 }
 
+double SparseMatrix::GetFrobeniusNorm() const
+{
+  double sum = 0.0;
+  for(int i=0; i<numRows; i++)
+  {
+    double rowSum = 0.;
+    for(int j=0; j<rowLength[i]; j++)
+      rowSum += columnEntries[i][j] * columnEntries[i][j];
+    sum += rowSum;
+  }
+  return sqrt(sum);
+}
+
 void SparseMatrix::DoOneGaussSeidelIteration(double * x, const double * b) const
 {
   for(int i=0; i<numRows; i++)
@@ -1627,16 +1870,21 @@ double SparseMatrix::CheckLinearSystemSolution(const double * x, const double *
   }
 
   free(bufferv);
-  
+
   return inftyNorm / inftyNorm_b;
 }
 
-void SparseMatrix::MakeDenseMatrix(double * denseMatrix) const
+void SparseMatrix::MakeDenseMatrix(double * denseMatrix, int numColumns) const
 {
-  memset(denseMatrix, 0, sizeof(double) * (numRows * GetNumColumns()));
+  if (numColumns == -1)
+    numColumns = GetNumColumns();
+  memset(denseMatrix, 0, sizeof(double) * (numRows * numColumns));
   for(int i=0; i< numRows; i++)
     for(int j=0; j<rowLength[i]; j++)
-      denseMatrix[numRows * columnIndices[i][j] + i] = columnEntries[i][j];
+    {
+      if (columnIndices[i][j] < numColumns)
+        denseMatrix[numRows * columnIndices[i][j] + i] = columnEntries[i][j];
+    }
 }
 
 void SparseMatrix::MakeDenseMatrixTranspose(int numColumns, double * denseMatrix) const
@@ -1657,7 +1905,7 @@ void SparseMatrix::MultiplyRow(int row, double scalar) // multiplies all element
     columnEntries[row][j] *= scalar;
 }
 
-int SparseMatrix::GetNumColumns() const 
+int SparseMatrix::GetNumColumns() const
 {
   int numColumns = -1;
   for(int i=0; i<numRows; i++)
@@ -1673,6 +1921,7 @@ int SparseMatrix::GetNumColumns() const
 
 void SparseMatrix::IncreaseNumRows(int numAddedRows)
 {
+  FreeAuxiliaryData();
   int newn = numRows + numAddedRows;
 
   rowLength = (int*) realloc (rowLength, sizeof(int) * newn);
@@ -1690,9 +1939,12 @@ void SparseMatrix::IncreaseNumRows(int numAddedRows)
   numRows = newn;
 }
 
-SparseMatrix SparseMatrix::ConjugateMatrix(SparseMatrix & U, int verbose)
+SparseMatrix SparseMatrix::ConjugateMatrix(const SparseMatrix & U, int verbose, int numColumns) const
 {
-  SparseMatrixOutline outline(U.GetNumColumns());
+  if (numColumns == -1)
+    numColumns = U.GetNumColumns();
+
+  SparseMatrixOutline outline(numColumns);
 
   for(int i=0; i<numRows; i++)
   {
@@ -1722,7 +1974,7 @@ SparseMatrix SparseMatrix::ConjugateMatrix(SparseMatrix & U, int verbose)
 
   if (verbose)
     printf("Creating sparse matrix from outline...\n");
- 
+
   return SparseMatrix(&outline);
 }
 
@@ -1756,7 +2008,7 @@ void SparseMatrix::BuildConjugationIndices(SparseMatrix & U, SparseMatrix & MTil
           if (iter == rowMaps[K].end())
           {
             listOfFourTuples singletonList;
-            singletonList.push_back(tuple);            
+            singletonList.push_back(tuple);
             rowMaps[K].insert(make_pair(L, singletonList));
           }
           else
@@ -1773,10 +2025,10 @@ void SparseMatrix::BuildConjugationIndices(SparseMatrix & U, SparseMatrix & MTil
   {
     (*precomputedIndices)[i] = (int**) malloc (sizeof(int*) * rowMaps[i].size());
     int j = 0;
-    for(rowMap::iterator iter = rowMaps[i].begin(); iter != rowMaps[i].end(); iter++)
+    for(rowMap::iterator iter = rowMaps[i].begin(); iter != rowMaps[i].end(); ++iter)
     {
       (*precomputedIndices)[i][j] = (int*) malloc (sizeof(int) * (4 * ((iter->second).size()) + 1));
-      ((*precomputedIndices)[i][j])[0] = (iter->second).size();
+      ((*precomputedIndices)[i][j])[0] = (int)((iter->second).size());
       for(int k=0; k<((*precomputedIndices)[i][j])[0]; k++)
       {
         ((*precomputedIndices)[i][j])[1+4*k+0] = ((iter->second)[k]).first.first;
@@ -1813,7 +2065,7 @@ void SparseMatrix::ConjugateMatrix(precomputedIndicesType precomputedIndices, Sp
   }
 }
 
-void SparseMatrix::ConjugateMatrix(double * U, int r, double * UTilde)
+void SparseMatrix::ConjugateMatrix(const double * U, int r, double * MTilde) const
 {
   double * MU = (double*) malloc (sizeof(double) * numRows * r);
   MultiplyMatrix(numRows, r, U, MU);
@@ -1825,7 +2077,7 @@ void SparseMatrix::ConjugateMatrix(double * U, int r, double * UTilde)
       double entry = 0.0;
       for(int k=0; k<numRows; k++)
         entry += U[i * numRows + k] * MU[j * numRows + k];
-      UTilde[j * r + i] = entry;
+      MTilde[j * r + i] = entry;
     }
 
   free(MU);
@@ -1841,12 +2093,25 @@ SparseMatrix * SparseMatrix::Transpose(int numColumns)
   for(int i=0; i<numRows; i++)
     for(int j=0; j<rowLength[i]; j++)
       outline.AddEntry(columnIndices[i][j], i, columnEntries[i][j]);
- 
-  return new SparseMatrix(&outline);;
+
+  return new SparseMatrix(&outline);
 }
 
-void SparseMatrix::SetRows(SparseMatrix * source, int startRow, int startColumn) 
+void SparseMatrix::AssignTransposedMatrix(SparseMatrix & AT)
 {
+  AT.BuildTranspositionIndices();
+  for(int i = 0; i < AT.numRows; i++)
+    for(int j = 0; j < AT.rowLength[i]; j++)
+    {
+      int index = AT.TransposedIndex(i,j);
+      int row = AT.columnIndices[i][j];
+      columnEntries[row][index] = AT.columnEntries[i][j];
+    }
+}
+
+void SparseMatrix::SetRows(const SparseMatrix * source, int startRow, int startColumn)
+{
+  FreeAuxiliaryData();
   for(int i=0; i<source->GetNumRows(); i++)
   {
     int row = startRow + i;
@@ -1863,8 +2128,14 @@ void SparseMatrix::SetRows(SparseMatrix * source, int startRow, int startColumn)
     }
   }
 }
+void SparseMatrix::AppendRows(const SparseMatrix * source)
+{
+  int oldNumRows = numRows;
+  IncreaseNumRows(source->GetNumRows());
+  SetRows(source, oldNumRows);
+}
 
-void SparseMatrix::AppendRowsColumns(SparseMatrix * source)
+void SparseMatrix::AppendRowsColumns(const SparseMatrix * source)
 {
   int * oldRowLengths = (int*) malloc (sizeof(int) * numRows);
   for(int i=0; i<numRows; i++)
@@ -1910,7 +2181,7 @@ void SparseMatrix::AppendRowsColumns(SparseMatrix * source)
 
   free(oldRowLengths);
 
-  // append zero diagonal in lower-right block (helps with some solvers)
+  // append zero diagonal in lower-right block (helps with some solvers, such as PARDISO)
   for(int row=0; row<source->GetNumRows(); row++)
   {
     rowLength[oldNumRows + row]++;
@@ -1918,7 +2189,7 @@ void SparseMatrix::AppendRowsColumns(SparseMatrix * source)
     columnEntries[oldNumRows + row] = (double*) realloc (columnEntries[oldNumRows + row], sizeof(double) * rowLength[oldNumRows + row]);
     columnIndices[oldNumRows + row][rowLength[oldNumRows + row] - 1] = oldNumRows + row;
     columnEntries[oldNumRows + row][rowLength[oldNumRows + row] - 1] = 0.0;
-  }  
+  }
 }
 
 SparseMatrix * SparseMatrix::CreateIdentityMatrix(int numRows)
@@ -1930,5 +2201,3 @@ SparseMatrix * SparseMatrix::CreateIdentityMatrix(int numRows)
   delete outline;
   return mat;
 }
-
-}
diff --git a/libraries/sparseMatrix/sparseMatrix.h b/libraries/sparseMatrix/sparseMatrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..c8e49c08652efc08fb35bdfc33f940c7a698df54
--- /dev/null
+++ b/libraries/sparseMatrix/sparseMatrix.h
@@ -0,0 +1,433 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseMatrix" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _SPARSE_MATRIX_H_
+#define _SPARSE_MATRIX_H_
+
+/*
+  The "SparseMatrix" class implements double-precision sparse matrices 
+  with common algebraic operations such as incremental construction, 
+  addition, mtx-vec multiplication, row-column deletion. 
+  The matrices can be loaded from and saved to a file.
+
+  The matrix can be rectangular (it need not be square). 
+  In memory, the matrix is stored in a row-based format. 
+  For each matrix row, the class stores the integer
+  indices of the columns containing non-zero entries, 
+  together with the corresponding double precision values. 
+  All quantities (rows, columns, etc.) in this class are 0-indexed.
+
+  Also included is a Conjugate Gradient iterative linear system solver 
+  (for positive-definite large sparse symmetric matrices).
+  The solver can be used without preconditioning, or with diagonal (Jacobi) preconditoning.
+  The solver either uses an explicitly provided sparse matrix, or you can only
+  provide the matrix-vector multiplication routine (without explicitly giving the matrix).
+  The CG Solver was implemented by following Jonathan Shewchuk's 
+  "An Introduction to the Conjugate Gradient Method Without the Agonizing Pain":
+  http://www.cs.cmu.edu/~jrs/jrspapers.html#cg
+
+  The class also includes a Gauss-Seidel iterative linear system solver.
+
+  There are two classes available to the user: SparseMatrixOutline and SparseMatrix.
+  The SparseMatrixOutline class should be used to construct the non-zero 
+  locations in the matrix, and (optionally) assign values to them.
+  You should then transform it into a SparseMatrix class, via SparseMatrix's
+  class constructor. The SparseMatrix class has all the computational routines,
+  but you can only add new non-zero entries to SparseMatrixOutline, not
+  to SparseMatrix.  The reason for this separation is that SparseMatrixOutline 
+  does not know the number of matrix entries ahead of time, so it uses STL's 
+  "map" datastructure to store the matrix data. In the SparseMatrix class, however, the number 
+  of sparse entries and their locations are fixed, so all operations can use 
+  known-length C arrays, which is faster.
+
+  So: you should first create an instance of SparseMatrixOutline, then create 
+  an instance of SparseMatrix by passing the SparseMatrixOutline object to 
+  SparseMatrix's constructor.
+  If your matrix is a text file on disk, you can load it to SparseMatrixOutline, 
+  or directly load it into SparseMatrix (which will, however, internally still 
+  proceed via SparseMatrixOutline).
+
+  The text disk file format is as follows:
+  <number of matrix rows>
+  <number of matrix columns>
+  one or more data lines, each giving one matrix entry, in the format:
+  <row index> <column index> <data value> 
+  (indices are 0-indexed)
+
+  Example:
+  
+    [0 17 -1 0]
+  A=[0  5  0 0]
+    [3  8  6 0]  
+
+  would be given as:
+
+  3
+  4
+  0 1 17 
+  0 2 -1
+  1 1 5
+  2 0 3
+  2 1 8
+  2 2 6
+*/
+
+#include <string.h>
+#include <vector>
+#include <map>
+
+class SparseMatrix;
+
+class SparseMatrixOutline
+{
+public:
+  // makes an empty sparse matrix with numRows rows
+  SparseMatrixOutline(int numRows);
+  virtual ~SparseMatrixOutline();
+
+  // makes a diagonal numRows x numRows sparse matrix; with a constant diagonal
+  SparseMatrixOutline(int numRows, double diagonal);
+  // makes a diagonal numRows x numRows sparse matrix; diagonal is a vector of n numbers
+  SparseMatrixOutline(int numRows, const double * diagonal);
+
+  // loads the sparse matrix from a text file
+  // if expand is greater than 1, the routine also expands each element into a diagonal block of size expand x expand... 
+  //   (expand option is useful for loading the mass matrix in structural mechanics (with expand=3 in 3D))
+  SparseMatrixOutline(const char * filename, int expand=1); 
+
+  // save matrix to a text file
+  int Save(const char * filename, int oneIndexed=0) const;
+
+  // initialize the matrix to be an empty matrix with numRows rows
+  void Initialize(int numRows);
+  // add entry at location (i,j) in the matrix
+  void AddEntry(int i, int j, double value=0.0);
+  // remove entry at location (i,j)
+  void RemoveEntry(int i, int j);
+  // add a 3x3 matrix at location (3*i, 3*j)
+  void AddBlock3x3Entry(int i, int j, const double * matrix3x3); // matrix3x3 should be given in row-major order
+  void AddBlock3x3Entry(int i, int j, double value=0.0); // matrix is assigned the value at all 3x3 elements
+  // add a block (sparse) matrix (optionally multiplied with "scalarFactor"), starting at row i, and column j, can expand each element of block into a diagonal block of size "expand"
+  void AddBlockMatrix(int i, int j, const SparseMatrix * block, double scalarFactor=1.0, int expand=1);
+  void AddBlockMatrix(int i, int j, const SparseMatrixOutline & block, double scalarFactor=1.0);
+  void IncreaseNumRows(int numAddedRows); // increases the number of matrix rows (new rows are added at the bottom of the matrix, and are all empty)
+
+  void MultiplyRow(int row, double scalar); // multiplies all elements in row 'row' with scalar 'scalar'
+
+  inline int Getn() const { return numRows; } // get number of rows
+  inline int GetNumRows() const { return numRows; } // get number of rows
+  int GetNumColumns() const; // get the number of columns (i.e., search for max column index)
+  int GetNumEntries() const; // get total number of non-zero matrix elements
+  double GetEntry(int i, int j) const; // returns the matrix entry at location (i,j) in the matrix (or zero if entry has not been assigned)
+  void Print() const;
+
+  // low-level routine which is rarely used
+  inline const std::map<int,double> & GetRow(int i) const { return columnEntries[i]; }
+  inline std::map<int,double> & GetRow(int i) { return columnEntries[i]; }
+  friend class SparseMatrix;
+
+protected:
+  int numRows;
+  std::vector< std::map<int,double> > columnEntries;
+  void Allocate();
+};
+
+class SparseMatrix
+{
+public:
+
+  SparseMatrix(const char * filename); // load from text file (same text file format as SparseMatrixOutline)
+  SparseMatrix(SparseMatrixOutline * sparseMatrixOutline); // create it from the outline
+  // create it by specifying all entries: number of rows, length of each row, indices of columns of non-zero entries in each row, values of non-zero entries in each row
+  // column indices in each row must be sorted (ascending)
+  // if shallowCopy=1, the class will not allocate its own internal buffers, but will assume ownership of the input rowLength, columnIndices and columnEntries parameters
+  SparseMatrix(int numRows, int * rowLength, int ** columnIndices, double ** columnEntries, int shallowCopy=0); 
+  SparseMatrix(int numSubMatrices, const SparseMatrix ** subMatrices, const int * numColumns); // construct a diagonal block matrix from submatrices
+
+  SparseMatrix(const SparseMatrix & source); // copy constructor
+  virtual ~SparseMatrix();
+
+  int Save(const char * filename, int oneIndexed=0) const; // save matrix to a disk text file 
+
+  // save matrix to a text file that can be imported into Matlab; Related Matlab commands: 
+  // load matname.dat;       # read the stored sparse matrix from disk
+  // M = spconvert(matname); # convert the loaded matrix to standard Matlab sparse format
+  // spy(M);                 # check matrix sparsity
+  // Mf = full(M);           # convert the sparse matrix to a full matrix
+  int SaveToMatlabFormat(const char * filename) const; 
+
+  // set/add value to the j-th sparse entry in the given row (NOT to matrix element at (row,j))
+  inline void SetEntry(int row, int j, double value) { columnEntries[row][j] = value; }
+  inline void AddEntry(int row, int j, double value) { columnEntries[row][j] += value; }
+  void ResetToZero(); // reset all entries to zero
+  void ResetRowToZero(int row); // reset all entries in the row to zero
+
+  inline int Getn() const { return numRows; } // get the number of rows
+  inline int GetNumRows() const { return numRows; }
+  inline int GetRowLength(int row) const { return rowLength[row]; }
+  int GetNumColumns() const; // get the number of columns (i.e., search for max column index)
+  // returns the j-th sparse entry in row i (NOT matrix element at (row, j))
+  inline double GetEntry(int row, int j) const { return columnEntries[row][j]; }
+  // returns the column index of the j-th sparse entry in the given row
+  inline int GetColumnIndex(int row, int j) const { return columnIndices[row][j]; } 
+  inline double ** GetEntries() const { return columnEntries; }
+  inline int ** GetColumnIndices() const { return columnIndices; }
+  inline int * GetRowLengths() const { return rowLength; }
+
+  // finds the compressed column index of element at location (row, jDense)
+  // returns -1 if column not found
+  int GetInverseIndex(int row, int jDense) const;
+
+  int GetNumEntries() const; // returns the total number of non-zero entries
+  double SumEntries() const; // returns the sum of all matrix entries
+  void SumRowEntries(double * rowSums) const; // returns the sum of all entries in each row
+  double GetMaxAbsEntry() const; // max abs value of a matrix entry
+  double GetInfinityNorm() const; // matrix infinity norm: maximum absolute row sum of the matrix
+  double GetFrobeniusNorm() const; // matrix Frobenius norm: sqrt of the sum of absolute squares of its elements
+  void Print(int sparsePrint=0) const; // prints the matrix out to standard output
+  double GetRowNorm2(int row) const;
+  bool HasInf() const; // has positive infinity or negative infinity
+  bool HasNaN() const; // has not-a-number
+  bool HasInfOrNaN() const; // has positive infinity, negative infinity or not-a-number
+
+  // matrix algebra (all involved matrices must have the same pattern of non-zero entries)
+  SparseMatrix operator+(const SparseMatrix & mat2) const;
+  SparseMatrix operator-(const SparseMatrix & mat2) const;
+  friend SparseMatrix operator* (const double alpha, const SparseMatrix & mat2); // warning: this function makes a local copy; "ScalarMultiply" is more efficient
+  SparseMatrix & operator=(const SparseMatrix & source); // matrices must have same size and locations of non-zero entries
+  SparseMatrix & operator*=(const double alpha);
+  SparseMatrix & operator+=(const SparseMatrix & mat2);
+  SparseMatrix & operator-=(const SparseMatrix & mat2);
+  bool operator==(const SparseMatrix & mat2) const;
+  bool operator!=(const SparseMatrix & mat2) const;
+  // check whether two sparse matrices share the same size and locations of non-zero entries
+  bool SameStructure(const SparseMatrix & mat2) const;
+  
+  void ScalarMultiply(const double alpha, SparseMatrix * dest=NULL); // dest = alpha * dest (if dest=NULL, operation is applied to this object)
+  void ScalarMultiplyAdd(const double alpha, SparseMatrix * dest=NULL); // dest += alpha * dest (if dest=NULL, operation is applied to this object)
+  void MultiplyRow(int row, double scalar); // multiplies all elements in row 'row' with scalar 'scalar'
+
+  // multiplies the sparse matrix with the given vector/matrix
+  void MultiplyVector(const double * vector, double * result) const; // result = A * vector
+  void MultiplyVectorAdd(const double * vector, double * result) const; // result += A * vector
+  void MultiplyVector(int startRow, int endRow, const double * vector, double * result) const; // result = A(startRow:endRow-1,:) * vector
+  void TransposeMultiplyVector(const double * vector, int resultLength, double * result) const; // result = trans(A) * vector
+  void TransposeMultiplyVectorAdd(const double * vector, double * result) const; // result += trans(A) * vector
+  void MultiplyMatrix(int numDenseRows, int numDenseColumns, const double * denseMatrix, double * result) const; // result = A * denseMatrix (denseMatrix is a numDenseRows x numDenseColumns dense matrix, result is a numRows x numDenseColumns dense matrix)
+  void MultiplyMatrixAdd(int numDenseRows, int numDenseColumns, const double * denseMatrix, double * result) const; // result += A * denseMatrix (denseMatrix is a numDenseRows x numDenseColumns dense matrix, result is a numDenseRows x numDenseColumns dense matrix)
+  void MultiplyMatrixTranspose(int numDenseColumns, const double * denseMatrix, double * result) const; // result = A * trans(denseMatrix) (trans(denseMatrix) is a dense matrix with 'numDenseColumns' columns, result is a numRows x numDenseColumns dense matrix)
+
+  // computes <M * vector, vector> (assumes symmetric M)
+  double QuadraticForm(const double * vector) const;
+  // normalizes vector in the M-norm: vector := vector / sqrt(<M * vector, vector>)  (assumes symmetric M)
+  void NormalizeVector(double * vector) const;
+  void ConjugateMatrix(const double * U, int r, double * MTilde) const; // computes MTilde = U^T M U (M can be a general square matrix, U need not be a square matrix; number of columns of U is r; sizes of M and U must be such that product is defined; output matrix will have size r x r, stored column-major)
+  SparseMatrix ConjugateMatrix(const SparseMatrix & U, int verbose=0, int numColumns=-1) const; // computes U^T M U (M is this matrix, and can be a general square matrix, U need not be a square matrix; sizes of M and U must be such that product is defined); numColumns is the number of columns of the result (U^T M U); this is important because there could be empty columns in U at the right border of the U matrix; if numColumns=-1, the routine will use U->GetNumColumns()
+
+  // builds indices for subsequent faster product computation (below)
+  // input: U, MTilde; MTilde must equal U^T M U, computed using the "ConjugateMatrix" routine above
+  // output: precomputedIndices
+  typedef int *** precomputedIndicesType;
+  void BuildConjugationIndices(SparseMatrix & U, SparseMatrix & MTilde, precomputedIndicesType * precomputedIndices); // note: must be debugged
+  // input: precomputedIndices, U
+  // output: MTilde
+  void ConjugateMatrix(precomputedIndicesType precomputedIndices, SparseMatrix & U, SparseMatrix & MTilde); // note: must be debugged
+
+  // writes all entries into the space provided by 'data'
+  // space must be pre-allocated
+  // data is written row after row, and by non-zero columns within each row
+  void MakeLinearDataArray(double * data) const;
+  // writes row indices of non-zero entries into array "indices"
+  // same order as for data
+  void MakeLinearRowIndexArray(int * indices) const;
+  // indices in this function version are double to ensure compatibility with Matlab
+  void MakeLinearRowIndexArray(double * indices) const;
+  // writes column indices
+  void MakeLinearColumnIndexArray(int * indices) const;
+  void MakeLinearColumnIndexArray(double * indices) const;
+
+  // Make a dense matrix (column-major storage).
+  // (this can be a huge matrix for large sparse matrices)
+  // Storage in denseMatrix must be pre-allocated.
+  // The size of the denseMatrix is: this->GetNumRows() x (numColumns == -1 ? this->GetNumColumns() : numColumns).
+  void MakeDenseMatrix(double * denseMatrix, int numColumns=-1) const;
+  // also transposes the matrix:
+  void MakeDenseMatrixTranspose(int numColumns, double * denseMatrix) const;
+
+  // removes row(s) and column(s) from the matrix
+  void RemoveRowColumn(int rowColumn); // 0-indexed
+  void RemoveRowsColumns(int numRemovedRowColumns, const int * removedRowColumns, int oneIndexed=0); // the rowColumns must be sorted (ascending)
+  void RemoveRowsColumnsSlow(int numRemovedRowColumns, const int * removedRowColumns, int oneIndexed=0); // the rowColumns need not be sorted
+
+  // removes row(s) from the matrix
+  void RemoveRow(int row); // 0-indexed
+  void RemoveRows(int numRemovedRows, const int * removedRows, int oneIndexed=0); // rows must be sorted (ascending)
+  void RemoveRowsSlow(int numRemovedRows, const int * removedRows, int oneIndexed=0); // the rows need not be sorted
+
+  // removes column(s) from the matrix
+  void RemoveColumn(int column); // 0-indexed
+  void RemoveColumns(int numRemovedColumns, const int * removedColumns, int oneIndexed=0); // columns must be sorted (ascending)
+  void RemoveColumnsSlow(int numRemovedColumns, const int * removedColumns, int oneIndexed=0); // columns need not be sorted 
+
+  void IncreaseNumRows(int numAddedRows); // increases the number of matrix rows (new rows are added at the bottom of the matrix, and are all empty)
+  void SetRows(const SparseMatrix * source, int startRow, int startColumn=0); // starting with startRow, overwrites the rows with those of matrix "source"; data is written into columns starting at startColumn
+  void AppendRowsColumns(const SparseMatrix * source); // appends the matrix "source" at the bottom of matrix, and trans(source) to the right of the matrix
+  void AppendRows(const SparseMatrix * source);
+
+  // transposition (note: the matrix need not be symmetric)
+  void BuildTranspositionIndices();
+  void FreeTranspositionIndices();
+  // returns the list position of the transposed element (row, list position j)
+  // must first call BuildTranspositionIndices()
+  inline int TransposedIndex(int row, int j) const { return transposedIndices[row][j]; }
+
+  // returns the transposed matrix
+  // numColumns is the number of columns in the original matrix;
+  // this is important in case there are zero columns at the end of the matrix
+  // if numColumns=-1 (default), GetNumColumns() will be called; however, this will lead to a transposed matrix with a fewer number of rows in case of empty columns at the end of the original matrix
+  SparseMatrix * Transpose(int numColumns=-1);
+  // assign a transposed matrix AT to this matrix; topology of AT must be transpose of topology of this matrix
+  // note: this function calls BuildTranspositionIndices internally, so you don't need to call BuildTranspositionIndices first
+  void AssignTransposedMatrix(SparseMatrix & AT);
+
+  // checks if the matrix is skew-symmetric
+  // the non-zero entry locations must form a symmetric pattern
+  // returns max ( abs ( A^T + A ) ) = || A^T + A ||_{\infty}
+  // note: this function calls BuildTranspositionIndices internally, so you don't need to call BuildTranspositionIndices first
+  double SkewSymmetricCheck(); 
+  // makes matrix symmetric by copying upper triangle + diagonal into the lower triangle
+  // the non-zero entry locations must form a symmetric pattern
+  // note: this function calls BuildTranspositionIndices internally, so you don't need to call BuildTranspositionIndices first
+  void SymmetrizeMatrix();
+
+  // pre-computes the sparse columns of diagonal matrix entries
+  // this routine will accelerate subsequent GetDiagonal or AddDiagonalMatrix calls, but is not necessary for GetDiagonal or AddDiagonalMatrix
+  void BuildDiagonalIndices();
+  void FreeDiagonalIndices();
+  void GetDiagonal(double * diagonal) const;
+  void AddDiagonalMatrix(double * diagonalMatrix);
+  void AddDiagonalMatrix(double constDiagonalElement);
+
+  // Build submatrix indices is used for pair of matrices where the sparsity of one matrix is a subset of another matrix (for example, mass matrix and stiffness matrix). 
+  // It allows you to assign/add a submatrix to the current matrix.
+  // The submatrix begins at row "startRow" and column "startColumn" of this matrix.
+  // submatrixID allows you to keep several submatrices at once
+  // Call this once to establish the correspondence:
+  void BuildSubMatrixIndices(const SparseMatrix & submatrix, int subMatrixID=0, int startRow=0, int startColumn=0);
+  void FreeSubMatrixIndices(int subMatrixID=0);
+  // assign a submatrix to the current matrix, whose elements are a subset of the elements of the current matrix
+  // note: the other entries of the current matrix are unmodified
+  void AssignSubMatrix(const SparseMatrix & submatrix, int subMatrixID=0);
+  // assign the current matrix to a submatrix
+  void AssignToSubMatrix(SparseMatrix & submatrix, int subMatrixID=0) const;
+  // add a matrix to the current matrix, whose elements are a subset of the elements of the current matrix
+  // += factor * mat2
+  // returns *this
+  SparseMatrix & AddSubMatrix(double factor, SparseMatrix & submatrix, int subMatrixID=0);
+
+  // Build supermatrix indices is used for pair of matrices with rows/columns removed.
+  // It allows you to assign a super matrix to the current matrix.
+  // oneIndexed: tells whether the fixed rows and columns are specified 1-indexed or 0-indexed
+  // First, call BuildSuperMatrixIndices once to inialize (all fixed rows and columns are indexed with respect the superMatrix):
+  void BuildSuperMatrixIndices(int numFixedRowColumns, const int * fixedRowColumns, const SparseMatrix * superMatrix, int oneIndexed=0); // use this version if the indices of removed rows and columns are the same
+  void BuildSuperMatrixIndices(int numFixedRows, const int * fixedRows, int numFixedColumns, const int * fixedColumns, const SparseMatrix * superMatrix, int oneIndexed=0); // allows arbitrary row and column indices
+  // Then, call this (potentially many times) to quickly assign the values at the appropriate places in the submatrix.
+  // For example, you can use this to copy data from a matrix into a submatrix obtained by a previous call to RemoveRowColumns.
+  void AssignSuperMatrix(const SparseMatrix & superMatrix);
+  void FreeSuperMatrixIndices();
+
+  // returns the total number of non-zero entries in the lower triangle (including diagonal)
+  int GetNumLowerTriangleEntries() const;
+  int GetNumUpperTriangleEntries() const;
+  // exports the matrix to format for NAG library
+  int GenerateNAGFormat(double * a,int * irow,int * icol, int * istr) const;
+
+  void GenerateCompressedRowMajorFormat(double * a, int * ia, int * ja, int upperTriangleOnly=0, int oneIndexed=0) const; 
+  void GenerateCompressedRowMajorFormat_four_array(double * values, int * columns, int * pointerB, int * pointerE, int upperTriangleOnly=0, int oneIndexed=0) const; 
+
+  // diagonal solve M * x = b
+  // ASSUMES the sparse matrix is diagonal !
+  // result is overwritten into rhs
+  // (to solve non-diagonal linear systems, you need to use an external library; or you can use the CGSolver class, or you can use the Gauss-Seidel iteration below)
+  void DiagonalSolve(double * rhs) const;
+
+  // performs one Gauss-Seidel iteration of solving the system A * x = b
+  // updates vector x in place, b is not modified
+  // (A can be a general matrix)
+  // assumes that diagonal entries of the matrix are set and are non-zero
+  void DoOneGaussSeidelIteration(double * x, const double * b) const;
+  void ComputeResidual(const double * x, const double * b, double * residual) const;
+  // checks if A * x - b is close to zero and prints out the findings
+  // returns ||A * x - b|| / ||b|| (all norms are infinity)
+  // passing a buffer (length of n) will avoid a malloc/free pair to generate scratch space for the residual
+  double CheckLinearSystemSolution(const double * x, const double * b, int verbose=1, double * buffer=NULL) const;
+
+  // below are low-level routines which are rarely used
+  inline double ** GetDataHandle() const { return columnEntries; }
+  inline double * GetRowHandle(int row) const { return columnEntries[row]; }
+
+  // create a nxn identity matrix
+  static SparseMatrix * CreateIdentityMatrix(int n);
+
+protected:
+
+  // compressed row storage
+  int numRows; // number of rows
+  int * rowLength; // length of each row
+  int ** columnIndices; // indices of columns of non-zero entries in each row
+  double ** columnEntries; // values of non-zero entries in each row
+
+  int * diagonalIndices;
+  int ** transposedIndices;
+
+  /*
+    numSubMatrixIDs specifies how many sub-matrix relationships we have
+    length(subMatrixIndices) == length(subMatrixIndexLengths) == length(subMatrixStartRow) == (numSubMatrixIDs + 1)
+    length(subMatrixIndexLengths[subMatrixID]) == length(subMatrixIndices[subMatrixID]) == number of rows = numRows
+    length(subMatrixIndices[subMatrixID][rowIndex]) == subMatrixIndexLengths[subMatrixID][rowIndex]
+  */
+  int numSubMatrixIDs;
+  int *** subMatrixIndices;
+  int ** subMatrixIndexLengths;
+  int * subMatrixStartRow;
+  int * subMatrixNumRows;
+
+  int ** superMatrixIndices;
+  int * superRows;
+
+  void InitFromOutline(SparseMatrixOutline * sparseMatrixOutline);
+  void Allocate();
+  void FreeAuxiliaryData();
+};
+
+#endif
+
diff --git a/src/libsparseSolver/ARPACKSolver.cpp b/libraries/sparseSolver/ARPACKSolver.cpp
similarity index 51%
rename from src/libsparseSolver/ARPACKSolver.cpp
rename to libraries/sparseSolver/ARPACKSolver.cpp
index fdd1a5b28f61cfa35fecd2f3d7954336ed783f4f..a7664bbdd57873493c60b75603bd267c1a50a375 100644
--- a/src/libsparseSolver/ARPACKSolver.cpp
+++ b/libraries/sparseSolver/ARPACKSolver.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * Code authors: Jernej Barbic, Hongyi Xu                                *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,15 +35,20 @@
  *                                                                       *
  *************************************************************************/
 
+#include "ARPACKSolver.h"
+#include "invMKSolver.h"
+#include "invZTAZSolver.h"
+#include "invZTAZMSolver.h"
+#include "ZTAZMultiplicator.h"
+#include "lapack-headers.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
 #include <iostream>
+
 using namespace std;
-#include "ARPACKSolver.h"
-#include "invMKSolver.h"
-#include "lapack-headers.h"
 
 //#define SPOOLES
 #define PARDISO
@@ -48,7 +57,7 @@ using namespace std;
 #define DSEUPD dseupd_
 #define INTEGER int
 
-/* Note:
+/* Note: 
   This code is now independent of ARPACK++ and calls ARPACK directly. It has been tested and works fine.
 
   Previously, it used ARPACK++, which, while convenient, required installing ARPACK++ (which required
@@ -68,7 +77,7 @@ using namespace std;
     double * TOL, double * RESID, INTEGER * NCV, double * V, INTEGER * LDV, INTEGER * IPARAM,
     INTEGER * IPNTR, double * WORKD, double * WORKL, INTEGER * LWORKL, INTEGER * INFO);
 
-  // call DSAUPD
+  // call DSAUPD 
   //c     ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM,
   //c       IPNTR, WORKD, WORKL, LWORKL, INFO )
 
@@ -78,7 +87,7 @@ using namespace std;
     double * TOL, double * RESID, INTEGER * NCV, double * V, INTEGER * LDV, INTEGER * IPARAM,
     INTEGER * IPNTR, double * WORKD, double * WORKL, INTEGER * LWORKL, INTEGER * INFO);
 
-  //c  call DSEUPD
+  //c  call DSEUPD  
   //c     ( RVEC, HOWMNY, SELECT, D, Z, LDZ, SIGMA, BMAT, N, WHICH, NEV, TOL,
   //c       RESID, NCV, V, LDV, IPARAM, IPNTR, WORKD, WORKL, LWORKL, INFO )
 }
@@ -93,7 +102,7 @@ using namespace std;
   #include "PardisoSolver.h"
 #endif
 
-int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, int numEigenvalues, double * eigenvalues, double * eigenvectors, char * mode, int numLinearSolverThreads, int verbose)
+int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, int numEigenvalues, double * eigenvalues, double * eigenvectors, const char * mode, int numLinearSolverThreads, int verbose)
 {
   int np = KInput->Getn();
   if (np != MInput->Getn())
@@ -140,10 +149,11 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
   #ifdef PARDISO
     if (verbose >= 1)
       printf("Linear solver: PARDISO (%d threads).\n", (numLinearSolverThreads == 0) ? 1 : numLinearSolverThreads);
-    int positiveDefinite = 0;
+
     int directIterative = 0;
-    PardisoSolver * pardisoSolver = new PardisoSolver(M, numLinearSolverThreads, positiveDefinite, directIterative, verbose);
-    pardisoSolver->ComputeCholeskyDecomposition(M);
+    PardisoSolver * pardisoSolver = new PardisoSolver(M, numLinearSolverThreads, PardisoSolver::REAL_SYM_INDEFINITE,
+      PardisoSolver::NESTED_DISSECTION, directIterative, verbose);
+    pardisoSolver->FactorMatrix(M);
     invMSolver = pardisoSolver;
   #endif
 
@@ -165,7 +175,7 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
               int maxitp = 0, ARFLOAT* residp = NULL, bool ishiftp = true);
 */
     ARSymGenEig<double, InvMKSolver, SparseMatrix> solver
-      (np, numEigenvalues,
+      (np, numEigenvalues, 
        &invMKSolver, &InvMKSolver::ComputeInvMK,
        M, (void (SparseMatrix::*)(double*,double*)) &SparseMatrix::MultiplyVector, "LM");
 
@@ -177,7 +187,7 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
     INTEGER IDO = 0;
     char BMAT = 'G';
     INTEGER N = np;
-    char WHICH[3] = "LM";
+    char WHICH[3] = "LM"; 
     INTEGER NEV = numEigenvalues;
     double TOL = 0.0;
     double * RESID = (double*) malloc (sizeof(double) * N);
@@ -188,18 +198,18 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
     INTEGER LDV = N;
     INTEGER IPARAM[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
     IPARAM[0] = 1; // no user-provided shifts
-    IPARAM[2] = maxIter;
+    IPARAM[2] = maxIter; 
     IPARAM[3] = 1;
     //IPARAM[4] = 0;
     IPARAM[6] = 2; // the mode (regular generalized eigenproblem)
-    INTEGER IPNTR[11];
+    INTEGER IPNTR[11]; 
     double * WORKD = (double*) malloc (sizeof(double) * 3 * N);
-    INTEGER LWORKL = 2 * (NCV*NCV + 8*NCV); // at least NCV**2 + 8*NCV;
+    INTEGER LWORKL = 2 * (NCV*NCV + 8*NCV); // at least NCV**2 + 8*NCV; 
     double * WORKL = (double*) malloc (sizeof(double) * LWORKL);
     INTEGER INFO = 0;
 
     double * buffer = (double*) malloc (sizeof(double) * N);
-
+  
     do
     {
       //printf("Entering dsaupd...\n"); fflush(NULL);
@@ -212,7 +222,7 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
       }
 
       //printf("IDO=%d\n", (int)IDO); fflush(NULL);
-
+  
       switch(IDO)
       {
         case -1:
@@ -255,8 +265,8 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
     double * SIGMA = NULL;
     DSEUPD(&RVEC, &HOWMNY, SELECT, D, Z, &LDZ, SIGMA,
       &BMAT, &N, WHICH, &NEV, &TOL, RESID, &NCV, V, &LDV, IPARAM, IPNTR, WORKD, WORKL, &LWORKL, &INFO);
-
-    free(SELECT);
+  
+    free(SELECT); 
     free(RESID);
     free(V);
     free(WORKD);
@@ -315,8 +325,8 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
     // Printing eigenvalues.
 
     cout << "Eigenvalues:" << endl;
-    //for (int i=numEigenvalues-nconv; i<numEigenvalues; i++)
-    for (int i=0; i<nconv; i++)
+    //for (int i=numEigenvalues-nconv; i<numEigenvalues; i++) 
+    for (int i=0; i<nconv; i++) 
     {
       cout << "  lambda[" << (i+1) << "]: " << eigenvalues[i] << endl;
     }
@@ -335,7 +345,7 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
     if (infinityNormM > infNorm)
       infNorm = infinityNormM;
 
-    for (int i=0; i<nconv; i++)
+    for (int i=0; i<nconv; i++) 
     {
       KInput->MultiplyVector(&eigenvectors[np*i],Ax);
       MInput->MultiplyVector(&eigenvectors[np*i],Bx);
@@ -350,7 +360,7 @@ int ARPACKSolver::SolveGenEigReg(SparseMatrix * KInput, SparseMatrix * MInput, i
     }
 
 /*
-    for (int i=0; i<nconv; i++)
+    for (int i=0; i<nconv; i++) 
     {
       cout << "||A*x(" << (i+1) << ") - lambda(" << (i+1);
       cout << ")*B*x(" << (i+1) << ")|| / |lambda| / max(||A||,||B||) = " << ResNorm[i] << endl;
@@ -376,13 +386,13 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
     return -1;
 
   // solve Kx = lambda Mx with ARPACK, shift-invert mode (mode number 3)
-  // need multiplication with K^{-1} M and with M
+  // need multiplication with (K-sigma*M)^{-1}, (K-sigma*M)^{-1} M, and with M
 
   SparseMatrix * KsigmaM = K;
   if (sigma != 0)
   {
     // compute KsigmaM = K - sigma * M
-    KsigmaM = new SparseMatrix(*K);
+    KsigmaM = new SparseMatrix(*K); 
     KsigmaM->BuildSubMatrixIndices(*M);
     KsigmaM->AddSubMatrix(-sigma, *M);
   }
@@ -408,10 +418,10 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
   #ifdef PARDISO
     if (verbose >= 1)
       printf("Linear solver: PARDISO (%d threads).\n", (numLinearSolverThreads == 0) ? 1 : numLinearSolverThreads);
-    int positiveDefinite = 0;
     int directIterative = 0;
-    PardisoSolver * pardisoSolver = new PardisoSolver(KsigmaM, numLinearSolverThreads, positiveDefinite, directIterative, verbose);
-    pardisoSolver->ComputeCholeskyDecomposition(KsigmaM);
+    PardisoSolver * pardisoSolver = new PardisoSolver(KsigmaM, numLinearSolverThreads, PardisoSolver::REAL_SYM_INDEFINITE,
+      PardisoSolver::NESTED_DISSECTION, directIterative, verbose);
+    pardisoSolver->FactorMatrix(KsigmaM);
     invKSolver = pardisoSolver;
   #endif
 
@@ -430,7 +440,7 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
   INTEGER IDO = 0;
   char BMAT = 'G';
   INTEGER N = np;
-  char WHICH[3] = "LM";
+  char WHICH[3] = "LM"; 
   INTEGER NEV = numEigenvalues;
   double TOL = 0.0;
   double * RESID = (double*) malloc (sizeof(double) * N);
@@ -441,17 +451,17 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
   INTEGER LDV = N;
   INTEGER IPARAM[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
   IPARAM[0] = 1; // no user-provided shifts
-  IPARAM[2] = maxIter;
+  IPARAM[2] = maxIter; 
   IPARAM[3] = 1;
   IPARAM[6] = 3; // the mode (shift-inverted generalized eigenproblem)
-  INTEGER IPNTR[11];
+  INTEGER IPNTR[11]; 
   double * WORKD = (double*) malloc (sizeof(double) * 3 * N);
-  INTEGER LWORKL = 2 * (NCV*NCV + 8*NCV); // at least NCV**2 + 8*NCV;
+  INTEGER LWORKL = 2 * (NCV*NCV + 8*NCV); // at least NCV**2 + 8*NCV; 
   double * WORKL = (double*) malloc (sizeof(double) * LWORKL);
   INTEGER INFO = 0;
 
   double * buffer = (double*) malloc (sizeof(double) * N);
-
+  
   if (verbose >= 1)
   {
     cout << "Dimension of the system            : " << np  << endl;
@@ -471,8 +481,8 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
     }
 
     //printf("IDO=%d\n", (int)IDO); fflush(NULL);
-    //c           ===> OP = (inv[K - sigma*M])*M  and  B = M.
-
+    //c           ===> OP = (inv[K - sigma*M])*M  and  B = M. 
+  
     switch(IDO)
     {
       case -1:
@@ -530,8 +540,8 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
   double SIGMA = sigma;
   DSEUPD(&RVEC, &HOWMNY, SELECT, D, Z, &LDZ, &SIGMA,
     &BMAT, &N, WHICH, &NEV, &TOL, RESID, &NCV, V, &LDV, IPARAM, IPNTR, WORKD, WORKL, &LWORKL, &INFO);
-
-  free(SELECT);
+  
+  free(SELECT); 
   free(RESID);
   free(V);
   free(WORKD);
@@ -565,8 +575,8 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
     // Printing eigenvalues.
 
     cout << "Eigenvalues:" << endl;
-    //for (int i=numEigenvalues-nconv; i<numEigenvalues; i++)
-    for (int i=0; i<nconv; i++)
+    //for (int i=numEigenvalues-nconv; i<numEigenvalues; i++) 
+    for (int i=0; i<nconv; i++) 
     {
       cout << "  lambda[" << (i+1) << "]: " << eigenvalues[i] << endl;
     }
@@ -585,7 +595,7 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
     if (infinityNormM > infNorm)
       infNorm = infinityNormM;
 
-    for (int i=0; i<nconv; i++)
+    for (int i=0; i<nconv; i++) 
     {
       K->MultiplyVector(&eigenvectors[np*i],Ax);
       M->MultiplyVector(&eigenvectors[np*i],Bx);
@@ -600,7 +610,7 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
     }
 
 /*
-    for (int i=0; i<nconv; i++)
+    for (int i=0; i<nconv; i++) 
     {
       cout << "||A*x(" << (i+1) << ") - lambda(" << (i+1);
       cout << ")*B*x(" << (i+1) << ")|| / |lambda| / max(||A||,||B||) = " << ResNorm[i] << endl;
@@ -618,342 +628,385 @@ int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEi
   return nconv;
 }
 
-/*
-DSAUPD
-
-c-----------------------------------------------------------------------
-c\BeginDoc
-c
-c\Name: dsaupd
-c
-c\Description:
-c
-c  Reverse communication interface for the Implicitly Restarted Arnoldi
-c  Iteration.  For symmetric problems this reduces to a variant of the Lanczos
-c  method.  This method has been designed to compute approximations to a
-c  few eigenpairs of a linear operator OP that is real and symmetric
-c  with respect to a real positive semi-definite symmetric matrix B,
-c  i.e.
-c
-c       B*OP = (OP')*B.
-c
-c  Another way to express this condition is
-c
-c       < x,OPy > = < OPx,y >  where < z,w > = z'Bw  .
-c
-c  In the standard eigenproblem B is the identity matrix.
-c  ( A' denotes transpose of A)
-c
-c  The computed approximate eigenvalues are called Ritz values and
-c  the corresponding approximate eigenvectors are called Ritz vectors.
-c
-c  dsaupd is usually called iteratively to solve one of the
-c  following problems:
-c
-c  Mode 1:  A*x = lambda*x, A symmetric
-c           ===> OP = A  and  B = I.
-c
-c  Mode 2:  A*x = lambda*M*x, A symmetric, M symmetric positive definite
-c           ===> OP = inv[M]*A  and  B = M.
-c           ===> (If M can be factored see remark 3 below)
-c
-c  Mode 3:  K*x = lambda*M*x, K symmetric, M symmetric positive semi-definite
-c           ===> OP = (inv[K - sigma*M])*M  and  B = M.
-c           ===> Shift-and-Invert mode
-c
-c  Mode 4:  K*x = lambda*KG*x, K symmetric positive semi-definite,
-c           KG symmetric indefinite
-c           ===> OP = (inv[K - sigma*KG])*K  and  B = K.
-c           ===> Buckling mode
-c
-c  Mode 5:  A*x = lambda*M*x, A symmetric, M symmetric positive semi-definite
-c           ===> OP = inv[A - sigma*M]*[A + sigma*M]  and  B = M.
-c           ===> Cayley transformed mode
-c
-c  NOTE: The action of w <- inv[A - sigma*M]*v or w <- inv[M]*v
-c        should be accomplished either by a direct method
-c        using a sparse matrix factorization and solving
-c
-c           [A - sigma*M]*w = v  or M*w = v,
-c
-c        or through an iterative method for solving these
-c        systems.  If an iterative method is used, the
-c        convergence test must be more stringent than
-c        the accuracy requirements for the eigenvalue
-c        approximations.
-c
-c\Usage:
-c  call dsaupd
-c     ( IDO, BMAT, N, WHICH, NEV, TOL, RESID, NCV, V, LDV, IPARAM,
-c       IPNTR, WORKD, WORKL, LWORKL, INFO )
-c
-c\Arguments
-c  IDO     Integer.  (INPUT/OUTPUT)
-c          Reverse communication flag.  IDO must be zero on the first
-c          call to dsaupd.  IDO will be set internally to
-c          indicate the type of operation to be performed.  Control is
-c          then given back to the calling routine which has the
-c          responsibility to carry out the requested operation and call
-c          dsaupd with the result.  The operand is given in
-c          WORKD(IPNTR(1)), the result must be put in WORKD(IPNTR(2)).
-c          (If Mode = 2 see remark 5 below)
-c          -------------------------------------------------------------
-c          IDO =  0: first call to the reverse communication interface
-c          IDO = -1: compute  Y = OP * X  where
-c                    IPNTR(1) is the pointer into WORKD for X,
-c                    IPNTR(2) is the pointer into WORKD for Y.
-c                    This is for the initialization phase to force the
-c                    starting vector into the range of OP.
-c          IDO =  1: compute  Y = OP * Z  and Z = B * X where
-c                    IPNTR(1) is the pointer into WORKD for X,
-c                    IPNTR(2) is the pointer into WORKD for Y,
-c                    IPNTR(3) is the pointer into WORKD for Z.
-c          IDO =  2: compute  Y = B * X  where
-c                    IPNTR(1) is the pointer into WORKD for X,
-c                    IPNTR(2) is the pointer into WORKD for Y.
-c          IDO =  3: compute the IPARAM(8) shifts where
-c                    IPNTR(11) is the pointer into WORKL for
-c                    placing the shifts. See remark 6 below.
-c          IDO = 99: done
-c          -------------------------------------------------------------
-c          After the initialization phase, when the routine is used in
-c          either the "shift-and-invert" mode or the Cayley transform
-c          mode, the vector B * X is already available and does not
-c          need to be recomputed in forming OP*X.
-c
-c  BMAT    Character*1.  (INPUT)
-c          BMAT specifies the type of the matrix B that defines the
-c          semi-inner product for the operator OP.
-c          B = 'I' -> standard eigenvalue problem A*x = lambda*x
-c          B = 'G' -> generalized eigenvalue problem A*x = lambda*B*x
-c
-c  N       Integer.  (INPUT)
-c          Dimension of the eigenproblem.
-c
-c  WHICH   Character*2.  (INPUT)
-c          Specify which of the Ritz values of OP to compute.
-c
-c          'LA' - compute the NEV largest (algebraic) eigenvalues.
-c          'SA' - compute the NEV smallest (algebraic) eigenvalues.
-c          'LM' - compute the NEV largest (in magnitude) eigenvalues.
-c          'SM' - compute the NEV smallest (in magnitude) eigenvalues.
-c          'BE' - compute NEV eigenvalues, half from each end of the
-c                 spectrum.  When NEV is odd, compute one more from the
-c                 high end than from the low end.
-c           (see remark 1 below)
-c
-c  NEV     Integer.  (INPUT)
-c          Number of eigenvalues of OP to be computed. 0 < NEV < N.
-c
-c  TOL     Double precision scalar.  (INPUT)
-c          Stopping criterion: the relative accuracy of the Ritz value
-c          is considered acceptable if BOUNDS(I) .LE. TOL*ABS(RITZ(I)).
-c          If TOL .LE. 0. is passed a default is set:
-c          DEFAULT = DLAMCH('EPS')  (machine precision as computed
-c                    by the LAPACK auxiliary subroutine DLAMCH).
-c
-c  RESID   Double precision array of length N.  (INPUT/OUTPUT)
-c          On INPUT:
-c          If INFO .EQ. 0, a random initial residual vector is used.
-c          If INFO .NE. 0, RESID contains the initial residual vector,
-c                          possibly from a previous run.
-c          On OUTPUT:
-c          RESID contains the final residual vector.
-c
-c  NCV     Integer.  (INPUT)
-c          Number of columns of the matrix V (less than or equal to N).
-c          This will indicate how many Lanczos vectors are generated
-c          at each iteration.  After the startup phase in which NEV
-c          Lanczos vectors are generated, the algorithm generates
-c          NCV-NEV Lanczos vectors at each subsequent update iteration.
-c          Most of the cost in generating each Lanczos vector is in the
-c          matrix-vector product OP*x. (See remark 4 below).
-c
-c  V       Double precision N by NCV array.  (OUTPUT)
-c          The NCV columns of V contain the Lanczos basis vectors.
-c
-c  LDV     Integer.  (INPUT)
-c          Leading dimension of V exactly as declared in the calling
-c          program.
-c
-c  IPARAM  Integer array of length 11.  (INPUT/OUTPUT)
-c          IPARAM(1) = ISHIFT: method for selecting the implicit shifts.
-c          The shifts selected at each iteration are used to restart
-c          the Arnoldi iteration in an implicit fashion.
-c          -------------------------------------------------------------
-c          ISHIFT = 0: the shifts are provided by the user via
-c                      reverse communication.  The NCV eigenvalues of
-c                      the current tridiagonal matrix T are returned in
-c                      the part of WORKL array corresponding to RITZ.
-c                      See remark 6 below.
-c          ISHIFT = 1: exact shifts with respect to the reduced
-c                      tridiagonal matrix T.  This is equivalent to
-c                      restarting the iteration with a starting vector
-c                      that is a linear combination of Ritz vectors
-c                      associated with the "wanted" Ritz values.
-c          -------------------------------------------------------------
-c
-c          IPARAM(2) = LEVEC
-c          No longer referenced. See remark 2 below.
-c
-c          IPARAM(3) = MXITER
-c          On INPUT:  maximum number of Arnoldi update iterations allowed.
-c          On OUTPUT: actual number of Arnoldi update iterations taken.
-c
-c          IPARAM(4) = NB: blocksize to be used in the recurrence.
-c          The code currently works only for NB = 1.
-c
-c          IPARAM(5) = NCONV: number of "converged" Ritz values.
-c          This represents the number of Ritz values that satisfy
-c          the convergence criterion.
-c
-c          IPARAM(6) = IUPD
-c          No longer referenced. Implicit restarting is ALWAYS used.
-c
-c          IPARAM(7) = MODE
-c          On INPUT determines what type of eigenproblem is being solved.
-c          Must be 1,2,3,4,5; See under \Description of dsaupd for the
-c          five modes available.
-c
-c          IPARAM(8) = NP
-c          When ido = 3 and the user provides shifts through reverse
-c          communication (IPARAM(1)=0), dsaupd returns NP, the number
-c          of shifts the user is to provide. 0 < NP <=NCV-NEV. See Remark
-c          6 below.
-c
-c          IPARAM(9) = NUMOP, IPARAM(10) = NUMOPB, IPARAM(11) = NUMREO,
-c          OUTPUT: NUMOP  = total number of OP*x operations,
-c                  NUMOPB = total number of B*x operations if BMAT='G',
-c                  NUMREO = total number of steps of re-orthogonalization.
-c
-c  IPNTR   Integer array of length 11.  (OUTPUT)
-c          Pointer to mark the starting locations in the WORKD and WORKL
-c          arrays for matrices/vectors used by the Lanczos iteration.
-c          -------------------------------------------------------------
-c          IPNTR(1): pointer to the current operand vector X in WORKD.
-c          IPNTR(2): pointer to the current result vector Y in WORKD.
-c          IPNTR(3): pointer to the vector B * X in WORKD when used in
-c                    the shift-and-invert mode.
-c          IPNTR(4): pointer to the next available location in WORKL
-c                    that is untouched by the program.
-c          IPNTR(5): pointer to the NCV by 2 tridiagonal matrix T in WORKL.
-c          IPNTR(6): pointer to the NCV RITZ values array in WORKL.
-c          IPNTR(7): pointer to the Ritz estimates in array WORKL associated
-c                    with the Ritz values located in RITZ in WORKL.
-c          Note: IPNTR(8:10) is only referenced by dseupd. See Remark 2.
-c          IPNTR(8): pointer to the NCV RITZ values of the original system.
-c          IPNTR(9): pointer to the NCV corresponding error bounds.
-c          IPNTR(10): pointer to the NCV by NCV matrix of eigenvectors
-c                     of the tridiagonal matrix T. Only referenced by
-c                     dseupd if RVEC = .TRUE. See Remarks.
-c          Note: IPNTR(8:10) is only referenced by dseupd. See Remark 2.
-c          IPNTR(11): pointer to the NP shifts in WORKL. See Remark 6 below.
-c          -------------------------------------------------------------
-c
-c  WORKD   Double precision work array of length 3*N.  (REVERSE COMMUNICATION)
-c          Distributed array to be used in the basic Arnoldi iteration
-c          for reverse communication.  The user should not use WORKD
-c          as temporary workspace during the iteration. Upon termination
-c          WORKD(1:N) contains B*RESID(1:N). If the Ritz vectors are desired
-c          subroutine dseupd uses this output.
-c          See Data Distribution Note below.
-c
-c  WORKL   Double precision work array of length LWORKL.  (OUTPUT/WORKSPACE)
-c          Private (replicated) array on each PE or array allocated on
-c          the front end.  See Data Distribution Note below.
-c
-c  LWORKL  Integer.  (INPUT)
-c          LWORKL must be at least NCV**2 + 8*NCV .
-c
-c  INFO    Integer.  (INPUT/OUTPUT)
-c          If INFO .EQ. 0, a randomly initial residual vector is used.
-c          If INFO .NE. 0, RESID contains the initial residual vector,
-c                          possibly from a previous run.
-c          Error flag on output.
-c          =  0: Normal exit.
-c          =  1: Maximum number of iterations taken.
-c                All possible eigenvalues of OP has been found. IPARAM(5)
-c                returns the number of wanted converged Ritz values.
-c          =  2: No longer an informational error. Deprecated starting
-c                with release 2 of ARPACK.
-c          =  3: No shifts could be applied during a cycle of the
-c                Implicitly restarted Arnoldi iteration. One possibility
-c                is to increase the size of NCV relative to NEV.
-c                See remark 4 below.
-c          = -1: N must be positive.
-c          = -2: NEV must be positive.
-c          = -3: NCV must be greater than NEV and less than or equal to N.
-c          = -4: The maximum number of Arnoldi update iterations allowed
-c                must be greater than zero.
-c          = -5: WHICH must be one of 'LM', 'SM', 'LA', 'SA' or 'BE'.
-c          = -6: BMAT must be one of 'I' or 'G'.
-c          = -7: Length of private work array WORKL is not sufficient.
-c          = -8: Error return from trid. eigenvalue calculation;
-c                Informational error from LAPACK routine dsteqr.
-c          = -9: Starting vector is zero.
-c          = -10: IPARAM(7) must be 1,2,3,4,5.
-c          = -11: IPARAM(7) = 1 and BMAT = 'G' are incompatable.
-c          = -12: IPARAM(1) must be equal to 0 or 1.
-c          = -13: NEV and WHICH = 'BE' are incompatable.
-c          = -9999: Could not build an Arnoldi factorization.
-c                   IPARAM(5) returns the size of the current Arnoldi
-c                   factorization. The user is advised to check that
-c                   enough workspace and array storage has been allocated.
-c
-c
-c\Remarks
-c  1. The converged Ritz values are always returned in ascending
-c     algebraic order.  The computed Ritz values are approximate
-c     eigenvalues of OP.  The selection of WHICH should be made
-c     with this in mind when Mode = 3,4,5.  After convergence,
-c     approximate eigenvalues of the original problem may be obtained
-c     with the ARPACK subroutine dseupd.
-c
-c  2. If the Ritz vectors corresponding to the converged Ritz values
-c     are needed, the user must call dseupd immediately following completion
-c     of dsaupd. This is new starting with version 2.1 of ARPACK.
-c
-c  3. If M can be factored into a Cholesky factorization M = LL'
-c     then Mode = 2 should not be selected.  Instead one should use
-c     Mode = 1 with  OP = inv(L)*A*inv(L').  Appropriate triangular
-c     linear systems should be solved with L and L' rather
-c     than computing inverses.  After convergence, an approximate
-c     eigenvector z of the original problem is recovered by solving
-c     L'z = x  where x is a Ritz vector of OP.
-c
-c  4. At present there is no a-priori analysis to guide the selection
-c     of NCV relative to NEV.  The only formal requirement is that NCV > NEV.
-c     However, it is recommended that NCV .ge. 2*NEV.  If many problems of
-c     the same type are to be solved, one should experiment with increasing
-c     NCV while keeping NEV fixed for a given test problem.  This will
-c     usually decrease the required number of OP*x operations but it
-c     also increases the work and storage required to maintain the orthogonal
-c     basis vectors.   The optimal "cross-over" with respect to CPU time
-c     is problem dependent and must be determined empirically.
-c
-c  5. If IPARAM(7) = 2 then in the Reverse communication interface the user
-c     must do the following. When IDO = 1, Y = OP * X is to be computed.
-c     When IPARAM(7) = 2 OP = inv(B)*A. After computing A*X the user
-c     must overwrite X with A*X. Y is then the solution to the linear set
-c     of equations B*Y = A*X.
-c
-c  6. When IPARAM(1) = 0, and IDO = 3, the user needs to provide the
-c     NP = IPARAM(8) shifts in locations:
-c     1   WORKL(IPNTR(11))
-c     2   WORKL(IPNTR(11)+1)
-c                        .
-c                        .
-c                        .
-c     NP  WORKL(IPNTR(11)+NP-1).
-c
-c     The eigenvalues of the current tridiagonal matrix are located in
-c     WORKL(IPNTR(6)) through WORKL(IPNTR(6)+NCV-1). They are in the
-c     order defined by WHICH. The associated Ritz estimates are located in
-c     WORKL(IPNTR(8)), WORKL(IPNTR(8)+1), ... , WORKL(IPNTR(8)+NCV-1).
-c
-c-----------------------------------------------------------------------------
-
-
-Chao Yang
-11/7/1997
-*/
+int ARPACKSolver::SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, SparseMatrix * C, int numEigenvalues, double * eigenvalues, double * eigenvectors, double sigma, double eps, int numLinearSolverThreads, int verbose)
+{
+  int np = K->Getn();
+  if (np != M->Getn())
+    return -1;
+
+  // convert to dense matrix
+  int numCons = C->Getn();
+  double * dataC = (double*) malloc (sizeof(double) * np * numCons);
+  C->MakeDenseMatrix(dataC, np);
+
+  // do SVD on C
+  Matrix<double> CM(numCons, np, dataC, false, false);
+  Matrix<double> UM(numCons, numCons);
+  Matrix<double> Sigma(numCons, 1);
+  double * VT = (double*) malloc (sizeof(double) * np  * numCons);
+  Matrix<double> VTM(numCons, np, VT, false, false); // same shape as CM
+
+  CM.SVD(UM, Sigma, VTM);
+
+  // regularize C
+  double * sigmaValues = Sigma.GetData();
+  int c = 0;
+  for(c = 0; c < numCons; c++) // find the rank of matrix C (=c)
+  {
+    if (sigmaValues[c] < eps)
+      break;
+  }
+
+  // VT, after only keeping rows 1..c
+  double * newC = (double*) malloc (sizeof(double) * np * c);
+  int counter = 0;
+  for(int i = 0; i < np; i++)
+  {
+    double * pointer = &VT[numCons * i];
+    for(int j = 0; j < c; j++)
+    {
+      newC[counter] = pointer[j];
+      counter++;
+    }
+  }
+
+  free(dataC);
+  free(VT);
+
+  // from now on, we just use newC
+  SparseMatrixOutline * POutline = new SparseMatrixOutline(np); // for the permutation matrix
+  double * bufferc = (double*) malloc(sizeof(double) * c);
+  // indices[i] tells the position of column i after the permutation
+  int * indices = (int*) calloc(np, sizeof(int));
+  for(int i = 0; i < np; i++)
+    indices[i] = i;
+
+  // Gaussian elimination
+  for(int i = 0; i < c; i++) // over all rows of newC
+  {
+    // find column with maximum absolute value in the current row
+    double maxValue = 0.0;
+    int column = i; // columns 1..i-1 are already zero, because of Gaussian elimination
+    for(int j = i; j < np; j++)
+    {
+      if (fabs(newC[ELT(c, i, j)]) > maxValue)
+      {
+	maxValue = fabs(newC[ELT(c, i, j)]);
+	column = j;
+      }
+    }
+    // now, "column" contains the column with max absolute value
+
+    // swap indices[column] and indices[i]
+    int tmp = indices[column];
+    indices[column] = indices[i];
+    indices[i] = tmp;
+
+    // swap columns i and "column"
+    memcpy(bufferc, &newC[ELT(c, 0, column)], sizeof(double) * c);
+    memcpy(&newC[ELT(c, 0, column)], &newC[ELT(c, 0, i)],  sizeof(double) * c);
+    memcpy(&newC[ELT(c, 0, i)], bufferc,  sizeof(double) * c);
+
+    double value = newC[ELT(c, i, i)];
+    for(int j = i+1; j < c; j++)
+    {
+      double scale = newC[ELT(c, j, i)] / value;
+      for(int l = i; l < np; l++)
+	newC[ELT(c, j, l)] -= scale * newC[ELT(c, i, l)];
+    }
+  }
+
+  for(int i = 0; i < np; i++)
+    POutline->AddEntry(indices[i], i, 1.0);
+
+  SparseMatrix * P = new SparseMatrix(POutline);
+  delete(POutline);
+
+  free(indices);
+  free(bufferc);
+
+  // form Cp and Cn
+  double * Cp = &newC[0];
+  double * Cn = &newC[c*c];
+
+  // solve Kx = lambda Mx with ARPACK, shift-invert mode (mode number 3)
+  // need multiplication with (K-sigma*M)^{-1}, (K-sigma*M)^{-1} M, and with M
+  SparseMatrix * KsigmaM = K;
+  if (sigma != 0)
+  {
+    // compute KsigmaM = K - sigma * M
+    KsigmaM = new SparseMatrix(*K);
+    KsigmaM->BuildSubMatrixIndices(*M);
+    KsigmaM->AddSubMatrix(-sigma, *M);
+  }
+
+  int npc = np - c;
+  Matrix<double> CnM (c, npc, Cn);
+  Matrix<double> CpM (c, c, Cp);
+  Matrix<double> invCpM = Inverse(CpM);
+
+  // form the nullspace Z
+  double * ZUpper = (double*) malloc (sizeof(double) * npc * c);
+  Matrix<double> ZupperM(c, npc, ZUpper, false, false);
+  ZupperM = invCpM * CnM;
+  ZupperM *= -1;
+
+  SparseMatrix * PKP = new SparseMatrix(KsigmaM->ConjugateMatrix(*P, 0, M->Getn()));
+
+  //thresholded Z, used as the preconditioner
+  SparseMatrixOutline * spZOutline = new SparseMatrixOutline(np);
+  for(int i = 0; i < c; i++)
+  {
+    for(int j = 0; j < np-c; j++)
+    {
+      if (ZUpper[ELT(c, i, j)] > 1E-10)
+	spZOutline->AddEntry(i, j, ZUpper[ELT(c, i, j)]);
+    }
+  }
+
+  for(int i = c ; i < np; i++)
+    spZOutline->AddEntry(i, i-c, 1.0);
+
+  SparseMatrix * spZ = new SparseMatrix(spZOutline);
+  delete(spZOutline);
+
+  SparseMatrix * ZKZ = new SparseMatrix(PKP->ConjugateMatrix(*spZ, 0, npc));
+
+  free(ZUpper);
+  delete(PKP);
+  delete(spZ);
+
+  int directIterative = 0;
+  PardisoSolver * pardisoSolver = new PardisoSolver(ZKZ, numLinearSolverThreads, PardisoSolver::REAL_SYM_INDEFINITE,
+    PardisoSolver::NESTED_DISSECTION, directIterative, verbose);
+  pardisoSolver->FactorMatrix(ZKZ);
+
+  // create M* multiplicator
+  ZTAZMultiplicator Mmult(M, P, &invCpM, &CnM);
+
+  // create (M)^{-1} solver
+  InvZTAZSolver invZTAZSolver(KsigmaM, pardisoSolver, P, &invCpM, &CnM);
+
+  // create (M)^{-1}*K solver
+  InvZTAZMSolver invZTAZMSolver(KsigmaM, M, pardisoSolver, P, &invCpM, &CnM);
+  const int maxIter = 10000;
+
+  // call ARPACK
+  INTEGER IDO = 0;
+  char BMAT = 'G';
+  INTEGER N = npc;
+  char WHICH[3] = "LM";
+  INTEGER NEV = numEigenvalues;
+  double TOL = 0.0;
+  double * RESID = (double*) malloc (sizeof(double) * N);
+  INTEGER NCV = 3 * NEV;
+  if (NCV > N)
+    NCV = N;
+  double * V = (double*) malloc (sizeof(double) * N * NCV);
+  INTEGER LDV = N;
+  INTEGER IPARAM[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  IPARAM[0] = 1; // no user-provided shifts
+  IPARAM[2] = maxIter;
+  IPARAM[3] = 1;
+  IPARAM[6] = 3; // the mode (shift-inverted generalized eigenproblem)
+  INTEGER IPNTR[11];
+  double * WORKD = (double*) malloc (sizeof(double) * 3 * N);
+  INTEGER LWORKL = 2 * (NCV*NCV + 8*NCV); // at least NCV**2 + 8*NCV; 
+  double * WORKL = (double*) malloc (sizeof(double) * LWORKL);
+  INTEGER INFO = 0;
+
+  double * buffer = (double*) malloc (sizeof(double) * N);
+
+  if (verbose >= 1)
+  {
+    cout << "Dimension of the system            : " << npc  << endl;
+    cout << "Number of 'requested' eigenvalues  : " << NEV << endl;
+    cout << "Entering the ARPACK eigenvalue routine..." << endl;
+    fflush(NULL);
+  }
+
+  do
+  {
+    DSAUPD(&IDO, &BMAT, &N, WHICH, &NEV, &TOL, RESID, &NCV, V, &LDV, IPARAM, IPNTR, WORKD, WORKL, &LWORKL, &INFO);
+    if (INFO != 0)
+    {
+      printf("Error: DSAUPD returned non-zero exit code %d .\n", (int)INFO);
+      delete(pardisoSolver);
+      delete(P);
+      free(newC);
+      delete(ZKZ);
+      return -1;
+    }
+
+    //printf("IDO=%d\n", (int)IDO); fflush(NULL);
+    //c           ===> OP = (inv[K - sigma*M])*M  and  B = M. 
+
+    switch(IDO)
+    {
+      case -1:
+	//c          IDO = -1: compute  Y = OP * X  where
+	//c                    IPNTR(1) is the pointer into WORKD for X,
+	//c                    IPNTR(2) is the pointer into WORKD for Y.
+	//c                    This is for the initialization phase to force the
+	//c                    starting vector into the range of OP.
+	//printf("IDO = -1\n");
+	invZTAZMSolver.ComputeInvZTAZM(&WORKD[IPNTR[0]-1], &WORKD[IPNTR[1]-1]);
+	break;
+
+      case 1:
+	//c          IDO =  1: compute  Y = (K - sigma*M)^-1 * Z
+	//c                    IPNTR(2) is the pointer into WORKD for Y,
+	//c                    IPNTR(3) is the pointer into WORKD for Z.
+	//c           (see dsdrv4.f example)
+	//printf("IDO = 1\n");
+	invZTAZSolver.ComputeInvZTAZ(&WORKD[IPNTR[1]-1], &WORKD[IPNTR[2]-1]);
+	break;
+
+      case 2:
+	//c          IDO =  2: compute  Y = B * X  where
+	//c                    IPNTR(1) is the pointer into WORKD for X,
+	//c                    IPNTR(2) is the pointer into WORKD for Y.
+	//printf("IDO = 2\n");
+	Mmult.Multiply(&WORKD[IPNTR[0]-1], &WORKD[IPNTR[1]-1]);
+	break;
+
+      case 3:
+	printf("Error: case IDO=3 should have never happened.\n");
+        delete(pardisoSolver);
+        delete(P);
+        free(newC);
+        delete(ZKZ);
+	return -1;
+	break;
+
+      case 99:
+	break;
+
+      default:
+	printf("Error: unknown case.\n");
+        delete(pardisoSolver);
+        delete(P);
+        free(newC);
+        delete(ZKZ);
+	return -1;
+	break;
+    };
+  }
+  while (IDO != 99);
+
+  // obtain the eigenvalues and eigenvectors
+  INTEGER RVEC = 1;
+  char HOWMNY = 'A'; // all eigenvectors
+  INTEGER * SELECT = (INTEGER*) malloc (sizeof(INTEGER) * NCV);
+  double * D = eigenvalues;
+  double * Z = eigenvectors;
+  INTEGER LDZ = N;
+  double SIGMA = sigma;
+  DSEUPD(&RVEC, &HOWMNY, SELECT, D, Z, &LDZ, &SIGMA,
+      &BMAT, &N, WHICH, &NEV, &TOL, RESID, &NCV, V, &LDV, IPARAM, IPNTR, WORKD, WORKL, &LWORKL, &INFO);
+
+  free(SELECT);
+  free(RESID);
+  free(V);
+  free(WORKD);
+  free(WORKL);
+  free(buffer);
+
+  if (sigma != 0)
+    delete(KsigmaM);
+
+  int nconv = IPARAM[4];
+
+  /*
+     Multiply the constrained eigenvectors with Z: 
+     Z = P [ -C_p^{-1} C_n  ]
+           [  I             ]
+   */
+
+  double * buffer2 = (double*) malloc(sizeof(double) * np * nconv);
+  for(int i = 0; i < nconv; i++)
+  {
+    Matrix<double> eM(npc, 1, &eigenvectors[npc*i], false, false);
+    Matrix<double> bcM(c, 1, &buffer2[np*i], false, false);
+    bcM = invCpM * (CnM * eM);
+    bcM *= -1.0;
+    for(int j = 0; j < npc; j++)
+      buffer2[np*i+c+j] = eigenvectors[npc*i+j];
+  }
+
+  for(int i = 0; i < nconv; i++)
+    P->MultiplyVector(&buffer2[np*i], &eigenvectors[np*i]);
+
+  free(buffer2);
+
+  // normalize results
+  for(int i=0; i<nconv; i++)
+    M->NormalizeVector(&eigenvectors[np*i]);
+  //solver.FindEigenvectors();
+  //nconv = solver.ConvergedEigenvalues();
+
+  if (verbose >= 1)
+  {
+    cout << "ARPACK solver is done." << endl;
+    //cout << "Dimension of the system            : " << np              << endl;
+    //cout << "Number of 'requested' eigenvalues  : " << NEV << endl;
+    cout << "Number of 'converged' eigenvalues  : " << nconv          << endl;
+    cout << "Number of Arnoldi vectors generated: " << NCV << endl;
+    cout << "Number of iterations taken         : " << IPARAM[2] << endl;
+    cout << endl;
+
+    // Printing eigenvalues.
+
+    cout << "Eigenvalues:" << endl;
+    for (int i=0; i<nconv; i++)
+      cout << "  lambda[" << (i+1) << "]: " << eigenvalues[i] << endl;
+    cout << endl;
+
+    // Printing the residual norm || A*x - lambda*B*x ||
+    // for the nconv accurately computed eigenvectors.
+
+    double * Ax      = new double[np];
+    double * Bx      = new double[np];
+    double * ResNorm = new double[nconv];
+
+    double infinityNormK = K->GetInfinityNorm();
+    double infinityNormM = M->GetInfinityNorm();
+    double infNorm = infinityNormK;
+    if (infinityNormM > infNorm)
+      infNorm = infinityNormM;
+
+    for (int i=0; i<nconv; i++)
+    {
+      K->MultiplyVector(&eigenvectors[np*i],Ax);
+      M->MultiplyVector(&eigenvectors[np*i],Bx);
+      for(int j=0; j<np; j++)
+	Ax[j] = Ax[j] - eigenvalues[i] * Bx[j];
+      //cblas_daxpy(np, -eigenvalues[i], Bx, 1, Ax, 1);
+      ResNorm[i] = 0;
+      for(int j=0; j<np; j++)
+	ResNorm[i] += Ax[j] * Ax[j];
+      ResNorm[i] = sqrt(ResNorm[i]) / fabs(eigenvalues[i]) / infNorm;
+      //ResNorm[i] = cblas_dnrm2(np, Ax, 1) / fabs(eigenvalues[i]);
+    }
+    /*
+       for (int i=0; i<nconv; i++) 
+       {
+       cout << "||A*x(" << (i+1) << ") - lambda(" << (i+1);
+       cout << ")*B*x(" << (i+1) << ")|| / |lambda| / max(||A||,||B||) = " << ResNorm[i] << endl;
+       }
+       cout << endl;
+     */
+
+    //printf("Cleaning up the ARPACK workspace.\n");fflush(NULL);
+    delete[] Ax;
+    delete[] Bx;
+    delete[] ResNorm;
+  }
+
+  delete(pardisoSolver);
+  delete(P);
+  free(newC);
+  delete(ZKZ);
+  return nconv;
+}
+
diff --git a/libraries/sparseSolver/ARPACKSolver.h b/libraries/sparseSolver/ARPACKSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..41f3cfdb41a9b2f82fe63614a5fdd5d2638d6190
--- /dev/null
+++ b/libraries/sparseSolver/ARPACKSolver.h
@@ -0,0 +1,89 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Computes the first largest or smallest eigenvalues and eigenvectors of
+  a large sparse matrix, using the ARPACK library:
+  http://www.caam.rice.edu/software/ARPACK/
+
+  There is also a routine to compute constrained modes, as published in:
+
+  Hongyi Xu and Jernej Barbic: Pose-Space Subspace Dynamics, 
+  ACM Transactions on Graphics 35(4) (SIGGRAPH 2016), Anaheim, CA, USA
+*/
+
+#ifndef _ARPACKSOLVER_H_
+#define _ARPACKSOLVER_H_
+
+#include "sparseMatrix.h"
+
+class ARPACKSolver
+{
+public:
+ 
+  // Perform eigensolve:
+  // K * x = lambda * M * x
+  // Returns the number of converged eigenvalues.
+  // Assumes that both K and M are symmetric, and that M > 0.
+  // Both matrices are given using the entire matrix (not just the lower/upper triangle).
+  // Mode is either "LM" or "SM" (with SM, must also have K > 0).
+  // Uses mode 2 of ARPACK (regular generalized eigenvalue problem).
+  int SolveGenEigReg(SparseMatrix * K, SparseMatrix * M, int numEigenvalues, double * eigenvalues, double * eigenvectors, const char * mode = "LM", int numLinearSolverThreads=0, int verbose=1);
+
+  // Perform eigensolve:
+  // K * x = lambda * M * x.
+  // Solves for the smallest (in absolute sense) eigenvalues.
+  // Returns the number of converged eigenvalues.
+  // Assumes that both K and M are symmetric, and that M >= 0.
+  // K can be singular.
+  // Uses mode 3 of ARPACK (shift-inverted generalized eigenvalue problem).
+  // "sigma" is the amount of shift. This is needed when K is singular.
+  int SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEigenvalues, double * eigenvalues, double * eigenvectors, double sigma=0.0, int numLinearSolverThreads=0, int verbose=1);
+
+  // Perform constrained eigensolve:
+  // K * x = lambda * M * x, subject to C x = 0.
+  // Solves for the smallest (in absolute sense) eigenvalues,
+  // subject to the constraint C x = 0.
+  // Returns the number of converged eigenvalues.
+  // Assumes that both K and M are symmetric, and that M >= 0.
+  // K can be singular.
+  // Uses mode 3 of ARPACK (shift-inverted generalized eigenvalue problem).
+  // "sigma" is the amount of shift. This is needed when K is singular.
+  // Matrix C can be singular; "eps" is a threshold for regularizing the matrix C.
+  int SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, SparseMatrix * C, int numEigenvalues, double * eigenvalues, double * eigenvectors, double sigma=0.0, double eps=1e-6, int numLinearSolverThreads=0, int verbose=1);
+
+protected:
+};
+
+#endif
+
diff --git a/src/libsparseSolver/CGSolver.cpp b/libraries/sparseSolver/CGSolver.cpp
similarity index 68%
rename from src/libsparseSolver/CGSolver.cpp
rename to libraries/sparseSolver/CGSolver.cpp
index fff8078daa9f2a29a899b104c566282c36299f30..d5543659c790b4229f5c191bed9ec96fa1315d09 100644
--- a/src/libsparseSolver/CGSolver.cpp
+++ b/libraries/sparseSolver/CGSolver.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,25 +35,16 @@
 #include <stdio.h>
 #include "CGSolver.h"
 
-namespace vega
-{
-CGSolver::CGSolver(SparseMatrix * A_): A(A_), numIterations(10000), epsilon(1e-6)
+CGSolver::CGSolver(SparseMatrix * A_): A(A_) 
 {
   numRows = A->GetNumRows();
   InitBuffers();
   multiplicator = CGSolver::DefaultMultiplicator;
   multiplicatorData = (void*)A;
   invDiagonal = NULL;
-  this->solverType = "PCG";
 }
 
-CGSolver::CGSolver(int numRows_, blackBoxProductType callBackFunction_, void * data_, double * diagonal):
-  numRows(numRows_),
-  numIterations(10000),
-  epsilon(1e-6),
-  multiplicator(callBackFunction_),
-  multiplicatorData(data_),
-  A(NULL)
+CGSolver::CGSolver(int numRows_, blackBoxProductType callBackFunction_, void * data_, double * diagonal): numRows(numRows_), multiplicator(callBackFunction_), multiplicatorData(data_), A(NULL)
 {
   InitBuffers();
   invDiagonal = (double*) malloc (sizeof(double) * numRows);
@@ -63,7 +58,6 @@ CGSolver::CGSolver(int numRows_, blackBoxProductType callBackFunction_, void * d
     for(int i=0; i<numRows; i++)
       invDiagonal[i] = 1.0 / diagonal[i];
   }
-  this->solverType = "PCG";
 }
 
 CGSolver::~CGSolver()
@@ -88,12 +82,12 @@ void CGSolver::InitBuffers()
 }
 
 // implements the virtual method from LinearSolver by calling "SolveLinearSystem" with default parameters
-int CGSolver::SolveLinearSystem(double * x, const double * b)
+int CGSolver::SolveLinearSystem(double * x, const double * b) 
 {
-  return SolveLinearSystemWithJacobiPreconditioner(x, b, 0);
+  return SolveLinearSystemWithJacobiPreconditioner(x, b, 1E-6, 1000, 0);
 }
 
-int CGSolver::SolveLinearSystemWithoutPreconditioner(double * x, const double * b, int verbose)
+int CGSolver::SolveLinearSystemWithoutPreconditioner(double * x, const double * b, double eps, int maxIterations, int verbose)
 {
   int iteration=1;
   multiplicator(multiplicatorData, x, r); //A->MultiplyVector(x,r);
@@ -105,8 +99,8 @@ int CGSolver::SolveLinearSystemWithoutPreconditioner(double * x, const double *
 
   double residualNorm2 = ComputeDotProduct(r, r);
   double initialResidualNorm2 = residualNorm2;
-  double eps2 = this->epsilon*this->epsilon;
-  while ((residualNorm2 > eps2 * initialResidualNorm2) && (iteration <= this->numIterations))
+
+  while ((residualNorm2 > eps * eps * initialResidualNorm2) && (iteration <= maxIterations))
   {
     if (verbose)
       printf("CG iteration %d: current L2 error vs initial error=%G\n", iteration, sqrt(residualNorm2 / initialResidualNorm2));
@@ -142,10 +136,68 @@ int CGSolver::SolveLinearSystemWithoutPreconditioner(double * x, const double *
     iteration++;
   }
 
-  return (iteration-1) * ((residualNorm2 > eps2 * initialResidualNorm2) ? 1 : 0);
+  return (iteration-1) * ((residualNorm2 > eps * eps * initialResidualNorm2) ? -1 : 1);
 }
 
-int CGSolver::SolveLinearSystemWithJacobiPreconditioner(double * x, const double * b, int verbose)
+int CGSolver::SolveLinearSystemWithPreconditioner(LinearSolver * preconditioner, double * x, const double * b, double eps, int maxIterations, int verbose)
+{
+  int iteration=1;
+  multiplicator(multiplicatorData, x, r); //A->MultiplyVector(x,r);
+  for (int i=0; i<numRows; i++)
+    r[i] = b[i] - r[i];
+  preconditioner->SolveLinearSystem(d, r); // d = M^{-1} * r
+
+  // residualNorm2 = < M^{-1} * r, r >
+  double residualNorm2 = ComputeDotProduct(d, r);
+  double initialResidualNorm2 = residualNorm2;
+
+  while ((residualNorm2 > eps * eps * initialResidualNorm2) && (iteration <= maxIterations))
+  {
+    if (verbose)
+      printf("CG iteration %d: current M^{-1}-L2 error vs initial error=%G\n", iteration, sqrt(residualNorm2 / initialResidualNorm2));
+
+    multiplicator(multiplicatorData, d, q); //A->MultiplyVector(d,q); // q = A * d
+    double dDotq = ComputeDotProduct(d, q);
+    double alpha = residualNorm2 / dDotq;
+
+    for(int i=0; i<numRows; i++)
+      x[i] += alpha * d[i];
+
+    if (iteration % 30 == 0)
+    {
+      // periodically compute the exact residual (Shewchuk, page 8)
+      multiplicator(multiplicatorData, x, r); //A->MultiplyVector(x,r);
+      for (int i=0; i<numRows; i++)
+	r[i] = b[i] - r[i];
+    }
+    else
+    {
+      for (int i=0; i<numRows; i++)
+	r[i] = r[i] - alpha * q[i];
+    }
+
+    double oldResidualNorm2 = residualNorm2;
+
+    preconditioner->SolveLinearSystem(q, r); // q = M^{-1} * r
+    // residualNorm2 = < M^{-1} * r, r >
+    residualNorm2 = ComputeDotProduct(q, r);
+    double beta = residualNorm2 / oldResidualNorm2;
+
+    for (int i=0; i<numRows; i++)
+      d[i] = q[i] + beta * d[i];
+
+    iteration++;
+  }
+
+  if (residualNorm2 < 0)
+  {
+    printf("Warning: residualNorm2=%G is negative. Input matrix might not be SPD. Solution could be incorrect.\n", residualNorm2);
+  }
+
+  return (iteration-1) * ((residualNorm2 > eps * eps * initialResidualNorm2) ? -1 : 1);
+}
+
+int CGSolver::SolveLinearSystemWithJacobiPreconditioner(double * x, const double * b, double eps, int maxIterations, int verbose)
 {
   if (invDiagonal == NULL)
   {
@@ -172,8 +224,7 @@ int CGSolver::SolveLinearSystemWithJacobiPreconditioner(double * x, const double
   double residualNorm2 = ComputeTriDotProduct(r, r, invDiagonal);
   double initialResidualNorm2 = residualNorm2;
 
-  double eps2 = this->epsilon*this->epsilon;
-  while ((residualNorm2 > eps2 * initialResidualNorm2) && (iteration <= this->numIterations))
+  while ((residualNorm2 > eps * eps * initialResidualNorm2) && (iteration <= maxIterations))
   {
     if (verbose)
       printf("CG iteration %d: current M^{-1}-L2 error vs initial error=%G\n", iteration, sqrt(residualNorm2 / initialResidualNorm2));
@@ -213,7 +264,7 @@ int CGSolver::SolveLinearSystemWithJacobiPreconditioner(double * x, const double
     printf("Warning: residualNorm2=%G is negative. Input matrix might not be SPD. Solution could be incorrect.\n", residualNorm2);
   }
 
-  return (iteration-1) * ((residualNorm2 > eps2 * initialResidualNorm2) ? 1 : 0);
+  return (iteration-1) * ((residualNorm2 > eps * eps * initialResidualNorm2) ? -1 : 1);
 }
 
 double CGSolver::ComputeDotProduct(double * v1, double * v2)
@@ -234,24 +285,3 @@ double CGSolver::ComputeTriDotProduct(double * x, double * y, double * z)
   return result;
 }
 
-void CGSolver::setNumberOfIterations ( const size_t iterations )
-{
-  this->numIterations = iterations;
-}
-
-void CGSolver::setEpsilon ( const double eps )
-{
-  this->epsilon = eps;
-}
-
-size_t CGSolver::getNumberOfIterations() const
-{
-  return this->numIterations;
-}
-
-double CGSolver::getEpsilon() const
-{
-  return this->epsilon;
-}
-
-}
diff --git a/libraries/sparseSolver/CGSolver.h b/libraries/sparseSolver/CGSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..94a84e29cbb1919dfe568839ba4b8d488937da0e
--- /dev/null
+++ b/libraries/sparseSolver/CGSolver.h
@@ -0,0 +1,116 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A conjugate gradient solver built on top of the sparse matrix class.
+  There are two solver versions: without preconditioning, and with 
+  Jacobi preconditioning.
+
+  You can either provide a sparse matrix, or a callback function to
+  multiply x |--> A * x .
+
+  The sparse matrix must be symmetric and positive-definite.
+
+  The CG solvers were implemented by following Jonathan Shewchuk's 
+  An Introduction to the Conjugate Gradient Method Without the Agonizing Pain:
+  http://www.cs.cmu.edu/~jrs/jrspapers.html#cg
+
+  See also sparseMatrix.h .
+*/
+
+#ifndef _CGSOLVER_H_
+#define _CGSOLVER_H_
+
+#include "linearSolver.h"
+#include "sparseMatrix.h"
+
+class CGSolver : public LinearSolver
+{
+public:
+
+  // Standard constructor. Provide the matrix A to be used for the solve, A x = b.
+  // Matrix A will not be modified. 
+  // Minor note: the code will generate an internal acceleration structure, by calling A->BuildDiagonalIndices() when using the Jacobi preconditioner. Technically speaking, this modifies the SparseMatrix object since it builds the acceleration structure. It does not modify
+  // the matrix A or any of its entries.
+  CGSolver(SparseMatrix * A);
+
+  // This constructor makes it possible to only provide a "black-box" matrix-vector multiplication routine 
+  // (no need to explicitly give the matrix).
+  // Given x, the routine must compute A * x, and store it into Ax.
+  // "data" should not be used/touched by the user-provided "black-box" routine.
+  // One can then use "SolveLinearSystemWithoutPreconditioner" to solve the linear system.
+  // In order to use "SolveLinearSystemWithJacobiPreconditioner", one needs to specify the diagonal of the matrix.
+  // If the diagonal is not specified, SolveLinearSystemWithJacobiPreconditioner will use the identity preconditioner, 
+  // i.e., the solve will be identical to SolveLinearSystemWithoutPreconditioner.
+  typedef void (*blackBoxProductType)(const void * data, const double * x, double * Ax);
+  CGSolver(int n, blackBoxProductType callBackFunction, void * data, double * diagonal=NULL);
+
+  ~CGSolver();
+
+  // solves A * x = b (without preconditioner)
+  // A must be symmetric positive-definite
+  // input: initial guess (in x)
+  // output: solution (in x)
+  // "eps" is the convergence criterium: solver converges when the L2 residual errors is less than eps times the initial L2 residual error, must have 0 < eps < 1
+  // maximum number of conjugate-gradient iterations is set by "maxIterations"
+  // return value is the number of iterations performed 
+  // if solver did not converge, the return value will have a negative sign
+  int SolveLinearSystemWithoutPreconditioner(double * x, const double * b, double eps=1e-6, int maxIterations=1000, int verbose=0);
+
+  // same as above, except it uses Jacobi preconditioning
+  // the employed error metric is M^{-1}-weighted L2 residual error (see Shewchuk)
+  int SolveLinearSystemWithJacobiPreconditioner(double * x, const double * b, double eps=1e-6, int maxIterations=1000, int verbose=0);
+
+  // Solve the linear system with a user-provided preconditioner.
+  // Solving the linear system "preconditioner * x = b" should approximate solving the linear system "(this matrix) * x = b".
+  int SolveLinearSystemWithPreconditioner(LinearSolver * preconditioner, double * x, const double * b, double eps=1e-6, int maxIterations=1000, int verbose=0);
+
+  virtual int SolveLinearSystem(double * x, const double * b); // implements the virtual method from LinearSolver by calling "SolveLinearSystemWithJacobiPreconditioner" with default parameters
+
+  // computes the dot product of two vectors
+  double ComputeDotProduct(double * v1, double * v2); // length of vectors v1, v2 equals numRows (dimension of A)
+
+protected:
+  int numRows;
+  blackBoxProductType multiplicator;
+  void * multiplicatorData;
+  SparseMatrix * A; 
+  double * r, * d, * q; // terminology from Shewchuk's work
+  double * invDiagonal;
+
+  double ComputeTriDotProduct(double * x, double * y, double * z); // sum_i x[i] * y[i] * z[i]
+  static void DefaultMultiplicator(const void * data, const double * x, double * Ax);
+  void InitBuffers();
+};
+
+#endif
+
diff --git a/libraries/sparseSolver/LagrangeMultiplierSolver.cpp b/libraries/sparseSolver/LagrangeMultiplierSolver.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6b7872ec2a8eb8006b6d64efc165ec36d444e29e
--- /dev/null
+++ b/libraries/sparseSolver/LagrangeMultiplierSolver.cpp
@@ -0,0 +1,298 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li, Hongyi Xu                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "LagrangeMultiplierSolver.h"
+#include "constrainedDOFs.h"
+#include "sparseMatrix.h"
+#include "performanceCounter.h"
+#include <iostream>
+#include <cassert>
+#include <vector>
+#include <set>
+#include <algorithm>
+using namespace std;
+
+LagrangeMultiplierSolver::LagrangeMultiplierSolver(const SparseMatrix * A, const SparseMatrix * J, const SparseMatrix * B, int numFixedDOFs_, const int * fixedDOFs_, int numThreads_, int updatable, int addDirichlet) : numFixedDOFs(numFixedDOFs_), numThreads(numThreads_)
+{
+  decompositionDone = false;
+  fixedDOFs.assign(fixedDOFs_, fixedDOFs_ + numFixedDOFs);
+  // create system matrix
+  m = A->GetNumRows();
+  mFree = m - numFixedDOFs;
+  assert(mFree >= 0);
+  if (J != NULL)
+    c = J->GetNumRows();
+  else 
+    c = 0;
+
+  if ((B != NULL) && (B->GetNumRows() != c)) 
+  {
+    printf("Error: matrix size mismatch in B.\n");
+    throw 1;
+  }
+
+  if (B != NULL) 
+  {
+    SparseMatrixOutline outline(m+c);
+    for(int i=0; i<m; i++)
+      for(int j=0; j<A->GetRowLength(i); j++)
+        outline.AddEntry(i, A->GetColumnIndex(i,j), A->GetEntry(i,j));
+
+    for(int i=0; i<c; i++)
+      for(int j=0; j<J->GetRowLength(i); j++)
+      {
+        outline.AddEntry(m+i, J->GetColumnIndex(i,j), J->GetEntry(i,j));
+        outline.AddEntry(J->GetColumnIndex(i,j), m+i, J->GetEntry(i,j));
+      }
+
+    for(int i=0; i<c; i++)
+      for(int j=0; j<B->GetRowLength(i); j++)
+        outline.AddEntry(m+i, m+B->GetColumnIndex(i,j), B->GetEntry(i,j));
+
+    // must add this because Pardiso requires diagonal elements to be set, even if zero
+    for(int i=0; i<c; i++)
+      outline.AddEntry(m+i, m+i, 0.0);
+
+    systemMatrix = new SparseMatrix(&outline);
+  }
+  else 
+  {
+    systemMatrix = new SparseMatrix(*A);
+    if (c > 0)
+      systemMatrix->AppendRowsColumns(J);
+  }
+  systemMatrix->RemoveRowsColumns(numFixedDOFs, &fixedDOFs[0]);
+
+  ADirichlet = JDirichlet = NULL;
+  if (addDirichlet)
+  {
+    // [ S11 S12 ] [x1]   [b1]
+    // [ S21 S22 ] [x2] = [b2], x2 is the fixedDOF
+    // then Dirichlet boundary condition is to give fixed values on x2
+    // To solve x1, we have:
+    // S11 x1 = b1 - S12 x2
+    // S12 is formed by
+    // S12 = [ A12 ],  A12 is ADirichlet
+    //       [ J12 ],  J12 is JDirichlet
+    DirichletBuffer.resize(numFixedDOFs);
+    DirichletFreeBuffer.resize(max(mFree, c));
+    ADirichlet = new SparseMatrix(*A);
+    ADirichlet->RemoveRows(numFixedDOFs, &fixedDOFs[0]);
+
+    vector<int> remainedDOFs(mFree);
+    ConstrainedDOFs::FindFreeDOFs(m, &remainedDOFs[0], numFixedDOFs, &fixedDOFs[0]);
+
+    ADirichlet->RemoveColumns(remainedDOFs.size(), remainedDOFs.data());
+
+    assert(ADirichlet->GetNumColumns() <= numFixedDOFs);
+    if (updatable)
+      ADirichlet->BuildSuperMatrixIndices(numFixedDOFs, &fixedDOFs[0], remainedDOFs.size(), remainedDOFs.data(), A);
+
+    if (c > 0)
+    {
+      JDirichlet = new SparseMatrix(*J);
+      JDirichlet->RemoveColumns(remainedDOFs.size(), remainedDOFs.data());
+      if (updatable)
+        JDirichlet->BuildSuperMatrixIndices(0, NULL, remainedDOFs.size(), remainedDOFs.data(), J);
+    }
+  }
+
+  pardisoSolver = new PardisoSolver(systemMatrix, numThreads, PardisoSolver::REAL_SYM_INDEFINITE);
+  //pardisoSolver->ComputeCholeskyDecomposition(systemMatrix);
+
+  xConstrained.resize(m+c - numFixedDOFs);
+  rhsConstrained = xConstrained;
+
+  Acons = NULL;
+  Jcons = NULL;
+  JTcons = NULL;
+  if (updatable)
+  {
+    Acons = new SparseMatrix(*A);
+    Acons->RemoveRowsColumns(numFixedDOFs, &fixedDOFs[0]);
+    Acons->BuildSuperMatrixIndices(numFixedDOFs, &fixedDOFs[0], A);
+
+    if (c > 0)
+    {
+      Jcons = new SparseMatrix(*J);
+      Jcons->RemoveColumns(numFixedDOFs, &fixedDOFs[0]);
+      Jcons->BuildSuperMatrixIndices(0, NULL, numFixedDOFs, &fixedDOFs[0], J);
+      Jcons->BuildTranspositionIndices();
+      JTcons = Jcons->Transpose(m-numFixedDOFs);
+      systemMatrix->BuildSubMatrixIndices(*JTcons, 0, 0, m-numFixedDOFs);
+    }
+
+    if (B != NULL)
+      systemMatrix->BuildSubMatrixIndices(*B, 1, m-numFixedDOFs, m-numFixedDOFs);
+  }
+//  cout << "LagMulSolver: " << endl << profiler.toString() << endl;
+}
+
+LagrangeMultiplierSolver::LagrangeMultiplierSolver(const LagrangeMultiplierSolver & other)
+{
+  decompositionDone = false;
+  m = other.m;
+  c = other.c;
+  numFixedDOFs = other.numFixedDOFs;
+  fixedDOFs = other.fixedDOFs;
+  numThreads = other.numThreads;
+  systemMatrix = new SparseMatrix(*other.systemMatrix);
+  xConstrained = other.xConstrained;
+  rhsConstrained = other.rhsConstrained;
+  Acons = (other.Acons ? new SparseMatrix(*other.Acons) : NULL);
+  Jcons = (other.Jcons ? new SparseMatrix(*other.Jcons) : NULL);
+  JTcons = (other.JTcons ? new SparseMatrix(*other.JTcons) : NULL);
+  ADirichlet = (other.ADirichlet ? new SparseMatrix(*other.ADirichlet) : NULL);
+  JDirichlet = (other.JDirichlet ? new SparseMatrix(*other.JDirichlet) : NULL);
+  DirichletBuffer = other.DirichletBuffer;
+  DirichletFreeBuffer = other.DirichletFreeBuffer;
+  mFree = other.mFree;
+  pardisoSolver = new PardisoSolver(systemMatrix, numThreads, PardisoSolver::REAL_SYM_INDEFINITE);
+}
+
+LagrangeMultiplierSolver::~LagrangeMultiplierSolver()
+{
+  delete systemMatrix;
+  delete pardisoSolver;
+  delete Acons;
+  delete Jcons;
+  delete JTcons;
+  delete ADirichlet;
+  delete JDirichlet;
+}
+
+void LagrangeMultiplierSolver::AddDirichletOnRhs(const double * x)
+{
+  if (DirichletBuffer.size() > 0)
+  {
+    for(int i = 0; i < numFixedDOFs; i++)
+      DirichletBuffer[i] = x[fixedDOFs[i]];
+    assert(ADirichlet);
+    ADirichlet->MultiplyVector(&DirichletBuffer[0], &DirichletFreeBuffer[0]);
+    for(int i = 0; i < mFree; i++)
+      rhsConstrained[i] -= DirichletFreeBuffer[i];
+
+    if (JDirichlet)
+    {
+      JDirichlet->MultiplyVector(&DirichletBuffer[0], &DirichletFreeBuffer[0]);
+      for(int i = 0; i < c; i++)
+        rhsConstrained[mFree + i] -= DirichletFreeBuffer[i];
+    }
+  }
+}
+
+MKL_INT LagrangeMultiplierSolver::SolveLinearSystem(double * x, const double * rhs)
+{
+  if (decompositionDone == false)
+  {
+    pardisoSolver->FactorMatrix(systemMatrix);
+    decompositionDone = true;
+  }
+
+  ConstrainedDOFs::RemoveDOFs(m+c, &rhsConstrained[0], rhs, numFixedDOFs, &fixedDOFs[0]);
+
+  AddDirichletOnRhs(x);
+
+  MKL_INT code =  pardisoSolver->SolveLinearSystem(&xConstrained[0], &rhsConstrained[0]);
+  ConstrainedDOFs::InsertDOFs(m+c, &xConstrained[0], x, numFixedDOFs, &fixedDOFs[0]);
+
+  // add back Dirichlet boundary conditions on x since those values have been set to zero in ConstrainedDOFs::InsertDOFs
+  if (DirichletBuffer.size() > 0)
+  {
+    for(int i = 0; i < numFixedDOFs; i++)
+      x[fixedDOFs[i]] = DirichletBuffer[i];
+  }
+  return code;
+}
+
+double LagrangeMultiplierSolver::CheckLinearSystemSolution(const double * x, const double * rhs, int verbose)
+{
+  ConstrainedDOFs::RemoveDOFs(m+c, &rhsConstrained[0], rhs, numFixedDOFs, &fixedDOFs[0]);
+  ConstrainedDOFs::RemoveDOFs(m+c, &xConstrained[0], x, numFixedDOFs, &fixedDOFs[0]);
+  AddDirichletOnRhs(x);
+  return systemMatrix->CheckLinearSystemSolution(&xConstrained[0], &rhsConstrained[0], verbose);
+}
+
+void LagrangeMultiplierSolver::UpdateAJB(const SparseMatrix * A, const SparseMatrix * J, const SparseMatrix * B)
+{
+  if (A != NULL)
+  {
+    assert(Acons);
+    Acons->AssignSuperMatrix(*A);
+    for(int i = 0; i < Acons->Getn(); i++) 
+    {
+      int rowLen = Acons->GetRowLength(i);
+      for(int j = 0; j < rowLen; j++)
+        systemMatrix->SetEntry(i,j,Acons->GetEntry(i,j));
+    }
+    //systemMatrix->AssignSubMatrix(*Acons, 0);
+
+    if (ADirichlet)
+      ADirichlet->AssignSuperMatrix(*A);
+  }
+
+  if (J != NULL && J->GetNumRows() > 0)
+  {
+    assert(Jcons);
+    assert(JTcons);
+    Jcons->AssignSuperMatrix(*J);
+    for(int i = 0; i < Jcons->Getn(); i++) 
+    {
+      int rowLen = Jcons->GetRowLength(i);
+      int row = i + Acons->Getn();
+      for(int j = 0; j < rowLen; j++)
+        systemMatrix->SetEntry(row,j,Jcons->GetEntry(i,j));
+    }
+    JTcons->AssignTransposedMatrix(*Jcons);
+    systemMatrix->AssignSubMatrix(*JTcons,0);
+
+    if (JDirichlet)
+      JDirichlet->AssignSuperMatrix(*J);
+  }
+
+  if (B != NULL)
+    systemMatrix->AssignSubMatrix(*B, 1);
+
+  decompositionDone = false;
+}
+
+void LagrangeMultiplierSolver::SetNumThreads(int numThreads_)
+{ 
+  numThreads = numThreads_; 
+  if (pardisoSolver != NULL)
+    pardisoSolver->SetNumThreads(numThreads);
+}
+
diff --git a/libraries/sparseSolver/LagrangeMultiplierSolver.h b/libraries/sparseSolver/LagrangeMultiplierSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..f819547387d5d1a118047ae89baa57f70013548c
--- /dev/null
+++ b/libraries/sparseSolver/LagrangeMultiplierSolver.h
@@ -0,0 +1,113 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li, Hongyi Xu                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _LAGRANGEMULTIPLIERSOLVER_H_
+#define _LAGRANGEMULTIPLIERSOLVER_H_
+
+/*
+
+  Solves
+
+        [ A  J^T ] [y]      = [rhs0]
+        [ J  B   ] [lambda]   [rhs1]
+
+  where A is sparse, symmetric and square (m x m), J is sparse and rectangular (c x m) (often a constraint gradient), and B is square and symmetric (c x c) (often zero)
+
+  This system arises when forward-simulating a dynamical system with constraints (enforced via Lagrange multipliers).
+
+  The Pardiso solver is used to solve the system.
+
+*/
+
+#include "PardisoSolver.h"
+
+class LagrangeMultiplierSolver
+{
+public:
+
+  // the constructor will also factor the matrix
+  // if J is passed as NULL, empty matrix is assumed for it (no constraints)
+  // if B is passed as NULL, zero matrix is assumed for B
+  // if updatable=1, one can use function UpdateAJB to update matrices A, J, B (note: unlike in the derived Updatable class, the entire matrix will be re-factored, even if only updating J or B)
+  // if addDirichletBoundaryCondition=1, fixedDOFs can be updated by assigning correct fixed values in x when calling SolveLinearSystem
+  LagrangeMultiplierSolver(const SparseMatrix * A, const SparseMatrix * J, const SparseMatrix * B = NULL,
+      int numFixedDOFs = 0, const int * fixedDOFs = NULL, int numThreads = 0, int updatable = 0, int addDirichletBoundaryCondition = 0);
+  LagrangeMultiplierSolver(const LagrangeMultiplierSolver &);
+  virtual ~LagrangeMultiplierSolver();
+
+  // updates the A, J, B matrix, assuming equal topology as previous A, J, B. 
+  // Pass NULL if you do not wish to update the corresponding matrix. 
+  // If you pass non-NULL for J (or B), then J (or B) MUST have been set in the constructor.
+  // updatable=1 must have been set in the constructor to use this function.
+  void UpdateAJB(const SparseMatrix * A, const SparseMatrix * J = NULL, const SparseMatrix * B = NULL); // updates the A, J, B matrix, assuming equal topology as previous A, J, B. Pass NULL if you do not wish to update the corresponding matrix. If you pass non-NULL for B, then B MUST have been set in the constructor.
+
+  // solve the linear system, using Pardiso
+  // the routine does not modify rhs
+  // if addDirichletBoundaryCondition is 0 when initializing,
+  // x is not an initial guess (it is ignored as input, and used only as output parameter)
+  // otherwise, the fixedDOFs in x are used for Dirichlet boundary conditions
+  // returns Pardiso exit code 
+  MKL_INT SolveLinearSystem(double * x, const double * rhs);
+
+  double CheckLinearSystemSolution(const double * x, const double * rhs, int verbose=1);
+
+  const SparseMatrix * GetSystemMatrix() const { return systemMatrix; }
+
+  int GetNumFixedDOFs() const { return numFixedDOFs; }
+  const int * GetFixedDOFs() const { return fixedDOFs.data(); }
+
+  int GetNumARows() const { return m; }
+  int GetNumJRows() const { return c; }
+
+  void SetNumThreads(int numThreads); // call at any time to change the number of threads
+
+protected:
+  void AddDirichletOnRhs(const double * x); // modify rhsConstrained to satisfy Dirichlet boundary condition
+  bool decompositionDone;
+  int m, c;
+  int numFixedDOFs; 
+  std::vector<int> fixedDOFs;
+  int numThreads;
+  SparseMatrix * systemMatrix;
+  PardisoSolver * pardisoSolver;
+  std::vector<double> xConstrained, rhsConstrained;
+  SparseMatrix * Acons;
+  SparseMatrix * Jcons;
+  SparseMatrix * JTcons;
+  SparseMatrix * ADirichlet, * JDirichlet; // used for updating Dirichlet conditions
+  std::vector<double> DirichletBuffer, DirichletFreeBuffer;
+  int mFree;
+};
+
+#endif
+
+
diff --git a/libraries/sparseSolver/PardisoSolver.cpp b/libraries/sparseSolver/PardisoSolver.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9cca7accf85ee69e7a4d690d914e7bfe48abc3d8
--- /dev/null
+++ b/libraries/sparseSolver/PardisoSolver.cpp
@@ -0,0 +1,456 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li, Hongyi Xu                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "PardisoSolver.h"
+#include "sparseSolverAvailability.h"
+
+#ifdef PARDISO_SOLVER_IS_AVAILABLE
+
+// Pardiso solver is available
+
+#ifdef __APPLE__
+  #include "TargetConditionals.h"
+#endif
+
+#include "mkl.h"
+
+#if defined(_WIN32) || defined(WIN32) || defined(_WIN64)
+  #define PARDISO pardiso
+#else
+  #define PARDISO pardiso
+#endif
+
+PardisoSolver::PardisoSolver(const SparseMatrix * A, int numThreads_, matrixType mtype_, reorderingType rtype_, int directIterative_, int verbose_): numThreads(numThreads_), mtype(mtype_), rtype(rtype_), directIterative(directIterative_), verbose(verbose_)
+{
+  mkl_set_num_threads(numThreads);
+
+  n = A->Getn();
+
+  if (verbose >= 1)
+    printf("Converting matrix to Pardiso format...\n");
+
+  int numEntries;
+  int upperTriangleOnly;  
+  if ((mtype == REAL_SPD) || (mtype == REAL_SYM_INDEFINITE))  // matrix is symmetric
+  {
+    numEntries = A->GetNumUpperTriangleEntries();
+    upperTriangleOnly = 1;
+  }
+  else  
+  {
+    // structural symmetric or unsymmetric
+    numEntries = A->GetNumEntries();
+    upperTriangleOnly = 0;
+  }
+  a = (double*) malloc (sizeof(double) * numEntries);  
+  ia = (int*) malloc (sizeof(int) * (A->GetNumRows() + 1));  
+  ja = (int*) malloc (sizeof(int) * numEntries);  
+  int oneIndexed = 1;
+  A->GenerateCompressedRowMajorFormat(a, ia, ja, upperTriangleOnly, oneIndexed);
+
+  if (verbose >= 2)
+    printf("numEntries: %d\n", numEntries);
+
+  // permute & do symbolic factorization
+
+  nrhs = 1; // Number of right hand sides.
+  maxfct = 1; // Maximum number of numerical factorizations.
+  mnum = 1; // Which factorization to use.
+  msglvl = verbose >= 1 ? verbose - 1 : 0; // Print statistical information to the output file
+  error = 0; // Initialize error flag
+
+  for (int i = 0; i < 64; i++) 
+    iparm[i] = 0;
+
+  iparm[0] = 1; // Do not use the solver default values (use custom values, provided below)
+  iparm[1] = rtype; // matrix re-ordering algorithm
+  iparm[2] = 0; // unused 
+
+  // use iterative-direct algorithm if requested
+  if (directIterative)
+  {
+    if (mtype == REAL_SPD)  
+      iparm[3] = 62; // matrix is symmetric positive-definite; use CGS iteration for symmetric positive-definite matrices
+    else
+      iparm[3] = 61; // use CGS iteration
+  }
+  else
+    iparm[3] = 0; 
+
+  iparm[4] = 0; // No user fill-in reducing permutation
+  iparm[5] = 0; // Write solution into x
+  iparm[6] = 0; // Output: number of iterative refinement steps performed
+  iparm[7] = 0; // Max numbers of iterative refinement steps (used during the solving stage). Value of 0 (default) means: The solver automatically performs two steps of iterative refinement when perturbed pivots are obtained during the numerical factorization.
+  iparm[8] = 0; // Reserved. Must set to 0.
+
+  // Pivoting perturbation; the values below are Pardiso's default values
+  // Pivoting only applies to REAL_UNSYM and REAL_SYM_INDEFINITE
+  if (mtype == REAL_UNSYM)
+    iparm[9] = 13; // For non-symmetric matrices, perturb the pivot elements with 1E-13
+  else 
+    iparm[9] = 8; // Use 1.0E-8 for symmetric indefinite matrices
+  
+  // Scaling and matching. The following below are the Pardiso defaults.
+  if (mtype == REAL_UNSYM)  // unsymmetric matrices
+  {
+    iparm[10] = 1; // enable scaling
+    iparm[12] = 1; // enable matching
+  }
+  else
+  {
+    iparm[10] = 0; // disable scaling
+    iparm[12] = 0; // disable matching
+  }
+
+  iparm[11] = 0; // Solve with transposed or conjugate transposed matrix A. Not in use here.
+
+  iparm[13] = 0; // Output: Number of perturbed pivots
+  iparm[14] = 0; // Output: Peak memory on symbolic factorization (in KB)
+  iparm[15] = 0; // Output: Permanent memory on symbolic factorization (in KB)
+  iparm[16] = 0; // Output: Size of factors/Peak memory on numerical factorization and solution (in KB)
+  
+  iparm[17] = -1; // Output: Report the number of non-zero elements in the factors.
+  iparm[18] = 0; // Report number of floating point operations (in 10^6 floating point operations) that are necessary to factor the matrix A. Disabled.
+  iparm[19] = 0; // Output: Report CG/CGS diagnostics.
+  iparm[20] = 1; // Pivoting for symmetric indefinite matrices: Apply 1x1 and 2x2 Bunch-Kaufman pivoting during the factorization process.
+  iparm[21] = 0; // Output: Inertia: number of positive eigenvalues.
+  iparm[22] = 0; // Output: Inertia: number of negative eigenvalues.
+
+  iparm[23] = 0; // Parallel factorization control. Use default.
+  iparm[24] = 0; // Parallel forward/backward solve control. Intel MKL PARDISO uses a parallel algorithm for the solve step.
+
+  // the other iparms (above 24) are left at 0
+
+  /* -------------------------------------------------------------------- *\
+     .. Initialize the internal solver memory pointer. This is only 
+     necessary for the FIRST call of the PARDISO solver. 
+  \* -------------------------------------------------------------------- */
+  for (int i=0; i<64; i++) 
+    pt[i] = 0;
+
+  if (verbose >= 1)
+    printf("Reordering and symbolically factorizing the matrix...\n");
+
+  /* -------------------------------------------------------------------- *\
+     .. Reordering and Symbolic Factorization. This step also allocates 
+     all memory that is necessary for the factorization.
+  \* -------------------------------------------------------------------- */
+  phase = 11;
+  PARDISO (pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase,
+    &n, a, ia, ja, NULL, &nrhs,
+    iparm, &msglvl, NULL, NULL, &error);
+
+  if (error != 0)
+  {
+    printf("Error: Pardiso matrix re-ordering/symbolic factorization returned non-zero exit code %d.\n", error);
+    throw error;
+  }
+
+  if (verbose >= 2)
+  {
+    printf("\nReordering and symbolic factorization completed...\n");
+    printf("Number of nonzeros in factors = %d\n", iparm[17]);
+    printf("Number of factorization MFLOPS = %d\n", iparm[18]);
+  }
+}
+
+PardisoSolver::~PardisoSolver()
+{
+  phase = -1;
+  PARDISO(pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, NULL, NULL, &error);
+
+  if (error != 0)
+    printf("Error: Pardiso Cholesky dealloacation returned non-zero exit code %d.\n", error);
+
+  free(a);
+  free(ia);
+  free(ja);
+}
+
+void PardisoSolver::DisabledSolverError() {}
+
+MKL_INT PardisoSolver::FactorMatrix(const SparseMatrix * A)
+{
+  if (directIterative)
+    return 0;
+
+  mkl_set_num_threads(numThreads);
+
+  // compute the factorization
+  if (verbose >= 1)
+    printf("Factoring the %d x %d matrix (%d threads)...\n", n, n, numThreads);
+
+  int upperTriangleOnly = 1;
+  int oneIndexed = 1;
+
+  if ((mtype == REAL_SPD) || (mtype == REAL_SYM_INDEFINITE))  // matrix is symmetric
+    upperTriangleOnly = 1;    // symmetric matrix can only be represented by its upper triangle elements
+  else  // structural symmetric or unsymmetric
+    upperTriangleOnly = 0;    // unsymmetric matrix must store all its elements
+  A->GenerateCompressedRowMajorFormat(a, NULL, NULL, upperTriangleOnly, oneIndexed);
+
+  // factor 
+  phase = 22;
+  PARDISO(pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, NULL, NULL,  &error);
+
+  if (error != 0)
+    printf("Error: Pardiso Cholesky decomposition returned non-zero exit code %d.\n", error);
+
+  if (verbose >= 1)
+    printf("Factorization completed.\n");
+
+  return error;
+}
+
+int PardisoSolver::SolveLinearSystem(double * x, const double * rhs)
+{
+  if (directIterative != 0)
+  {
+    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
+    return 101;
+  }
+
+  mkl_set_num_threads(numThreads); 
+
+  if (verbose >= 2)
+    printf("Solving linear system...(%d threads)\n", numThreads);
+
+  phase = 33;
+  PARDISO(pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)rhs, x, &error);
+
+  if (error != 0)
+    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
+
+  if (verbose >= 2)
+    printf("Solve completed.\n"); 
+
+  return (int)error;
+}
+
+MKL_INT PardisoSolver::ForwardSubstitution(double * y, const double * rhs)
+{
+  if (directIterative != 0)
+  {
+    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
+    return 101;
+  }
+
+  if (verbose >= 2)
+    printf("Performing forward substitution...(%d threads)\n", numThreads);
+
+  mkl_set_num_threads(numThreads); 
+
+  int maxIterRefinementSteps = iparm[7];
+  iparm[7] = 0;
+
+  phase = 331;
+  PARDISO(pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)rhs, y, &error);
+
+  iparm[7] = maxIterRefinementSteps;
+
+  if (error != 0)
+    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
+
+  if (verbose >= 2)
+    printf("Solve completed.\n"); 
+
+  return error;
+}
+
+MKL_INT PardisoSolver::DiagonalSubstitution(double * v, const double * y)
+{
+  if (directIterative != 0)
+  {
+    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
+    return 101;
+  }
+
+  if (mtype == REAL_SPD)
+  {
+    printf("Error: diagonal substitution should not be used for positive-definite matrices.\n");
+    return 102;
+  }
+
+  mkl_set_num_threads(numThreads); 
+
+  if (verbose >= 2)
+    printf("Performing forward substitution...(%d threads)\n", numThreads);
+
+  int maxIterRefinementSteps = iparm[7];
+  iparm[7] = 0;
+
+  phase = 332;
+  PARDISO(pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)y, v, &error);
+
+  iparm[7] = maxIterRefinementSteps;
+
+  if (error != 0)
+    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
+
+  if (verbose >= 2)
+    printf("Solve completed.\n"); 
+
+  return error;
+}
+
+MKL_INT PardisoSolver::BackwardSubstitution(double * x, const double * y)
+{
+  if (directIterative != 0)
+  {
+    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
+    return 101;
+  }
+
+  mkl_set_num_threads(numThreads); 
+
+  if (verbose >= 2)
+    printf("Performing forward substitution...(%d threads)\n", numThreads);
+
+  int maxIterRefinementSteps = iparm[7];
+  iparm[7] = 0;
+
+  phase = 333;
+  PARDISO(pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)y, x, &error);
+
+  iparm[7] = maxIterRefinementSteps;
+
+  if (error != 0)
+    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
+
+  if (verbose >= 2)
+    printf("Solve completed.\n"); 
+
+  return error;
+}
+
+MKL_INT PardisoSolver::SolveLinearSystemMultipleRHS(double * x, const double * rhs, int numRHS)
+{
+  if (directIterative != 0)
+  {
+    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
+    return 101;
+  }
+
+  if (verbose >= 2)
+    printf("Solving linear system...(%d threads)\n", numThreads);
+
+  mkl_set_num_threads(numThreads); 
+
+  phase = 33;
+  PARDISO(pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase, &n, a, ia, ja, NULL, &numRHS, iparm, &msglvl, (double*)rhs, x, &error);
+
+  if (error != 0)
+    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
+
+  if (verbose >= 2)
+    printf("Solve completed.\n");
+
+  return error;
+}
+
+MKL_INT PardisoSolver::SolveLinearSystemDirectIterative(const SparseMatrix * A, double * x, const double * rhs)
+{
+  if (directIterative != 1)
+  {
+    printf("Error: direct-iterative flag was not specified in the constructor.\n");
+    return 102;
+  }
+
+  mkl_set_num_threads(numThreads); 
+
+  if (verbose >= 2)
+    printf("Solving linear system...(%d threads, direct-iterative)\n", numThreads);
+
+  int upperTriangleOnly = 1;
+  int oneIndexed = 1;
+  A->GenerateCompressedRowMajorFormat(a, NULL, NULL, upperTriangleOnly, oneIndexed);
+
+  phase = 23;
+  PARDISO(pt, &maxfct, &mnum, (MKL_INT*)&mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)rhs, x, &error);
+
+  if (error != 0)
+    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
+
+  if (verbose >= 2)
+    printf("Solve completed.\n"); 
+
+  return error;
+}
+
+#else
+
+// Pardiso Solver is not available
+
+PardisoSolver::PardisoSolver(const SparseMatrix * A, int numThreads_, matrixType mtype_, reorderingType rtype_, int directIterative_, int verbose_): numThreads(numThreads_), mtype(mtype_), rtype(rtype_), directIterative(directIterative_), verbose(verbose_)
+{
+  DisabledSolverError();
+}
+
+PardisoSolver::~PardisoSolver()
+{
+}
+
+void PardisoSolver::DisabledSolverError()
+{
+  printf("Error: Pardiso solver called, but it has not been installed/compiled/enabled. After installation, enable it in \"sparseSolverAvailability.h\".\n");
+  throw 1;
+}
+
+MKL_INT PardisoSolver::FactorMatrix(const SparseMatrix * A)
+{
+  DisabledSolverError();
+  return 1;
+}
+
+MKL_INT PardisoSolver::SolveLinearSystem(double * x, const double * rhs)
+{
+  DisabledSolverError();
+  return 1;
+}
+
+MKL_INT PardisoSolver::SolveLinearSystemMultipleRHS(double * x, const double * rhs, int numRHS)
+{
+  DisabledSolverError();
+  return 1;
+}
+
+MKL_INT PardisoSolver::SolveLinearSystemDirectIterative(const SparseMatrix * A, double * x, const double * rhs)
+{
+  DisabledSolverError();
+  return 1;
+}
+
+#endif
+
diff --git a/libraries/sparseSolver/PardisoSolver.h b/libraries/sparseSolver/PardisoSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..6055db7d40fe959192def94fc3c1b52512d35874
--- /dev/null
+++ b/libraries/sparseSolver/PardisoSolver.h
@@ -0,0 +1,131 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li, Hongyi Xu                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _PARDISOSOLVER_H_
+#define _PARDISOSOLVER_H_
+
+/*
+  Solves A * x = rhs, where A is a sparse matrix. The following matrix types are supported:
+    structurally symmetric matrix
+    symmetric positive-definite matrix
+    symmetric indefinite matrix
+    unsymmetric matrix
+  The solution is obtained using the Pardiso library from Intel MKL, using multi-threading.
+  Three steps are performed: 
+    1. matrix re-ordering and symbolic factorization (in the constructor)
+    2. numerical matrix factorization (FactorMatrix)
+    3. the actual linear system solve (SolveLinearSystem)
+  The solution method is direct (not iterative), unless one uses the direct-iterative mode. 
+  As such, convergence is robust, and there is no need to tune convergence parameters, unlike, say, in the conjugate gradient method.
+  Memory requirements are minimized by re-ordering the matrix before applying the matrix decomposition.
+  However, for very large systems (e.g. matrices of size 200,000 x 200,000, on a machine with 2 GB RAM), 
+  the matrix decomposition might run out of memory.
+*/
+
+#include "linearSolver.h"
+#include "sparseMatrix.h"
+
+#define MKL_INT int
+
+class PardisoSolver : public LinearSolver
+{
+public:
+  // The constructor computes the permutation to re-order A, and performs symbolic factorization.
+  // Only the topology of A matters for the constructor. A is not modified.
+  // Note: after calling the constructor, you must call "FactorMatrix" to perform numerical factorization.
+  //  "mtype" gives the matrix type:
+  //  = 1   structurally symmetric matrix
+  //  = 2   symmetric positive-definite matrix
+  //  = -2  symmetric indefinite matrix
+  //  = 11  unsymmetric matrix
+  typedef enum { REAL_STRUCTURAL_SYM = 1, REAL_SPD = 2, REAL_SYM_INDEFINITE = -2, REAL_UNSYM = 11 } matrixType;
+  // Matrix re-ordering is specified as follows:
+  // = 0   minimum degree ordering
+  // = 2   nested dissection algorithm from the METIS package
+  // = 3   parallel (OpenMP) version of nested dissection; it can decrease the computation time on multi-core computers, especially when the constructor takes a long time
+  typedef enum { MINIMUM_DEGREE_ORDERING = 0, NESTED_DISSECTION = 2, PARALLEL_NESTED_DISSECTION = 3 } reorderingType;
+  // must have: numThreads >= 1
+  // "directIterative" specifies whether a direct-iterative procedure is used (see Intel MKL's documentation)
+  PardisoSolver(const SparseMatrix * A, int numThreads = 1, matrixType mtype = REAL_SYM_INDEFINITE, reorderingType rtype = NESTED_DISSECTION, int directIterative = 0, int verbose = 0);
+
+  virtual ~PardisoSolver();
+
+  // set the number of threads to use
+  // must have: numThreads >= 1
+  void SetNumThreads(int numThreads) { this->numThreads = numThreads; }
+
+  // Perform the numerical matrix factorization (Cholesky for symmetric matrices, LU for other matrices).
+  // This step is **mandatory** (constructor does not perform it).
+  // You must factor the matrix at least once; and every time the entries of the matrix A change.
+  // If the topology of A changes, you must call the constructor again.
+  // A is not modified.
+  MKL_INT FactorMatrix(const SparseMatrix * A); 
+
+  // solve: A * x = rhs, using the previously computed matrix factorization
+  // rhs is not modified
+  virtual int SolveLinearSystem(double * x, const double * rhs);
+
+  // solve multiple right-hand sides
+  MKL_INT SolveLinearSystemMultipleRHS(double * x, const double * rhs, int numRHS);
+
+  // solve: A * x = rhs, using the direct-iterative solver
+  MKL_INT SolveLinearSystemDirectIterative(const SparseMatrix * A, double * x, const double * rhs);
+
+  /*
+    Advanced routines exposing the substeps of the solve.
+    For REAL_SYM_INDEFINITE, SolveLinearSystem is equivalent to: ForwardSubstitution + DiagonalSubstitution + BackwardSubstitution
+    For REAL_SPD, SolveLinearSystem is equivalent to: ForwardSubstitution + BackwardSubstitution (DiagonalSubstitution is not used)
+  */
+  MKL_INT ForwardSubstitution(double * y, const double * rhs); // L y = rhs
+  MKL_INT DiagonalSubstitution(double * v, const double * y);  // D v = y
+  MKL_INT BackwardSubstitution(double * x, const double * v);  // L^T x = v
+
+protected:
+  int n;
+  double * a;
+  int * ia, * ja;
+  void *pt[64];
+  MKL_INT iparm[64];
+
+  int numThreads;
+  matrixType mtype;
+  reorderingType rtype;
+  int directIterative;
+  int verbose;
+  MKL_INT nrhs; 
+  MKL_INT maxfct, mnum, phase, error, msglvl;
+
+  static void DisabledSolverError();
+};
+
+#endif
+
diff --git a/src/libsparseSolver/SPOOLESSolver.cpp b/libraries/sparseSolver/SPOOLESSolver.cpp
similarity index 84%
rename from src/libsparseSolver/SPOOLESSolver.cpp
rename to libraries/sparseSolver/SPOOLESSolver.cpp
index 876973aea66403d3e5a722fc088e5a10b20fad30..64023906ade00dff327354cf5500591fc32d08f6 100644
--- a/src/libsparseSolver/SPOOLESSolver.cpp
+++ b/libraries/sparseSolver/SPOOLESSolver.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,28 +30,9 @@
  *                                                                       *
  *************************************************************************/
 
-/*
-
-  Solves A * x = rhs, where A is sparse, usually large, and symmetric.
-
-  The solution is obtained using the SPOOLES library (which is free software).
-  The solution method is direct (not iterative). As such, convergence
-  is often very robust, and there is no need to tune convergence parameters,
-  unlike, say, in the Conjugate gradient method.
-  Memory requirements are minimized by re-ordering the matrix before applying
-  Cholesky decomposition.
-  However, for very large systems (e.g. 200,000 x 200,000 matrices on a
-  2Gb machine), the Cholesky decomposition might run out of memory.
-
-  Jernej Barbic, MIT, 2007-2009
-
-*/
-
 #include "SPOOLESSolver.h"
 #include "sparseSolverAvailability.h"
 
-namespace vega
-{
 #ifdef SPOOLES_SOLVER_IS_AVAILABLE
 
 // SPOOLES solver is available
@@ -122,8 +107,6 @@ SPOOLESSolver::SPOOLESSolver(const SparseMatrix * A, int verbose)
 
   if (verbose >= 1)
     printf("Factorization completed.\n");
-
-  this->solverType = "SPOOLES";
 }
 
 SPOOLESSolver::~SPOOLESSolver()
@@ -156,7 +139,7 @@ int SPOOLESSolver::SolveLinearSystem(double * x, const double * rhs)
 
   // zero the solution
   DenseMtx_zero(mtx_x);
-
+  
   // solve
   if (verbose >= 2)
     printf("Solving the linear system...\n");
@@ -167,11 +150,11 @@ int SPOOLESSolver::SolveLinearSystem(double * x, const double * rhs)
   if (rc != 1)
   {
     printf("Error: linear system solve failed. Bridge_solve exit code: %d.\n", rc);
-    return rc;
+    return (rc == 0) ? 1 : rc;
   }
 
   if (verbose >= 2)
-    printf("Solve completed.\n");
+    printf("Solve completed.\n"); 
 
   // store result
   for(int i=0; i < n; i++)
@@ -183,7 +166,7 @@ int SPOOLESSolver::SolveLinearSystem(double * x, const double * rhs)
     fclose(fout);
   */
 
-  return (rc != 1);
+  return 0;
 }
 
 #else
@@ -197,7 +180,6 @@ SPOOLESSolver::SPOOLESSolver(const SparseMatrix * A, int verbose)
 
 SPOOLESSolver::~SPOOLESSolver()
 {
-  DisabledSolverError();
 }
 
 void SPOOLESSolver::DisabledSolverError()
@@ -211,6 +193,6 @@ int SPOOLESSolver::SolveLinearSystem(double * x, const double * rhs)
   DisabledSolverError();
   return 1;
 }
-}
+
 #endif
 
diff --git a/libraries/sparseSolver/SPOOLESSolver.h b/libraries/sparseSolver/SPOOLESSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..52cab017fa913a35b9974cb1e3f616dc01c00cf9
--- /dev/null
+++ b/libraries/sparseSolver/SPOOLESSolver.h
@@ -0,0 +1,81 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _SPOOLESSOLVER_H_
+#define _SPOOLESSOLVER_H_
+
+/*
+  Solves A * x = rhs, where A is sparse, usually large, and symmetric.
+  
+  The solution is obtained using the SPOOLES library (which is free software).
+  The solution method is direct (not iterative). As such, convergence
+  is often very robust, and there is no need to tune convergence parameters,
+  unlike, say, in the Conjugate gradient method.
+  Memory requirements are minimized by re-ordering the matrix before applying
+  Cholesky decomposition.
+  However, for very large systems (e.g. 200,000 x 200,000 matrices on a 
+  2Gb machine), the Cholesky decomposition might run out of memory.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "linearSolver.h"
+#include "sparseMatrix.h"
+
+class SPOOLESSolver : public LinearSolver
+{
+public:
+
+  // this constructor will re-order A (in an internal copy), and then perform complete Cholesky factorization (via SPOOLES)
+  // A is not modified
+  SPOOLESSolver(const SparseMatrix * A, int verbose=0);
+  virtual ~SPOOLESSolver();
+
+  // solve: A * x = rhs, using SPOOLES
+  // uses the Cholesky factors obtained in the constructor
+  // rhs is not modified
+  virtual int SolveLinearSystem(double * x, const double * rhs);
+
+protected:
+  int n;
+  void * bridgePointer;
+  void * mtx_xPointer;
+  void * mtx_rhsPointer;
+  void * APointer;
+  FILE * msgFile;
+  int verbose;
+
+  static void DisabledSolverError();
+};
+
+#endif
+
diff --git a/src/libsparseSolver/SPOOLESSolverMT.cpp b/libraries/sparseSolver/SPOOLESSolverMT.cpp
similarity index 92%
rename from src/libsparseSolver/SPOOLESSolverMT.cpp
rename to libraries/sparseSolver/SPOOLESSolverMT.cpp
index e9e5611767d4c60e107c1dbda7c4116617c34170..1e787e7857b7888fb7ab3b2ae446634854054003 100644
--- a/src/libsparseSolver/SPOOLESSolverMT.cpp
+++ b/libraries/sparseSolver/SPOOLESSolverMT.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,8 +34,6 @@
 #include "SPOOLESSolver.h"
 #include "sparseSolverAvailability.h"
 
-namespace vega
-{
 #ifdef SPOOLES_SOLVER_IS_AVAILABLE
 
 // SPOOLES solver is available
@@ -87,7 +89,7 @@ SPOOLESSolverMT::SPOOLESSolverMT(const SparseMatrix * A, int numThreads, int ver
   double nfactorops;
   rc = BridgeMT_factorStats(bridgeMT, type, SPOOLES_SYMMETRIC, &nfront,
                             &nfind, &nfent, &nsolveops, &nfactorops);
-  if ( rc != 1 )
+  if ( rc != 1 ) 
   {
     printf("Error: BridgeMT_factorStats returned exit code %d.\n", rc);
     throw 1;
@@ -121,11 +123,11 @@ SPOOLESSolverMT::SPOOLESSolverMT(const SparseMatrix * A, int numThreads, int ver
   int permuteflag  = 1 ;
   int error;
   rc = BridgeMT_factor(bridgeMT, mtxA, permuteflag, &error);
-  if ( rc == 1 )
+  if ( rc == 1 ) 
   {
     fprintf(msgFile, "\n\n factorization completed successfully\n") ;
-  }
-  else
+  } 
+  else 
   {
     printf("Error: factorization returned exit code %d (error %d).\n", rc, error);
     throw 1;
@@ -168,8 +170,6 @@ SPOOLESSolverMT::SPOOLESSolverMT(const SparseMatrix * A, int numThreads, int ver
 
   if (verbose >= 1)
     printf("Factorization completed.\n");
-
-  this->solverType = "SPOOLES";
 }
 
 SPOOLESSolverMT::~SPOOLESSolverMT()
@@ -202,7 +202,7 @@ int SPOOLESSolverMT::SolveLinearSystem(double * x, const double * rhs)
 
   // zero the solution
   DenseMtx_zero(mtx_x);
-
+  
   // solve
   if (verbose >= 2)
     printf("Solving the linear system...\n");
@@ -220,7 +220,7 @@ int SPOOLESSolverMT::SolveLinearSystem(double * x, const double * rhs)
   if (rc != 1)
   {
     printf("Error: linear system solve failed. BridgeMT_solve exit code: %d.\n", rc);
-    return rc;
+    return (rc == 0) ? 1 : rc;
   }
 
   fprintf(msgFile, "\n\n ----- SOLVE -----\n") ;
@@ -238,7 +238,7 @@ int SPOOLESSolverMT::SolveLinearSystem(double * x, const double * rhs)
   fflush(msgFile) ;
 
   if (verbose >= 2)
-    printf("Solve completed.\n");
+    printf("Solve completed.\n"); 
 
   // store result
   for(int i=0; i < n; i++)
@@ -250,7 +250,7 @@ int SPOOLESSolverMT::SolveLinearSystem(double * x, const double * rhs)
     fclose(fout);
   */
 
-  return rc;
+  return 0;
 }
 
 #else
@@ -264,7 +264,6 @@ SPOOLESSolverMT::SPOOLESSolverMT(const SparseMatrix * A, int numThreads, int ver
 
 SPOOLESSolverMT::~SPOOLESSolverMT()
 {
-  DisabledSolverError();
 }
 
 void SPOOLESSolverMT::DisabledSolverError()
@@ -278,6 +277,6 @@ int SPOOLESSolverMT::SolveLinearSystem(double * x, const double * rhs)
   DisabledSolverError();
   return 1;
 }
-}
+
 #endif
 
diff --git a/libraries/sparseSolver/SPOOLESSolverMT.h b/libraries/sparseSolver/SPOOLESSolverMT.h
new file mode 100644
index 0000000000000000000000000000000000000000..1abd219b826772370e135ee457bd555e1bef8d09
--- /dev/null
+++ b/libraries/sparseSolver/SPOOLESSolverMT.h
@@ -0,0 +1,85 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _SPOOLESSOLVERMT_H_
+#define _SPOOLESSOLVERMT_H_
+
+/*
+
+Solves A * x = rhs, where A is sparse, usually large, and symmetric.
+Uses a multi-threaded approach to perform the solve.
+
+The solution is obtained using the the SPOOLES library (which is free software).
+The solution method is direct (not iterative). As such, convergence
+is often very robust, and there is no need to tune convergence parameters,
+unlike, say, in the Conjugate gradient method.
+Memory requirements are minimized by re-ordering the matrix before applying
+Cholesky decomposition.
+However, for very large systems (e.g. 200,000 x 200,000 matrices on a 
+2Gb machine), the Cholesky decomposition might run out of memory.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "sparseMatrix.h"
+#include "linearSolver.h"
+
+class SPOOLESSolverMT : public LinearSolver
+{
+public:
+
+  // this constructor will re-order A (in an internal copy), and then perform complete Cholesky factorization (via SPOOLES)
+  // A is not modified
+  SPOOLESSolverMT(const SparseMatrix * A, int numThreads, int verbose=0);
+  virtual ~SPOOLESSolverMT();
+
+  // solve: A * x = rhs, using SPOOLES
+  // uses the Cholesky factors obtained in the constructor
+  // rhs is not modified
+  virtual int SolveLinearSystem(double * x, const double * rhs);
+
+protected:
+  int n;
+  void * bridgeMTPointer;
+  void * mtx_xPointer;
+  void * mtx_rhsPointer;
+  void * APointer;
+  FILE * msgFile;
+  int verbose;
+  int nsolveops;
+
+  static void DisabledSolverError();
+};
+
+#endif
+
diff --git a/libraries/sparseSolver/ZTAZMultiplicator.cpp b/libraries/sparseSolver/ZTAZMultiplicator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..193c11d87cb5e41ff3e02ba776199ffe3cc7f6d2
--- /dev/null
+++ b/libraries/sparseSolver/ZTAZMultiplicator.cpp
@@ -0,0 +1,83 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ZTAZMultiplicator.h"
+#include "performanceCounter.h"
+
+ZTAZMultiplicator::ZTAZMultiplicator(SparseMatrix * A_, SparseMatrix * P_, Matrix<double> * invCp_, Matrix<double> * Cn_): A(A_), P(P_), invCp(invCp_), Cn(Cn_)
+{
+  buffer1 = (double*) malloc (sizeof(double) * A->Getn());
+  buffer2 = (double*) malloc (sizeof(double) * A->Getn());
+  invCpCn = new Matrix<double>((*invCp) * (*Cn));
+  invCpCnT = new Matrix<double>(Transpose(*invCpCn));
+}
+
+ZTAZMultiplicator::~ZTAZMultiplicator()
+{
+  free(buffer1);
+  free(buffer2);
+  delete(invCpCn);
+  delete(invCpCnT);
+}
+
+/*
+  computes x |--> Z^T A Z x
+
+  Z = P [ -C_p^{-1} C_n  ]
+        [   I            ]
+*/
+void ZTAZMultiplicator::Multiply(const double * x, double * output)
+{
+  int n3 = A->Getn();
+  int c = Cn->Getm(); // number of rows in Cn
+  Matrix<double> xM(n3 - c, 1, x, false, false); // dimension of x must be n3 - c
+  Matrix<double> buff_c(c, 1, buffer1, false, false);
+  buff_c = *invCpCn * xM;
+  buff_c *= -1.0; // first c rows of buffer1 are now set
+  memcpy(&buffer1[c], x, sizeof(double) * (n3-c)); // multiplication with identity
+
+  P->MultiplyVector(buffer1, buffer2);
+  A->MultiplyVector(buffer2, buffer1);
+  P->TransposeMultiplyVector(buffer1, n3, buffer2);
+
+  Matrix<double> buff2_c(c, 1, buffer2, false, false);
+  Matrix<double> outputM(n3-c, 1, output, false, false); // size of output is n3 - c
+  outputM = *invCpCnT * buff2_c;
+  outputM *= -1.0;
+  for(int i = 0; i < n3-c; i++)
+    output[i] += buffer2[i+c];
+}
+
diff --git a/libraries/sparseSolver/ZTAZMultiplicator.h b/libraries/sparseSolver/ZTAZMultiplicator.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ffdfb8815ef238105a8d107f237f581ace54a48
--- /dev/null
+++ b/libraries/sparseSolver/ZTAZMultiplicator.h
@@ -0,0 +1,69 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Helper class for the ARPACK solver.
+*/
+
+#ifndef _ZTAZMULTIPLICATOR_H_
+#define _ZTAZMULTIPLICATOR_H_
+
+#include "sparseMatrix.h"
+#include "matrix.h"
+
+/*
+  computes x |--> Z^T A Z x
+
+  Z = P [ -C_p^{-1} C_n  ]
+        [   I            ]
+*/
+
+class ZTAZMultiplicator
+{
+public:
+  ZTAZMultiplicator(SparseMatrix * A, SparseMatrix * P, Matrix<double> * invCp, Matrix<double> * Cn);
+  ~ZTAZMultiplicator();
+  void Multiply(const double * x, double * output);
+
+protected:
+  SparseMatrix * A;
+  SparseMatrix * P;
+  Matrix<double> * invCp;
+  Matrix<double> * Cn;
+  Matrix<double> * invCpCn;
+  Matrix<double> * invCpCnT;
+  double * buffer1;
+  double * buffer2;
+};
+
+#endif
+
diff --git a/src/libsparseSolver/invMKSolver.cpp b/libraries/sparseSolver/invMKSolver.cpp
similarity index 79%
rename from src/libsparseSolver/invMKSolver.cpp
rename to libraries/sparseSolver/invMKSolver.cpp
index 91dcea94f4c9ec7b6c6c6301d0c15838d94c071a..6892c4245cf3dd8180ed876abd5447e645b31b9c 100644
--- a/src/libsparseSolver/invMKSolver.cpp
+++ b/libraries/sparseSolver/invMKSolver.cpp
@@ -1,24 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "Large Modal Deformation Factory",                                    *
- * a pre-processing utility for model reduction of                       *
- * deformable objects undergoing large deformations.                     *
- *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,8 +36,6 @@
 #include <string.h>
 #include "invMKSolver.h"
 
-namespace vega
-{
 InvMKSolver::InvMKSolver(LinearSolver * invMSolver, SparseMatrix * K)
 {
   this->invMSolver = invMSolver;
@@ -57,4 +55,3 @@ void InvMKSolver::ComputeInvMK(double * x, double * output)
   memcpy(x, buffer, sizeof(double) * K->Getn());
   invMSolver->SolveLinearSystem(output, buffer);
 }
-}
diff --git a/src/util/largeModalDeformationFactory/StVKReducedInternalForcesWX.h b/libraries/sparseSolver/invMKSolver.h
similarity index 58%
rename from src/util/largeModalDeformationFactory/StVKReducedInternalForcesWX.h
rename to libraries/sparseSolver/invMKSolver.h
index 0a90c861ec1b5567b9f58a6ac11bcc0f46b21646..d44358a75a2c5e3ca3732104110d2d7e1f649a96 100644
--- a/src/util/largeModalDeformationFactory/StVKReducedInternalForcesWX.h
+++ b/libraries/sparseSolver/invMKSolver.h
@@ -1,24 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "Large Modal Deformation Factory",                                    *
- * a pre-processing utility for model reduction of                       *
- * deformable objects undergoing large deformations.                     *
- *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -32,32 +32,27 @@
  *************************************************************************/
 
 /*
-  A multi-threaded version of the StVKReducedInternalForces class, using 
-  the wxWidgets threading API. This class requires the wxWidgets header 
-  files. 
+  Helper class for the ARPACK solver.
 */
 
-#ifndef _STVKREDUCEDINTERNALFORCESWX_H_
-#define _STVKREDUCEDINTERNALFORCESWX_H_
+#ifndef _INVMKSOLVER_H_
+#define _INVMKSOLVER_H_
 
-#include "StVKReducedInternalForces.h"
+#include "linearSolver.h"
+#include "sparseMatrix.h"
 
-class StVKReducedInternalForcesWX : public StVKReducedInternalForces
+// computes x |--> M^{-1} K x
+class InvMKSolver
 {
 public:
-  // creates the reduced coefficients
-  StVKReducedInternalForcesWX(int r, double * U, VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity, int numThreads);
-
-  ~StVKReducedInternalForcesWX();
-
-  int GetStartElement(int rank);  
-  int GetEndElement(int rank);
+  InvMKSolver(LinearSolver * invMSolver, SparseMatrix * K);
+  ~InvMKSolver();
+  void ComputeInvMK(double * x, double * output);
 
 protected:
-  int numThreads;
-  int * startElement, * endElement;
-  double * internalForceBuffer;
+  double * buffer;
+  LinearSolver * invMSolver;
+  SparseMatrix * K;
 };
 
 #endif
-
diff --git a/libraries/sparseSolver/invZTAZMSolver.cpp b/libraries/sparseSolver/invZTAZMSolver.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..215da4c1df96289e53b60333656c06e0f96dce72
--- /dev/null
+++ b/libraries/sparseSolver/invZTAZMSolver.cpp
@@ -0,0 +1,62 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "invZTAZMSolver.h"
+
+InvZTAZMSolver::InvZTAZMSolver(SparseMatrix * A, SparseMatrix * M, PardisoSolver * ZTAZPardisoSolver, SparseMatrix * P, Matrix<double> * invCp, Matrix<double> * Cn)
+{
+  zTMZMultiplicator = new ZTAZMultiplicator(M, P, invCp, Cn);
+  invZTAZSolver = new InvZTAZSolver(A, ZTAZPardisoSolver, P, invCp, Cn);
+
+  int n3 = A->Getn();
+  int c = Cn->Getm(); // number of rows in Cn
+  dim = n3 - c;
+  buffer = (double*) malloc (sizeof(double) * dim);
+}
+
+InvZTAZMSolver::~InvZTAZMSolver()
+{
+  delete(zTMZMultiplicator);
+  delete(invZTAZSolver);
+  free(buffer);
+}
+
+// output <-- (Z^T A Z)^{-1} (Z^T M Z) x
+// x <-- (Z^T M Z) x
+void InvZTAZMSolver::ComputeInvZTAZM(double * x, double * output)
+{
+  zTMZMultiplicator->Multiply(x, buffer);
+  memcpy(x, buffer, sizeof(double) * dim);
+  invZTAZSolver->ComputeInvZTAZ(output, buffer);
+}
+
diff --git a/libraries/sparseSolver/invZTAZMSolver.h b/libraries/sparseSolver/invZTAZMSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..ba7238974d4f44ae29bb32ceaddc5043a4923753
--- /dev/null
+++ b/libraries/sparseSolver/invZTAZMSolver.h
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Helper class for the ARPACK solver.
+*/
+
+#ifndef _INVZTAZMSOLVER_H_
+#define _INVZTAZMSOLVER_H_
+
+#include "invZTAZSolver.h"
+#include "ZTAZMultiplicator.h"
+
+/*
+  Given a vector x, compute (Z^T A Z)^{-1} (Z^T M Z) x
+  Z = P [ -C_p^{-1} C_n  ]
+        [   I            ]
+*/
+
+class InvZTAZMSolver
+{
+public:
+  InvZTAZMSolver(SparseMatrix * A, SparseMatrix * M, PardisoSolver * ZTAZPardisoSolver, 
+                 SparseMatrix * P, Matrix<double> * invCp, Matrix<double> * Cn);
+  virtual ~InvZTAZMSolver();
+
+  // output <-- (Z^T A Z)^{-1} (Z^T M Z) x
+  // x <-- (Z^T M Z) x
+  void ComputeInvZTAZM(double * x, double * output);
+
+protected:
+  InvZTAZSolver * invZTAZSolver;
+  ZTAZMultiplicator * zTMZMultiplicator;
+  double * buffer;
+  int dim;
+};
+
+#endif
+
diff --git a/libraries/sparseSolver/invZTAZSolver.cpp b/libraries/sparseSolver/invZTAZSolver.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c5fb2b726a01de308b665c82006f8260026cf2e3
--- /dev/null
+++ b/libraries/sparseSolver/invZTAZSolver.cpp
@@ -0,0 +1,66 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "invZTAZSolver.h"
+#include "performanceCounter.h"
+
+InvZTAZSolver::InvZTAZSolver(SparseMatrix * A, PardisoSolver * ZTAZPardisoSolver_, SparseMatrix * P, Matrix<double> * invCp, Matrix<double> * Cn):ZTAZPardisoSolver(ZTAZPardisoSolver_)
+{
+  int n3 = A->Getn();
+  int c = Cn->Getm(); // number of rows in Cn
+
+  zTAZMultiplicator = new ZTAZMultiplicator(A, P, invCp, Cn);
+  cgSolver = new CGSolver(n3 - c, InvZTAZSolver::Multiplicator, (void*)zTAZMultiplicator);
+}
+
+void InvZTAZSolver::Multiplicator(const void * data, const double * x, double * Ax)
+{
+  ZTAZMultiplicator * zTAZMultiplicator = (ZTAZMultiplicator*)data;
+  zTAZMultiplicator->Multiply(x, Ax);
+}
+
+InvZTAZSolver::~InvZTAZSolver()
+{
+  delete(zTAZMultiplicator);
+  delete(cgSolver);
+}
+
+void InvZTAZSolver::ComputeInvZTAZ(double * x, double * output)
+{
+  //cgSolver->SolveLinearSystem(x, output);
+  ZTAZPardisoSolver->SolveLinearSystem(x, output);
+
+  cgSolver->SolveLinearSystemWithPreconditioner(ZTAZPardisoSolver, x, output, 1E-3, 200, 0);
+ // cgSolver->SolveLinearSystemWithoutPreconditioner(x, output, 1E-3, 200, 0);
+}
+
diff --git a/libraries/sparseSolver/invZTAZSolver.h b/libraries/sparseSolver/invZTAZSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..66920271a692a0a5b8d7eb096690d91237502c10
--- /dev/null
+++ b/libraries/sparseSolver/invZTAZSolver.h
@@ -0,0 +1,69 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Hongyi Xu, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Helper class for the ARPACK solver.
+*/
+
+#ifndef _INVZTAZSOLVER_H_
+#define _INVZTAZSOLVER_H_
+
+#include "ZTAZMultiplicator.h"
+#include "CGSolver.h"
+#include "PardisoSolver.h"
+/*
+  Given a vector rhs, compute (Z^T A Z)^{-1} rhs
+  Z = P [ -C_p^{-1} C_n  ]
+        [   I            ]
+*/
+class InvZTAZSolver
+{
+public:
+  InvZTAZSolver(SparseMatrix * A, PardisoSolver * ZTAZPardisoSolver, SparseMatrix * P, Matrix<double> * invCp, Matrix<double> * Cn);
+  virtual ~InvZTAZSolver();
+
+  // Solve for: (Z^T A Z) x = rhs
+  //   x = (Z^T A Z)^{-1} rhs
+  // rhs is not modified
+  void ComputeInvZTAZ(double * x, double * rhs);
+
+protected:
+  PardisoSolver * ZTAZPardisoSolver;
+  CGSolver * cgSolver;
+  ZTAZMultiplicator * zTAZMultiplicator;
+
+  static void Multiplicator(const void * data, const double * x, double * Ax);
+
+};
+
+#endif
+
diff --git a/src/libsparseSolver/linearSolver.cpp b/libraries/sparseSolver/linearSolver.cpp
similarity index 76%
rename from src/libsparseSolver/linearSolver.cpp
rename to libraries/sparseSolver/linearSolver.cpp
index c046fe44797092629785cc14d712ecc9fda4b78f..5bdfb42d9be9d32613e82c5fb30b56bc30a2a054 100644
--- a/src/libsparseSolver/linearSolver.cpp
+++ b/libraries/sparseSolver/linearSolver.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,16 +32,5 @@
 
 #include "linearSolver.h"
 
-namespace vega
-{
 LinearSolver::~LinearSolver() {}
-const std::string& LinearSolver::getSolverType() const
-{
-    return solverType;
-}
-void LinearSolver::setSolverType ( std::string& type )
-{
-    this->solverType = type;
-}
 
-}
diff --git a/libraries/sparseSolver/linearSolver.h b/libraries/sparseSolver/linearSolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ef61fc226dfa35a67ec928db7fb5380ba632ce9
--- /dev/null
+++ b/libraries/sparseSolver/linearSolver.h
@@ -0,0 +1,59 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _LINEARSOLVER_H_
+#define _LINEARSOLVER_H_
+
+/*
+  Abstract class to solve
+  A * x = rhs, where A is a square matrix.
+  
+  Jernej Barbic, USC, 2010
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+class LinearSolver
+{
+public:
+  virtual ~LinearSolver();
+
+  // solve: A * x = rhs
+  // return: 0 on success, error code otherwise
+  virtual int SolveLinearSystem(double * x, const double * rhs) = 0;
+
+protected:
+};
+
+#endif
+
diff --git a/libraries/sparseSolver/sparseSolverAvailability.h b/libraries/sparseSolver/sparseSolverAvailability.h
new file mode 100644
index 0000000000000000000000000000000000000000..d0452052f803157af0a323146ae67d94d7e60dfa
--- /dev/null
+++ b/libraries/sparseSolver/sparseSolverAvailability.h
@@ -0,0 +1,10 @@
+#ifndef _SPARSESOLVER_AVAILABILITY_H_
+#define _SPARSESOLVER_AVAILABILITY_H_
+
+// uncomment the corresponding line if Pardiso or SPOOLES is installed and configured
+
+//#define PARDISO_SOLVER_IS_AVAILABLE
+//#define SPOOLES_SOLVER_IS_AVAILABLE
+
+#endif
+
diff --git a/src/libopenGLHelper/saveScreenShot.h b/libraries/sparseSolver/sparseSolvers.h
similarity index 70%
rename from src/libopenGLHelper/saveScreenShot.h
rename to libraries/sparseSolver/sparseSolvers.h
index 9e492fcfe0b2dc3298ef3580fadbf7b480765945..0dfe921cbd2eddc61fd6e974a96beb899450f805 100644
--- a/src/libopenGLHelper/saveScreenShot.h
+++ b/libraries/sparseSolver/sparseSolvers.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
+ * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC   *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
  * http://www.jernejbarbic.com/code                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,19 +30,13 @@
  *                                                                       *
  *************************************************************************/
 
-#ifndef _SAVESCREENSHOT_H_
-#define _SAVESCREENSHOT_H_
+#ifndef _SPARSESOLVERS_H_
+#define _SPARSESOLVERS_H_
 
-#include "imageIO.h"
-
-/* Write a screenshot to the specified filename */
-
-class Screenshot
-{
-public:
-  static void SaveScreenshot(char * filename, ImageIO::fileFormatType fileFormat, int windowWidth, int windowHeight);
-  static void SaveStencilBuffer(char * filename, ImageIO::fileFormatType fileFormat, int windowWidth, int windowHeight, int rescale=0);
-};
+#include "PardisoSolver.h"
+#include "CGSolver.h"
+#include "SPOOLESSolver.h"
+#include "SPOOLESSolverMT.h"
 
 #endif
 
diff --git a/libraries/stencilForceModel/StVKStencilForceModel.cpp b/libraries/stencilForceModel/StVKStencilForceModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d467ba223f3de05b08243bb8c2c1f6677853c62
--- /dev/null
+++ b/libraries/stencilForceModel/StVKStencilForceModel.cpp
@@ -0,0 +1,56 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "StVKStencilForceModel.h"
+#include <cassert>
+using namespace std;
+
+StVKStencilForceModel::StVKStencilForceModel(StVKFEM * fem): stvkFEM(fem)
+{
+  n3 = stvkFEM->GetVolumetricMesh()->getNumVertices() * 3;
+  numStencilsInDifferentTypes.push_back(stvkFEM->GetVolumetricMesh()->getNumElements());
+  numStencilVerticesInDifferentTypes.push_back(stvkFEM->GetVolumetricMesh()->getNumElementVertices());
+}
+
+StVKStencilForceModel::~StVKStencilForceModel() {}
+
+void StVKStencilForceModel::GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix)
+{
+  assert(stencilType == 0);
+  stvkFEM->ComputeElementLocalEnergyAndInternalForcesAndStiffnessMatrix(u, stencilId, energy, internalForces, tangentStiffnessMatrix);
+}
+
+const int *StVKStencilForceModel::GetStencilVertexIndices(int stencilType, int stencilId) const
+{
+  assert(stencilType == 0);
+  return stvkFEM->GetVolumetricMesh()->getVertexIndices(stencilId);
+}
diff --git a/libraries/stencilForceModel/StVKStencilForceModel.h b/libraries/stencilForceModel/StVKStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..0fde039afe46335643d66a0b8ac8f065caea7ce1
--- /dev/null
+++ b/libraries/stencilForceModel/StVKStencilForceModel.h
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVK_STENCIL_FORCEMODEL_H_
+#define _STVK_STENCIL_FORCEMODEL_H_
+
+#include "StVKFEM.h"
+#include "stencilForceModel.h"
+
+// Stencils for StVK FEM.
+// A stencil is one FEM element.
+// See comments in the parent class.
+class StVKStencilForceModel: public StencilForceModel
+{
+public:
+  StVKStencilForceModel(StVKFEM * stvkFEM);
+  virtual ~StVKStencilForceModel();
+
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+  StVKFEM * GetForceModelHandle() { return stvkFEM; }
+
+protected:
+  StVKFEM * stvkFEM;
+};
+
+#endif
+
diff --git a/libraries/stencilForceModel/clothBWStencilForceModel.cpp b/libraries/stencilForceModel/clothBWStencilForceModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5ca186bc6952ccb32f56ceec24ff36c07372208
--- /dev/null
+++ b/libraries/stencilForceModel/clothBWStencilForceModel.cpp
@@ -0,0 +1,82 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "clothBWStencilForceModel.h"
+#include <iostream>
+#include <cstdlib>
+
+ClothBWStencilForceModel::ClothBWStencilForceModel(ClothBW * clothBW_): clothBW(clothBW_)
+{ 
+  numStencilsInDifferentTypes.push_back(clothBW->GetNumTriangles());
+  numStencilsInDifferentTypes.push_back(clothBW->GetNumQuads());
+
+  numStencilVerticesInDifferentTypes.push_back(3);
+  numStencilVerticesInDifferentTypes.push_back(4);
+
+  n3 = clothBW->GetNumVertices() * 3;
+}
+
+ClothBWStencilForceModel::~ClothBWStencilForceModel()
+{
+}
+
+void ClothBWStencilForceModel::GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix)
+{
+  if (stencilType == 0)
+    clothBW->ComputeTriangleElement(stencilId, u, energy, internalForces, tangentStiffnessMatrix);
+  else if (stencilType == 1)
+    clothBW->ComputeQuadElement(stencilId, u, energy, internalForces, tangentStiffnessMatrix);
+  else {
+    std::cerr << "Wrong type of element!" << std::endl;
+    abort();
+  }
+}
+
+const int *ClothBWStencilForceModel::GetStencilVertexIndices(int stencilType, int stencilId) const
+{
+  if (stencilType == 0)
+    return clothBW->GetTriangleVertexIndices(stencilId);
+  else if (stencilType == 1)
+    return clothBW->GetQuadVertexIndices(stencilId);
+  else {
+    std::cerr << "Wrong type of element!" << std::endl;
+    abort();
+  }
+
+  return nullptr;
+}
+
+void ClothBWStencilForceModel::GetVertexGravityForce(int vid, double gravity[3])
+{
+  clothBW->ComputeVertexGravity(vid, gravity);
+}
+
diff --git a/libraries/stencilForceModel/clothBWStencilForceModel.h b/libraries/stencilForceModel/clothBWStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..37cd5494036a485a7e938a7f967beac840ad79e0
--- /dev/null
+++ b/libraries/stencilForceModel/clothBWStencilForceModel.h
@@ -0,0 +1,61 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef _CLOTHBW_STENCIL_FORCEMODEL_H_
+#define _CLOTHBW_STENCIL_FORCEMODEL_H_
+
+#include "clothBW.h"
+#include "stencilForceModel.h"
+
+// Stencils for cloth simulation. There are two different stencils here.
+// One is a triangle (# vertices = 3) (for in-place stretch and shear);
+// and the other is an edge joining two triangles (# vertices = 4) (for bending).
+// See comments in the parent class.
+class ClothBWStencilForceModel : public StencilForceModel
+{
+public:
+  ClothBWStencilForceModel(ClothBW * clothBW);
+  virtual ~ClothBWStencilForceModel();
+
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+
+  virtual void GetVertexGravityForce(int vertexId, double gravity[3]) override;
+
+  ClothBW * GetForceModelHandle() { return clothBW; }
+protected:
+  ClothBW * clothBW;
+};
+
+#endif
+
diff --git a/libraries/stencilForceModel/corotationalLinearFEMStencilForceModel.cpp b/libraries/stencilForceModel/corotationalLinearFEMStencilForceModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c3a87ef4b40e191fd364ecd02ed8387cbda5c720
--- /dev/null
+++ b/libraries/stencilForceModel/corotationalLinearFEMStencilForceModel.cpp
@@ -0,0 +1,60 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "corotationalLinearFEMStencilForceModel.h"
+#include <cassert>
+
+CorotationalLinearFEMStencilForceModel::CorotationalLinearFEMStencilForceModel(CorotationalLinearFEM * fem) : corotationalLinearFEM(fem)
+{
+  n3 = corotationalLinearFEM->GetVolumetricMesh()->getNumVertices() * 3;
+
+  numStencilsInDifferentTypes.push_back(corotationalLinearFEM->GetVolumetricMesh()->getNumElements());
+  numStencilVerticesInDifferentTypes.push_back(corotationalLinearFEM->GetVolumetricMesh()->getNumElementVertices());
+
+  warp = 1;
+}
+
+CorotationalLinearFEMStencilForceModel::~CorotationalLinearFEMStencilForceModel()
+{
+}
+
+const int *CorotationalLinearFEMStencilForceModel::GetStencilVertexIndices(int stencilType, int stencilId) const
+{
+  assert(stencilType == 0);
+
+  return corotationalLinearFEM->GetVolumetricMesh()->getVertexIndices(stencilId);
+}
+
+void CorotationalLinearFEMStencilForceModel::GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix)
+{
+  corotationalLinearFEM->ComputeElementEnergyAndForceAndStiffnessMatrix(stencilId, u, energy, internalForces, tangentStiffnessMatrix, warp);
+}
diff --git a/libraries/stencilForceModel/corotationalLinearFEMStencilForceModel.h b/libraries/stencilForceModel/corotationalLinearFEMStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..f8774ab19a1e888e21e7cfb58ca8f039a9745ba3
--- /dev/null
+++ b/libraries/stencilForceModel/corotationalLinearFEMStencilForceModel.h
@@ -0,0 +1,60 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _COROTATIONALLINEARFEM_STENCIL_FORCEMODEL_H_
+#define _COROTATIONALLINEARFEM_STENCIL_FORCEMODEL_H_
+
+#include "corotationalLinearFEM.h"
+#include "stencilForceModel.h"
+
+// Stencils for corotational linear FEM.
+// A stencil is one FEM element.
+// See comments in the parent class.
+class CorotationalLinearFEMStencilForceModel : public StencilForceModel
+{
+public:
+  CorotationalLinearFEMStencilForceModel(CorotationalLinearFEM * corotationalLinearFEM);
+  virtual ~CorotationalLinearFEMStencilForceModel();
+
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+  CorotationalLinearFEM * GetForceModelHandle() { return corotationalLinearFEM; }
+  void SetWarp(int warp) { this->warp = warp; }
+
+protected:
+  CorotationalLinearFEM * corotationalLinearFEM;
+  int warp;
+};
+
+#endif
+
diff --git a/libraries/stencilForceModel/forceModelAssembler.cpp b/libraries/stencilForceModel/forceModelAssembler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b1e25d61cd6aa27c227dff6340fca860abf61bd5
--- /dev/null
+++ b/libraries/stencilForceModel/forceModelAssembler.cpp
@@ -0,0 +1,328 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "forceModelAssembler.h"
+#include <cassert>
+
+using namespace std;
+
+ForceModelAssembler::ForceModelAssembler(StencilForceModel *eleFM) : stencilForceModel(eleFM)
+{
+  r = stencilForceModel->Getn3();
+  SparseMatrixOutline *smo = new SparseMatrixOutline(r);
+  SparseMatrixOutline *smo1 = new SparseMatrixOutline(r / 3);
+
+  for (int eltype = 0; eltype < stencilForceModel->GetNumStencilTypes(); eltype++) 
+  {
+    int nelev = stencilForceModel->GetNumStencilVertices(eltype);
+    for (int ele = 0; ele < stencilForceModel->GetNumStencils(eltype); ele++) 
+    {
+      const int *vertexIndices = stencilForceModel->GetStencilVertexIndices(eltype, ele);
+
+      for (int vi = 0; vi < nelev; vi++) 
+      {
+        for (int vj = 0; vj < nelev; vj++) 
+        {
+          smo->AddBlock3x3Entry(vertexIndices[vi], vertexIndices[vj]);
+          smo1->AddEntry(vertexIndices[vi], vertexIndices[vj]);
+        }
+      }
+    }
+  }
+
+  // compute stiffness matrix topology
+  Ktemplate = new SparseMatrix(smo);
+  delete smo;
+
+  SparseMatrix *vertexK = new SparseMatrix(smo1);
+  delete smo1;
+
+  inverseIndices.resize(stencilForceModel->GetNumStencilTypes());
+  for (int eltype = 0; eltype < stencilForceModel->GetNumStencilTypes(); eltype++) 
+  {
+    int nelev = stencilForceModel->GetNumStencilVertices(eltype);
+    std::vector<int> &indices = inverseIndices[eltype];
+    indices.resize(nelev * nelev * stencilForceModel->GetNumStencils(eltype));
+
+    for (int ele = 0; ele < stencilForceModel->GetNumStencils(eltype); ele++) 
+    {
+      const int *vertexIndices = stencilForceModel->GetStencilVertexIndices(eltype, ele);
+      int *inverseVtxIdx = indices.data() + ele * nelev * nelev;
+
+      for (int vi = 0; vi < nelev; vi++) 
+      {
+        for (int vj = 0; vj < nelev; vj++) 
+        {
+          int vtxColIdx = vertexK->GetInverseIndex(vertexIndices[vi], vertexIndices[vj]);
+          assert(vtxColIdx >= 0);
+          inverseVtxIdx[vj * nelev + vi] = vtxColIdx;
+        } // end vi
+      } // end vj
+    } // end ele
+  } // end ele type
+
+  delete vertexK;
+
+  // initialize all necessary buffers
+  bufferExamplars.resize(stencilForceModel->GetNumStencilTypes());
+  for (int eltype = 0; eltype < stencilForceModel->GetNumStencilTypes(); eltype++) 
+  {
+    int nelev = stencilForceModel->GetNumStencilVertices(eltype);
+    bufferExamplars[eltype].resize(nelev * 3 + nelev * nelev * 9);
+  }
+
+#ifdef USE_TBB
+  localBuffers.resize(stencilForceModel->GetNumStencilTypes());
+  for (int eltype = 0; eltype < stencilForceModel->GetNumStencilTypes(); eltype++) 
+  {
+    localBuffers[eltype] = new tbb::enumerable_thread_specific<Buffer>(bufferExamplars[eltype]);
+  }
+
+  partitioners = new tbb::affinity_partitioner[stencilForceModel->GetNumStencilTypes()];
+
+  internalForceVertexLocks = new tbb::spin_mutex[r / 3];
+  stiffnessMatrixVertexRowLocks = new tbb::spin_mutex[r / 3];
+#endif
+}
+
+ForceModelAssembler::~ForceModelAssembler()
+{
+#ifdef USE_TBB
+  delete[] internalForceVertexLocks;
+  delete[] stiffnessMatrixVertexRowLocks;
+  delete[] partitioners;
+#endif
+}
+
+void ForceModelAssembler::GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix)
+{
+  *tangentStiffnessMatrix = new SparseMatrix(*Ktemplate);
+  std::vector<double> zeros(stencilForceModel->Getn3(), 0.0);
+  GetEnergyAndForceAndMatrix(zeros.data(), nullptr, nullptr, *tangentStiffnessMatrix);
+}
+
+void ForceModelAssembler::GetEnergyAndForceAndMatrix(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
+{
+  // reset to zero
+  if (internalForces)
+    memset(internalForces, 0, sizeof(double) * r);
+
+  if (tangentStiffnessMatrix)
+    tangentStiffnessMatrix->ResetToZero();
+
+#ifdef USE_TBB
+  for (auto itt = energyLocalBuffer.begin(); itt != energyLocalBuffer.end(); ++itt)
+    *itt = 0.0;
+
+  tbb::parallel_for(0, stencilForceModel->GetNumStencilTypes(), 1, [&] (int eltype) 
+  {
+    tbb::enumerable_thread_specific<Buffer> &tls = *localBuffers[eltype];
+    int nelev = stencilForceModel->GetNumStencilVertices(eltype);
+    int nele = stencilForceModel->GetNumStencils(eltype);
+
+    tbb::parallel_for(0, nele, 1, [&] (int ele) 
+    {
+      Buffer &localBuffer = tls.local();
+      double *fEle = localBuffer.data();
+      double *KEle = localBuffer.data() + nelev * 3;
+
+      double energyEle = 0;
+
+      stencilForceModel->GetStencilLocalEnergyAndForceAndMatrix(eltype, ele, u,
+        (energy ? &energyEle : nullptr),
+        (internalForces ? fEle : nullptr),
+        (tangentStiffnessMatrix ? KEle : nullptr)
+      );
+
+      const int *vIndices = stencilForceModel->GetStencilVertexIndices(eltype, ele);
+
+      if (internalForces) 
+      {
+        for (int v = 0; v < nelev; v++) 
+        {
+          internalForceVertexLocks[vIndices[v]].lock();
+
+          internalForces[vIndices[v] * 3] += fEle[v * 3];
+          internalForces[vIndices[v] * 3 + 1] += fEle[v * 3 + 1];
+          internalForces[vIndices[v] * 3 + 2] += fEle[v * 3 + 2];
+
+          internalForceVertexLocks[vIndices[v]].unlock();
+        }
+      }
+
+      if (tangentStiffnessMatrix) 
+      {
+        const int *vtxColIndices = inverseIndices[eltype].data() + ele * nelev * nelev;
+
+        // write matrices in place
+        for (int va = 0; va < nelev; va++) 
+        {
+          int vIdxA = vIndices[va];
+          stiffnessMatrixVertexRowLocks[vIdxA].lock();
+
+          for (int vb = 0; vb < nelev; vb++) 
+          {
+            int columnIndexCompressed = vtxColIndices[vb * nelev + va];
+
+            for (int i = 0; i < 3; i++) 
+            {
+              for (int j = 0; j < 3; j++) 
+              {
+                int row = 3 * vIdxA + i;
+                int columnIndex = 3 * columnIndexCompressed + j;
+
+                int local_row = 3 * va + i;
+                int local_col = 3 * vb + j;
+
+                tangentStiffnessMatrix->AddEntry(row, columnIndex, KEle[local_col * nelev * 3 + local_row]);
+              } // i
+            } // j
+          } // vb
+          stiffnessMatrixVertexRowLocks[vIdxA].unlock();
+        } // va
+      }
+
+      if (energy) 
+      {
+        energyLocalBuffer.local() += energyEle;
+      }
+    }, partitioners[eltype]);
+  });
+
+  if (energy) 
+  {
+    *energy = 0;
+    for (auto itt = energyLocalBuffer.begin(); itt != energyLocalBuffer.end(); ++itt) 
+    {
+      *energy += *itt;
+    }
+  }
+#else
+  for (int eltype = 0; eltype < stencilForceModel->GetNumStencilTypes(); eltype++) 
+  {
+    int nelev = stencilForceModel->GetNumStencilVertices(eltype);
+    int nele = stencilForceModel->GetNumStencils(eltype);
+
+    for (int ele = 0; ele < nele; ele++) 
+    {
+      double *fEle = bufferExamplars[eltype].data();
+      double *KEle = bufferExamplars[eltype].data() + nelev * 3;
+
+      double energyEle = 0;
+
+      stencilForceModel->GetStencilLocalEnergyAndForceAndMatrix(eltype, ele, u,
+        (energy ? &energyEle : nullptr),
+        (internalForces ? fEle : nullptr),
+        (tangentStiffnessMatrix ? KEle : nullptr)
+      );
+
+      const int *vIndices = stencilForceModel->GetStencilVertexIndices(eltype, ele);
+
+      if (internalForces) 
+      {
+        for (int v = 0; v < nelev; v++) 
+        {
+          internalForces[vIndices[v] * 3] += fEle[v * 3];
+          internalForces[vIndices[v] * 3 + 1] += fEle[v * 3 + 1];
+          internalForces[vIndices[v] * 3 + 2] += fEle[v * 3 + 2];
+        }
+      }
+
+      if (tangentStiffnessMatrix) 
+      {
+        const int *vtxColIndices = inverseIndices[eltype].data() + ele * nelev * nelev;
+
+        // write matrices in place
+        for (int va = 0; va < nelev; va++) 
+        {
+          int vIdxA = vIndices[va];
+          for (int vb = 0; vb < nelev; vb++) 
+          {
+            int columnIndexCompressed = vtxColIndices[vb * nelev + va];
+
+            for (int i = 0; i < 3; i++) 
+            {
+              for (int j = 0; j < 3; j++) 
+              {
+                int row = 3 * vIdxA + i;
+                int columnIndex = 3 * columnIndexCompressed + j;
+
+                int local_row = 3 * va + i;
+                int local_col = 3 * vb + j;
+
+                tangentStiffnessMatrix->AddEntry(row, columnIndex, KEle[local_col * nelev * 3 + local_row]);
+              } // i
+            } // j
+          } // vb
+        } // va
+      }
+
+      if (energy) 
+      {
+        *energy += energyEle;
+      }
+    }
+  }
+#endif
+
+  if (internalForces)
+  {
+    for (int vi = 0; vi < stencilForceModel->Getn3() / 3; vi++) {
+      double g[3];
+      stencilForceModel->GetVertexGravityForce(vi, g);
+      internalForces[vi * 3 + 1] += g[1];
+    }
+  }
+}
+
+double ForceModelAssembler::GetElasticEnergy(const double *u)
+{
+  double E = 0;
+  GetEnergyAndForceAndMatrix(u, &E, nullptr, nullptr);
+
+  return E;
+}
+
+void ForceModelAssembler::GetInternalForce(const double * u, double * internalForces)
+{
+  GetEnergyAndForceAndMatrix(u, nullptr, internalForces, nullptr);
+}
+
+void ForceModelAssembler::GetTangentStiffnessMatrix(const double * u, SparseMatrix *tangentStiffnessMatrix)
+{
+  GetEnergyAndForceAndMatrix(u, nullptr, nullptr, tangentStiffnessMatrix);
+}
+
+void ForceModelAssembler::GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
+{
+  GetEnergyAndForceAndMatrix(u, nullptr, internalForces, tangentStiffnessMatrix);
+}
diff --git a/libraries/stencilForceModel/forceModelAssembler.h b/libraries/stencilForceModel/forceModelAssembler.h
new file mode 100644
index 0000000000000000000000000000000000000000..6e0b40d49c9306b9d7ea4b229d8a448491064038
--- /dev/null
+++ b/libraries/stencilForceModel/forceModelAssembler.h
@@ -0,0 +1,98 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _FORCEMODEL_ASSEMBLER_H_
+#define _FORCEMODEL_ASSEMBLER_H_
+
+#include "forceModel.h"
+#include "stencilForceModel.h"
+
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+
+/*
+  For each stencil type, this class assembles values at individual stencils into global object quantities.
+  E.g., form the global internal force vector from stencil force vectors, or form the global tangent stiffness matrix
+  from stencil tangent stiffness matrices.
+  Please see also stencilForceModel.h for more information.
+  This class inherits from ForceModel and implements the necessary interfaces.
+  If Intel TBB is provided, assembly will be performed in parallel.
+  The number of threads can be controlled outside the class using the Intel TBB APIs.
+  If Intel TBB is not provided, the computation will be single-threaded.
+*/
+
+class ForceModelAssembler : public ForceModel
+{
+public:
+  ForceModelAssembler(StencilForceModel *stencilForceModel);
+  virtual ~ForceModelAssembler();
+
+  // See comments in the parent class for the following functions.
+  virtual double GetElasticEnergy(const double * u) override;
+  virtual void GetInternalForce(const double * u, double * internalForces) override;
+  virtual void GetTangentStiffnessMatrixTopology(SparseMatrix ** tangentStiffnessMatrix) override;
+  virtual void GetTangentStiffnessMatrix(const double * u, SparseMatrix * tangentStiffnessMatrix) override;
+  virtual void GetForceAndMatrix(const double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix) override;
+
+  // This function computes the energy, internal forces and tangent stiffness matrix of the 'object'.
+  // u is the displacement vector of all vertices.
+  // energy points to a double variable.
+  // internalForces points to a double array which dimension is the same as u.
+  // tangentStiffnessMatrix point to a sparse matrix object.
+  // energy, internalForces, tangentStiffnessMatrix can be nullptr. If nullptr, the corresponding quantity will not be computed.
+  virtual void GetEnergyAndForceAndMatrix(const double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix);
+
+protected:
+  StencilForceModel * stencilForceModel = nullptr;
+  SparseMatrix * Ktemplate = nullptr;
+  std::vector<std::vector<int>> inverseIndices;
+
+  // data structures for parallelism
+#ifdef USE_TBB
+  typedef tbb::cache_aligned_allocator<double> BufferAllocator;
+  typedef std::vector<double, BufferAllocator> Buffer;
+  std::vector<tbb::enumerable_thread_specific<Buffer>*> localBuffers;
+  tbb::affinity_partitioner * partitioners = nullptr;
+  tbb::enumerable_thread_specific<double> energyLocalBuffer;
+  tbb::spin_mutex * internalForceVertexLocks, *stiffnessMatrixVertexRowLocks = nullptr;
+#else
+  // data structures for single-threaded computation
+  typedef std::allocator<double> BufferAllocator;
+  typedef std::vector<double, BufferAllocator> Buffer;
+#endif
+
+  std::vector<Buffer> bufferExamplars;
+};
+
+#endif
+
diff --git a/libraries/stencilForceModel/isotropicHyperelasticFEMStencilForceModel.cpp b/libraries/stencilForceModel/isotropicHyperelasticFEMStencilForceModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..67c54714a91a005fa3a76c72669c5ec2fb31d45b
--- /dev/null
+++ b/libraries/stencilForceModel/isotropicHyperelasticFEMStencilForceModel.cpp
@@ -0,0 +1,60 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "isotropicHyperelasticFEMStencilForceModel.h"
+#include <cassert>
+
+using namespace std;
+
+IsotropicHyperelasticFEMStencilForceModel::IsotropicHyperelasticFEMStencilForceModel(IsotropicHyperelasticFEM * isotropicHyperelasticFEM_): isotropicHyperelasticFEM(isotropicHyperelasticFEM_)
+{
+  n3 = 3 * isotropicHyperelasticFEM->GetTetMesh()->getNumVertices();
+  numStencilsInDifferentTypes.push_back(isotropicHyperelasticFEM->GetTetMesh()->getNumElements());
+  numStencilVerticesInDifferentTypes.push_back(isotropicHyperelasticFEM->GetTetMesh()->getNumElementVertices());
+}
+
+IsotropicHyperelasticFEMStencilForceModel::~IsotropicHyperelasticFEMStencilForceModel() {}
+
+void IsotropicHyperelasticFEMStencilForceModel::GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix)
+{
+  assert(stencilType == 0);
+
+  isotropicHyperelasticFEM->GetElementLocalEnergyAndForceAndMatrix(stencilId, u, energy, internalForces, tangentStiffnessMatrix);
+}
+
+const int *IsotropicHyperelasticFEMStencilForceModel::GetStencilVertexIndices(int stencilType, int stencilId) const
+{
+  assert(stencilType == 0);
+
+  return isotropicHyperelasticFEM->GetTetMesh()->getVertexIndices(stencilId);
+}
+
diff --git a/libraries/stencilForceModel/isotropicHyperelasticFEMStencilForceModel.h b/libraries/stencilForceModel/isotropicHyperelasticFEMStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..7bbd0176e6482cacbad34e64ba5d963491239a25
--- /dev/null
+++ b/libraries/stencilForceModel/isotropicHyperelasticFEMStencilForceModel.h
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _ISOTROPICHYPERELASTICFEM_STENCIL_FORCEMODEL_H_
+#define _ISOTROPICHYPERELASTICFEM_STENCIL_FORCEMODEL_H_
+
+#include "isotropicHyperelasticFEM.h"
+#include "stencilForceModel.h"
+
+// Stencils for isotropic hyperelastic FEM.
+// A stencil here refers to one FEM element.
+// See comments in the parent class.
+class IsotropicHyperelasticFEMStencilForceModel : public StencilForceModel
+{
+public:
+  IsotropicHyperelasticFEMStencilForceModel(IsotropicHyperelasticFEM * isotropicHyperelasticFEM);
+  virtual ~IsotropicHyperelasticFEMStencilForceModel();
+
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+  IsotropicHyperelasticFEM * GetForceModelHandle() { return isotropicHyperelasticFEM; }
+
+protected:
+  IsotropicHyperelasticFEM * isotropicHyperelasticFEM;
+};
+
+#endif
+
diff --git a/libraries/stencilForceModel/linearFEMStencilForceModel.cpp b/libraries/stencilForceModel/linearFEMStencilForceModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c80378405ebf68eb6ab445bfe8a59fe40c44435
--- /dev/null
+++ b/libraries/stencilForceModel/linearFEMStencilForceModel.cpp
@@ -0,0 +1,119 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "linearFEMStencilForceModel.h"
+
+#ifdef USE_TBB
+#include <tbb/tbb.h>
+#endif
+
+#include <cassert>
+#include <cstring>
+#include <iostream>
+
+using namespace std;
+
+LinearFEMStencilForceModel::LinearFEMStencilForceModel(StencilForceModel * fem): stencilForceModel(fem)
+{
+  assert(dynamic_cast<LinearFEMStencilForceModel*>(stencilForceModel) == nullptr);
+
+  n3 = fem->Getn3();
+  elementK.resize(fem->GetNumStencilTypes());
+
+  std::vector<double> u(n3, 0.0);
+  for (int eltype = 0; eltype < fem->GetNumStencilTypes(); eltype++) 
+  {
+    numStencilsInDifferentTypes.push_back(stencilForceModel->GetNumStencils(eltype));
+    numStencilVerticesInDifferentTypes.push_back(stencilForceModel->GetNumStencilVertices(eltype));
+
+    int nelev = stencilForceModel->GetNumStencilVertices(eltype);
+    int nele = stencilForceModel->GetNumStencils(eltype);
+
+    elementK[eltype].resize(nele * nelev * nelev * 9);
+
+    cout << "Computing element stiffness matrices.." << endl;
+
+#ifdef USE_TBB
+    tbb::parallel_for(0, nele, [&] (int el) 
+    {
+      stencilForceModel->GetStencilLocalEnergyAndForceAndMatrix(eltype, el, u.data(), nullptr, nullptr, elementK[eltype].data() + el * nelev * nelev * 9);
+    }, tbb::static_partitioner());
+#else
+    for (int el = 0; el < nele; el++)
+    {
+      stencilForceModel->GetStencilLocalEnergyAndForceAndMatrix(eltype, el, u.data(), nullptr, nullptr, elementK[eltype].data() + el * nelev * nelev * 9);
+    }
+#endif
+
+    cout << "Done." << endl;
+  }
+}
+
+LinearFEMStencilForceModel::~LinearFEMStencilForceModel() {}
+
+void LinearFEMStencilForceModel::GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix)
+{
+  int nelev = numStencilVerticesInDifferentTypes[stencilType];
+  int dof = nelev * 3;
+  int dof2 = dof * dof;
+  const double *K0 = elementK[stencilType].data() + stencilId * dof2;
+  const int *vtxIdx = stencilForceModel->GetStencilVertexIndices(stencilType, stencilId);
+
+  if (tangentStiffnessMatrix)
+    memcpy(tangentStiffnessMatrix, K0, sizeof(double) * dof2);
+
+  if (internalForces) 
+  {
+    memset(internalForces, 0, sizeof(double) * dof);
+    for (int i = 0; i < dof; i++)
+    {
+      for (int j = 0; j < dof; j++)
+      {
+        internalForces[j] += K0[i * dof + j] * u[vtxIdx[i / 3] * 3 + (i % 3)];
+      }
+    }
+  }
+
+  if (energy)
+  {
+    *energy = 0.0;
+    for (int i = 0; i < dof; ++i)
+      for (int j = 0; j < dof; ++j)
+        *energy += u[vtxIdx[i / 3] * 3 + (i % 3)] * u[vtxIdx[j / 3] * 3 + (j % 3)] * K0[i * dof + j] * 0.5;
+  }
+
+}
+
+const int *LinearFEMStencilForceModel::GetStencilVertexIndices(int stencilType, int stencilId) const
+{
+  return stencilForceModel->GetStencilVertexIndices(stencilType, stencilId);
+}
diff --git a/libraries/stencilForceModel/linearFEMStencilForceModel.h b/libraries/stencilForceModel/linearFEMStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..b82436fc0803e09866eca4a90ebfa2b321e81aac
--- /dev/null
+++ b/libraries/stencilForceModel/linearFEMStencilForceModel.h
@@ -0,0 +1,64 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+
+#ifndef _LINEARFEM_STENCIL_FORCEMODEL_H_
+#define _LINEARFEN_STENCIL_FORCEMODEL_H_
+
+#include "stencilForceModel.h"
+
+#include <vector>
+
+// Stencils for linear FEM.
+// A stencil is one FEM element.
+// See comments in the parent class.
+class LinearFEMStencilForceModel : public StencilForceModel
+{
+public:
+  // this class is able to linearize any non-linear force model
+  // therefore, any StencilForceModel (except itself) can be used to initialize this class.
+  LinearFEMStencilForceModel(StencilForceModel * stencilForceModel);
+  virtual ~LinearFEMStencilForceModel();
+
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+  StencilForceModel * GetForceModelHandle() { return stencilForceModel; }
+
+protected:
+  StencilForceModel * stencilForceModel;
+
+  std::vector<std::vector<double>> elementK;
+};
+
+#endif
+
diff --git a/libraries/stencilForceModel/massSpringStencilForceModel.cpp b/libraries/stencilForceModel/massSpringStencilForceModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb28362c8929cca6a853c9fde36eb9c928db65b2
--- /dev/null
+++ b/libraries/stencilForceModel/massSpringStencilForceModel.cpp
@@ -0,0 +1,58 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "massSpringStencilForceModel.h"
+#include <cassert>
+
+using namespace std;
+
+MassSpringStencilForceModel::MassSpringStencilForceModel(MassSpringSystem *system) : massSpringSystem(system)
+{
+  n3 = massSpringSystem->GetNumParticles() * 3;
+
+  numStencilsInDifferentTypes.push_back(massSpringSystem->GetNumEdges());
+  numStencilVerticesInDifferentTypes.push_back(2);
+}
+
+void MassSpringStencilForceModel::GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double *u, double *energy, double *internalForces, double *tangentStiffnessMatrix)
+{
+  assert(stencilType == 0);
+
+  massSpringSystem->ComputeEdgeInfo(stencilId, u, energy, internalForces, tangentStiffnessMatrix);
+}
+
+const int * MassSpringStencilForceModel::GetStencilVertexIndices(int stencilType, int stencilId) const
+{
+  assert(stencilType == 0);
+
+  return massSpringSystem->GetEdges() + stencilId * 2;
+}
diff --git a/libraries/stencilForceModel/massSpringStencilForceModel.h b/libraries/stencilForceModel/massSpringStencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..9d41ed1fb5e17d8edd3110364d8734c8caaf67d1
--- /dev/null
+++ b/libraries/stencilForceModel/massSpringStencilForceModel.h
@@ -0,0 +1,55 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _MASS_SPRING_STENCIL_FORCEMODEL_H_
+#define _MASS_SPRING_STENCIL_FORCEMODEL_H_
+
+#include "massSpringSystem.h"
+#include "stencilForceModel.h"
+
+// Stencils for mass-spring systems.
+// A stencil is one spring.
+// See comments in the parent class.
+class MassSpringStencilForceModel : public StencilForceModel
+{
+public:
+  MassSpringStencilForceModel(MassSpringSystem *system);
+  virtual ~MassSpringStencilForceModel() {}
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const override;
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) override;
+
+protected:
+  MassSpringSystem *massSpringSystem;
+};
+
+#endif
+
diff --git a/libraries/stencilForceModel/stencilForceModel.h b/libraries/stencilForceModel/stencilForceModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..76b4f73014ca45a7a12b5ffb1dd3c117aaeffb58
--- /dev/null
+++ b/libraries/stencilForceModel/stencilForceModel.h
@@ -0,0 +1,98 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Stencil Force Model" library , Copyright (C) 2018 USC                *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Bohan Wang, Jernej Barbic                               *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STENCILFORCEMODEL_H_
+#define _STENCILFORCEMODEL_H_
+
+#include <vector>
+
+/*
+  To simulate a deformable object, we usually discretize it into elements.
+  For example, a 3D solid can be represented by a set of tetrahedra,
+  and a cloth can be represented by a set of triangles. When we simulate 
+  an object, we need to evaluate the elastic energy, internal forces
+  and sometimes the tangent stiffness matrix. These quantities are usually
+  evaluated by computing them for each individual element
+  and then assembled into a single global per-object vector.
+  For example, when we compute the elastic energy of a tetrahedral mesh,
+  we can compute the energy of each tetrahedron first and then add them together.
+  In summary, the entire evaluation process can be considered as
+  an assembling of quantities at the individual units. We call the units "stencils".
+  In cloth simulation, a stencil is a single triangle for in-plane stretch and shear.
+  Another stencil type is an edge between two triangles to model the bending energy.
+  In FEM solid simulation, each element can be treated as a stencil.
+  In mass-spring systems, each spring can be treated as a stencil.
+  In this file, we define a class called StencilForceModel that provides an 
+  abstract interface to stencils.
+*/
+
+class StencilForceModel
+{
+public:
+  StencilForceModel() {}
+  virtual ~StencilForceModel() {}
+
+  // Get the number of degrees of freedom (DOF) of the object (= # vertices x 3).
+  int Getn3() const { return n3; }
+  // Return the number of different types of stencils within a single object.
+  int GetNumStencilTypes() const { return (int)numStencilsInDifferentTypes.size(); }
+  // Return the number of stencils for each type.
+  int GetNumStencils(int stencilType) const { return numStencilsInDifferentTypes[stencilType]; }
+  // Return the number of vertices involved in a single stencil.
+  int GetNumStencilVertices(int stencilType) const { return numStencilVerticesInDifferentTypes[stencilType]; }
+  // Return the number of DOFs of the internal forces in a single stencil.
+  int GetStencilInternalForceSize(int stencilType) const { return numStencilVerticesInDifferentTypes[stencilType] * 3; }
+  // Return the size of the stiffness matrix of a single stencil.
+  int GetStencilStiffnessMatrixSize(int stencilType) const { return GetStencilInternalForceSize(stencilType) * GetStencilInternalForceSize(stencilType); }
+  // Compute the energy, internal forces, tangent stiffness matrix of stencil stencilId in type stencilType.
+  // Parameter u is the displacement vector of object vertices in R^n3.
+  // Energy points to a double value.
+  // internalForces points to a dense vector.
+  // tangentStiffnessMatrix points to a dense column-major matrix
+  // The pointers energy, internalForces and tangentStiffnessMatrix can be nullptr, in which case the corresponding quantity will not be computed.
+  virtual void GetStencilLocalEnergyAndForceAndMatrix(int stencilType, int stencilId, const double * u, double * energy, double * internalForces, double * tangentStiffnessMatrix) = 0;
+  
+  // Return an array of vertex indices that a stencil 'stencilId' in type 'stencilType' involves.
+  // Typically, a tetrahedron involves 4 vertices. The return pointer will point to an array with 4 integers.
+  virtual const int *GetStencilVertexIndices(int stencilType, int stencilId) const = 0;
+
+  // Vertex gravity.
+  virtual void GetVertexGravityForce(int vertexId, double gravity[3]) { gravity[0] = gravity[1] = gravity[2] = 0.0; }
+
+protected:
+  std::vector<int> numStencilsInDifferentTypes;
+  std::vector<int> numStencilVerticesInDifferentTypes;
+  int n3;
+};
+
+#endif
+
diff --git a/src/libstvk/StVKCubeABCD.cpp b/libraries/stvk/StVKCubeABCD.cpp
similarity index 80%
rename from src/libstvk/StVKCubeABCD.cpp
rename to libraries/stvk/StVKCubeABCD.cpp
index 8a76611cefccfcd4f7f2a26815b58d3becc0bdab..1e4d7708a1d789f8e4514622c92d77b40b6cd863 100644
--- a/src/libstvk/StVKCubeABCD.cpp
+++ b/libraries/stvk/StVKCubeABCD.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include "StVKCubeABCD.h"
 #include "cubicMeshIntegrals.cpp"
 
-namespace vega
-{
 StVKCubeABCD::StVKCubeABCD(double cubeSize)
 {
   double * rawIntegrals = cubicMeshIntegrals;
@@ -76,4 +78,3 @@ StVKCubeABCD::StVKCubeABCD(double cubeSize)
         }
 }
 
-}
diff --git a/libraries/stvk/StVKCubeABCD.h b/libraries/stvk/StVKCubeABCD.h
new file mode 100644
index 0000000000000000000000000000000000000000..46d8deab621904f92d330780ff01b86c12f8a1d4
--- /dev/null
+++ b/libraries/stvk/StVKCubeABCD.h
@@ -0,0 +1,65 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVKCUBEABCD_H_
+#define _STVKCUBEABCD_H_
+
+#include "StVKElementABCD.h"
+
+/*
+  This classes stores the St.Venant-Kirchhoff A,B,C,D coefficients for a cube element.
+  See also StVKInternalForces.h .
+*/
+
+class StVKCubeABCD : public StVKElementABCD
+{
+public:
+
+  // computes the ABCD coefficients 
+  StVKCubeABCD(double cubeSize); // the size of the cube is cubeSize x cubeSize x cubeSize
+
+  inline virtual Mat3d A(void * elementIterator, int i, int j) { return A_[i][j]; }
+  inline virtual double B(void * elementIterator, int i, int j) { return B_[i][j]; }
+  inline virtual Vec3d C(void * elementIterator, int i, int j, int k) { return C_[i][j][k]; }
+  inline virtual double D(void * elementIterator, int i, int j, int k, int l) { return D_[i][j][k][l]; }
+
+  virtual ~StVKCubeABCD() {}
+
+protected:
+  Mat3d A_[8][8];
+  double B_[8][8];
+  Vec3d C_[8][8][8];
+  double D_[8][8][8][8];
+};
+
+#endif
+
diff --git a/src/libstvk/StVKElementABCD.cpp b/libraries/stvk/StVKElementABCD.cpp
similarity index 77%
rename from src/libstvk/StVKElementABCD.cpp
rename to libraries/stvk/StVKElementABCD.cpp
index a4b3249789b8e7defa7c43d34fcba3689604ca01..5095c973d46e8fb0ee80959b95709fcee1e79679 100644
--- a/src/libstvk/StVKElementABCD.cpp
+++ b/libraries/stvk/StVKElementABCD.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include <stdlib.h>
 #include "StVKElementABCD.h"
 
-namespace vega
-{
 void StVKElementABCD::AllocateElementIterator(void ** elementIterator)
 {
   *elementIterator = (void*)(int*) malloc (sizeof(int));
@@ -46,4 +48,3 @@ void StVKElementABCD::PrepareElement(int el, void * elementIterator) // must cal
   *(int*) elementIterator = el;
 }
 
-}
diff --git a/libraries/stvk/StVKElementABCD.h b/libraries/stvk/StVKElementABCD.h
new file mode 100644
index 0000000000000000000000000000000000000000..616954d62ca7079349003bead5545bce650bcc35
--- /dev/null
+++ b/libraries/stvk/StVKElementABCD.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVKELEMENTABCD_H_
+#define _STVKELEMENTABCD_H_
+
+#include "minivector.h"
+
+/*
+  This abstract class serves as storage space for the St.Venant-Kirchhoff A,B,C,D coefficients for a mesh element. 
+  See also StVKInternalForces.h .
+*/
+
+class StVKElementABCD
+{
+public:
+
+  // the iterator can be different types in the derived implementation classes
+  virtual Mat3d A(void * elementIterator, int i, int j) = 0;
+  virtual double B(void * elementIterator, int i, int j) = 0;
+  virtual Vec3d C(void * elementIterator, int i, int j, int k) = 0;
+  virtual double D(void * elementIterator, int i, int j, int k, int l) = 0;
+
+  virtual ~StVKElementABCD() {};
+
+  virtual void AllocateElementIterator(void ** elementIterator);
+  virtual void ReleaseElementIterator(void * elementIterator);
+  virtual void PrepareElement(int el, void * elementIterator); // must call each time before accessing an element
+
+protected:
+};
+
+#endif
+
diff --git a/src/libstvk/StVKElementABCDLoader.cpp b/libraries/stvk/StVKElementABCDLoader.cpp
similarity index 83%
rename from src/libstvk/StVKElementABCDLoader.cpp
rename to libraries/stvk/StVKElementABCDLoader.cpp
index 32b328b4c91a08cb0f5afaa854a9b8b46e7fb7bb..43e535159cc077587a8894d30bdfe3ee7f48bf7b 100644
--- a/src/libstvk/StVKElementABCDLoader.cpp
+++ b/libraries/stvk/StVKElementABCDLoader.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -33,8 +37,6 @@
 #include "cubicMesh.h"
 #include "tetMesh.h"
 
-namespace vega
-{
 StVKElementABCD * StVKElementABCDLoader::load(VolumetricMesh * volumetricMesh, unsigned int loadingFlag)
 {
   if (volumetricMesh == NULL)
@@ -77,4 +79,4 @@ StVKElementABCD * StVKElementABCDLoader::load(VolumetricMesh * volumetricMesh, u
 
   return stVKElementABCD;
 }
-}
+
diff --git a/libraries/stvk/StVKElementABCDLoader.h b/libraries/stvk/StVKElementABCDLoader.h
new file mode 100644
index 0000000000000000000000000000000000000000..cefdc1457dd772e9c4693162436ba44f3d99f5aa
--- /dev/null
+++ b/libraries/stvk/StVKElementABCDLoader.h
@@ -0,0 +1,55 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  A loader class for StVK cubic (unreduced) coefficients.
+  See also StVKReducedInternalForces.h .
+*/
+
+#ifndef _STVKELEMENTABCDLOADER_H_
+#define _STVKELEMENTABCDLOADER_H_
+
+#include "volumetricMesh.h"
+#include "StVKElementABCD.h"
+
+class StVKElementABCDLoader
+{
+public:
+  // creates the StVKElementABCD coefficients, given a volumetric mesh
+  // loadingFlag: 
+  //   0 : use the low-memory version (default)
+  //   1 : use the high-memory version (only applies with tet meshes); with this setting, computation speeds will be higher, at the expense of more memory (however, difference is typically not large and speeds might even decrease with large meshes when running out of memory)
+  static StVKElementABCD * load(VolumetricMesh * volumetricMesh, unsigned int loadingFlag=0); 
+};
+
+#endif
+
diff --git a/libraries/stvk/StVKFEM.cpp b/libraries/stvk/StVKFEM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..61242e9ac738b028959f30356ae4165039411acd
--- /dev/null
+++ b/libraries/stvk/StVKFEM.cpp
@@ -0,0 +1,347 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Bohan Wang, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "StVKFEM.h"
+#include "volumetricMeshENuMaterial.h"
+
+#include <cassert>
+#include <cstdio>
+
+StVKFEM::StVKFEM(VolumetricMesh * volumetricMesh_, StVKElementABCD * precomputedABCDIntegrals_, bool addGravity_, double g_):
+  volumetricMesh(volumetricMesh_), precomputedIntegrals(precomputedABCDIntegrals_), g(g_), addGravity(addGravity_)
+{
+  int numElements = volumetricMesh->getNumElements();
+
+  lambdaLame.resize(numElements, 0.0);
+  muLame.resize(numElements, 0.0);
+  internalElementData.resize(numElements, nullptr);
+
+  for(int el=0; el<numElements; el++)
+  {
+    VolumetricMesh::Material * material = volumetricMesh->getElementMaterial(el);
+    VolumetricMesh::ENuMaterial * eNuMaterial = downcastENuMaterial(material);
+    if (eNuMaterial == NULL)
+    {
+      printf("Error: StVKInternalForces: mesh does not consist of E, nu materials.\n");
+      throw 1;
+    }
+
+    lambdaLame[el] = eNuMaterial->getLambda();
+    muLame[el] = eNuMaterial->getMu();
+
+    void * elIter;
+    precomputedIntegrals->AllocateElementIterator(&elIter);
+    precomputedIntegrals->PrepareElement(el, elIter);
+    internalElementData[el] = elIter;
+  }
+
+  numElementVertices = volumetricMesh->getNumElementVertices();
+
+  dof = numElementVertices * 3;
+  dof2 = dof * dof;
+
+  InitGravity();
+}
+
+StVKFEM::~StVKFEM()
+{
+  for (int el=0; el < volumetricMesh->getNumElements(); el++) {
+   precomputedIntegrals->ReleaseElementIterator(internalElementData[el]);
+  }
+}
+
+void StVKFEM::InitGravity()
+{
+  if (addGravity)
+  {
+    gravityForce.resize(3 * volumetricMesh->getNumVertices(), 0.0);
+    volumetricMesh->computeGravity(gravityForce.data(), g);
+  }
+}
+
+void StVKFEM::ComputeElementLocalEnergyAndInternalForcesAndStiffnessMatrix(const double *u, int el, double * energy, double * fint, double * K)
+{
+  void *elIter = internalElementData[el];
+  const int *vertices = volumetricMesh->getVertexIndices(el);
+  double lambda = lambdaLame[el]; 
+  double mu = muLame[el];
+
+  if (energy)
+    *energy = 0;
+
+  if (fint)
+  {
+    memset(fint, 0, dof * sizeof(double));
+
+    for (int c=0; c<numElementVertices; c++) // over all vertices of the voxel, computing force on vertex c
+    {
+      Vec3d qc(u[3*vertices[c]+0], u[3*vertices[c]+1], u[3*vertices[c]+2]);
+
+      // linear terms
+      for (int a=0; a<numElementVertices; a++) // over all vertices
+      {
+        Vec3d qa(u[3*vertices[a]+0], u[3*vertices[a]+1], u[3*vertices[a]+2]);
+
+        Vec3d force = lambda * (precomputedIntegrals->A(elIter,c,a) * qa) +
+                      (mu * precomputedIntegrals->B(elIter,a,c)) * qa +
+                      mu * (precomputedIntegrals->A(elIter,a,c) * qa);
+
+        fint[3*c+0] += force[0];
+        fint[3*c+1] += force[1];
+        fint[3*c+2] += force[2];
+
+        if (energy)
+          *energy += 0.5 * dot(qc, force);
+      }
+    }
+
+    for (int c=0; c<numElementVertices; c++) // over all vertices of the voxel, computing force on vertex c
+    {
+      Vec3d qc(u[3*vertices[c]+0], u[3*vertices[c]+1], u[3*vertices[c]+2]);
+
+      // quadratic terms
+      for (int a=0; a<numElementVertices; a++) // over all vertices
+      {
+        for(int b=0; b<numElementVertices; b++)
+        {
+/*
+          Vec3d force(0,0,0);
+          Vec3d qa(vertexDisplacements[3*vertices[a]+0],
+                   vertexDisplacements[3*vertices[a]+1],
+                   vertexDisplacements[3*vertices[a]+2]);
+
+          Vec3d qb(vertexDisplacements[3*vertices[b]+0],
+                   vertexDisplacements[3*vertices[b]+1],
+                   vertexDisplacements[3*vertices[b]+2]);
+
+          double dotp = dot(qa,qb);
+
+          force += 0.5 * lambda * dotp * precomputedIntegrals->C(el,c,a,b) +
+                   mu * dotp * precomputedIntegrals->C(el,a,b,c);
+
+          Vec3d C = lambda * precomputedIntegrals->C(el,a,b,c) +
+                    mu * (precomputedIntegrals->C(el,c,a,b) + precomputedIntegrals->C(el,b,a,c)); 
+
+          force += dot(C,qa) * qb;
+
+          forces[3*vertices[c]+0] += force[0];
+          forces[3*vertices[c]+1] += force[1];
+          forces[3*vertices[c]+2] += force[2];
+*/
+
+          double qa[3] = { u[3*vertices[a]+0], u[3*vertices[a]+1], u[3*vertices[a]+2] };
+          double qb[3] = { u[3*vertices[b]+0], u[3*vertices[b]+1], u[3*vertices[b]+2] };
+
+          double dotp = qa[0] * qb[0] + qa[1] * qb[1] + qa[2] * qb[2];
+
+          Vec3d forceTerm1 = 0.5 * lambda * dotp * precomputedIntegrals->C(elIter,c,a,b) +
+                             mu * dotp * precomputedIntegrals->C(elIter,a,b,c);
+
+          Vec3d C = lambda * precomputedIntegrals->C(elIter,a,b,c) +
+                    mu * (precomputedIntegrals->C(elIter,c,a,b) + precomputedIntegrals->C(elIter,b,a,c)); 
+
+          double dotCqa = C[0] * qa[0] + C[1] * qa[1] + C[2] * qa[2];
+
+          double force[3] = 
+          {
+            forceTerm1[0] + dotCqa * qb[0],
+            forceTerm1[1] + dotCqa * qb[1],
+            forceTerm1[2] + dotCqa * qb[2],
+          };
+
+          fint[3*c+0] += force[0];
+          fint[3*c+1] += force[1];
+          fint[3*c+2] += force[2];
+
+          if (energy)
+            *energy += dot(qc, Vec3d(force)) / 3.0;
+        }
+      }
+    }
+
+    for (int c=0; c<numElementVertices; c++) // over all vertices of the voxel, computing force on vertex c
+    {
+      int vc = vertices[c];
+      Vec3d qc(u[3*vc+0], u[3*vc+1], u[3*vc+2]);
+      // cubic terms
+      for(int a=0; a<numElementVertices; a++) // over all vertices
+      {
+        int va = vertices[a];
+        for(int b=0; b<numElementVertices; b++)
+        {
+          int vb = vertices[b];
+          for(int d=0; d<numElementVertices; d++)
+          {
+            int vd = vertices[d];
+/*
+            Vec3d qa(vertexDisplacements[3*va+0],
+                     vertexDisplacements[3*va+1],
+                     vertexDisplacements[3*va+2]);
+
+            Vec3d qb(vertexDisplacements[3*vb+0],
+                     vertexDisplacements[3*vb+1],
+                     vertexDisplacements[3*vb+2]);
+
+            Vec3d qd(vertexDisplacements[3*vd+0],
+                     vertexDisplacements[3*vd+1],
+                     vertexDisplacements[3*vd+2]);
+
+            double dotp = dot(qa,qb);
+
+            Vec3d force = 0.5 * lambda * dotp * precomputedIntegrals_->D(a,b,c,d) * qd +
+                          mu * dotp * precomputedIntegrals_->D(a,c,b,d) * qd;
+
+            forces[3*vertices[c]+0] += force[0];
+            forces[3*vertices[c]+1] += force[1];
+            forces[3*vertices[c]+2] += force[2];
+*/
+            const double * qa = &(u[3*va]);
+            const double * qb = &(u[3*vb]);
+            const double * qd = &(u[3*vd]);
+
+            double dotp = qa[0] * qb[0] + qa[1] * qb[1] + qa[2] * qb[2]; 
+            double scalar = dotp * (0.5 * lambda * precomputedIntegrals->D(elIter,a,b,c,d) + mu * precomputedIntegrals->D(elIter,a,c,b,d) );
+
+            double force[3] = { scalar * qd[0], scalar * qd[1], scalar * qd[2] };
+
+            fint[3*c+0] += force[0];
+            fint[3*c+1] += force[1];
+            fint[3*c+2] += force[2];
+
+            if (energy)
+              *energy += dot(qc, Vec3d(force)) / 4.0;
+          }
+        }
+      }
+    }
+  }
+
+  if (K)
+  {    
+    memset(K, 0, dof2 * sizeof(double));
+
+    for (int c=0; c<numElementVertices; c++) // over all vertices of the voxel, computing row of vertex c
+    {
+      // linear terms
+      for (int a=0; a<numElementVertices; a++) // over all vertices
+      {
+        Mat3d matrix(1.0);
+        matrix *= mu * precomputedIntegrals->B(elIter,a,c);
+        matrix += lambda * precomputedIntegrals->A(elIter,c,a) +
+                  mu * precomputedIntegrals->A(elIter,a,c);
+
+        AddMatrix3x3Block(c, a, matrix, K);
+      }
+    }
+
+    for (int c=0; c<numElementVertices; c++) // over all vertices of the voxel, computing row of vertex c
+    {
+      // quadratic terms
+      for (int e=0; e<numElementVertices; e++) // compute contribution to block (c,e) of the stiffness matrix
+      {
+        double matrix[9];
+        memset(matrix, 0, sizeof(double) * 9);
+
+        for(int a=0; a<numElementVertices; a++)
+        {
+          double qa[3] = { u[3*vertices[a]+0], u[3*vertices[a]+1], u[3*vertices[a]+2] };
+
+          Vec3d C0v = lambda * precomputedIntegrals->C(elIter,c,a,e) + mu * (precomputedIntegrals->C(elIter,e,a,c) + precomputedIntegrals->C(elIter,a,e,c));
+          double C0[3] = {C0v[0], C0v[1], C0v[2]};
+
+          // C0 tensor qa
+          matrix[0] += C0[0] * qa[0]; matrix[1] += C0[0] * qa[1]; matrix[2] += C0[0] * qa[2];
+          matrix[3] += C0[1] * qa[0]; matrix[4] += C0[1] * qa[1]; matrix[5] += C0[1] * qa[2];
+          matrix[6] += C0[2] * qa[0]; matrix[7] += C0[2] * qa[1]; matrix[8] += C0[2] * qa[2];
+
+          Vec3d C1v = lambda * precomputedIntegrals->C(elIter,e,a,c) + mu * (precomputedIntegrals->C(elIter,c,e,a) + precomputedIntegrals->C(elIter,a,e,c));
+          double C1[3] = {C1v[0], C1v[1], C1v[2]};
+
+          // qa tensor C1
+          matrix[0] += qa[0] * C1[0]; matrix[1] += qa[0] * C1[1]; matrix[2] += qa[0] * C1[2];
+          matrix[3] += qa[1] * C1[0]; matrix[4] += qa[1] * C1[1]; matrix[5] += qa[1] * C1[2];
+          matrix[6] += qa[2] * C1[0]; matrix[7] += qa[2] * C1[1]; matrix[8] += qa[2] * C1[2];
+
+          Vec3d C2v = lambda * precomputedIntegrals->C(elIter,a,e,c) + mu * (precomputedIntegrals->C(elIter,c,a,e) + precomputedIntegrals->C(elIter,e,a,c));
+          double C2[3] = {C2v[0], C2v[1], C2v[2]};
+
+          // qa dot C2
+          double dotp = qa[0]*C2[0] + qa[1]*C2[1] + qa[2]*C2[2];
+          matrix[0] += dotp; 
+          matrix[4] += dotp; 
+          matrix[8] += dotp;
+        }
+
+        AddMatrix3x3Block(c, e, matrix, K);
+      }
+    }
+
+    for (int c=0; c<numElementVertices; c++) // over all vertices of the voxel, computing derivative on force on vertex c
+    {
+      // cubic terms
+      for (int e=0; e<numElementVertices; e++) // compute contribution to block (c,e) of the stiffness matrix
+      {
+        double matrix[9];
+        memset(matrix, 0, sizeof(double) * 9);
+        for(int a=0; a<numElementVertices; a++)
+        {
+          int va = vertices[a];
+          const double * qa = &(u[3*va]);
+          for(int b=0; b<numElementVertices; b++)
+          {
+            int vb = vertices[b];
+
+            const double * qb = &(u[3*vb]);
+
+            double D0 = lambda * precomputedIntegrals->D(elIter,a,c,b,e) +
+                        mu * ( precomputedIntegrals->D(elIter,a,e,b,c) + precomputedIntegrals->D(elIter,a,b,c,e) );
+
+            matrix[0] += D0 * qa[0] * qb[0]; matrix[1] += D0 * qa[0] * qb[1]; matrix[2] += D0 * qa[0] * qb[2];
+            matrix[3] += D0 * qa[1] * qb[0]; matrix[4] += D0 * qa[1] * qb[1]; matrix[5] += D0 * qa[1] * qb[2];
+            matrix[6] += D0 * qa[2] * qb[0]; matrix[7] += D0 * qa[2] * qb[1]; matrix[8] += D0 * qa[2] * qb[2];
+
+            double D1 = 0.5 * lambda * precomputedIntegrals->D(elIter,a,b,c,e) +
+                        mu * precomputedIntegrals->D(elIter,a,c,b,e);
+
+            double dotpD = D1 * (qa[0] * qb[0] + qa[1] * qb[1] + qa[2] * qb[2]);
+
+            matrix[0] += dotpD; 
+            matrix[4] += dotpD; 
+            matrix[8] += dotpD; 
+          }
+        }
+
+        AddMatrix3x3Block(c, e, matrix, K);
+      }
+    }
+  }
+}
diff --git a/libraries/stvk/StVKFEM.h b/libraries/stvk/StVKFEM.h
new file mode 100644
index 0000000000000000000000000000000000000000..caa00f13b108cc334ecc355bb4c7018d6b72ce2b
--- /dev/null
+++ b/libraries/stvk/StVKFEM.h
@@ -0,0 +1,124 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Bohan Wang, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVK_FEM_H_
+#define _STVK_FEM_H_
+
+#include "volumetricMesh.h"
+#include "StVKElementABCD.h"
+
+#include <vector>
+
+
+// This class implements the StVK force model in a element style.
+// It specifically computes the simulation quantities in a scale of a FEM element,
+// using 'ComputeElementLocalEnergyAndInternalForcesAndStiffnessMatrix'.
+// If you want to compute the quantities in object scale,
+// please look into stencilForceModel lib,
+// or use stVKInternalForces.* and stVKStiffnessMatrix.*.
+// The later files are the same as the previous version only for backward compatibility.
+// The same functionality can be achieved by using stencilForceModel/forceModelAssembler.*.
+
+class StVKFEM
+{
+public:
+  StVKFEM(VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity=false, double g=9.81);
+  virtual ~StVKFEM();  
+
+  // enables or disables the gravity (note: you can also set this in the constructor; 
+  // use this routine to turn the gravity on/off during the simulation)
+  // if addGravity is enabled, ComputeForces will subtract the gravity force from the internal forces 
+  // (note: subtraction, not addition, is used because the internal forces are returned with the sign as described in the f_int(x) comment above)
+  void SetGravity(bool addGravity) { this->addGravity = addGravity; InitGravity(); } 
+
+  // get simulation mesh
+  const VolumetricMesh * GetVolumetricMesh() const { return volumetricMesh; }
+
+  // get integral class object
+  StVKElementABCD * GetPrecomputedIntegrals() const { return precomputedIntegrals; }
+
+  // compute the energy E, internal forces f and stiffness matrix K of a single FEM element.
+  // u is the input displacement with dimention (# all vtx x 3)
+  // E is a pointer to a double
+  // f is a pointer to a dense vector
+  // K is a pointer to a dense matrix in column major
+  // E, f, K can be nullptr. If it is, the function will not compute it.
+  void ComputeElementLocalEnergyAndInternalForcesAndStiffnessMatrix(const double *u, int el, double * energy, double * fint, double * K);
+
+protected:
+  VolumetricMesh * volumetricMesh;
+  StVKElementABCD * precomputedIntegrals;
+
+  std::vector<double> gravityForce;
+  double g;
+  bool addGravity;  
+  void InitGravity(); // aux function
+
+  int numElementVertices;
+  void ResetVector(double * vec); // aux function
+
+  std::vector<double> lambdaLame;
+  std::vector<double> muLame;
+  std::vector<void*> internalElementData;
+
+  int dof, dof2;
+
+  inline void AddMatrix3x3Block(int c, int a, const Mat3d & matrix, double * K)
+  {
+    for (int k = 0; k < 3; k++) 
+    {
+      for (int l = 0; l < 3; l++)
+      {
+        int row = c * 3 + k;
+        int col = a * 3 + l;
+
+        K[col * dof + row] += matrix[k][l];
+      }
+    }
+  }
+
+  inline void AddMatrix3x3Block(int c, int a, const double matrix[9], double * K)
+  {
+    for (int k = 0; k < 3; k++) 
+    {
+      for (int l = 0; l < 3; l++)
+      {
+        int row = c * 3 + k;
+        int col = a * 3 + l;
+
+        K[col * dof + row] += matrix[k * 3 + l];
+      }
+    }
+  }
+};
+
+#endif
diff --git a/src/libstvk/StVKHessianTensor.cpp b/libraries/stvk/StVKHessianTensor.cpp
similarity index 93%
rename from src/libstvk/StVKHessianTensor.cpp
rename to libraries/stvk/StVKHessianTensor.cpp
index be87f3fb289db0e4b8b48bb13a9120e2ce8ae627..f522f1e7f56a4dd8fd2cedb570b21b6c2e200445 100644
--- a/src/libstvk/StVKHessianTensor.cpp
+++ b/libraries/stvk/StVKHessianTensor.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include "StVKHessianTensor.h"
 #include "volumetricMeshENuMaterial.h"
 
-namespace vega
-{
 #define QUADRATICFORM(m,x,y)\
   m[0] * x[0] * y[0] + \
   m[1] * x[1] * y[0] + \
@@ -222,7 +224,7 @@ int StVKHessianTensor::ComputeHessianAtZero(int verbose)
   return 0;
 }
 
-void StVKHessianTensor::EvaluateHessianQuadraticForm(double * phir, double * phis, double * result)
+void StVKHessianTensor::EvaluateHessianQuadraticForm(const double * phir, const double * phis, double * result)
 {
   // reset result to zero
   memset(result,0,sizeof(double)*3*numVertices_);
@@ -239,8 +241,8 @@ void StVKHessianTensor::EvaluateHessianQuadraticForm(double * phir, double * phi
     k = index.third;
     double * entry = pos->second;
 
-    double * phisk = &phis[3*k];
-    double * phirj = &phir[3*j];
+    const double * phisk = &phis[3 * k];
+    const double * phirj = &phir[3 * j];
 
     double * hijk0 = &entry[0];
     double * hijk1 = &entry[9];
@@ -257,7 +259,7 @@ void StVKHessianTensor::EvaluateHessianQuadraticForm(double * phir, double * phi
   }
 }
 
-int StVKHessianTensor::SaveHessianAtZeroToFile(char * filename)
+int StVKHessianTensor::SaveHessianAtZeroToFile(const char * filename)
 {
   FILE * fout = fopen(filename,"wb");
 
@@ -400,7 +402,7 @@ void StVKHessianTensor::EvaluateHessianQuadraticFormDirect(double * phir, double
   printf("\n");
 }
 
-void StVKHessianTensor::EvaluateHessianQuadraticFormDirectAll(double * Ulin, int k, double * result, int numRigidModes)
+void StVKHessianTensor::EvaluateHessianQuadraticFormDirectAll(double * Ulin, int k, double * result, int numRigidModes, int verbose)
 {
   double entry[27];
   double * hijk0 = &entry[0];
@@ -423,17 +425,20 @@ void StVKHessianTensor::EvaluateHessianQuadraticFormDirectAll(double * Ulin, int
   void * elIter;
   precomputedIntegrals->AllocateElementIterator(&elIter);
 
-  printf("Evaluating the Hessian quadratic form (rhs matrix)...\n");
-  printf("  Total num elements: %d \n",numElements_);
-  printf("  Total num DOFs: %d \n",m3);
-  printf("  Total num linear modes: %d \n",k);
-  printf("  Total num rigid modes: %d \n",numRigidModes);
-  printf("  Total num derivatives: %d \n",numDeriv);
+  if (verbose)
+  {
+    printf("Evaluating the Hessian quadratic form (rhs matrix)...\n");
+    printf("  Total num elements: %d \n",numElements_);
+    printf("  Total num DOFs: %d \n",m3);
+    printf("  Total num linear modes: %d \n",k);
+    printf("  Total num rigid modes: %d \n",numRigidModes);
+    printf("  Total num derivatives: %d \n",numDeriv);
+  }
 
   for(int el=0; el < numElements_; el++)
   {
     precomputedIntegrals->PrepareElement(el, elIter);
-    if (el % 200 == 0)
+    if ((el % 200 == 0) && (verbose))
     {
       printf("%d ",el);
       fflush(NULL);
@@ -501,7 +506,8 @@ void StVKHessianTensor::EvaluateHessianQuadraticFormDirectAll(double * Ulin, int
 
   precomputedIntegrals->ReleaseElementIterator(elIter);
 
-  printf("\n");
+  if (verbose)
+    printf("\n");
 }
 
 void StVKHessianTensor::ComputeStiffnessMatrixCorrection(double * u, double * du, SparseMatrix * dK)
@@ -678,4 +684,3 @@ void StVKHessianTensor::AddCubicTermsContribution(double * u, double * du, Spars
 }
 
 
-}
diff --git a/libraries/stvk/StVKHessianTensor.h b/libraries/stvk/StVKHessianTensor.h
new file mode 100644
index 0000000000000000000000000000000000000000..be18f70f0ccaf26ddbbcb00197aa3836a25b942c
--- /dev/null
+++ b/libraries/stvk/StVKHessianTensor.h
@@ -0,0 +1,101 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVKHESSIANTENSOR_H_
+#define _STVKHESSIANTENSOR_H_
+
+/*
+  The second derivative (Hessian tensor) of internal elastic forces.
+  See also StVKInternalForces.h .
+*/
+
+#include "triple.h"
+#include "sparseMatrix.h"
+#include "volumetricMesh.h"
+#include "StVKElementABCD.h"
+#include "StVKStiffnessMatrix.h"
+
+class StVKHessianTensor
+{
+public:
+
+  StVKHessianTensor(StVKStiffnessMatrix * stVKStiffnessMatrix);
+  virtual ~StVKHessianTensor();
+
+  inline int numVertices() { return numVertices_; }
+  inline int numElements() { return numElements_; }
+
+  // uses the Hessian tensor to approximate the change in the tangent stiffness matrix, if configuration is changed from u to u + du (du is assumed small)
+  // any required Hessian tensor quantities (in configuration u) are computed automatically
+  virtual void ComputeStiffnessMatrixCorrection(double * u, double * du, SparseMatrix * dK);
+
+  // compute the vector result=(H:u)v, where H is the Hessian in the zero deformation configuration
+  // high-memory version; must call ComputeHessianAtZero before calling EvaluateHessianQuadraticForm
+  int ComputeHessianAtZero(int verbose=1); 
+  int SaveHessianAtZeroToFile(const char * filename);
+  void EvaluateHessianQuadraticForm(const double * u, const double * v, double * result); 
+
+  // compute the vector result=(H:u)v, where H is the Hessian in the zero deformation configuration
+  // low-memory version; no need to call ComputeHessianAtZero, but longer computation times for smaller meshes
+  void EvaluateHessianQuadraticFormDirect(double * u, double * v, double * result);
+
+  // computes (H:u)v, for all pairs of columns of the matrix Ulin; k is the number of columns of Ulin
+  // the first numRigidModes columns will not be used for this computation
+  // result is the output matrix; must be pre-allocated with (k-numRigidModes) * (k-numRigidModes+1) / 2 columns
+  // low-memory version; no need to call ComputeHessianAtZero
+  void EvaluateHessianQuadraticFormDirectAll(double * Ulin, int k, double * result, int numRigidModes=0, int verbose=1);
+  
+  // low-level routines (advanced use)
+  void AddQuadraticTermsContribution(double * u, double * du, SparseMatrix * dK, int elementLow=-1, int elementHigh=-1);
+  void AddCubicTermsContribution(double * u, double * du, SparseMatrix * dK, int elementLow=-1, int elementHigh=-1);
+
+protected:
+  int numVertices_;
+  int numElements_;
+
+  StVKStiffnessMatrix * stVKStiffnessMatrix;
+  VolumetricMesh * volumetricMesh;
+  StVKElementABCD * precomputedIntegrals;
+
+  int numElementVertices;
+ 
+  typedef triple<int,int,int> triIndex;
+  typedef std::map<triIndex,double*> hessianType;
+  hessianType hessian;
+  void AddTensor3x3x3Block(int v1, int v2, int v3, Vec3d & vec, int type);
+
+  double * lambdaLame;
+  double * muLame;
+};
+
+#endif
+
diff --git a/src/libstvk/StVKInternalForces.cpp b/libraries/stvk/StVKInternalForces.cpp
similarity index 88%
rename from src/libstvk/StVKInternalForces.cpp
rename to libraries/stvk/StVKInternalForces.cpp
index 2eb29a31df85af9d10879fb0c122d6d01335ebed..6452963aa875fea5d8d8116052461875f737bb46 100644
--- a/src/libstvk/StVKInternalForces.cpp
+++ b/libraries/stvk/StVKInternalForces.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include "StVKInternalForces.h"
 #include "volumetricMeshENuMaterial.h"
 
-namespace vega
-{
 StVKInternalForces::StVKInternalForces(VolumetricMesh * volumetricMesh_, StVKElementABCD * precomputedABCDIntegrals_, bool addGravity_, double g_): volumetricMesh(volumetricMesh_), precomputedIntegrals(precomputedABCDIntegrals_), gravityForce(NULL), addGravity(addGravity_), g(g_) 
 {
   int numElements = volumetricMesh->getNumElements();
@@ -73,12 +75,12 @@ void StVKInternalForces::InitGravity()
   }  
 }
 
-double StVKInternalForces::ComputeEnergy(double * vertexDisplacements)
+double StVKInternalForces::ComputeEnergy(const double * vertexDisplacements)
 {
   return ComputeEnergyContribution(vertexDisplacements, 0, volumetricMesh->getNumElements());
 }
 
-double StVKInternalForces::ComputeEnergyContribution(double * vertexDisplacements, int elementLow, int elementHigh, double * buffer)
+double StVKInternalForces::ComputeEnergyContribution(const double * vertexDisplacements, int elementLow, int elementHigh, double * buffer)
 {
   if (buffer == NULL)
     buffer = this->buffer;
@@ -109,7 +111,7 @@ double StVKInternalForces::ComputeEnergyContribution(double * vertexDisplacement
   return energy;
 }
 
-void StVKInternalForces::ComputeForces(double * vertexDisplacements, double * forces)
+void StVKInternalForces::ComputeForces(const double * vertexDisplacements, double * forces)
 {
   //PerformanceCounter forceCounter;
 
@@ -129,7 +131,7 @@ void StVKInternalForces::ComputeForces(double * vertexDisplacements, double * fo
   //printf("Internal forces: %G\n", forceCounter.GetElapsedTime());
 }
 
-void StVKInternalForces::AddLinearTermsContribution(double * vertexDisplacements, double * forces, int elementLow, int elementHigh)
+void StVKInternalForces::AddLinearTermsContribution(const double * vertexDisplacements, double * forces, int elementLow, int elementHigh)
 {
   if (elementLow < 0)
     elementLow = 0;
@@ -175,7 +177,7 @@ void StVKInternalForces::AddLinearTermsContribution(double * vertexDisplacements
   precomputedIntegrals->ReleaseElementIterator(elIter);
 }
 
-void StVKInternalForces::AddQuadraticTermsContribution(double * vertexDisplacements, double * forces, int elementLow, int elementHigh)
+void StVKInternalForces::AddQuadraticTermsContribution(const double * vertexDisplacements, double * forces, int elementLow, int elementHigh)
 {
   if (elementLow < 0)
     elementLow = 0;
@@ -256,7 +258,7 @@ void StVKInternalForces::AddQuadraticTermsContribution(double * vertexDisplaceme
   precomputedIntegrals->ReleaseElementIterator(elIter);
 }
 
-void StVKInternalForces::AddCubicTermsContribution(double * vertexDisplacements, double * forces, int elementLow, int elementHigh)
+void StVKInternalForces::AddCubicTermsContribution(const double * vertexDisplacements, double * forces, int elementLow, int elementHigh)
 {
   if (elementLow < 0)
     elementLow = 0;
@@ -313,9 +315,9 @@ void StVKInternalForces::AddCubicTermsContribution(double * vertexDisplacements,
             forces[3*vertices[c]+1] += force[1];
             forces[3*vertices[c]+2] += force[2];
 */
-            double * qa = &(vertexDisplacements[3*va]);
-            double * qb = &(vertexDisplacements[3*vb]);
-            double * qd = &(vertexDisplacements[3*vd]);
+            const double * qa = &(vertexDisplacements[3*va]);
+            const double * qb = &(vertexDisplacements[3*vb]);
+            const double * qd = &(vertexDisplacements[3*vd]);
             double * force = &(forces[3*vc]);
 
             double dotp = qa[0] * qb[0] + qa[1] * qb[1] + qa[2] * qb[2]; 
@@ -340,4 +342,3 @@ void StVKInternalForces::ResetVector(double * vec)
   memset(vec, 0, sizeof(double) * 3 * volumetricMesh->getNumVertices());
 }
 
-}
diff --git a/libraries/stvk/StVKInternalForces.h b/libraries/stvk/StVKInternalForces.h
new file mode 100644
index 0000000000000000000000000000000000000000..b030911765408564d091562e75091489d3fd6bc9
--- /dev/null
+++ b/libraries/stvk/StVKInternalForces.h
@@ -0,0 +1,125 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVKINTERNALFORCES_H_
+#define _STVKINTERNALFORCES_H_
+
+/*
+
+Given a 3D volumetric mesh, and a vector of displacements of its vertices,
+the classes StVKInternalForces, StVKStiffnessMatrix and StVKHessianTensor
+compute internal elastic strain energy, internal forces, tangent stiffness
+matrices, and the second derivative of internal forces (the Hessian), under
+an isotropic geometrically nonlinear Finite Element Method (FEM) elastic model.
+Large deformations are therefore supported ("geometric nonlinearities"). 
+The strain-stress relationship is linear (the St. Venant-Kirchhoff model).
+Material parameters (Young's modulus, Poisson ratio, mass density) can be set 
+for each individual mesh element, and can therefore vary across the mesh.
+
+Supported volumetric mesh types:
+1. Tetrahedral meshes 
+2. Meshes consisting of cubes, obtained by voxelizing a triangle geometry 
+   (the cubes are a subset of a regular 3D grid; all cubes are axis-aligned and 
+    of the same size)
+
+We were able to run the code under Unix, Max OS X, and Windows.
+
+This code was designed for interactive simulation, but can also be used offline. 
+The routines were optimized for speed. Our multi-threaded implementation is 
+interactive up to about several thousand tetrahedra.
+
+The code also supports reduced FEM deformable models ([Barbic and James 2005], 
+SIGGRAPH 2005). The corresponding classes use "Reduced" in their name. So, this 
+code essentially gives two separate simulators: full simulations (no reduction), 
+and reduced simulations.
+
+With tet meshes, there is a choice between a low-memory implementation and a 
+high-memory implementation. With a few thousand tetrahedra (depending on the 
+size and availability of your memory), you can gain a small speedup (e.g. 1.5x) 
+by using a high-memory version (see StVKTetHighMemoryABCD.h).
+
+Note: all matrices are stored in the column-major order (same format as in LAPACK).
+
+*/
+
+#include "volumetricMesh.h"
+#include "StVKElementABCD.h"
+
+class StVKInternalForces
+{
+public:
+
+  // before creating this class, you must first create the volumetric mesh, and the precomputed integrals
+  // you can use the StVKElementABCDLoader.h header file to create the "precomputedABCDIntegrals"
+  StVKInternalForces(VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity=false, double g=9.81);
+  virtual ~StVKInternalForces();
+
+  // both vertex displacements and internal forces refer to the vertices of the simulation mesh
+  // they must be (pre-allocated) vectors of length 3 * numVertices
+  // the internal forces are returned with the sign corresponding to f_int(x) on the left side of the equation M * x'' + f_int(x) = f_ext
+  // i.e., the computed internal forces are negatives of the actual physical internal forces acting on the material
+  virtual void ComputeForces(const double * vertexDisplacements, double * internalForces);
+  
+  // enables or disables the gravity (note: you can also set this in the constructor; use this routine to turn the gravity on/off during the simulation)
+  void SetGravity(bool addGravity) { this->addGravity = addGravity; InitGravity(); } // if addGravity is enabled, ComputeForces will subtract the gravity force from the internal forces (note: subtraction, not addition, is used because the internal forces are returned with the sign as described in the f_int(x) comment above)
+
+  virtual double ComputeEnergy(const double * vertexDisplacements); // get the nonlinear elastic strain energy
+
+  inline VolumetricMesh * GetVolumetricMesh() { return volumetricMesh; }
+  inline StVKElementABCD * GetPrecomputedIntegrals() { return precomputedIntegrals; }
+
+  // === advanced routines below === 
+  double ComputeEnergyContribution(const double * vertexDisplacements, int elementLow, int elementHigh, double * buffer = NULL); // compute the contribution to strain energy due to the specified elements; needs a buffer for internal calculations; you can pass NULL (and then an internal buffer will be used), or pass your own buffer (useful with multi-threading)
+  void AddLinearTermsContribution(const double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
+  void AddQuadraticTermsContribution(const double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
+  void AddCubicTermsContribution(const double * vertexDisplacements, double * forces, int elementLow=-1, int elementHigh=-1);
+  
+protected:
+  VolumetricMesh * volumetricMesh;
+  StVKElementABCD * precomputedIntegrals;
+
+  double * gravityForce;
+  bool addGravity;
+  double g;
+  void InitGravity(); // aux function
+
+  double * buffer;
+  int numElementVertices;
+
+  void ResetVector(double * vec); // aux function
+
+  double * lambdaLame;
+  double * muLame;
+};
+
+#endif
+
diff --git a/src/libstvk/StVKStiffnessMatrix.cpp b/libraries/stvk/StVKStiffnessMatrix.cpp
similarity index 90%
rename from src/libstvk/StVKStiffnessMatrix.cpp
rename to libraries/stvk/StVKStiffnessMatrix.cpp
index 0b7305c53c899955b83ffe28445a596965b56c0f..221cc7a2234300afe4c31057e155dfd43b730e58 100644
--- a/src/libstvk/StVKStiffnessMatrix.cpp
+++ b/libraries/stvk/StVKStiffnessMatrix.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,8 +33,6 @@
 #include "StVKStiffnessMatrix.h"
 #include "volumetricMeshENuMaterial.h"
 
-namespace vega
-{
 StVKStiffnessMatrix::StVKStiffnessMatrix(StVKInternalForces *  stVKInternalForces)
 {
   precomputedIntegrals = stVKInternalForces->GetPrecomputedIntegrals();
@@ -135,7 +137,7 @@ StVKStiffnessMatrix::~StVKStiffnessMatrix()
 }
 
 // the master function
-void StVKStiffnessMatrix::ComputeStiffnessMatrix(double * vertexDisplacements, SparseMatrix * sparseMatrix)
+void StVKStiffnessMatrix::ComputeStiffnessMatrix(const double * vertexDisplacements, SparseMatrix * sparseMatrix)
 {
   //PerformanceCounter stiffnessCounter;
   sparseMatrix->ResetToZero();
@@ -148,7 +150,7 @@ void StVKStiffnessMatrix::ComputeStiffnessMatrix(double * vertexDisplacements, S
   //printf("Stiffness matrix: %G\n", stiffnessCounter.GetElapsedTime());
 }
 
-void StVKStiffnessMatrix::AddLinearTermsContribution(double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow, int elementHigh)
+void StVKStiffnessMatrix::AddLinearTermsContribution(const double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow, int elementHigh)
 {
   if (elementLow < 0)
     elementLow = 0;
@@ -196,7 +198,7 @@ void StVKStiffnessMatrix::AddLinearTermsContribution(double * vertexDisplacement
       dataHandle[rowc+k][3*column[c8+(where)]+l] += matrix[3*k+l];\
     }
 
-void StVKStiffnessMatrix::AddQuadraticTermsContribution(double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow, int elementHigh)
+void StVKStiffnessMatrix::AddQuadraticTermsContribution(const double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow, int elementHigh)
 {
   if (elementLow < 0)
     elementLow = 0;
@@ -272,7 +274,7 @@ void StVKStiffnessMatrix::AddQuadraticTermsContribution(double * vertexDisplacem
   precomputedIntegrals->ReleaseElementIterator(elIter);
 }
 
-void StVKStiffnessMatrix::AddCubicTermsContribution(double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow, int elementHigh)
+void StVKStiffnessMatrix::AddCubicTermsContribution(const double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow, int elementHigh)
 {
   if (elementLow < 0)
     elementLow = 0;
@@ -310,12 +312,12 @@ void StVKStiffnessMatrix::AddCubicTermsContribution(double * vertexDisplacements
         for(int a=0; a<numElementVertices; a++)
         {
           int va = vertices[a];
-          double * qa = &(vertexDisplacements[3*va]);
+          const double * qa = &(vertexDisplacements[3*va]);
           for(int b=0; b<numElementVertices; b++)
           {
             int vb = vertices[b];
 
-            double * qb = &(vertexDisplacements[3*vb]);
+            const double * qb = &(vertexDisplacements[3*vb]);
 
             double D0 = lambda * precomputedIntegrals->D(elIter,a,c,b,e) +
                         mu * ( precomputedIntegrals->D(elIter,a,e,b,c) + precomputedIntegrals->D(elIter,a,b,c,e) );
@@ -344,4 +346,4 @@ void StVKStiffnessMatrix::AddCubicTermsContribution(double * vertexDisplacements
 
   precomputedIntegrals->ReleaseElementIterator(elIter);
 }
-}
+
diff --git a/libraries/stvk/StVKStiffnessMatrix.h b/libraries/stvk/StVKStiffnessMatrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..2b0843310b4483d14e8b781b4f3d88358458ac10
--- /dev/null
+++ b/libraries/stvk/StVKStiffnessMatrix.h
@@ -0,0 +1,107 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Computes the tangent stiffness matrix of a StVK elastic deformable object.
+  The tangent stiffness matrix depends on the deformable configuration.
+  As a special case, the routine can compute the stiffness matrix in the rest configuration.
+  See also StVKInternalForces.h .
+*/
+
+#ifndef _STVKSTIFFNESSMATRIX_H_
+#define _STVKSTIFFNESSMATRIX_H_
+
+#include "sparseMatrix.h"
+#include "StVKInternalForces.h"
+
+class StVKStiffnessMatrix
+{
+public:
+
+  // initializes the computation of the tangent stiffness matrix
+  StVKStiffnessMatrix(StVKInternalForces *  stVKInternalForces);
+  virtual ~StVKStiffnessMatrix();
+
+  // generates a zero matrix with the same pattern of non-zero entries as the tangent stiffness matrix
+  // note: sparsity pattern does not depend on the deformable configuration
+  void GetStiffnessMatrixTopology(SparseMatrix ** stiffnessMatrixTopology); 
+
+  // evaluates the tangent stiffness matrix in the given deformation configuration
+  // "vertexDisplacements" is an array of vertex deformations, of length 3*n, where n is the total number of mesh vertices
+  virtual void ComputeStiffnessMatrix(const double * vertexDisplacements, SparseMatrix * sparseMatrix);
+
+  inline void ResetStiffnessMatrix(SparseMatrix * sparseMatrix) {sparseMatrix->ResetToZero();}
+
+  inline VolumetricMesh * GetVolumetricMesh() { return volumetricMesh; }
+  inline StVKElementABCD * GetPrecomputedIntegrals() { return precomputedIntegrals; }
+
+  // === the routines below are meant for advanced usage ===
+
+  // auxiliary functions, these will add the contributions into 'forces'
+  void AddLinearTermsContribution(const double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
+  void AddQuadraticTermsContribution(const double * vertexDisplacements,SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
+  void AddCubicTermsContribution(const double * vertexDisplacements, SparseMatrix * sparseMatrix, int elementLow=-1, int elementHigh=-1);
+
+  void GetMatrixAccelerationIndices(int *** row__, int *** column__) { *row__ = row_; *column__ = column_;}
+
+protected:
+
+  int numElementVertices;
+
+  // acceleration indices
+  int ** row_;
+  int ** column_;
+
+  VolumetricMesh * volumetricMesh;
+  StVKElementABCD * precomputedIntegrals;
+
+  double * lambdaLame;
+  double * muLame;
+
+  // adds a 3x3 block matrix corresponding to a derivative of force on vertex c wrt to vertex a
+  // c is 0..7
+  // a is 0..7
+  inline void AddMatrix3x3Block(int c, int a, int element, Mat3d & matrix, SparseMatrix * sparseMatrix);
+};
+
+inline void StVKStiffnessMatrix::AddMatrix3x3Block(int c, int a, int element, Mat3d & matrix, SparseMatrix * sparseMatrix)
+{
+  int * row = row_[element];
+  int * column = column_[element];
+
+  for(int k=0; k<3; k++)
+    for(int l=0; l<3; l++)
+      sparseMatrix->AddEntry(3*row[c]+k, 3*column[numElementVertices*c+a]+l, matrix[k][l]);
+}
+
+#endif
+
diff --git a/src/libstvk/StVKTetABCD.cpp b/libraries/stvk/StVKTetABCD.cpp
similarity index 84%
rename from src/libstvk/StVKTetABCD.cpp
rename to libraries/stvk/StVKTetABCD.cpp
index 176f4a22165e51858993539341a9c0236c01789e..66350b2655fda6eafedbab97c272336683180de4 100644
--- a/src/libstvk/StVKTetABCD.cpp
+++ b/libraries/stvk/StVKTetABCD.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,8 +32,6 @@
 
 #include "StVKTetABCD.h"
 
-namespace vega
-{
 StVKTetABCD::StVKTetABCD(TetMesh * tetMesh)
 {
   int numElements = tetMesh->getNumElements();
@@ -41,7 +43,7 @@ StVKTetABCD::StVKTetABCD(TetMesh * tetMesh)
   {
     Vec3d vertices[4];
     for(int i=0; i<4; i++)
-      vertices[i] = *(tetMesh->getVertex(el, i));
+      vertices[i] = tetMesh->getVertex(el, i);
     StVKSingleTetABCD(vertices, &elementsData[el]);
   }
 
@@ -56,7 +58,7 @@ StVKTetABCD::~StVKTetABCD()
 
 void StVKTetABCD::StVKSingleTetABCD(Vec3d vtx[4], elementData * target)
 {
-  double det = TetMesh::getTetDeterminant(&vtx[0], &vtx[1], &vtx[2], &vtx[3]);
+  double det = TetMesh::getTetDeterminant(vtx[0], vtx[1], vtx[2], vtx[3]);
   target->volume = fabs(det / 6);
 
   for(int i=0; i<4; i++)
@@ -77,7 +79,7 @@ void StVKTetABCD::StVKSingleTetABCD(Vec3d vtx[4], elementData * target)
           columns[countJ][countI] = vtx[ii][jj];
           countJ++;
         }
-        int sign = (((i + j) % 2) == 0) ? 1 : -1;
+        int sign = (((i + j) % 2) == 0) ? -1 : 1;
         target->Phig[i][j] = 1.0 * sign * dot(Vec3d(1,1,1), cross(columns[0],columns[1])) / det;
         countI++;
       }
@@ -129,4 +131,3 @@ double StVKTetABCD::D(void * elementIterator, int i, int j, int k, int l)
   return cache->elementPointer->volume * cache->dots[i][j] * cache->dots[k][l]; 
 }
 
-}
diff --git a/libraries/stvk/StVKTetABCD.h b/libraries/stvk/StVKTetABCD.h
new file mode 100644
index 0000000000000000000000000000000000000000..87ab897a7f7820849d067d3f4144b0ce83723e41
--- /dev/null
+++ b/libraries/stvk/StVKTetABCD.h
@@ -0,0 +1,84 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVKTETABCD_H_
+#define _STVKTETABCD_H_
+
+#include "StVKElementABCD.h"
+#include "tetMesh.h"
+
+/*
+  This class stores the St.Venant-Kirchhoff A,B,C,D coefficients for a tetrahedral element.
+  This is the low-memory version (the version that we use most often).
+  See also StVKInternalForces.h .
+*/
+
+class StVKTetABCD : public StVKElementABCD
+{
+public:
+
+  // computes the ABCD coefficients 
+  StVKTetABCD(TetMesh * tetMesh);
+
+  virtual Mat3d A(void * elementIterator, int i, int j);
+  virtual double B(void * elementIterator, int i, int j);
+  virtual Vec3d C(void * elementIterator, int i, int j, int k);
+  virtual double D(void * elementIterator, int i, int j, int k, int l);
+
+  typedef struct
+  {
+    double volume;
+    Vec3d Phig[4]; // gradient of a basis function
+  } elementData;
+
+  typedef struct
+  {
+    elementData * elementPointer;
+    double dots[4][4];
+  } ElementCache;
+
+  void AllocateElementIterator(void ** elementIterator);
+  void ReleaseElementIterator(void * elementIterator);
+  void PrepareElement(int el, void * elementIterator); // must call each time before accessing an element
+
+  virtual ~StVKTetABCD();
+
+protected:
+
+  elementData * elementsData;
+
+  // creates the elementData structure for a tet
+  void StVKSingleTetABCD(Vec3d vertices[4], elementData * target);
+};
+
+#endif
+
diff --git a/src/libstvk/StVKTetHighMemoryABCD.cpp b/libraries/stvk/StVKTetHighMemoryABCD.cpp
similarity index 85%
rename from src/libstvk/StVKTetHighMemoryABCD.cpp
rename to libraries/stvk/StVKTetHighMemoryABCD.cpp
index 9955d4ae11f35aea829f67d8386d74c7bb7c1a42..32e0942511d14d3a97fdcb628c026fa3f7b1ebc1 100644
--- a/src/libstvk/StVKTetHighMemoryABCD.cpp
+++ b/libraries/stvk/StVKTetHighMemoryABCD.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,8 +34,6 @@
 
 //#define CONTIGUOUSBLOCK
 
-namespace vega
-{
 StVKTetHighMemoryABCD::StVKTetHighMemoryABCD(TetMesh * tetMesh)
 {
   int numElements = tetMesh->getNumElements();
@@ -59,7 +61,7 @@ StVKTetHighMemoryABCD::StVKTetHighMemoryABCD(TetMesh * tetMesh)
   {
     Vec3d vertices[4];
     for(int i=0; i<4; i++)
-      vertices[i] = *(tetMesh->getVertex(el, i));
+      vertices[i] = tetMesh->getVertex(el, i);
     StVKSingleTetABCD(vertices, A_[el], B_[el], C_[el], D_[el]);
   }
 
@@ -81,7 +83,7 @@ StVKTetHighMemoryABCD::~StVKTetHighMemoryABCD()
 
 void StVKTetHighMemoryABCD::StVKSingleTetABCD(Vec3d vtx[4], Mat3d A[4][4], double B[4][4], Vec3d C[4][4][4], double D[4][4][4][4])
 {
-  double det = TetMesh::getTetDeterminant(&vtx[0], &vtx[1], &vtx[2], &vtx[3]);
+  double det = TetMesh::getTetDeterminant(vtx[0], vtx[1], vtx[2], vtx[3]);
   double volume = fabs(det / 6);
 
   Vec3d Phig[4];
@@ -103,7 +105,7 @@ void StVKTetHighMemoryABCD::StVKSingleTetABCD(Vec3d vtx[4], Mat3d A[4][4], doubl
           columns[countJ][countI] = vtx[ii][jj];
           countJ++;
         }
-        int sign = (((i + j) % 2) == 0) ? 1 : -1;
+        int sign = (((i + j) % 2) == 0) ? -1 : 1;
         Phig[i][j] = 1.0 * sign * dot(Vec3d(1,1,1), cross(columns[0],columns[1])) / det;
         countI++;
       }
@@ -133,4 +135,3 @@ void StVKTetHighMemoryABCD::StVKSingleTetABCD(Vec3d vtx[4], Mat3d A[4][4], doubl
           D[i][j][k][l] = volume * dot(Phig[i],Phig[j]) * dot(Phig[k],Phig[l]);
 }
 
-}
diff --git a/libraries/stvk/StVKTetHighMemoryABCD.h b/libraries/stvk/StVKTetHighMemoryABCD.h
new file mode 100644
index 0000000000000000000000000000000000000000..058a37967989d328168ec4838c6c9da3c40c7916
--- /dev/null
+++ b/libraries/stvk/StVKTetHighMemoryABCD.h
@@ -0,0 +1,72 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _STVKTETHIGHMEMORYABCD_H_
+#define _STVKTETHIGHMEMORYABCD_H_
+
+#include "StVKElementABCD.h"
+#include "tetMesh.h"
+
+/*
+  Class "StVKTetHighMemoryABCD" stores (explicitly) the St.Venant-Kirchhoff 
+  A,B,C,D coefficients for a tetrahedron (high-memory version).
+  It enables fast coefficient access, but requires more memory than 
+  the "StVKTetABCD" class. The class "StVKTetHighMemoryABCD" is practical 
+  up to a few thousand tetrahedra, depending on the size of your memory. 
+  In our simulations, we usually used "StVKTetABCD" for all the tet meshes.
+*/
+
+class StVKTetHighMemoryABCD : public StVKElementABCD
+{
+public:
+
+  // computes the ABCD coefficients 
+  StVKTetHighMemoryABCD(TetMesh * tetMesh);
+
+  inline virtual Mat3d A(void * elementIterator, int i, int j) { return A_[*(int*)elementIterator][i][j]; }
+  inline virtual double B(void * elementIterator, int i, int j) { return B_[*(int*)elementIterator][i][j]; }
+  inline virtual Vec3d C(void * elementIterator, int i, int j, int k) { return C_[*(int*)elementIterator][i][j][k]; }
+  inline virtual double D(void * elementIterator, int i, int j, int k, int l) { return D_[*(int*)elementIterator][i][j][k][l]; }
+
+  virtual ~StVKTetHighMemoryABCD();
+
+protected:
+  Mat3d (*A_) [4][4];
+  double (*B_) [4][4];
+  Vec3d (*C_) [4][4][4];
+  double (*D_) [4][4][4][4];
+
+  void StVKSingleTetABCD(Vec3d vertices[4], Mat3d A[4][4], double B[4][4], Vec3d C[4][4][4], double D[4][4][4][4]);
+};
+
+#endif
+
diff --git a/src/libstvk/cubicMeshIntegrals.cpp b/libraries/stvk/cubicMeshIntegrals.cpp
similarity index 99%
rename from src/libstvk/cubicMeshIntegrals.cpp
rename to libraries/stvk/cubicMeshIntegrals.cpp
index fed73a23f3ba9f9798b3eb579a6bd12a4e94c0ed..99b73eeb153c8b18855124b465a44cab870cdb17 100644
--- a/src/libstvk/cubicMeshIntegrals.cpp
+++ b/libraries/stvk/cubicMeshIntegrals.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
+ * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC           *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/libraries/virtualTets/geometryQueryER.cpp b/libraries/virtualTets/geometryQueryER.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7d6a1598bd0297d8fe2f1dc0f7fe06173ca3a846
--- /dev/null
+++ b/libraries/virtualTets/geometryQueryER.cpp
@@ -0,0 +1,160 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "geometryQueryER.h"
+using namespace std;
+
+namespace
+{
+
+// scaledTriangleNormal must not be zero-length, but need not be unit length
+// is queryPoint to the left of the edge (edgeStart -> edgeEnd)
+bool isToLeftOfTriangleEdge(const Vec3ER & queryPoint, const Vec3ER & scaledTriangleNormal, const Vec3ER & edgeStart, const Vec3ER & edgeEnd)
+{
+  ER d = dot(cross(edgeEnd - edgeStart, queryPoint - edgeStart), scaledTriangleNormal);
+  return ER_sign(d) > 0;
+}
+
+ER squaredDistanceToPlane(const Vec3ER & scaledPlaneNormal, const Vec3ER & delta)
+{
+  ER d = dot(scaledPlaneNormal, delta);
+  ER normalLen2 = len2(scaledPlaneNormal);
+  return (d*d) / normalLen2;
+}
+
+// distance from query point to line segment (segStart -> segEnd)
+ER squaredDistanceToLineSegment(const Vec3ER & queryPoint, const Vec3ER & lineStart, const Vec3ER & lineEnd)
+{
+  Vec3ER lineVec = lineEnd - lineStart;
+
+  Vec3ER segStart2Query = queryPoint - lineStart;
+  ER d = dot(lineVec, segStart2Query);
+
+  // return distance from segStart to query point
+  if (ER_sign(d) <= 0) return len2(segStart2Query);
+
+  ER lineLen2 = len2(lineVec);
+  // return distance from segEnd to query point
+  if (d > lineLen2) return len2(queryPoint - lineEnd);
+
+  // return distance from the infinite line to query point
+  return len2(cross(lineVec, segStart2Query)) / lineLen2;
+}
+
+Vec3ER getClosestPointToPlane(const Vec3ER & queryPoint, const Vec3ER & scaledPlaneNormal, const Vec3ER & planeStart)
+{
+  ER d = dot(scaledPlaneNormal, queryPoint - planeStart);
+  ER normalLen2 = len2(scaledPlaneNormal);
+  assert(normalLen2 > 0);
+  return queryPoint - d * scaledPlaneNormal / normalLen2;
+}
+
+Vec3ER getClosestPointToLineSegment(const Vec3ER & queryPoint, const Vec3ER & lineStart, const Vec3ER & lineEnd)
+{
+  Vec3ER lineVec = lineEnd - lineStart;
+
+  ER d = dot(lineVec, queryPoint - lineStart);
+
+  // the closest point is lineStart
+  if (ER_sign(d) <= 0) return lineStart;
+
+  ER lineLen2 = len2(lineVec);
+  // the closest point is lineEnd
+  if (d >= lineLen2) return lineEnd;
+
+  return lineStart + (d / lineLen2) * lineVec;
+}
+
+} // anonymous namespace
+
+// also returns the closest feature to the query point:
+//  0: vertex0
+//  1: vertex1
+//  2: vertex2
+//  3: edge among 01
+//  4: edge among 12
+//  5: edge among 20
+//  6: the face itself
+ER squaredDistanceToTriangle(const Vec3ER & queryPoint, const Vec3ER & vertex0, const Vec3ER & vertex1, const Vec3ER & vertex2, int & feature)
+{
+  Vec3ER scaledNormal = cross(vertex1 - vertex0, vertex2 - vertex0);
+
+  if(scaledNormal != VEC3ER_NULL
+     && isToLeftOfTriangleEdge(queryPoint, scaledNormal, vertex0, vertex1)
+     && isToLeftOfTriangleEdge(queryPoint, scaledNormal, vertex1, vertex2)
+     && isToLeftOfTriangleEdge(queryPoint, scaledNormal, vertex2, vertex0))
+  {
+    // the closest point on triangle to queryPoint is the same closet point on the plane where the triangle lies to queryPoint
+    feature = 6;
+    return squaredDistanceToPlane(scaledNormal, queryPoint-vertex0);
+  }
+  else // the projection of the queryPoint onto the triangle plane is outside the triangle, or the triangle is degenerate
+  {    // then we query the closest distance from the query point to all the three edges
+    ER d0 = squaredDistanceToLineSegment(queryPoint, vertex1, vertex0);
+    ER d1 = squaredDistanceToLineSegment(queryPoint, vertex2, vertex1);
+    ER d2 = squaredDistanceToLineSegment(queryPoint, vertex0, vertex2);
+
+    pair<ER, int> sortBuffer[3] = { { d0, 0 }, { d1, 1 }, { d2, 2 } };
+    sort(sortBuffer, sortBuffer+3);
+    if (sortBuffer[0].first == sortBuffer[1].first)
+    {
+      // closest feature is a vertex
+      int edgeIDToFeatureMap[3][3] = { {-1, 1, 0}, {1, -1, 2}, {0, 2, -1} };
+      feature = edgeIDToFeatureMap[sortBuffer[0].second][sortBuffer[1].second];
+      assert(feature >= 0);
+    }
+    else
+    { // closest feature is an edge
+      feature = sortBuffer[0].second + 3;
+    }
+    return sortBuffer[0].first;
+  }
+}
+
+Vec3ER getClosestPointToTriangleWithFeature(const Vec3ER & queryPoint, const Vec3ER & vertex0, const Vec3ER & vertex1, const Vec3ER & vertex2, int feature)
+{
+  if (feature == 0) return vertex0;
+  else if (feature == 1) return vertex1;
+  else if (feature == 2) return vertex2;
+  else if (feature == 3) // edge 01
+    return getClosestPointToLineSegment(queryPoint, vertex0, vertex1);
+  else if (feature == 4) // edge 12
+    return getClosestPointToLineSegment(queryPoint, vertex1, vertex2);
+  else if (feature == 5) // edge 20
+    return getClosestPointToLineSegment(queryPoint, vertex2, vertex0);
+  // else, feature == 6, on triangle
+  const Vec3ER e1 = vertex1 - vertex0;
+  const Vec3ER oe3 = vertex2 - vertex0;
+  const Vec3ER scaledNormal = cross(e1, oe3);
+  return getClosestPointToPlane(queryPoint, scaledNormal, vertex0);
+}
+
diff --git a/libraries/virtualTets/geometryQueryER.h b/libraries/virtualTets/geometryQueryER.h
new file mode 100644
index 0000000000000000000000000000000000000000..0ee07976cc4fb3d071d639339332288ed70db69e
--- /dev/null
+++ b/libraries/virtualTets/geometryQueryER.h
@@ -0,0 +1,55 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef GEOMETRYQUERYER_H
+#define GEOMETRYQUERYER_H
+
+#include "vec3ER.h"
+
+/*
+  Helper functions for exact geometry operations.
+*/
+
+// Also returns the closest feature to the query point:
+//  0: vertex0
+//  1: vertex1
+//  2: vertex2
+//  3: edge among 01
+//  4: edge among 12
+//  5: edge among 20
+//  6: the face itself
+ER squaredDistanceToTriangle(const Vec3ER & queryPoint, const Vec3ER & vertex0, const Vec3ER & vertex1, const Vec3ER & vertex2, int & feature);
+
+Vec3ER getClosestPointToTriangleWithFeature(const Vec3ER & queryPoint, const Vec3ER & vertex0, const Vec3ER & vertex1, const Vec3ER & vertex2, int feature);
+
+#endif
+
diff --git a/libraries/virtualTets/tetTriCutFeature.cpp b/libraries/virtualTets/tetTriCutFeature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0ec518ddd3f6720c1a61d4e96d6d0d60cc4d3653
--- /dev/null
+++ b/libraries/virtualTets/tetTriCutFeature.cpp
@@ -0,0 +1,146 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "tetTriCutFeature.h"
+#include <cassert>
+#include <array>
+using namespace std;
+
+TetShape::TetShape(const TetMeshRef & tetMesh, int tetID) : tetID(tetID)
+{
+  Vec4i tet = tetMesh.tet(tetID);
+  for(int v : tet)
+  {
+    position[v] = tetMesh.pos(v);
+  }
+  assert(position.size() == 4);
+
+  OTetKey tetkey(tet);
+  for(int i = 0; i < 4; i++)
+  {
+    auto otrikey = tetkey.oFaceKey(i);
+    int v0 = otrikey[0], v1 = otrikey[1], v2 = otrikey[2];
+    Vec3d te1 = tetMesh.pos(v1) - tetMesh.pos(v0);
+    Vec3d te2 = tetMesh.pos(v2) - tetMesh.pos(v0);
+    Vec3d normal = cross(te1, te2);
+    normal.normalize();
+    assert(normal.hasNaN() == false);
+    faceNormal[otrikey.uTriKey()] = normal;
+  }
+  assert(faceNormal.size() == 4);
+}
+
+
+bool TetShape::hasFace(const UTriKey & face) const
+{
+  return faceNormal.find(face) != faceNormal.end();
+}
+
+Vec3d TetShape::getNormal(const UTriKey & face) const
+{
+  auto iter = faceNormal.find(face);
+  assert(iter != faceNormal.end());
+  return iter->second;
+}
+
+Vec3d TetShape::getFaceCenter(const UTriKey & face) const
+{
+  auto iter = faceNormal.find(face);
+  assert(iter != faceNormal.end());
+  Vec3d center(0.0);
+  for(int i = 0; i < 3; i++)
+    center += getPos(face[i]);
+  return center / 3.0;
+}
+
+Vec3d TetShape::getTetCenter() const
+{
+  Vec3d center(0.0);
+  for(const auto & p : position)
+    center += p.second;
+  return center / 4.0;
+}
+
+Vec3d TetShape::getPos(int vtxID) const
+{
+  auto iter = position.find(vtxID);
+  assert(iter != position.end());
+  return iter->second;
+}
+
+array<UTriKey, 2> TetShape::getNeighboringFace(const UEdgeKey & edge) const
+{
+  array<UTriKey, 2> ret;
+  int sz = 0;
+  for(const auto & p : faceNormal)
+  {
+    if (p.first.hasUEdge(edge))
+    {
+      ret[sz++] = p.first;
+    }
+  }
+  if (sz != 2)
+  {
+    cout << "Error in TetShape: ";
+    for(auto p : faceNormal) { cout << p.first << " "; }
+    cout << " querying edge: " << edge << endl;
+  }
+  assert(sz == 2);
+  return ret;
+}
+
+array<UTriKey, 3> TetShape::getNeighboringFace(int vtxID) const
+{
+  array<UTriKey, 3> ret;
+  int sz = 0;
+  for(const auto & p : faceNormal)
+  {
+    if (p.first.hasIndex(vtxID)) { ret[sz++] = p.first; }
+  }
+  assert(sz == 3);
+  return ret;
+}
+
+vector<UTriKey> TetTriCutFeature::getTouchingTetFaces(const TetShape & tetShape) const
+{
+  if (isInsideTet()) return {};
+  if (isTetFace()) return { getTetFace() };
+  if (isTetEdge())
+  {
+    auto faces = tetShape.getNeighboringFace(getTetEdge());
+    assert(faces[0].isValidTriangle() && faces[1].isValidTriangle());
+    return { faces[0], faces[1] };
+  }
+  // tet vtx
+  auto faces = tetShape.getNeighboringFace(getTetVertex());
+  return { faces[0], faces[1], faces[2] };
+}
diff --git a/libraries/virtualTets/tetTriCutFeature.h b/libraries/virtualTets/tetTriCutFeature.h
new file mode 100644
index 0000000000000000000000000000000000000000..90f29a98960a8adf851e002944bc6b955d140f4d
--- /dev/null
+++ b/libraries/virtualTets/tetTriCutFeature.h
@@ -0,0 +1,142 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TETTRICUTFEATURE_H
+#define TETTRICUTFEATURE_H
+
+#include "triKey.h"
+#include "tetKey.h"
+#include "hashHelper.h"
+#include "tetMeshGeo.h"
+#include <vector>
+#include <map>
+
+
+// to perform geometric queries on a tet
+struct TetShape
+{
+  std::map<UTriKey, Vec3d> faceNormal;
+  std::map<int, Vec3d> position; // vtxID in the mesh -> vtx position
+  int tetID = -1;
+
+  TetShape(const TetMeshRef & tetMesh, int tetID);
+
+  bool hasFace(const UTriKey & face) const;
+
+  Vec3d getNormal(const UTriKey & face) const;
+
+  Vec3d getPos(int vtxID) const; // vtxID is the global vtx ID on the mesh, NOT local ID: [0,3]
+  Vec3d getFaceCenter(const UTriKey & face) const;
+  Vec3d getTetCenter() const;
+
+  // get the two tet faces that share the tet edge
+  std::array<UTriKey, 2> getNeighboringFace(const UEdgeKey & edge) const;
+  // get the three tet faces that sahre the vtx with global ID: vtxID
+  std::array<UTriKey, 3> getNeighboringFace(int vtxID) const;
+};
+
+
+// a unique pair of tet and tri, used to represent an intersection vertex between a tet and a triangle
+// from a tet mesh and triangle mesh, respectively
+struct TetTriCutFeature
+{
+  TetTriCutFeature() {} // initialize to be a null feature, with triFeature = {-1,-1,-1} and tetFeature = {-1,-1,-1,-1}
+  TetTriCutFeature(const UTriKey & tri, const UTetKey & tet) : triFeature(tri), tetFeature(tet) {}
+  UTriKey triFeature; // default initialization to {-1,-1,-1}
+  UTetKey tetFeature; // default initialization to {-1,-1,-1,-1}
+  // the indices stored in triFeature and tetFeature are sorted
+
+  // tetFeature can be {-1,-1,-1, a}, {-1,-1, a, b}, {-1, a, b, c} or {a, b, c, d}, where a,b,c,d >= 0
+  // {-1,-1,-1, a} means the represented vertex is a tet mesh vtx with tetVtxID = a
+  // {-1,-1, a, b} means the represented vertex is on a tet edge between tetVtxID a and b
+  // {-1, a, b, c} means the represented vertex is on the interior of a tet face, whose vertices are a, b and c
+  // { a, b, c, d} means the represented vertex is on the interior of a tet, whose vertices are a,b,c,d
+  bool isInsideTet() const { return tetFeature[0] >= 0; }
+  bool isTetFace() const { return tetFeature[0] < 0 && tetFeature[1] >= 0; }
+  bool isTetEdge() const { return tetFeature[1] < 0 && tetFeature[2] >= 0; }
+  bool isTetVertex() const { return tetFeature[2] < 0 && tetFeature[3] >= 0; }
+
+  // triFeature can be {-1,-1, a}, {-1, a, b} or {a, b, c}, where a,b,c >= 0
+  // {-1,-1, a} means the represented vertex is a triangle vertex with triVtxID = a
+  // {-1, a, b} means the represented vertex is on a triangle edge between triVtxID a and b
+  // { a, b, c} means the represented vertex is on the interior of a triangle, whose vertices are a, b and c
+  bool isInsideTri() const { return triFeature[0] >= 0; }
+  bool isTriEdge() const { return triFeature[0] < 0 && triFeature[1] >= 0; }
+  bool isTriVertex() const { return triFeature[1] < 0 && triFeature[2] >= 0; }
+
+  UTetKey getTet() const { return tetFeature; }
+  UTriKey getTetFace() const { return *(UTriKey*)(&tetFeature[1]); }
+  UEdgeKey getTetEdge() const { return *(UEdgeKey*)(&tetFeature[2]); }
+  int getTetVertex() const { return tetFeature[3]; }
+
+  UTriKey getTri() const { return triFeature; }
+  UEdgeKey getTriEdge() const { return *(UEdgeKey*)(&triFeature[1]); }
+  int getTriVertex() const { return triFeature[2]; }
+
+  bool operator == (const TetTriCutFeature & f2) const { return triFeature == f2.triFeature && tetFeature == f2.tetFeature; }
+  bool operator != (const TetTriCutFeature & f2) const { return !((*this) == f2); }
+  bool operator < (const TetTriCutFeature & f2) const
+  {
+    if (triFeature < f2.triFeature) return true;
+    if (f2.triFeature < triFeature) return false;
+    return tetFeature < f2.tetFeature;
+  }
+
+  // return the tet faces the represented vertex touches on tetShape
+  // if the represented vertex is on the interior of the tet, return {}
+  // if it is on the tet face, return only that face
+  // if it is on the tet edge, return the two tet faces sharing this edge
+  // else, it can only be a tet vtx, return the three tet faces sharing this vtx
+  std::vector<UTriKey> getTouchingTetFaces(const TetShape & tetShape) const;
+};
+
+namespace std
+{
+  template <>
+  struct hash<TetTriCutFeature>
+  {
+    size_t operator()(const TetTriCutFeature & k) const
+    {
+      size_t v = hash<int>()(k.triFeature[0]);
+      v = hashCombine(v, hash<int>()(k.triFeature[1]));
+      v = hashCombine(v, hash<int>()(k.triFeature[2]));
+      v = hashCombine(v, hash<int>()(k.tetFeature[0]));
+      v = hashCombine(v, hash<int>()(k.tetFeature[1]));
+      v = hashCombine(v, hash<int>()(k.tetFeature[2]));
+      v = hashCombine(v, hash<int>()(k.tetFeature[3]));
+      return v;
+    }
+  };
+}
+
+
+#endif
diff --git a/libraries/virtualTets/tetTriCuttingData.h b/libraries/virtualTets/tetTriCuttingData.h
new file mode 100644
index 0000000000000000000000000000000000000000..ec24fd0307b18240ef3387b119cde5837ac3ec90
--- /dev/null
+++ b/libraries/virtualTets/tetTriCuttingData.h
@@ -0,0 +1,75 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TETTRICUTTINGDATA_H
+#define TETTRICUTTINGDATA_H
+
+#include "tetTriMeshCutting.h"
+#include <vector>
+#include <mutex>
+
+/*
+  Datastructure to use our virtual tets in the immersion algorithm.
+*/
+
+struct TetTriCuttingData
+{
+  // Definition:
+  //   cut triangle mesh: the new triangle mesh which is the result of the cut on the input triangle mesh by all the tets
+  //     The newly geneated cuts are modeled by adding new vertices on the cut triangles.
+  //     New vertices are shared by neighboring cut triangles. So after the cut, the topology of the input triangle mesh
+  //     is preserved. Only its surface is subdivided by the tets' cut.
+  //   cut feature: a pair of tetKey and triKey, used to store which geometry feature
+  //     (tet: interior/face/edge/vtx, tri: interior, edge, vtx) a cut vertex lies on
+  // TetTriMeshCutting::cutTriGroups: tetID -> cut triangle info in each tet
+  //   cut triangle info includes: 1) the 3 cutVtxID of the triangle, 2) the original triangle ID where this cut triangle is from
+  std::vector<Vec3d> cutVtxPos;           // cut triangle mesh vertex positions
+  std::vector<Vec3ER> cutVtxPosER;        // cut triangle mesh vertex positions in exact arithmetic
+  std::vector<TetTriCutFeature> features; // cut feature of each cutVtx
+  std::vector<Vec3ER> tetPosER;           // tet mesh position in exact arithmetic
+  std::vector<TetTriMeshCutting::CutTriGroup> cutTriGroups; // cut triangle info in each tet
+
+
+  // used to protect ER because they are not thread-safe.
+//  mutable bool multiThreading = false;
+//  mutable std::mutex * cutVtxPosMutex = nullptr;
+//  mutable std::mutex * tetPosMutex = nullptr;
+//  mutable std::vector<std::mutex> * tetPosVecMutex = nullptr;
+//  mutable std::vector<std::mutex> * cutVtxPosVecMutex = nullptr;
+};
+
+struct TetTriIntersectingData
+{
+  std::vector<std::vector<int>> triInTet;
+  std::vector<Vec3ER> tetPosER;            // exact real for tet pos
+};
+#endif
diff --git a/libraries/virtualTets/tetTriMeshCutting.cpp b/libraries/virtualTets/tetTriMeshCutting.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9690a82e4fef9ccf40a78f408aa1d304b7e4bec6
--- /dev/null
+++ b/libraries/virtualTets/tetTriMeshCutting.cpp
@@ -0,0 +1,572 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "tetTriMeshCutting.h"
+#include "tetKey.h"
+#include "containerHelper.h"
+#include "basicAlgorithms.h"
+#include "exactOctree.h"
+#include "predicates.h"
+#include "profiler.h"
+#include "performanceCounter.h"
+#include "planeER.h"
+#include "meshIntersection.h"
+#include "plane.h"
+#include <cassert>
+#include <array>
+#include <fstream>
+#include <unordered_map>
+#include <mutex>
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+
+using namespace std;
+
+//static int debugInt = 0;
+
+///////////////////////////////////////////////////////////////
+//                    TetTriMeshCutting
+///////////////////////////////////////////////////////////////
+
+
+static Profiler profiler;
+static Profiler cutProfiler;
+//static StopWatch wnTimer;
+
+// namespace {
+// class ExitRegister {
+// public:
+//   ~ExitRegister() {
+//     cout << "Time for tet tri cut: " << endl;
+//     cout << profiler.toString() << endl;
+//     cout << "Time inside the cut:" << endl;
+//     cout << cutProfiler.toString() << endl;
+//   }
+// };
+// static ExitRegister exitRegister_;
+// }
+
+TetTriMeshCutting::TetTriMeshCutting(const TetMeshRef & tetMesh, TriMeshRef triMesh)
+: tetMesh(tetMesh), triMesh(triMesh)
+{}
+
+void TetTriMeshCutting::computeIntersectingTriTets()
+{
+  profiler.startTimer("embedding");
+
+  tri2tet.clear();
+  tri2tet.resize(triMesh.numTriangles());
+
+  // tet2tri: tetID -> intersecting triIDs
+  tet2tri = computeTrianglesIntersectingEachTetExact(tetMesh, triMesh);
+
+  // build tri2tet from tet2tri
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    for(int tri : tet2tri[tetID]) {  tri2tet[tri].push_back(tetID); }
+  }
+
+  vector<int> outsideTriangles; // triangleIDs not intersected by any tet
+  for(int i = 0; i < triMesh.numTriangles(); i++)
+  {
+    if (tri2tet[i].size() == 0) { outsideTriangles.push_back(i); }
+  }
+
+  if (outsideTriangles.size() > 0)
+  {
+    cout << "warning: " << outsideTriangles.size() << " triangle(s) not embedded in the tet mesh" << endl;
+//    throw 1;
+  }
+
+  profiler.stopLastTimer();
+}
+
+void TetTriMeshCutting::computeCutTriangles(const std::vector<Vec3ER> * triVtxPosER)
+{
+  assert(tet2tri.size() > 0);
+
+  profiler.startTimer("prepare to cut");
+
+  ER ZERO = 0.0;
+
+  // build tetVtxPosER
+  tetVtxPosER.clear();
+  cutTriVtxPosER.clear(); // stores vtx positions on the cut triangle mesh
+  for(int i = 0; i < tetMesh.numVertices(); i++)
+  {
+    Vec3d v = tetMesh.pos(i);
+    tetVtxPosER.emplace_back(v[0], v[1], v[2]);
+  }
+
+  // the cut tri mesh inherits the vtx from the input tri mesh
+  // those vtx have the same vtxIDs in cut tri mesh as the input tri mesh
+  if (triVtxPosER) // if triVtxPosER has been provided
+  {
+    assert(sizei(*triVtxPosER) == triMesh.numVertices());
+    cutTriVtxPosER = (*triVtxPosER); // initialize the inherited vertices from the input tri mesh
+  }
+  else // compute triVtxPosER
+  {
+    for(int i = 0; i < triMesh.numVertices(); i++)
+    {
+      Vec3d v = triMesh.pos(i);
+      cutTriVtxPosER.emplace_back(v[0], v[1], v[2]);
+    }
+  }
+
+  // The basic algorithm works as follows: we go through each triangle/tet pair and compute vertices and triangles in the
+  // cut triangle mesh, and store them in a global buffer.
+  // We denote those new vertices as cutTriVtx.
+  // We parallelize over each tet for multi-threading.
+  // Because we are using CGAL's lazy evaluation exact kernel, we need to be careful as exact numbers are dependent on each other.
+  // One computed cutTriVtx exact position is dependent on the triangle and tet pair where the intersection is from.
+  // So this cutTriVtx posER is dependent on three triangle vtx posER and four tet vtx posER.
+  // Therefore, we lock seven mutexes for one triangle/tet pair and after computing the cutTriVtxes, we lock one more mutex
+  // to access the global buffer to store the cutTriVtxes.
+
+  // stores new cutTriVtx on the cut triangle mesh
+  // we use a list instead of a vector here because we want to avoid thread contest
+  // If it is a vector, then vector will reallocate its memory when its capacity is not enough.
+  // At this time, all the previous data stored in newCutTriVtxPosER will be touched.
+  // However, since we parallelize over triangle/tet pairs, the previous data is still dependent on tet/tri values touched
+  // by another thread. This might be dangerous.
+  // So to be safe, we use a list.
+  list<Vec3ER> newCutTriVtxPosER;
+
+  vector<UTriKey> allTetFaces; // find all tet faces, which are used as cutting plane
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    OTetKey tetkey(tetMesh.tet(tetID));
+    for(int j = 0; j < 4; j++)
+    {
+      UTriKey utri = tetkey.uFaceKey(j);
+      allTetFaces.push_back(utri);
+    }
+  }
+  sortAndDeduplicate(allTetFaces);
+
+  // build the mapping from tet face key to tetFaceID
+  // this mapping is useful because we want to save exact arithmetic operations
+  // on converting tet faces to planes for cutting
+  // TODO: since allTetFaces is already sorted, we can use a std::lower_bound to find the mapping from face key to faceID,
+  // thus we don't need this map<UTriKey, int>
+  map<UTriKey, int> tetFaceID;
+  for(size_t i = 0; i < allTetFaces.size(); i++) { tetFaceID[allTetFaces[i]] = i; }
+
+  // build planes from tet faces, those planes will be used in the Sutherland-Hodgman algorithm
+  vector<FastPlane> allTetPlanes;
+  for(size_t i = 0; i < allTetFaces.size(); i++)
+  {
+    UTriKey t = allTetFaces[i];
+    allTetPlanes.emplace_back(tetMesh.pos(t[0]), tetMesh.pos(t[1]), tetMesh.pos(t[2]));
+  }
+
+  // initialize the double-precision buffer for cut triMesh vtx pos
+  cutTriVtxPos = triMesh.exportPositions();
+  cutVtxFeatures.clear();
+  cutVtxFeatures.resize(cutTriVtxPos.size());
+
+  typedef std::unordered_map<TetTriCutFeature, int> FeatureIDMap; // feature -> vtxID
+  FeatureIDMap featureIDMap;
+
+  const TetTriCutFeature featurePairNull;
+
+  profiler.stopLastTimer();
+
+  profiler.startTimer("cut");
+  // Now we will loop over each tet, cut the triangles intersecting this tet with the four tet faces
+  // We use parallelize this loop. The result of the cut, the cut vtx, feature and cut triangles are then
+  // stored in cutTriVtxPos, cutVtxFeatures and cutTrisInTet
+
+  cutTrisInTet.clear();
+  cutTrisInTet.resize(tetMesh.numTets());
+#ifdef USE_TBB
+  mutex cutVtxMutex; // used to lock the access to the buffer storing the cut result
+  vector<mutex> tetVtxMutex(tetMesh.numVertices()); // used to lock the access to each exact tet vtx pos
+  vector<mutex> triVtxMutex(triMesh.numVertices()); // used to lock the access to each exact input tri vtx pos
+  tbb::parallel_for(tbb::blocked_range<int>(0, tetMesh.numTets()), [&](const tbb::blocked_range<int> & rng)
+  {
+    for (int tetID = rng.begin(); tetID != rng.end(); ++tetID)
+#else
+    for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+#endif
+    {
+      if (tet2tri[tetID].size() == 0) { continue; } // no tris in this tet
+
+      const auto & tris = tet2tri[tetID]; // triangles intersecting this tet
+
+      OTetKey tetkey(tetMesh.tet(tetID));
+      UTetKey utetkey(tetMesh.tet(tetID));
+      CutTriGroup triGroupInTet;
+
+      const int UNINIT = -100;
+
+      for(int triID : tris) // for each triangle, cut it with the four tet faces
+      {
+        Vec3i t = triMesh.tri(triID);
+        // struct CutVertex: temporary cut vertex data after cutting by one plane
+        // There are four tet faces, so we do four iterations of the Sutherland-Hodgman algorithm
+        // CutVertex stores the result after one iteration
+        struct CutVertex
+        {
+          Vec3d pos;    // vtx pos
+          Vec3ER posER; // vtx pos in exact arithmetic
+          Vec3ER bary;  // barycentric coords of this vtx with respect to the three triangle vertices
+          int ID = -1;  // cutVtxID
+          UTriKey triFeature; // the triangle interior / edge / vertex this vertex lies in
+          UTetKey tetFeature; // the tet interior / face / edge / vertex this vertex lies in
+          int inOut[4] = { UNINIT, UNINIT, UNINIT, UNINIT }; // local face ID [0,3] -> whether this vtx is outside (+1) or inside (-1) the face
+        };
+
+
+#ifdef USE_TBB
+        // guard to all the traingle vtx
+        // this will not cause dead locks, because we acquire the lock of each tri vtx according to
+        // the order of the tri vtx
+        // UTriKey store its vtx indices strictly sorted
+        // So utrikey[0] < utrikey[1] < utrikey[2]
+        // Since it follows an order of tri vtx indices, deadlock will not occur.
+        UTriKey utrikey(t);
+        lock_guard<mutex> triVtxGd0(triVtxMutex[utrikey[0]]);
+        lock_guard<mutex> triVtxGd1(triVtxMutex[utrikey[1]]);
+        lock_guard<mutex> triVtxGd2(triVtxMutex[utrikey[2]]);
+
+        // guard to all the tet faces
+        // this will not cause dead locks, because we acquire the lock of each tet vtx according to
+        // the order of the tet vtx
+        // utetkey store its vtx indices strictly sorted
+        // So utetkey[0] < utetkey[1] < utetkey[2] < utetkey[3]
+        // Since it follows an order of tet vtx indices, deadlock will not occur.
+        lock_guard<mutex> guard0(tetVtxMutex[utetkey[0]]);
+        lock_guard<mutex> guard1(tetVtxMutex[utetkey[1]]);
+        lock_guard<mutex> guard2(tetVtxMutex[utetkey[2]]);
+        lock_guard<mutex> guard3(tetVtxMutex[utetkey[3]]);
+#endif
+
+        vector<CutVertex> cutVertices(3); // initialize the triangle before any cut
+
+
+        for(int i = 0; i < 3; i++)
+        {
+          int vtxID = t[i];
+          cutVertices[i].ID = vtxID;
+          cutVertices[i].pos = triMesh.pos(vtxID);
+          cutVertices[i].posER = cutTriVtxPosER[vtxID];
+          cutVertices[i].triFeature = UTriKey(-1, -1, vtxID);
+          Vec3i b(0);
+          b[i] = 1.0;
+          cutVertices[i].bary = Vec3ER(b[0], b[1], b[2]);
+        }
+
+        for(int fID = 0; fID < 4; fID++)
+        {
+          OTriKey otri = tetkey.oFaceKey(fID);
+          UTriKey utri = tetkey.uFaceKey(fID);
+          int planeID = tetFaceID[utri];
+          assert(planeID >= 0);
+          // planeOutward: 1: if vtx indices in otri is the same as utri, which means that the plane stored
+          //                  in allTetPlanes(ER) has the orientation of facing outward the tet
+          //              -1: otherwise
+          int planeOutward = int(otri[1] == utri[1]) * 2 - 1;
+          assert(planeOutward == 1 || planeOutward == -1);
+
+  //        const auto & plane = allTetPlanesER[planeID];
+
+          FastPlaneER plane(tetVtxPosER[utri[0]], tetVtxPosER[utri[1]], tetVtxPosER[utri[2]]);
+
+          vector<CutVertex> outputCutVertices;
+
+          for(size_t i = 0; i < cutVertices.size(); i++)
+          {
+            cutVertices[i].inOut[fID] = planeOutward * plane.outside(cutVertices[i].posER);
+          }
+
+  //        cutProfiler.startTimer("cutVtx");
+          // loop over each edge of the current polygon stored in cutVertices
+          assert(cutVertices.size() > 0);
+          for(size_t cutVtxID = 0; cutVtxID < cutVertices.size(); cutVtxID++)
+          {
+            // get edge <v0, v1>
+            const CutVertex & v0 = cutVertices[cutVtxID];
+            const CutVertex & v1 = cutVertices[(cutVtxID+1)%cutVertices.size()];
+            const int & l0 = v0.inOut[fID];
+            const int & l1 = v1.inOut[fID];
+            assert(l0 != UNINIT && l1 != UNINIT);
+            // core Sutherland–Hodgman algorithm code: cut edge <v0, v1> with the plane
+
+            // v0 is on the plane, or if both vtx inside or on clippling plane
+            if (l0 == 0 || (l0 < 0 && l1 <= 0))
+            {
+              outputCutVertices.push_back(v0); // we retain v0 in the cut polygon
+              continue;
+            }
+
+            // both vtx outside clippling plane, or if v0 outside and v1 on the plane
+            else if (l0 > 0 && l1 >= 0)
+            {
+              continue;
+            }
+
+            // now, either: v0 inside and v1 outside
+            //          or: v0 outside and v1 inside
+            // in either case, we will generate a new cut vertex different than v0 or v1
+            if (l0 < 0) // if v0 inside
+            {
+              outputCutVertices.push_back(v0); // we retain v0 in the cut polygon
+            }
+
+            // generate the new vertex
+            CutVertex vi;
+
+            // compute weight for where on the edge this new vtx lies
+            ER a0 = plane.scaledDistance(v0.posER); // we use scaledDistance to avoid normalization operations for exact arithmetic
+            ER a1 = plane.scaledDistance(v1.posER);
+            ER asum = a0 + a1;
+
+            // we compute both exact and double-precision values because we need both exact and double-precision positions
+            double a0d = allTetPlanes[planeID].scaledDistance(v0.pos);
+            double a1d = allTetPlanes[planeID].scaledDistance(v1.pos);
+
+            vi.pos = (a1d * v0.pos + a0d * v1.pos) / (a0d + a1d);
+            vi.posER = (a1 * v0.posER + a0 * v1.posER) / asum;
+            vi.bary = (a1 * v0.bary + a0 * v1.bary) / asum;
+            for(int k = 0; k < fID; k++) // we add back the missing inOut data for the new cut vtx
+            {
+              // integrity check: for all the previous faces, v0 and v1 should be considered inside or exactly on the face
+              assert(v0.inOut[k] <= 0 && v1.inOut[k] <= 0);
+              vi.inOut[k] = ((v0.inOut[k] == 0 && v1.inOut[k] == 0) ? 0 : -1); // assign vi.inOut
+            }
+            vi.inOut[fID] = 0;
+  //          for(int k = fID+1; k < 4; k++)
+  //            vi.inOut[k] = ((v0.inOut[k] == 0 && v1.inOut[k] == 0) ? 0 : -1);
+
+            outputCutVertices.push_back(vi); // finally, store the new vtx vi
+          } // end for(size_t cutVtxID = 0; cutVtxID < cutVertices.size(); cutVtxID++)
+  //        cutProfiler.stopLastTimer();
+          if (outputCutVertices.size() < 3) // if the cut resulting a degenerate triangle,
+          {                                 // then we should skip this cut
+            cutVertices.clear();            // set cutVertices to be empty so that we can get out of the outer loop
+            break;
+          }
+          cutVertices.swap(outputCutVertices); // store the cut polygon to cutVertices for the next iteration
+        } // end fID loop on tet faces
+
+        if (cutVertices.size() < 3) continue; // we don't store degenerate triangles
+
+        // now we build vtx cut feature
+        // TODO: CutVertex::bary's only usage is to find the correct triFeature.
+        // But we can achieve this without exact arithmetic by tracking the location of each cut vtx during cutting
+        for(size_t i = 0; i < cutVertices.size(); i++)
+        {
+          CutVertex & v = cutVertices[i];
+          bool zeroBary[3];
+          for(int j = 0; j < 3; j++)
+            zeroBary[j] = (v.bary[j] == ZERO);
+
+          if (zeroBary[0] == true && zeroBary[1] == true) // v is a triangle vertex t[2]
+          {
+            assert(v.ID == t[2]);
+            v.triFeature = UTriKey(-1,-1, t[2]);
+          }
+          else if (zeroBary[0] == true && zeroBary[2] == true)  // v is a triangle vertex t[1]
+          {
+            assert(v.ID == t[1]);
+            v.triFeature = UTriKey(-1,-1, t[1]);
+          }
+          else if (zeroBary[1] == true && zeroBary[2] == true)  // v is a triangle vertex t[0]
+          {
+            assert(v.ID == t[0]);
+            v.triFeature = UTriKey(-1,-1, t[0]);
+          }
+          else if (zeroBary[0] == true) // v is on triangle edge <t[1], t[2]>
+          {
+            v.triFeature = UTriKey(-1, t[1], t[2]);
+          }
+          else if (zeroBary[1] == true) // v is on triangle edge <t[0], t[2]>
+          {
+            v.triFeature = UTriKey(-1, t[0], t[2]);
+          }
+          else if (zeroBary[2] == true) // v is on triangle edge <t[0], t[1]>
+          {
+            v.triFeature = UTriKey(-1, t[0], t[1]);
+          }
+          else // v is on the interior of the triangle
+          {
+            v.triFeature = UTriKey(t);
+          }
+
+          for(int j = 0; j < 4; j++)
+          {
+            assert(v.inOut[j] <= 0);
+          }
+          int inOutSum = v.inOut[0] + v.inOut[1] + v.inOut[2] + v.inOut[3];
+          assert(inOutSum <= -1);
+          if (inOutSum == -1) // it's a tet vtx
+          {
+            int fID = 0;
+            for(; fID < 4; fID++) { if (v.inOut[fID] < 0) break; }
+            assert(fID < 4);
+            v.tetFeature = UTetKey(-1,-1,-1,tetkey[fID]);
+          }
+          else if (inOutSum == -2)  // it's a tet edge
+          {
+            UTriKey f[2]; // first get the two tet faces sharing this tet edge
+            for(int fID = 0; fID < 4; fID++)
+            {
+              if (v.inOut[fID] == 0)
+              {
+                if (f[0][0] < 0) { f[0] = tetkey.uFaceKey(fID); }
+                else { assert(f[1][0] < 0); f[1] = tetkey.uFaceKey(fID); }
+              }
+            }
+            assert(f[1][0] >= 0);
+            UEdgeKey e = f[0].getSharedUEdge(f[1]);
+            assert(e[0] >= 0);
+            v.tetFeature = UTetKey(-1, -1, e[0], e[1]);
+          }
+          else if (inOutSum == -3)  // it's a tet face
+          {
+            int fID = 0;
+            for(; fID < 4; fID++)
+            {
+              if (v.inOut[fID] == 0)  { break; }
+            }
+            assert(fID < 4);
+            UTriKey f = tetkey.uFaceKey(fID);
+            v.tetFeature = UTetKey(-1, f[0], f[1], f[2]);
+          }
+          else  // it's inside the tet
+          {
+            v.tetFeature = tetkey.uTetKey();
+          }
+        } // end for(size_t i = 0; i < cutVertices.size(); i++) to build vtx cut feature
+
+        bool isOnTetFace = false; // whether this cut polygon is on a tet face
+        UTriKey tetFaceThisTriIsOn;
+        for(int fID = 0; fID < 4; fID++)
+        {
+          isOnTetFace = all_of(cutVertices.begin(), cutVertices.end(), [&](const CutVertex & v) { return v.inOut[fID] == 0; });
+          if (isOnTetFace) { tetFaceThisTriIsOn = tetkey.uFaceKey(fID); break; }
+        }
+        // this triangle cannot be exactly on more than two tet faces, unless this triangle is degenerate and lies on one tet edge
+        // but we require input triangles are free of degeneracy
+
+        {
+#ifdef USE_TBB
+            lock_guard<mutex> cutVtxlock(cutVtxMutex);
+#endif
+          for(size_t i = 0; i < cutVertices.size(); i++)
+          {
+            CutVertex & v = cutVertices[i];
+            TetTriCutFeature fp(v.triFeature, v.tetFeature);
+
+            // now we have triFeature and tetFeature ready, we can store each vtx
+            if (v.ID >= 0)  // they are input triangle mesh vtx
+            {
+              if (cutVtxFeatures[v.ID] != featurePairNull) { assert(cutVtxFeatures[v.ID] == fp); }
+              else { cutVtxFeatures[v.ID] = fp; }
+              continue;
+            }
+
+            // it's a new cut vtx, we search for its feature
+            if (featureIDMap.find(fp) == featureIDMap.end())
+            {
+              // new feature, we will create a new cut vtx ID for it
+              int newID = cutTriVtxPos.size();
+              featureIDMap[fp] = newID;
+              cutTriVtxPos.push_back(v.pos);
+              newCutTriVtxPosER.push_back(v.posER);
+              cutVtxFeatures.push_back(fp);
+              assert(featureIDMap.size() + triMesh.numVertices() == cutTriVtxPos.size());
+              v.ID = newID;
+            }
+            else
+            {
+              v.ID = featureIDMap[fp];
+            }
+          } // end loop on cutVertices
+        } // end threading-safe block
+
+        // now all cut vertices have been processed, we can triangulate the cut polygon into triangles
+        vector<int> IDList(cutVertices.size());
+        for(size_t i = 0; i < cutVertices.size(); i++)
+          IDList[i] = cutVertices[i].ID;
+
+        vector<Vec3i> triangleList = triangulatePolygon(IDList);
+        assert(hasInvalidTriangles(triangleList) == false);
+
+        int triIDStart = triGroupInTet.tri.size();
+        triGroupInTet.tri.insert(triGroupInTet.tri.end(), triangleList.begin(), triangleList.end());
+        for(size_t i = 0; i < triangleList.size(); i++)
+          triGroupInTet.oriID.push_back(triID);
+        if (isOnTetFace)
+        {
+          for(size_t i = 0; i < triangleList.size(); i++)
+            triGroupInTet.cutTriIDsOnFace[tetFaceThisTriIsOn].push_back(triIDStart+i);
+  //        cout << "has tri on tet face: " << triID << " " << tetID << endl;
+        }
+      } // end triID
+
+      cutTrisInTet[tetID] = move(triGroupInTet);
+    } // end each tetID
+#ifdef USE_TBB
+  }, tbb::auto_partitioner()); //end for locations
+#endif
+
+  cutTriVtxPosER.insert(cutTriVtxPosER.end(), newCutTriVtxPosER.begin(), newCutTriVtxPosER.end());
+  profiler.stopLastTimer();
+}
+
+void TetTriMeshCutting::saveCutTriMesh(string filename) const
+{
+  vector<Vec3i> allTris;
+  for(const auto & triGroup: cutTrisInTet)
+  {
+    const auto & tri = triGroup.tri;
+    allTris.insert(allTris.end(), tri.begin(), tri.end());
+  }
+  TriMeshGeo mesh(cutTriVtxPos, move(allTris));
+  mesh.save(filename);
+}
+
+TriMeshGeo TetTriMeshCutting::exportCutTriMesh() const
+{
+  vector<Vec3i> allTris;
+  for(const auto & triGroup: cutTrisInTet)
+  {
+    const auto & tri = triGroup.tri;
+    allTris.insert(allTris.end(), tri.begin(), tri.end());
+  }
+  return TriMeshGeo(cutTriVtxPos, allTris);
+}
+
diff --git a/libraries/virtualTets/tetTriMeshCutting.h b/libraries/virtualTets/tetTriMeshCutting.h
new file mode 100644
index 0000000000000000000000000000000000000000..0d7da733b080ea8b99cadf4e63f22ad80ba0dad8
--- /dev/null
+++ b/libraries/virtualTets/tetTriMeshCutting.h
@@ -0,0 +1,115 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TETTRIMESHCUTTING_H
+#define TETTRIMESHCUTTING_H
+
+#include "triMeshGeo.h"
+#include "tetMeshGeo.h"
+#include "tetKey.h"
+#include "triKey.h"
+#include "vec3ER.h"
+#include "tetTriCutFeature.h"
+
+/*
+  Our implementation of the Sutherland-Hodgman algorithm for tet vs tri intersection.
+  We assume input tet is in relatively good quality, and input triangle mesh has no degenerate triangles
+*/
+
+// exact intersection and cutting between a tet mesh and a triangle mesh
+class TetTriMeshCutting
+{
+public:
+  TetTriMeshCutting(const TetMeshRef & tetMesh, TriMeshRef triMesh);
+
+  // compute which tet intersects which triangle, and which triangle intersects which tet
+  // using the exact geometry predicates
+  // call this function before using getTrianglesIntersectingTet() or computeCutTriangles()
+  // you can get the result of this computation from getTrianglesIntersectingTet()
+  // note that, if tet and triangle only intersect at boundary, i.e. only touch each other,
+  // we still count them as intersecting
+  // note 2: triangle completely inside a tet is considered as intersecting
+  void computeIntersectingTriTets();
+  const std::vector<int> & getTrianglesIntersectingTet(int tetID) const { return tet2tri[tetID]; }
+
+  // compute the triangles cut by all the tets from the tet mesh
+  // call this before calling saveCutTriMesh(), exportCutTriMesh and other functions below
+  // note that, if tet and triangle only intersect at boundary, i.e. only touch each other,
+  // we DON'T count the triangle as cut by the tet
+  // optional: provide triangle mesh vtx positions in exact arithmetric
+  // note, we use multi-threading to accelerate exact arithmetic operations. This REQUIRES
+  // input Vec3ERs in exactTriVtxPos have no dependencies with each other
+  // If all Vec3ERs are built directly from Vec3ds, then there is no dependecies
+  // However, if one Vec3ER is built by, e.g. the summation of the other two Vec3ER in exactTriVtxPos, then
+  // there is a dependency between Vec3ERs in exactTriVtxPos and multi-threading will crash!
+  void computeCutTriangles(const std::vector<Vec3ER> * exactTriVtxPos = nullptr);
+
+  // save the cut triangle mesh to disk
+  void saveCutTriMesh(std::string filename) const;
+  // export the cut triangle mesh
+  TriMeshGeo exportCutTriMesh() const;
+
+  const std::vector<Vec3d> & cutTriPositions() const { return cutTriVtxPos; }
+  const std::vector<TetTriCutFeature> & cutVertexFeatures() const { return cutVtxFeatures; }
+
+  const std::vector<Vec3ER> & cutTriPositionsER() const { return cutTriVtxPosER; }
+  const std::vector<Vec3ER> & tetPositionsER() const { return tetVtxPosER; }
+
+  struct CutTriGroup
+  {
+    std::vector<Vec3i> tri; // cut triangles with cut vtxID
+    std::vector<int> oriID; // original uncut triangle ID for each cut triangle
+    std::map<UTriKey,std::vector<int>> cutTriIDsOnFace; // record degenerate case where cut triangles are completely on a tet face
+                                                        // store sorted triangle IDs on each tet face if they exist
+  };
+
+  const CutTriGroup & getCutTriGroup(int tetID) const { return cutTrisInTet[tetID]; }
+
+protected:
+  TetMeshRef tetMesh; // input tet mesh
+  TriMeshRef triMesh; // input tri mesh
+
+  std::vector<Vec3ER> tetVtxPosER; // exact arithmetic tet vtx pos
+
+  std::vector<std::vector<int> > tri2tet; // tri ID -> intersecting tets ID
+  std::vector<std::vector<int> > tet2tri; // tet ID -> sorted intersecting tri ID
+
+  // positions and TetTriCutFeatures of cut tri vtx
+  std::vector<Vec3d> cutTriVtxPos;
+  std::vector<Vec3ER> cutTriVtxPosER;
+  std::vector<TetTriCutFeature> cutVtxFeatures;
+
+  std::vector<CutTriGroup> cutTrisInTet;
+
+};
+
+#endif
diff --git a/libraries/virtualTets/tetTriPiece.cpp b/libraries/virtualTets/tetTriPiece.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c741a9d838cfd67146239ed0b447c7ca85ea02e3
--- /dev/null
+++ b/libraries/virtualTets/tetTriPiece.cpp
@@ -0,0 +1,729 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "tetTriPiece.h"
+
+#include "minivector.h"
+#include "basicAlgorithms.h"
+#include "containerHelper.h"
+#include "valueIndex.h"
+#include "profiler.h"
+//#include "triMeshOctreeK.h"
+#include "geometryQuery.h"
+#include "geometryQueryER.h"
+#include "predicates.h"
+#include <cassert>
+using namespace std;
+
+//extern Profiler profiler;
+//#define USE_MULTITHREADING
+
+// given two cut vtx on the piece boundary, try to find the tet face the piece boundary edge between them lies on
+// we need the tet face because when we do pseudo-normal test on pieces, if the closest site is on the boundary of a piece,
+// then we need to tet face to form a locally consistently-orientated shape to compute the correct pseudo-normal
+// Note: if the piece boundary edge is on a tet edge, then there are two tet faces we can choose from
+// we should choose the one that can form a local consistently-orientated shape around the closest site
+static UTriKey getTetFaceFromFeatureOnEdge(TetTriCutFeature f0, TetTriCutFeature f1, const Vec3d & triNormal, const TetShape & tetShape)
+{
+  if (f0.isTetFace() && f1.isTetFace()) // if both two vtx are on the interior of tet faces
+  {
+    assert(f0.tetFeature == f1.tetFeature); // assert they are on the same tet face
+    return f0.getTetFace(); // then return this face
+  }
+  // since the two vtx are on the piece boundary, they should be inside the tet
+  assert(f0.isInsideTet() == false && f1.isInsideTet() == false);
+  if (f0.isTetFace() || f1.isTetFace()) // if either one is on the tet face
+  {
+    if (f1.isTetFace()) swap(f0, f1); // assuming v0 with feature f0 is on the tet face
+    UTriKey face = f0.getTetFace();
+    if (f1.isTetEdge()) // if v1 is on a tet edge
+    {
+      assert(face.hasUEdge(f1.getTetEdge())); // then v1 must be on the edge of the same tet face as v0 is on
+    }
+    else if (f1.isTetVertex()) // if v1 is on a tet vtx
+    {
+      assert(face.hasIndex(f1.getTetVertex())); // then v1 must be on the vtx of the same tet face as v0 is on
+    }
+    return face;
+  }
+
+  // in the case that v0 and v1 are on the same tet edge, we have to use piece normal information to
+  // find out which tet face they lie on
+  auto processTetEdgeCase = [&](UEdgeKey e0)
+  {
+      auto faces = tetShape.getNeighboringFace(e0); // get the two tet faces sharing this tet edge
+      assert(faces[1].isValidTriangle());
+
+      // let's check which face is what we want
+      // by checking which tet face lies inside/outside the plane of the triangle this input boundary seg lies
+      int v0 = faces[0].getVertexOppositeEdge(e0);
+      int v1 = faces[1].getVertexOppositeEdge(e0);
+
+      Vec3d diff0 = tetShape.getPos(v0) - tetShape.getPos(e0[0]);
+      Vec3d diff1 = tetShape.getPos(v1) - tetShape.getPos(e0[0]);
+      double dot0 = dot(diff0, triNormal), dot1 = dot(diff1, triNormal);
+      int minFace = 0; // face local index [0,1] with the smallest dot product
+      if (dot0 > dot1) { swap(dot0, dot1); minFace = 1; }
+      // now dot0 <= dot1
+      // it should be assumed that the triangle of this edge e0 shoule be inside the dihedral angle of faces[0] and faces[1]
+      // so dot1 should >= 0 and dot0 should <= 0
+      // we don't use exact aritmethic here because unless the tet is nearly degenerate, the angle between
+      // the two tet faces should be large enough so that dot0 and dot1 should be reasonably different
+      assert(dot1 >= -1e-10);
+      assert(dot0 <= 1e-10);
+      return faces[minFace];
+  };
+
+  if (f0.isTetEdge() && f1.isTetEdge()) // if both are on tet edges
+  {
+    auto e0 = f0.getTetEdge(), e1 = f1.getTetEdge();
+    assert(e0.shareIndex(e1)); // e0 and e1 must have common vtx, otherwise the piece edge <v0,v1> does ont lie on the tet boundary
+    if (e0 != e1)
+    {
+      UTriKey face;
+      // if one vtx of e0 is the same as e1[0], then we know the tet face the two tet edges lie on
+      if (e0[0] == e1[0] || e0[1] == e1[0]) face = UTriKey(e0[0], e0[1], e1[1]);
+      // else if one vtx of e0 is the same as e1[1], then we also know the tet face
+      else if (e0[0] == e1[1] || e0[1] == e1[1]) face = UTriKey(e0[0], e0[1], e1[0]);
+      assert(face.isValidTriangle());
+      return face;
+    }
+    else // e0 == e1, two vtx lie on the same edge, we have to use piece normal information to find out the tet face
+    {
+      return processTetEdgeCase(e0);
+    }
+  }
+
+  if (f0.isTetEdge() || f1.isTetEdge()) // if only one the cut vtx is on a tet edge
+  {
+    if (f1.isTetEdge()) swap(f0, f1); // make sure v0 is on the tet edge
+    // now f0 is tet edge, then v1 is on a tet vtx
+    auto e0 = f0.getTetEdge(); // get the tet edge
+    int v1 = f1.getTetVertex();
+    if (e0[0] == v1 || e0[1] == v1) // if the tet vtx v1 is on is also the vtx of the tet edge e0
+    {
+      return processTetEdgeCase(e0); // then we have two candidate tet faces
+    }
+    UTriKey face(e0[0], e0[1], v1); // otherwise, we can locate the tet face
+    assert(tetShape.hasFace(face));
+    return face;
+  }
+  // now f0 and f1 are both tet vtx
+  // the edge <v0, v1> is identical to a tet edge, again we have two candidate tet faces
+  assert(f0.isTetVertex() && f1.isTetVertex());
+  int v0 = f0.getTetVertex(), v1 = f1.getTetVertex();
+//  if (v0 == v1) {
+//    cout << "Error in " << __func__ << ", v0 == v1, f0: " <<
+//        f0.triFeature << " " << f0.tetFeature << ", f1: " <<
+//        f1.triFeature << " " << f1.tetFeature << endl;
+//  }
+  assert(v0 != v1);
+  return processTetEdgeCase(UEdgeKey(v0, v1));
+}
+
+TetTriPiece::TetTriPiece(int tet) : tetID(tet) {}
+
+TetTriPiece::TetTriPiece(int tet, const std::vector<int> & triIDs, const CutTriGroup & group, const TetTriCuttingData & cutting,
+  const TriMeshPseudoNormal & oriMeshNormal, const TetShape & tetShape) : tetID(tet)
+{
+  const vector<Vec3d> & cutPos = cutting.cutVtxPos;
+  const vector<TetTriCutFeature> & features = cutting.features;
+
+  if (triIDs.size() == 0) return;
+  groupTriID = triIDs;
+  int exactOnTetFaceTriID = -1;
+  UTriKey tetFaceExactlyOn;
+  for(int triID : triIDs)
+  {
+    for(int vID: group.tri[triID]) vtx.push_back(vID);
+    // remove cut triangles completely on the tet face because they don't change the inOutTest result
+    bool exactOnTetFace = false;
+    for(const auto & p : group.cutTriIDsOnFace)
+      if (binary_search(p.second.begin(), p.second.end(), triID))
+      {
+        exactOnTetFace = true;
+        exactOnTetFaceTriID = triID; // we only record one such degenerate-pos triID because only one is needed afterwards
+        tetFaceExactlyOn = p.first;
+        break;
+      }
+    if (exactOnTetFace) { continue; }
+    gnrCutTri.push_back(group.tri[triID]);
+    gnrOriTriID.push_back(group.oriID[triID]);
+  }
+  sortAndDeduplicate(vtx);
+  if (gnrCutTri.size() == 0) // this piece only contains triangles completely on tet faces
+  {
+    // we will compute the in/out result in this case here
+    Vec3d oriTriNormal = oriMeshNormal.triNormal(group.oriID[exactOnTetFaceTriID]);
+    double d = dot(oriTriNormal, tetShape.getNormal(tetFaceExactlyOn));
+    assert(d > 1 - 1e-6 || d < -1 + 1e-6); // d should be either +1 or -1
+    // if this degenerate-pos triangle has the same orientation as the tet face it is on, then this all points inside this tet
+    // is considered as IN for this triangle; otherwise, OUT
+    inOutWhenAllTriOnTetFaces = (d > 0 ? -1 : +1);
+    return;
+  }
+//  getVerticesInTriangles(tri, vtx);
+  assert(hasInvalidTriangles(gnrCutTri) == false);
+
+  // build touchedTetFaces: tet faces which are intersected by triangles in tri
+  for(int v : vtx)
+  {
+    auto faces = features[v].getTouchingTetFaces(tetShape);
+    touchedTetFaces.insert(faces.begin(), faces.end());
+  }
+
+  try
+  {
+    gnrTriNbr = TriangleNeighbor(gnrCutTri);
+  } catch(int)
+  {
+    cout << "Error, one triangle group in the tet is not edge-manifold" << endl;
+    TriMeshRef triMesh(cutPos, gnrCutTri);
+    triMesh.save("Debug.obj");
+    throw 1;
+  }
+}
+
+void TetTriPiece::buildBoundaryData(const TetTriCuttingData & cutting, const TriMeshPseudoNormal & oriMeshNormal,
+    const TetShape & tetShape, Profiler * profiler)
+{
+  initializeBoundaryData = true;
+  ProfilerExtraSection preprocesTime(profiler, "triCompPreProcess");
+  preprocesTime.start();
+
+  const vector<Vec3d> & cutPos = cutting.cutVtxPos;
+  const vector<TetTriCutFeature> & features = cutting.features;
+  const vector<Vec3ER> & cutPosER = cutting.cutVtxPosER;
+
+  TriMeshRef triMesh(cutPos, gnrCutTri); // the triangle mesh for this piece
+
+  auto triBou = gnrTriNbr.findBoundaryTriangles(gnrCutTri); // get the boundaries of the piece
+  bool debug = false;
+
+  // For each boundary edge in tri, compute its missing tet face and angle between them
+  for(auto p : triBou)
+  {
+    OEdgeKey edge = p.second; // get one boundary edge
+    int ev0 = edge[0], ev1 = edge[1];
+    int triID = p.first; // the triangle ID where this boundary edge belongs to
+    Vec3d triNormal = oriMeshNormal.triNormal(gnrOriTriID[triID]); // get the normal of this triangle
+
+    // get the tet face that this tri boundary edge lies
+    auto tetFace = getTetFaceFromFeatureOnEdge(features[ev0], features[ev1], triNormal, tetShape);
+    assert(tetShape.hasFace(tetFace));
+
+    // compute the angle between the tet face and the tri
+    // the angle is [0, M_PI]
+    double angle = M_PI - getVectorAngle(triNormal, tetShape.getNormal(tetFace));
+    angle = clamp(angle, 0.0, M_PI);
+
+    assert(edgeBouAngle.find(edge) == edgeBouAngle.end());
+    edgeBouAngle[edge] = make_pair(angle, tetFace); // store the angle and the tet face
+  }
+
+  // next, compute pseudo-normal for piece boundary vertices
+  auto loops = gnrTriNbr.findBoundaryLoops(gnrCutTri); // get the boundary edge loops
+  for(const auto & loop : loops) // for one loop
+  {
+    int nl = loop.size(); // numVtx on this loop
+
+    // first, find boundary triangles of this loop
+    vector<int> bouTri(nl); // loop edgeID -> boundary triangleID in tri
+    for(size_t i = 0; i < loop.size(); i++)
+    {
+      OEdgeKey edge(loop[i], loop[(i+1)%nl]);
+      int tri = gnrTriNbr.getTriangleAtEdge(edge);
+      assert(tri >= 0);
+      bouTri[i] = tri;
+    }
+
+    // then, compute the pseudo-normal at vertex in the loop
+    for(size_t i = 0; i < loop.size(); i++)
+    {
+      int v = loop[i]; // v: vtx ID on loop[i]
+      if (debug)
+      {
+        cout << "compute correct pseudo-normal on cut vtx: " << v << endl;
+      }
+      // pv: previous vtxID, nv: next vtxID
+      int pv = loop[(i+nl-1)%nl], nv = loop[(i+1)%nl];
+      // pe: previous edge, ne: next edge
+      OEdgeKey pe(pv, v), ne(v, nv);
+      // ptriID: the triangle ID of the previous edge, ntriID: the triangle ID of the next edge
+      int ptriID = bouTri[(i+nl-1)%nl], ntriID = bouTri[i];
+      OTriKey pt(gnrCutTri[ptriID]), nt(gnrCutTri[ntriID]);
+      assert(edgeBouAngle.find(pe) != edgeBouAngle.end());
+      assert(edgeBouAngle.find(ne) != edgeBouAngle.end());
+      // get the previous tet face for pe, and the next tet face for ne
+      UTriKey ptetFace = edgeBouAngle[pe].second, ntetFace = edgeBouAngle[ne].second;
+
+      // compute weighted normal on tri, without tet faces
+      Vec3d normal(0.0);
+      double normalLen = 0.0; // length of the un-normalized normal, used to determine whether we are in a degeneracy case
+      // find the fan of triangles around v, starting at the boundary edge of <pv, v>
+      auto nbrTris = gnrTriNbr.findTrianglesArroundBoundaryVertex(pv, v, gnrCutTri);
+      assert(find(nbrTris.begin(), nbrTris.end(), ptriID) != nbrTris.end());
+      assert(find(nbrTris.begin(), nbrTris.end(), ntriID) != nbrTris.end());
+
+      auto getVectorAngleER = [&](Vec3ER & ej, Vec3ER & ek) -> double
+      {
+        double l2ej = ER_toDouble(len2(ej)), l2ek = ER_toDouble(len2(ek));
+        double ejdotek = ER_toDouble(dot(ej, ek));
+        assert(l2ej > 0 && l2ek > 0);
+        double lejlek = sqrt(l2ej * l2ek);
+        assert(lejlek > 0);
+        double cosAngle = ejdotek / lejlek;
+        cosAngle = clamp(cosAngle, -1.0, 1.0);
+        double angle = acos(cosAngle);
+        return angle;
+      };
+
+      auto getRobustAngle = [&](int vi, int vj, int vk) -> double
+      {
+        Vec3d pi = triMesh.pos(vi), pj = triMesh.pos(vj), pk = triMesh.pos(vk);
+        Vec3d ej = pj - pi, ek = pk - pi;
+        if (len2(ej) < 1e-10 || len2(ek) < 1e-10) // triangle too small, not enough precision
+        {                                         // switch to exact arithmetic
+          Vec3ER ej = cutPosER[vj] - cutPosER[v], ek = cutPosER[vk] - cutPosER[v];
+          return getVectorAngleER(ej, ek);
+        }
+
+        double cosAngle = dot(ej, ek) / sqrt(len2(ej) * len2(ek));
+        cosAngle = clamp(cosAngle, -1.0, 1.0);
+        double angle = acos(cosAngle);
+        return angle;
+      };
+
+      for(int triID : nbrTris) // for each triangle in the fan
+      {
+        int oriID = gnrOriTriID[triID];
+        // we use the triangle normal from the original mesh because it's more robust than computing it
+        // directly from the cut triangles
+        Vec3d oriNormal = oriMeshNormal.triNormal(oriID);
+        Vec3i tri = triMesh.tri(triID);
+        int li = tri.getInvertedIndex(v); // li: [0,3), local index of the vtx in the triangle "tri"
+        int lj = (li+1)%3, lk = (li+2)%3; // lj, lk: [0,3) local indices of the other two vtx in tri
+        int vj = triMesh.tri(triID)[lj], vk = triMesh.tri(triID)[lk];
+        double angle = getRobustAngle(v, vj, vk);
+//        double angle = triMesh.getTriangleAngleAtVertexRobust(triID, v);
+
+        normal += angle * oriNormal;
+        if (debug) // debug code
+        {
+          cout << "triangle triID " << triID << " is " << triMesh.tri(triID) << endl;
+          int i = triMesh.tri(triID).getInvertedIndex(v);
+          int j = (i+1)%3, k = (i+2)%3;
+          cout << "edge vector: " << triMesh.pos(triID, j) - triMesh.pos(triID, i) << " " << triMesh.pos(triID, k) - triMesh.pos(triID, i) << endl;
+
+          int vj = triMesh.tri(triID)[j];
+          int vk = triMesh.tri(triID)[k];
+          Vec3ER ej = cutPosER[vj] - cutPosER[v];
+          Vec3ER ek = cutPosER[vk] - cutPosER[v];
+          cout << "edge vector ER: " << ej << " " << ek << endl;
+          cout << "angle of comp tri is " << angle * 180.0 / M_PI << " triID " << triID << " tri normal " << oriNormal << endl;
+        }
+      }
+
+      if (debug) // debug code
+      {
+        Vec3d n2 = normal;
+        n2.normalize();
+        cout << "triangle only normal is " << n2 << endl;
+      }
+
+      // try to find the tet faces for this vertex pseudo-normal
+      if (ptetFace == ntetFace)
+      {
+        // this vtx lies inside one tet face
+        // easy case:
+
+        Vec3d tetFaceNormal = tetShape.getNormal(ptetFace);
+
+        // the cut triangles on the piece craete an angle "/_" on the tet face
+        // now we have to determine which value (angle or 2*M_PI - angle) to use for computing the angle-weighted pseudo-normal
+        // we need to check this because locally around the vtx v, faces should be consistently oriented
+        // we achieve this by doing following operations:
+        double angle = getRobustAngle(v, pv, nv);
+
+        if (debug) // debug code
+        {
+          cout << "angle between two boundary edges are " << angle * 180.0 / M_PI << endl;
+        }
+
+        // to determine whether we should use angle or 2M_PI - angle, we check the cross product of pe and ne
+        Vec3ER crossResult = cross(cutPosER[v] - cutPosER[pv], cutPosER[nv] - cutPosER[v]);
+        Vec3ER tetFaceNormalER(tetFaceNormal[0], tetFaceNormal[1], tetFaceNormal[2]);
+//        Vec3d crossResult = cross(triMesh.pos(v) - triMesh.pos(pv), triMesh.pos(nv) - triMesh.pos(v));
+        if (dot(crossResult, tetFaceNormalER) > 0)
+        {
+          angle = 2*M_PI - angle;
+        }
+        normal += angle * tetFaceNormal;
+
+        if (debug) // debug
+        {
+          cout << "the angle is on the same tet face: " << ptetFace << endl;
+          cout << "angle " << angle * 180.0 / M_PI << " tetFaceNormal: " << tetFaceNormal << endl;
+        }
+      }
+      else // in this case, the piece boundary vtx is on a tet edge, this means two tet faces will take part in the pseudo-normal computation
+      {
+        assert(ptetFace.shareUEdge(ntetFace)); // the two tet faces must share a tet edge
+        UEdgeKey tetEdge = ptetFace.getSharedUEdge(ntetFace);
+        // the piece create a "cut" on the tet edge
+        // now again we have a choice of picking which angles to form a consistently oriented local shape
+        // The angles are determined by a direction, which is either the direction of the tet edge, or its reverse
+        // after we have this direction, the angles between this direction and -pe / ne will be the two angles for the two faces
+        int dsign = 0;
+
+        const vector<Vec3ER> & tetPosER = cutting.tetPosER;
+#ifdef USE_MULTITHREADING
+        assert(cutting.multiThreading);
+        assert(cutting.cutVtxPosVecMutex);
+        assert(cutting.cutVtxPosVecMutex->size() == cutting.cutVtxPos.size());
+        assert(cutting.tetPosVecMutex);
+        assert(cutting.tetPosVecMutex->size() == cutting.tetPosER.size());
+//          lock_guard<mutex> cutPosLock(*cutting.cutVtxPosMutex);
+//          lock_guard<mutex> tetPosLock(*cutting.tetPosMutex);
+        UTriKey utriKey(v, nv, pv);
+        lock_guard<mutex> lock0(cutting.cutVtxPosVecMutex->at(utriKey[0]));
+        lock_guard<mutex> lock1(cutting.cutVtxPosVecMutex->at(utriKey[1]));
+        lock_guard<mutex> lock2(cutting.cutVtxPosVecMutex->at(utriKey[2]));
+        lock_guard<mutex> lock3(cutting.tetPosVecMutex->at(tetEdge[0]));
+        lock_guard<mutex> lock4(cutting.tetPosVecMutex->at(tetEdge[1]));
+#endif
+        // get triangle normal of triangle <pv, v, nv>
+        Vec3ER triNormal = cross(cutPosER[v] - cutPosER[pv], cutPosER[nv] - cutPosER[v]);
+        // get tet edge vector <te0, te1>
+        Vec3ER tetEdgeVec = tetPosER[tetEdge[1]] - tetPosER[tetEdge[0]]; // te0 -> te1
+        // check which tet vtx on tetEdge is under the triangle <pv, v, nv>
+        ER d = dot(tetEdgeVec, triNormal);
+        dsign = ER_sign(d); // we use exact-arithmetic here for robustness
+
+        if (dsign == 0)
+        {
+          // degenerate case, this should not happen when
+          cout << "Error: dsign == 0 when finding missing tet face normals and angles on a boundary piece vtx on a tet edge" << endl;
+          cout << "tetID " << tetID << endl;
+          cout << "tri: " << streamRange(gnrCutTri) << endl;
+          cout << "v: " << triMesh.pos(v) << endl;
+          cout << "trinormal: " << triNormal << endl;
+          cout << "tetEdgeVec: " << tetEdgeVec << endl;
+        }
+
+        assert(dsign != 0); // assert that this triMesh plane is not parallel with tetEdge, otherwise this triMesh will be degenerate into a line, not a triangle
+        if (dsign > 0) { tetEdgeVec = - tetEdgeVec; }
+
+        // now we find the direction, we can compute the two angles, and finally, the correct pseudo-normal
+        Vec3ER v2pv = cutPosER[pv] - cutPosER[v];
+        Vec3ER v2nv = cutPosER[nv] - cutPosER[v];
+        double alpha0 = getVectorAngleER(v2pv, tetEdgeVec);
+        normal += alpha0 * tetShape.getNormal(ptetFace);
+        double alpha1 = getVectorAngleER(v2nv, tetEdgeVec);
+        normal += alpha1 * tetShape.getNormal(ntetFace);
+
+        if (debug) // debug code
+        {
+          cout << "the angle is on two tet faces: " << ptetFace << " " << ntetFace << endl;
+          cout << "angle " << alpha0 * 180.0 / M_PI << " tetFaceNormal: " << tetShape.getNormal(ptetFace) << endl;
+          cout << "angle " << alpha1 * 180.0 / M_PI << " tetFaceNormal: " << tetShape.getNormal(ntetFace) << endl;
+        }
+      }
+
+      normalLen = len2(normal);
+      if (normalLen > 0) normal.normalize();
+      // we store the len2 of normal because we need it to measure the degeneracy of this pseudo-normal
+      vtxBouNormal.insert(make_pair(v, make_pair(normalLen, normal)));
+
+      if (debug) // debug code
+      {
+        cout << "Finally, normalLen2: " << normalLen << " normal " << normal << endl;
+      }
+    } // end i in loop
+  } // end loop in loops
+}
+
+void TetTriPiece::tryBuildingBoundaryData(const TetTriCuttingData & cutting, const TriMeshPseudoNormal & oriMeshNormal,
+    const TetShape & tetShape, Profiler * profiler)
+{
+  if (initializeBoundaryData == false)
+    buildBoundaryData(cutting, oriMeshNormal, tetShape, profiler);
+}
+
+//void TetTriComp::buildTree(const vector<Vec3ER> & cutPosK) {
+//  if (tri.size() > 0) {
+//    octree.build(cutPosK, tri, 5, 10);
+//  }
+//  treeBuilt = true;
+//}
+
+int TetTriPiece::getClosestTriangle(const TetTriCuttingData & cutting, const Vec3d & queryPoint,
+    const Vec3ER & queryPointER, int & feature, ER & dist2)
+{
+  const auto & cutPosER = cutting.cutVtxPosER;
+  // try using brute-force:
+
+  auto getDist = [&](int triID, int & minFeature)
+  {
+    Vec3i t = gnrCutTri[triID];
+#ifdef USE_MULTITHREADING
+    assert(cutting.multiThreading);
+    assert(cutting.cutVtxPosVecMutex);
+    assert(cutting.cutVtxPosVecMutex->size() == cutting.cutVtxPos.size());
+    UTriKey utri(t);
+    lock_guard<mutex> lock0(cutting.cutVtxPosVecMutex->at(utri[0]));
+    lock_guard<mutex> lock1(cutting.cutVtxPosVecMutex->at(utri[1]));
+    lock_guard<mutex> lock2(cutting.cutVtxPosVecMutex->at(utri[2]));
+#endif
+    return squaredDistanceToTriangle(queryPointER, cutPosER[t[0]], cutPosER[t[1]], cutPosER[t[2]], minFeature);
+  };
+
+  // we use brute-force to search for the closest triangle and site
+  // we also tried building an octree tree, but it was slower
+  int minTriID = 0, minFeature = 0;
+  dist2 = getDist(0, minFeature);
+  for(size_t triID = 1; triID < gnrCutTri.size(); triID++)
+  {
+    ER d2 = getDist(triID, feature);
+    if (d2 < dist2)
+    {
+      dist2 = d2;
+      minTriID = triID;
+      minFeature = feature;
+    }
+  }
+  feature = minFeature;
+  return minTriID;
+
+//  if (treeBuilt == false) { buildTree(cutPosK); }
+//  return octree.getClosestTriangle(cutPosK, tri, queryPointK, feature);
+}
+
+int TetTriPiece::inOutTest(const Vec3d & queryPoint, const Vec3ER & queryPointER, const TetTriCuttingData & cuttingData,
+    const TriMeshPseudoNormal & oriMeshNormal, const TetShape & tetShape, Profiler * profiler)
+{
+  bool debug = false;
+
+  if (debug)
+  {
+    cout << "vtx " << streamRange(vtx) << endl;
+    cout << "#gnrCutTri: " << gnrCutTri.size() << " " << inOutWhenAllTriOnTetFaces << endl;
+  }
+
+  if (vtx.size() == 0) { return 1; } // interior tet, all query points are considered outside
+  if (gnrCutTri.size() == 0) { return inOutWhenAllTriOnTetFaces; } // only triangles completely on tet faces
+
+  const vector<Vec3d> & cutPositions = cuttingData.cutVtxPos;
+  const vector<Vec3ER> & cutPosER = cuttingData.cutVtxPosER;
+
+  ProfilerExtraSection testTime(profiler, "inOutTest");
+  testTime.start();
+
+  TriMeshRef mesh(cutPositions, gnrCutTri); // triangle mesh of this piece
+  int feature = -1;
+  ER closestDist;
+  // the closest triangle on this piece to the query point
+  int cTriID = getClosestTriangle(cuttingData, queryPoint, queryPointER, feature, closestDist);
+  assert(cTriID >= 0 && feature >= 0);
+  if (debug)
+  {
+    cout << "closest triID is " << cTriID << endl;
+  }
+  if (ER_toDouble(closestDist) == 0.0)
+  {
+    return 1; // mesh are touching, we treate it as separate
+  }
+
+  Vec3i closestTri = gnrCutTri[cTriID];
+
+  Vec3d projedPt = getClosestPointToTriangleWithNormalAndFeature(queryPoint, mesh.pos(cTriID, 0), mesh.pos(cTriID, 1), mesh.pos(cTriID, 2),
+      oriMeshNormal.triNormal(gnrOriTriID[cTriID]), feature);
+  Vec3d diff = queryPoint - projedPt;
+  Vec3d normal(0.0);
+
+
+  if (debug)
+  {
+    cout << "query pt is " << queryPoint << endl;
+    cout << "projected pt is " << projedPt << " f " << feature << endl;
+  }
+
+  auto computeFinalDotProduct = [&]() -> int
+  {
+    if (len2(diff) < 1e-10)
+    {
+//      cout << "warning: diff is too small in inout test: " << diff << endl;
+      // to avoid errors, we use exact-arithmetic to compute the in/out result
+      Vec3ER projectedPtER = getClosestPointToTriangleWithFeature(queryPointER,
+          cutPosER[closestTri[0]], cutPosER[closestTri[1]], cutPosER[closestTri[2]], feature);
+      Vec3ER diffER = queryPointER - projectedPtER;
+      Vec3ER normalER(normal[0], normal[1], normal[2]);
+      int sign = ER_sign(dot(normalER, diffER));
+      assert(sign != 0);
+      assert(sign == 1 || sign == -1);
+      return sign;
+    }
+
+    if (dot(normal, diff) > 0) { return 1; }
+    return -1;
+  };
+
+  if (feature < 3) // closest feature is a vtx
+  {
+    int vtxID = closestTri[feature];
+    if (vtxID < oriMeshNormal.numVertices()) // it's a vtx from original input mesh
+    {
+      normal = oriMeshNormal.vtxNormal(vtxID);
+      return computeFinalDotProduct();
+    }
+    // else, it's a cut vtx, which is on the piece boundary
+    if (debug)
+      cout << "projected pt is on boundary vtx: " << vtxID << ", has boundary normals: " << endl;
+
+    // try building boundary data related to pseudo-normals on the boundary, if not yet
+    tryBuildingBoundaryData(cuttingData, oriMeshNormal, tetShape, profiler);
+
+    // get the vtx boundary pseudo-normal
+    // vtxBouNormal is a multimap, because one boundary vtx can have more than one pseudo-normal
+    // this may seem wrong, but actually since we allow the piece to be only edge-manifold, two
+    // manifold triangle groups in the piece can be connected via only one sharing vtx. In this case,
+    // the piece triangle mesh is edge-manifold, but not manifold.
+    // but we can still run our method on this case.
+    // each returned value in this multimap represents one local edge-manifold on this vtx: vtxID
+    // we check in-out by: query point is out only if all local edge-manifolds return out
+    auto iterPair = vtxBouNormal.equal_range(vtxID);
+    assert(iterPair.first != vtxBouNormal.end());
+
+    for(auto iter = iterPair.first; iter != iterPair.second; iter++)
+    {
+      const auto & p = iter->second;
+      if (debug)
+        cout << "scaled normal len2 " << p.first << " normal " << p.second << endl;
+      if (p.first < 1e-6)
+      {           // the scaled normal is too short, close to a degenerte feature
+        continue; // we assume it's outside, so we do nothing in this loop
+      }
+      normal = p.second;
+      if (computeFinalDotProduct() > 0) { continue; } // it's out, do nothing
+      return -1; // it's inside one local edge-manifold, so it must return inside
+    }
+    // now no local edge-manifolds hold this query point. It's outside
+    return 1;
+  }
+  else if (feature < 6) // closest feature is an edge
+  {
+    int nbrTriID = gnrTriNbr.getTriangleNeighbors(cTriID)[feature-3]; // nbr local triangleID sharing this edge
+    int oriID = gnrOriTriID[cTriID];
+    int nbrOriID = (nbrTriID >= 0 ? gnrOriTriID[nbrTriID] : oriID);
+    // if cTriID and its nbr, nbrTriID are from the same input triangle,
+    // or if cTriID has no nbr sharing this edge
+    // then the normal is assigned to the normal of the original input triangle oriID
+    if (oriID == nbrOriID) { normal = oriMeshNormal.triNormal(oriID); }
+    else // else, TriID and its nbr, nbrTriID are from the two different input triangles,
+    {    // then normal is assigned as the average of the two normals
+      normal = oriMeshNormal.triNormal(oriID) + oriMeshNormal.triNormal(nbrOriID);
+      normal.normalize();
+      assert(normal.hasNaN() == false);
+    }
+    if (debug)
+      cout << "clost pt is on an edge" << endl;
+    if (nbrTriID >= 0) // this edge is not a boundary edge in this comp
+    {
+      if (debug)
+        cout << "its not a boundary edge, the normal is " << normal << endl;
+      return computeFinalDotProduct();
+    }
+
+    // else, this edge is on the boundary
+    tryBuildingBoundaryData(cuttingData, oriMeshNormal, tetShape, profiler);
+
+    // we can still use this normal but we have to know the dihedral angle as well
+    Vec3i ctri = gnrCutTri[cTriID];
+    OEdgeKey cedge(ctri[feature-3], ctri[(feature-2)%3]);
+    auto it = edgeBouAngle.find(cedge);
+    assert(it != edgeBouAngle.end());
+    double angle = it->second.first; // the angle between the triangle cTriID and the tet face that should also contribute to the pseudo-normal computation
+    UTriKey tetFace = it->second.second; // the tet face that should also contribute to the pseudo-normal computation
+    if (debug)
+      cout << "the edge is on a boundary, tri normal " << normal << ", cedge: " << cedge << endl;
+    if (angle < 1e-2) // the angle between cTriID and tetFace is too small, we think the query point can only be outside the piece
+    {
+      if (debug)
+      {
+        cout << "the angle between the tet face & tri face " << rad2deg(angle) << " is too small, we think the query pt is outside" << endl;
+        cout << "the tet face involved is " << tetFace << " normal " << tetShape.getNormal(tetFace) << endl;
+      }
+      return 1; // if the dihedral angle is too small, we think it cannot be inside (theoretically it cannot be inside if angle < 90)
+    }
+    if (angle > M_PI - 1e-2) // again, if the angle is too large, then we think the query point can only be inside the piece
+    {
+      if (debug)
+      {
+        cout << "the angle between the tet face & tri face " << rad2deg(angle) << " is too large, we think the query pt is inside" << endl;
+        cout << "the tet face involved is " << tetFace << " normal " << tetShape.getNormal(tetFace) << endl;
+      }
+      return -1;
+    }
+    int oriTID = gnrOriTriID[cTriID];
+    assert(oriTID >= 0 && oriTID < oriMeshNormal.numTriangles());
+    // then the final normal is the averge of the triangle normal and the tet normal
+    normal = oriMeshNormal.triNormal(oriTID) + tetShape.getNormal(tetFace);
+    normal.normalize();
+    assert(normal.hasNaN() == false);
+    if (debug)
+      cout << "the normal is " << normal << endl;
+    return computeFinalDotProduct();
+  }
+  // else, closest feature is triangle interior, simplest case
+  int oriTID = gnrOriTriID[cTriID];
+  assert(oriTID >= 0 && oriTID < oriMeshNormal.numTriangles());
+  normal = oriMeshNormal.triNormal(oriTID);
+  return computeFinalDotProduct();
+}
+
+// merge the two pieces
+TetTriPiece mergePiece(const TetTriPiece & comp0, const TetTriPiece & comp1, int tet, const TetTriMeshCutting::CutTriGroup & group,
+    const TetTriCuttingData & cutting, const TriMeshPseudoNormal & oriMeshNormal, const TetShape & tetShape)
+{
+  vector<int> groupTriID = comp0.groupTriID;
+  groupTriID.insert(groupTriID.end(), comp1.groupTriID.begin(), comp1.groupTriID.end());
+  sort(groupTriID.begin(), groupTriID.end());
+  assert(unique(groupTriID.begin(), groupTriID.end()) == groupTriID.end());
+
+  return TetTriPiece(tet, groupTriID, group, cutting, oriMeshNormal, tetShape);
+}
+
+TriMeshRef TetTriPiece::mesh(const TetTriCuttingData & cut) const
+{
+  assert(gnrCutTri.size() > 0);
+  return { cut.cutVtxPos, gnrCutTri };
+}
diff --git a/libraries/virtualTets/tetTriPiece.h b/libraries/virtualTets/tetTriPiece.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd57a40f5bdf84fda337b3aa35c0d14695cb9eda
--- /dev/null
+++ b/libraries/virtualTets/tetTriPiece.h
@@ -0,0 +1,100 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef TETTRIPIECE_H
+#define TETTRIPIECE_H
+
+#include "tetTriMeshCutting.h"
+#include "triMeshPseudoNormal.h"
+#include "triMeshNeighbor.h"
+//#include "triMeshOctreeK.h"
+#include "tetKey.h"
+#include "tetTriCuttingData.h"
+#include "profiler.h"
+#include <vector>
+#include <map>
+
+/*
+  Represents one "piece" (a contiguous parts of a triangle belonging to one tet), as defined in our paper submission.
+*/
+
+struct TetTriPiece
+{
+  using CutTriGroup = TetTriMeshCutting::CutTriGroup;
+  int tetID = -1;
+  std::vector<int> groupTriID; // the triangle IDs in the CutTriGroup passed in the constructor
+  std::vector<Vec3i> gnrCutTri;  // cut triangles (with cut vtxIDs) of this piece which are in general positions
+                                 // general position means that the triangles don't completely lie on tet faces
+  std::vector<int> vtx;    // cut vtxIDs inside tri
+  std::vector<int> gnrOriTriID; // original, input, uncut triangle IDs where triangles in gnrCutTri are from
+//  ExactTriMeshOctreeK octree;
+  TriangleNeighbor gnrTriNbr;
+  std::set<UTriKey> touchedTetFaces; // these tet faces are intersected by triangles in tri
+  signed char inOutWhenAllTriOnTetFaces = 0; // the inOut value if all triangles are exactly on tet faces
+
+  TetTriPiece(int tet); // constructor for a tet with no pieces
+  // triIDs: the triangle IDs in the input CutTriGroup group
+  TetTriPiece(int tet, const std::vector<int> & triIDs, const CutTriGroup & group, const TetTriCuttingData & cutting,
+    const TriMeshPseudoNormal & oriMeshNormal, const TetShape & tetShape);
+
+  //  void buildTree(const std::vector<Vec3ER> & cutPositions);
+  // get the index of the closest triangle in the piece
+  // also returns: feature: which site (edge/vtx/interior) the closest point on the triangle lies
+  //               dist2: the squared distance between the query point and the closest site
+  int getClosestTriangle(const TetTriCuttingData & cutting, const Vec3d & queryPoint, const Vec3ER & queryPointK, int & feature, ER & dist2);
+
+  // build data for computing pseudo-normal on piece boundaries during inside/outside test
+  // we don't need to use CSG to create a close manifold mesh to do inside/outside test
+  // But we have to find the correct pseudo-normals on piece boundaries, which are the pseudo-normals of the meshes which
+  // would be created by CSG
+  void buildBoundaryData(const TetTriCuttingData & cutting, const TriMeshPseudoNormal & oriMeshNormal, const TetShape & tetShape,
+      Profiler * profiler = nullptr);
+  void tryBuildingBoundaryData(const TetTriCuttingData & cutting, const TriMeshPseudoNormal & oriMeshNormal, const TetShape & tetShape,
+      Profiler * profiler = nullptr);
+
+  int inOutTest(const Vec3d & queryPoint, const Vec3ER & queryPointK, const TetTriCuttingData & cuttingData,
+      const TriMeshPseudoNormal & oriMeshNormal, const TetShape & tetShape, Profiler * profiler = nullptr);
+
+  TriMeshRef mesh(const TetTriCuttingData & cut) const;
+
+protected:
+  //  bool treeBuilt = false;
+  bool initializeBoundaryData = false; // flag to represent whether we have boundary data computed
+  std::map<OEdgeKey, std::pair<double, UTriKey>> edgeBouAngle; // piece boundary edge -> angle between the tet face, tet face
+  std::multimap<int, std::pair<double, Vec3d>> vtxBouNormal; // boundary vtx weighted normal on tet faces
+};
+
+TetTriPiece mergePiece(const TetTriPiece & comp0, const TetTriPiece & comp1, int tet, const TetTriMeshCutting::CutTriGroup & group,
+    const TetTriCuttingData & cutting, const TriMeshPseudoNormal & oriMeshNormal, const TetShape & tetShape);
+
+#endif
+
diff --git a/libraries/virtualTets/virtualTets-via-csg.cpp b/libraries/virtualTets/virtualTets-via-csg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..69db8ef64451c2952a063047f2d1a68cbc09abeb
--- /dev/null
+++ b/libraries/virtualTets/virtualTets-via-csg.cpp
@@ -0,0 +1,670 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "virtualTets-via-csg.h"
+#include "triMeshNeighbor.h"
+#include "triMeshPseudoNormal.h"
+#include "containerHelper.h"
+#include "disjointSet.h"
+#include "tetTriMeshCutting.h"
+#include "exactOctree.h"
+#include "basicAlgorithms.h"
+#include "profiler.h"
+#include "windingNumberTree.h"
+#include "tetTriCuttingData.h"
+#include "createTriMesh.h"
+#include "geometryQuery.h"
+#include "planeER.h"
+#include "iglRemeshSelfIntersection.h"
+#include "labelOuterTets.h"
+#include "tetTriPiece.h"
+#include "valueIndex.h"
+#include <iostream>
+#include <sstream>
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+
+using namespace std;
+
+class TetER
+{
+public:
+  TetER(const Vec3ER & v0, const Vec3ER & v1, const Vec3ER & v2, const Vec3ER & v3);
+  int outside(const Vec3ER & v) const;
+
+protected:
+  FastPlaneER plane[4];
+};
+
+TetER::TetER(const Vec3ER & v0, const Vec3ER & v1, const Vec3ER & v2, const Vec3ER & v3)
+: plane { {v1, v2, v3}, {v0, v3, v2}, {v0, v1, v3}, {v0, v2, v1} }
+{}
+
+int TetER::outside(const Vec3ER & v) const
+{
+  for(int i = 0; i < 4; i++)
+  {
+    int o = plane[i].outside(v);
+    if (o >= 0) return o;
+  }
+  return -1;
+}
+
+TetMeshGeo createVirtualTetsMeshViaCSG(const TetMeshGeo & tetMesh, const TriMeshGeo triMesh, BarycentricCoordinates * coord,
+    vector<int> * newTetVtxID2OldTetVtxID, vector<int> * newTetID2OldTetID, vector<vector<int>> * tetTris,
+    bool verbose, Profiler * profiler)
+{
+  vector<Vec3ER> triPosK;
+  for(int i = 0; i < triMesh.numVertices(); i++)
+  {
+    Vec3d p = triMesh.pos(i);
+    triPosK.emplace_back(p[0], p[1], p[2]);
+  }
+  return createVirtualTetsMeshViaCSG(tetMesh, triMesh, triPosK, coord, newTetVtxID2OldTetVtxID, newTetID2OldTetID, tetTris,
+      verbose, profiler);
+}
+
+struct TetTriCompData
+{
+  int tetID = -1;
+  vector<int> vtx; // original tri mesh vtxIDs in this tet copy
+  vector<int> tri; // original tri mesh triIDs intersected with this tet copy
+  map<UTriKey, int> tetFaceRel; // tet face relation: tet face key -> 1: face is outside this comp, 0: touching, -1: inside
+  void check() const
+  {
+    assert(tetID >= 0);
+    for(int v : vtx) { assert(v >= 0); }
+    for(int t : tri) { assert(t >= 0); }
+  }
+};
+
+
+TetMeshGeo createVirtualTetsMeshViaCSG(const TetMeshGeo & tetMesh, const TriMeshGeo triMesh,
+    const std::vector<Vec3ER> & exactTriPos, BarycentricCoordinates * coord,
+    vector<int> * newTetVtxID2OldTetVtxID, vector<int> * newTetID2OldTetID, vector<vector<int>> * tetTris,
+    bool verbose, Profiler * profiler)
+{
+  if (profiler) profiler->startTimer("tetTriEmbedding");
+  TetTriMeshCutting cutting(tetMesh, triMesh);
+  cout << "computing embedding..." << endl;
+  cutting.computeIntersectingTriTets();
+  cout << "computing cut..." << endl;
+  if (profiler) profiler->stopLastTimer();
+
+  TetTriIntersectingData data;
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    data.triInTet.push_back(cutting.getTrianglesIntersectingTet(tetID));
+  }
+  for(int vtxID = 0; vtxID < tetMesh.numVertices(); vtxID++)
+  {
+    Vec3d p = tetMesh.pos(vtxID);
+    Vec3ER pER(p[0], p[1], p[2]);
+    data.tetPosER.emplace_back(move(pER));
+  }
+
+  return createVirtualTetsMeshViaCSG(tetMesh, triMesh, exactTriPos, data, coord, newTetVtxID2OldTetVtxID, newTetID2OldTetID, tetTris,
+      verbose, profiler);
+}
+
+
+TetMeshGeo  createVirtualTetsMeshViaCSG(const TetMeshGeo & tetMesh, const TriMeshGeo triMesh,
+    const std::vector<Vec3ER> & exactTriPos, const TetTriIntersectingData & tetTriIntersectingData,
+    BarycentricCoordinates * coord,
+    vector<int> * newTetVtxID2OldTetVtxID, vector<int> * newTetID2OldTetID, vector<vector<int>> * tetTris,
+    bool verbose, Profiler * profiler)
+    {
+
+  cout << "Begin virtual tets..." << endl;
+  if (newTetVtxID2OldTetVtxID) newTetVtxID2OldTetVtxID->clear();
+  if (newTetID2OldTetID) newTetID2OldTetID->clear();
+
+  TriMeshNeighbor triMeshNeighbor;
+  TriangleNeighbor triangleNeighbor;
+  try
+  {
+    triMeshNeighbor = TriMeshNeighbor(triMesh);
+    triangleNeighbor = TriangleNeighbor(triMesh.triangles());
+  } catch(int)
+  {
+    cout << "Error: the input mesh is not edge-manifold" << endl;
+    throw 1;
+  }
+
+  auto triangleBoundaryLoops = triangleNeighbor.findBoundaryLoops(triMesh.triangles());
+  if (triangleBoundaryLoops.size() > 0)
+  {
+    cout << "Error: the input mesh has boundary" << endl;
+    throw 1;
+  }
+
+  if (profiler) profiler->startTimer("airTets");
+
+  auto isTetBou = [&](int tetID)
+  {
+    return tetTriIntersectingData.triInTet[tetID].size() > 0;
+  };
+
+  WindingNumberTree wnTree;
+  wnTree.build(triMesh);
+  auto isTetOuter = [&](int tetID)
+  {
+    double wn = wnTree.windingNumber(triMesh, tetMesh.ref().computeTetCenter(tetID));
+    return wn < 0.5;
+  };
+
+  TetNeighbor tetNeighbor(tetMesh.tets());
+  auto tetOutside = labelOuterTets(tetMesh, tetNeighbor, isTetBou, isTetOuter);
+  if (profiler) profiler->stopLastTimer();
+
+  vector<TetTriCompData> tetCopies;
+  if (profiler) profiler->startTimer("buildComps");
+
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    bool debug = false;
+    if (tetOutside[tetID]) continue;
+    if (tetID % 100 == 0)
+      cout << "process comps in tetID " << tetID << "/" << tetMesh.numTets() << endl;
+
+//    if (tetID == 828) debug = true;
+
+    const vector<int> & interTriSet = tetTriIntersectingData.triInTet[tetID];
+    if (debug)
+    {
+      cout << "intersecting tris: " << streamRange(interTriSet) << endl;
+    }
+    if (interTriSet.size() == 0)
+    {
+      // inner tet:
+      TetTriCompData data;
+      data.tetID = tetID;
+      TetShape tetShape(tetMesh, tetID);
+      for(auto p : tetShape.faceNormal)
+      {
+//        cout << "inner tet " << tetID << " face " << p.first << endl;
+        data.tetFaceRel[p.first] = -1;
+      }
+      tetCopies.emplace_back(move(data));
+      continue;
+    }
+
+    TriMeshGeo tetSurface = createTetSurfaceMesh(tetMesh.pos(tetID, 0), tetMesh.pos(tetID, 1), tetMesh.pos(tetID, 2), tetMesh.pos(tetID, 3));
+    vector<Vec3i> subTris;
+    vector<int> inputTriIDs(interTriSet.begin(), interTriSet.end());
+    getSubMesh(triMesh, inputTriIDs, subTris);
+    TriMeshRef subMesh(triMesh.positions(), subTris);
+    vector<int> sub2triVtx;
+    map<int,int> tri2subVtx;
+    TriMeshGeo rivSubMesh = removeIsolatedVerticesWithMap(subMesh, &sub2triVtx, &tri2subVtx);
+    TriMeshGeo mergedMesh = mergeMesh(tetSurface, rivSubMesh);
+
+    ProfilerExtraSection cellSelfInterTimer(profiler, "cellSelfInter");
+    cellSelfInterTimer.start();
+    auto selfInterRet = iglInterface::remeshSelfIntersection(mergedMesh, false, false, debug);
+    cellSelfInterTimer.stop();
+
+    const auto & cutMesh = selfInterRet.cutMesh;
+
+    if (debug)
+    {
+      cutMesh.save("debugViaCSGCutMesh.obj");
+    }
+
+    map<int, vector<int>> cellID2cutTriIDs;
+    map<int, set<UTriKey>> cellID2InnerTetFace;
+    map<int, set<UTriKey>> cellID2TouchingTetFace;
+    for(int cutTriID = 0; cutTriID < cutMesh.numTriangles(); cutTriID++)
+    {
+      int patchID = selfInterRet.triPatchIDs[cutTriID];
+      int cellID = selfInterRet.cellIDsAtPatch[patchID].second; // we only get the cell inside this patch
+      int mergedTriID = selfInterRet.oldTriIDs[cutTriID];
+      if (mergedTriID < 4) // these are from tet faces
+      {
+        if (debug)
+        {
+          cout << "found tet surface on cut mesh: " << cutTriID << endl;
+        }
+        bool allFromTetFace = true;
+        for(int i = 0; i < 3; i++)
+        {
+          if (cutMesh.tri(cutTriID)[i] >= 4) { allFromTetFace = false; break; }
+        }
+        Vec3i tetSurfaceTri = tetSurface.tri(mergedTriID);  // [0, 4)
+        Vec3i tetFace;
+        for(int i = 0; i < 3; i++)
+          tetFace[i] = tetMesh.tet(tetID)[tetSurfaceTri[i]];
+        if (allFromTetFace)
+        {
+          if (debug && setNotFind(cellID2InnerTetFace[cellID], UTriKey(tetFace)))
+            cout << "cellID " << cellID << " has inner face " << UTriKey(tetFace) << endl;
+          cellID2InnerTetFace[cellID].emplace(tetFace);
+        }
+        else
+        {
+          if (debug && setNotFind(cellID2TouchingTetFace[cellID], UTriKey(tetFace)))
+            cout << "cellID " << cellID << " has touching face " << UTriKey(tetFace) << endl;
+          cellID2TouchingTetFace[cellID].emplace(tetFace);
+        }
+      }
+      else // these are from tri mesh
+      {
+        if (cellID == 0) continue; // these are triangles outside the tet
+        cellID2cutTriIDs[cellID].push_back(cutTriID);
+      }
+    }
+
+    const auto & tetPosER = tetTriIntersectingData.tetPosER;
+    TetER tetER(tetPosER[tetMesh.tetVtxID(tetID, 0)], tetPosER[tetMesh.tetVtxID(tetID, 1)],
+                tetPosER[tetMesh.tetVtxID(tetID, 2)], tetPosER[tetMesh.tetVtxID(tetID, 3)]);
+    map<int, int> cutVtxID2Outside;
+    if (debug)
+    {
+      for(const auto & p : cellID2cutTriIDs)
+      {
+        int cellID = p.first;
+        cout << "At cell " << cellID << " tris " << streamRange(p.second) << ", vtx inout: ";
+        set<int> cutTriVtx;
+        for(int cutTriID : p.second)
+        {
+          for(int cutVtxID : cutMesh.tri(cutTriID))
+          {
+            cutTriVtx.insert(cutVtxID);
+          }
+        }
+
+        for(int cutVtxID : cutTriVtx)
+        {
+          auto it = cutVtxID2Outside.find(cutVtxID);
+          if (it == cutVtxID2Outside.end())
+          {
+            int o = tetER.outside(selfInterRet.cutPosExact[cutVtxID]);
+            cutVtxID2Outside[cutVtxID] = o;
+          }
+          cout << cutVtxID << "->" << cutVtxID2Outside[cutVtxID] << " ";
+        }
+        cout << endl;
+      }
+    }
+    for(const auto & p : cellID2cutTriIDs)
+    {
+      int cellID = p.first;
+
+      // first check whether it's outside or inside the tet
+      int cellInOut = 0;
+      for(int cutTriID : p.second)
+      {
+        for(int cutVtxID : cutMesh.tri(cutTriID))
+        {
+          auto it = cutVtxID2Outside.find(cutVtxID);
+          if (it != cutVtxID2Outside.end())
+          {
+            int p = it->second;
+            if (p != 0) // outside or inside
+            {
+              cellInOut = p;
+              break;
+            }
+          }
+          // compute inOut of this cutVtxID
+          int o = tetER.outside(selfInterRet.cutPosExact[cutVtxID]);
+          cutVtxID2Outside[cutVtxID] = o;
+          if (o != 0)
+          {
+            cellInOut = o;
+            break;
+          }
+        }
+        if (cellInOut != 0) break;
+      }
+
+      if (cellInOut > 0) continue; // outer patch
+
+      if (debug)
+      {
+        cout << "CellID " << cellID << " " << streamRange(p.second) << endl;
+      }
+
+      TetTriCompData data;
+      data.tetID = tetID;
+
+      for(int cutTriID : p.second)
+      {
+        for(int j = 0; j < 3; j++)
+        {
+          int cutVtx = cutMesh.tri(cutTriID)[j];
+          // if cutVtx is tet surface vtx or vtx from cut itnersections
+          if (cutVtx < 4 || cutVtx >= mergedMesh.numVertices()) continue;
+          int subVtxID = cutVtx - 4;
+          assert(subVtxID < rivSubMesh.numVertices());
+          int triVtxID = sub2triVtx[subVtxID];
+          assert(inVectorRange(triVtxID, triMesh.positions()));
+          data.vtx.push_back(triVtxID);
+        }
+
+        int mergedTriID = selfInterRet.oldTriIDs[cutTriID];
+        assert(mergedTriID >= 4);
+        int subMeshTriID = mergedTriID - 4;
+        assert(inVectorRange(subMeshTriID, inputTriIDs));
+        int inputTriID = inputTriIDs[subMeshTriID];
+        data.tri.push_back(inputTriID);
+      }
+
+      sortAndDeduplicate(data.vtx);
+      sortAndDeduplicate(data.tri);
+
+      for(UTriKey key : cellID2InnerTetFace[cellID])
+      {
+        assert(mapNotFind(data.tetFaceRel, key));
+        data.tetFaceRel[key] = -1;
+      }
+      for(UTriKey key : cellID2TouchingTetFace[cellID])
+      {
+        if (mapNotFind(data.tetFaceRel, key) == false)
+        {
+          cout << "Error: found tet face " << key << " at both inner and touching at tetID " << tetID << endl;
+          TetShape tetShape(tetMesh, tetID);
+          cout << "All tet faces: " ;
+          for(auto p : tetShape.faceNormal) cout << p.first << " ";
+          cout << endl;
+          cout << "cellID2InnerTetFace: ";
+          for(auto key : cellID2InnerTetFace[cellID]) cout << key << " ";
+          cout << endl;
+          cout << "cellID2TouchingTetFace: ";
+          for(auto key : cellID2TouchingTetFace[cellID]) cout << key << " ";
+          cout << endl;
+        }
+        assert(mapNotFind(data.tetFaceRel, key));
+        data.tetFaceRel[key] = 0;
+      }
+
+      data.check();
+      tetCopies.emplace_back(move(data));
+    } // end cellID2cutTriIDs
+  } // end for each tetID
+
+//  for(const auto & c : tetCopies) c.check();
+  if (profiler) profiler->stopLastTimer();
+
+  if (profiler) profiler->startTimer("connectComps");
+  vector<vector<int>> compsInTet(tetMesh.numTets());
+  for(size_t i = 0; i < tetCopies.size(); i++)
+  {
+    compsInTet[tetCopies[i].tetID].push_back(i);
+  }
+
+  // now let's connect those tet copies
+  vector<map<UTriKey, int>> tetNbrs(tetMesh.numTets());
+  map<UTriKey, vector<int>> tetFaceMap;
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    Vec4i tet = tetMesh.tet(tetID);
+    UTetKey tetkey(&tet[0]);
+    for(int i = 0; i < 4; i++)
+    {
+      UTriKey face = tetkey.uFaceKey(i);
+      tetFaceMap[face].push_back(tetID);
+    }
+  }
+
+  for(const auto & p : tetFaceMap)
+  {
+    assert(p.second.size() <= 2);
+    const auto & nbr = p.second;
+    if (nbr.size() == 2)
+    {
+      tetNbrs[nbr[0]].emplace(p.first, nbr[1]); // we need to visit each tet connection only once
+    }
+  }
+
+  vector<Vec4i> outputTets(tetCopies.size());
+
+  vector<DisjointSetDynamic> dset(tetMesh.numVertices());
+
+  auto mergeVtxOnTetFace = [&](int copyID, int nbrCopyID, UTriKey face)
+  {
+    assert(copyID >= 0 && copyID < (int)tetCopies.size());
+    assert(nbrCopyID >= 0 && nbrCopyID < (int)tetCopies.size());
+    int tetID = tetCopies[copyID].tetID;
+    int nbrtetID = tetCopies[nbrCopyID].tetID;
+    Vec4i tet = tetMesh.tet(tetID);
+    Vec4i nbrtet = tetMesh.tet(nbrtetID);
+
+    for(int vtxID = 0; vtxID < 3; vtxID++)
+    {
+      int v = face[vtxID];
+      int i = tet.getInvertedIndex(v);
+      assert(i >= 0);
+      int nbri = nbrtet.getInvertedIndex(v);
+      assert(nbri >= 0);
+      dset[v].unionSet(4*copyID + i, 4*nbrCopyID + nbri);
+    }
+  };
+
+  for(size_t copyID = 0; copyID < tetCopies.size(); copyID++)
+  {
+    int tetID = tetCopies[copyID].tetID;
+    TetShape tetShape(tetMesh, tetID);
+    UTetKey tetkey(tetMesh.tet(tetID));
+    for(int faceID = 0; faceID < 4; faceID++)
+    {
+      UTriKey face = tetkey.uFaceKey(faceID);
+      auto it = tetNbrs[tetID].find(face);
+      if (it == tetNbrs[tetID].end()) continue;
+      int nbrTetID = it->second;
+      TetShape nbrTetShape(tetMesh, nbrTetID);
+
+      for(int nbrCopyID : compsInTet[nbrTetID])
+      {
+        // TODO: this is not safe, if one triangle is exactly touch the tet face at a triangle edge,
+        // then this could miss
+        if (intersectSorted(tetCopies[copyID].tri, tetCopies[nbrCopyID].tri)
+            || intersectSorted(tetCopies[copyID].vtx, tetCopies[nbrCopyID].vtx))
+            {
+          // connect them
+          mergeVtxOnTetFace(copyID, nbrCopyID, face);
+          continue;
+        }
+        // although the two copies are not connected through cut triMesh vtx,
+        // they can still be connected through inner space
+        auto faceIt = tetCopies[copyID].tetFaceRel.find(face);
+        if (faceIt == tetCopies[copyID].tetFaceRel.end() || faceIt->second != -1) continue;
+        auto nbrFaceIt = tetCopies[nbrCopyID].tetFaceRel.find(face);
+        if (nbrFaceIt == tetCopies[nbrCopyID].tetFaceRel.end() || nbrFaceIt->second != -1) continue;
+
+//        cout << "Found tet merging..." << endl;
+        mergeVtxOnTetFace(copyID, nbrCopyID, face);
+      } // end nbrCopyID
+    }
+  }
+
+  vector<Vec3d> outputTetPositions;
+  vector<map<int, int>> repSetID2NewID(tetMesh.numVertices());
+  for(size_t i = 0; i < tetCopies.size(); i++)
+  {
+    for(int j = 0; j < 4; j++)
+    {
+      int uniqueID = (int)i * 4 + j;
+      int oriVtxID = tetMesh.tet(tetCopies[i].tetID)[j];
+      DisjointSetDynamic & vdset = dset[oriVtxID];
+      int repSetID = vdset.findSet(uniqueID);
+      auto it = repSetID2NewID[oriVtxID].find(repSetID);
+      int newID = -1;
+      if (it == repSetID2NewID[oriVtxID].end())
+      {
+        newID = outputTetPositions.size();
+        repSetID2NewID[oriVtxID].emplace(repSetID, newID);
+        outputTetPositions.push_back(tetMesh.pos(oriVtxID));
+      }
+      else
+      {
+        newID = it->second;
+      }
+      outputTets[i][j] = newID;
+
+      if (newTetVtxID2OldTetVtxID)
+      {
+        if ((*newTetVtxID2OldTetVtxID).size() <= (size_t)newID)
+        {
+          (*newTetVtxID2OldTetVtxID).resize(newID+1);
+        }
+        (*newTetVtxID2OldTetVtxID)[newID] = tetMesh.tet(tetCopies[i].tetID)[j];
+      }
+    }
+  }
+
+  // remove redundant tet copies
+  map<Vec4i, int> removeMap;
+  for(size_t copyID = 0; copyID < tetCopies.size(); copyID++)
+  {
+    Vec4i tet = outputTets[copyID];
+    auto it = removeMap.find(tet);
+    if (it == removeMap.end())
+      removeMap.emplace(tet, copyID);
+    else // merge the stored tetCopy and this tetCopy
+    {
+      int prevCopyID = it->second;
+      if (!(tetCopies[prevCopyID].tetID == tetCopies[copyID].tetID))
+      {
+        cout << "copyID " << copyID << " preCopyID " << prevCopyID << " tet " << tet << " tetID" << tetCopies[copyID].tetID
+            << " pre tetID " << tetCopies[prevCopyID].tetID << endl;
+      }
+      assert(tetCopies[prevCopyID].tetID == tetCopies[copyID].tetID);
+
+      vectorInsertRangeBack(tetCopies[prevCopyID].vtx, tetCopies[copyID].vtx);
+      vectorInsertRangeBack(tetCopies[prevCopyID].tri, tetCopies[copyID].tri);
+      sortAndDeduplicate(tetCopies[prevCopyID].vtx);
+      sortAndDeduplicate(tetCopies[prevCopyID].tri);
+
+      tetCopies[prevCopyID].check();
+    }
+  }
+
+  outputTets.clear();
+  vector<TetTriCompData> newTetCopies;
+  for(auto p : removeMap)
+  {
+    outputTets.push_back(p.first);
+    if (newTetID2OldTetID) newTetID2OldTetID->push_back(tetCopies[p.second].tetID);
+    newTetCopies.emplace_back(move(tetCopies[p.second]));
+  }
+  for(const auto & c : newTetCopies)
+  {
+    c.check();
+  }
+
+  if (profiler) profiler->stopLastTimer();
+//  cout << if (profiler) profiler->toString() << endl;
+
+  TetMeshGeo newTetMesh(move(outputTetPositions), move(outputTets));
+
+  if (coord)
+  {
+    // compute barycentric weights
+    vector<double> weights(triMesh.numVertices() * 4);
+    vector<int> tetVtxIndices(triMesh.numVertices() * 4, -1);
+    vector<int> embeddingTetIndices(triMesh.numVertices());
+    for(size_t copyID = 0; copyID < newTetCopies.size(); copyID++)
+    {
+      int tetID = newTetCopies[copyID].tetID;
+      for(int v : newTetCopies[copyID].vtx)
+      {
+        if (v >= triMesh.numVertices()) continue; // this tri vtx is a cut vtx
+        if (tetVtxIndices[4*v] >= 0) continue; // this tri vtx has been assigned a weight before
+        getTetBarycentricWeights(triMesh.pos(v), tetMesh.pos(tetID, 0), tetMesh.pos(tetID, 1), tetMesh.pos(tetID, 2), tetMesh.pos(tetID, 3),
+            &weights[4*v]);
+        newTetMesh.tet(copyID).convertToArray(&tetVtxIndices[4*v]);
+        embeddingTetIndices[v] = copyID;
+      }
+    }
+
+    // if some input triangle vtx are outside the tet mesh,
+    // we will find the closet tets to embed them
+    vector<int> outerTriVtxIDs;
+    for(int triVtxID = 0; triVtxID < triMesh.numVertices(); triVtxID++)
+    {
+      if (tetVtxIndices[4*triVtxID] >= 0) continue;
+      outerTriVtxIDs.push_back(triVtxID);
+    }
+
+    if (outerTriVtxIDs.size() > 0)
+    {
+      TetMeshRef newTetMeshRef = newTetMesh.ref();
+#ifdef USE_TBB
+tbb::parallel_for(0, sizei(outerTriVtxIDs), [&](const int & i)
+#else
+    for(int i = 0; i < sizei(outerTriVtxIDs); i++)
+#endif
+    {
+      int triVtxID = outerTriVtxIDs[i];
+      // now this vtx is not embedded because it is out of tet mesh
+      Vec3d pos = triMesh.pos(triVtxID);
+
+      MinValueIndex vi;
+      for(int newTetID = 0; newTetID < newTetMesh.numTets(); newTetID++)
+      {
+        vi.update(newTetMeshRef.computeSquaredDistanceToTet(newTetID, pos), newTetID);
+      }
+
+      assert(vi.index >= 0);
+      newTetMeshRef.computeTetBarycentricWeights(vi.index, pos, &weights[4*triVtxID]);
+      memcpy(&tetVtxIndices[4*triVtxID], newTetMesh.tet(vi.index).data(), sizeof(int) * 4);
+      embeddingTetIndices[triVtxID] = vi.index;
+#ifdef USE_TBB
+    });
+#else
+    }
+#endif
+  }
+
+
+    BarycentricCoordinates bary(triMesh.numVertices(), 4, tetVtxIndices.data(), weights.data(), embeddingTetIndices.data());
+    *coord = bary;
+  }
+
+  if (tetTris)
+  {
+    tetTris->resize(newTetMesh.numTets());
+    for(int tetCopyID = 0; tetCopyID < newTetMesh.numTets(); tetCopyID++)
+    {
+      auto tris = newTetCopies[tetCopyID].tri;
+      sortAndDeduplicate(tris);
+      (*tetTris)[tetCopyID] = move(tris);
+    }
+  }
+
+  // XXX
+//  exit(0);
+
+  return newTetMesh;
+}
diff --git a/libraries/virtualTets/virtualTets-via-csg.h b/libraries/virtualTets/virtualTets-via-csg.h
new file mode 100644
index 0000000000000000000000000000000000000000..2237a7a0e0d21ab4615435d5235e70d1721e2b8f
--- /dev/null
+++ b/libraries/virtualTets/virtualTets-via-csg.h
@@ -0,0 +1,84 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef VIRTUALTETSVIACSG_H
+#define VIRTUALTETSVIACSG_H
+
+#include "tetMeshGeo.h"
+#include "triMeshGeo.h"
+#include "barycentricCoordinates.h"
+#include "vec3ER.h"
+#include "tetTriCuttingData.h"
+#include "profiler.h"
+
+/*
+  This file implements "virtual tets" using constructive solid geometry, as described in:
+
+  Eftychios Sifakis, Kevin Der, and Ronald Fedkiw. 2007.
+  Arbitrary Cutting of Deformable Tetrahedralized Objects.
+  In Symp. on Computer Animation (SCA 2017). 73–80.
+
+  The usage of these routines is the same as in virtualTets.h . Please see that file for usage.
+*/
+
+TetMeshGeo createVirtualTetsMeshViaCSG(
+    const TetMeshGeo & tetMesh,
+    const TriMeshGeo triMesh,
+    BarycentricCoordinates * coord = nullptr,
+    std::vector<int> * newTetVtxID2OldTetVtxID = nullptr,
+    std::vector<int> * newTetID2OldTetID = nullptr,
+    std::vector<std::vector<int>> * tetTris = nullptr,
+    bool verbose = false, Profiler * profiler = nullptr);
+
+TetMeshGeo createVirtualTetsMeshViaCSG(
+    const TetMeshGeo & tetMesh,
+    const TriMeshGeo triMesh,
+    const std::vector<Vec3ER> & exactTriPos,
+    BarycentricCoordinates * coord = nullptr,
+    std::vector<int> * newTetVtxID2OldTetVtxID = nullptr,
+    std::vector<int> * newTetID2OldTetID = nullptr,
+    std::vector<std::vector<int>> * tetTris = nullptr,
+    bool verbose = false, Profiler * profiler = nullptr);
+
+TetMeshGeo createVirtualTetsMeshViaCSG(
+    const TetMeshGeo & tetMesh,
+    const TriMeshGeo triMesh,
+    const std::vector<Vec3ER> & exactTriPos,
+    const TetTriIntersectingData & tetTriIntersectingData,
+    BarycentricCoordinates * coord = nullptr,
+    std::vector<int> * newTetVtxID2OldTetVtxID = nullptr,
+    std::vector<int> * newTetID2OldTetID = nullptr,
+    std::vector<std::vector<int>> * tetTris = nullptr,
+    bool verbose = false, Profiler * profiler = nullptr);
+
+#endif
+
diff --git a/libraries/virtualTets/virtualTets.cpp b/libraries/virtualTets/virtualTets.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2772decaf41140b724853b129be3bfee7393beda
--- /dev/null
+++ b/libraries/virtualTets/virtualTets.cpp
@@ -0,0 +1,845 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "virtualTets.h"
+#include "triMeshNeighbor.h"
+#include "triMeshPseudoNormal.h"
+#include "containerHelper.h"
+#include "disjointSet.h"
+#include "tetTriMeshCutting.h"
+#include "basicAlgorithms.h"
+#include "profiler.h"
+#include "windingNumberTree.h"
+#include "listIO.h"
+#include "geometryQuery.h"
+#include "tetTriCuttingData.h"
+#include "valueIndex.h"
+#include "createTriMesh.h"
+#include "labelOuterTets.h"
+#include "tetTriPiece.h"
+#include <iostream>
+#include <sstream>
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
+using namespace std;
+
+// The simplest routine with minimal input.
+TetMeshGeo createVirtualTetsMesh(const TetMeshGeo & tetMesh, const TriMeshGeo & triMesh, BarycentricCoordinates * coord,
+    vector<int> * newTetVtxID2OldTetVtxID, vector<int> * newTetID2OldTetID, vector<vector<int>> * tetTris,
+    bool verbose, Profiler * profiler)
+{
+  // compute triangle mesh postions in exact arithmetic
+  vector<Vec3ER> triPosER;
+  for(int i = 0; i < triMesh.numVertices(); i++)
+  {
+    Vec3d p = triMesh.pos(i);
+    triPosER.emplace_back(p[0], p[1], p[2]);
+  }
+  // call the overloading function with exact triMesh positions as additional input
+  return createVirtualTetsMesh(tetMesh, triMesh, triPosER, coord, newTetVtxID2OldTetVtxID, newTetID2OldTetID, tetTris,
+      verbose, profiler);
+}
+
+// The routine with triangle mesh postions in exact arithmetic as additional input
+TetMeshGeo createVirtualTetsMesh(const TetMeshGeo & tetMesh, const TriMeshGeo & triMesh,
+    const std::vector<Vec3ER> & exactTriPos, BarycentricCoordinates * coord,
+    vector<int> * newTetVtxID2OldTetVtxID, vector<int> * newTetID2OldTetID, vector<vector<int>> * tetTris,
+    bool verbose, Profiler * profiler)
+{
+  TetTriCuttingData cuttingData;
+  ProfilerSection tetTriCutSec(profiler, "tetTriCutting");
+
+  // use class TetTriMeshCutting to compute data in TetTriCuttingData
+  tetTriCutSec.start();
+  TetTriMeshCutting cutting(tetMesh, triMesh);
+
+  cout << "Computing tet-triangle intersection..." << endl;
+  // compute which tet intersect with triangle
+  cutting.computeIntersectingTriTets();
+  
+  // Definition:
+  //   cut triangle mesh: the new triangle mesh which is the result of the cut on the input triangle mesh by all the tets
+  //     The newly geneated cuts are modeled by adding new vertices on the cut triangles.
+  //     New vertices are shared by neighboring cut triangles. So after the cut, the topology of the input triangle mesh
+  //     is preserved. Only its surface is subdivided by the tets' cut.
+
+  // Here we compute the cut triangle mesh
+  cout << "Performing Sutherland-Hodgman tet-triangle clipping algorithm..." << endl;
+  cutting.computeCutTriangles(&exactTriPos);
+  cout << "Finished clipping." << endl;
+  tetTriCutSec.stop();
+
+  // Definition:
+  //   cut feature: a pair of tetKey and triKey, used to store which geometry feature
+  //     (tet: interior/face/edge/vtx, tri: interior, edge, vtx) a cut vertex lies on
+  // cuttingData.cutTriGroups: tetID -> cut triangle info in each tet
+  //   cut triangle info includes: 1) the 3 cutVtxID of the triangle, 2) the original triangle ID where this cut triangle is from
+
+  cuttingData.cutVtxPos = cutting.cutTriPositions();     // cut triangle mesh vertex positions
+  cuttingData.cutVtxPosER = cutting.cutTriPositionsER();  // cut triangle mesh vertex positions in exact arithmetic
+  cuttingData.features = cutting.cutVertexFeatures(); // cut feature of each cut triangle mesh vertex
+  cuttingData.tetPosER = cutting.tetPositionsER();     // tet mesh position in exact arithmetic
+  for(int i = 0; i < tetMesh.numTets(); i++)
+    cuttingData.cutTriGroups.push_back(cutting.getCutTriGroup(i)); // cut triangle info in each tet
+
+  // call the last, most complicated routine to do the work
+  return createVirtualTetsMesh(tetMesh, triMesh, cuttingData, coord, newTetVtxID2OldTetVtxID, newTetID2OldTetID, tetTris,
+      true, verbose, profiler);
+}
+
+// the last, most complicated routine where TetTriCuttingData and removeAirTets are additional input
+TetMeshGeo createVirtualTetsMesh(const TetMeshGeo & tetMesh, const TriMeshGeo & triMesh,
+    const TetTriCuttingData & cuttingData, BarycentricCoordinates * coord,
+    vector<int> * newTetVtxID2OldTetVtxID, vector<int> * newTetID2OldTetID, vector<vector<int>> * tetTris, bool removeAirTets,
+    bool verbose, Profiler * profiler)
+{
+  ProfilerSection buildCopiesSec(profiler, "buildTetCopies");
+  ProfilerSection connectCopiesSec(profiler, "connectTetCopies");
+  ProfilerSection finalStuffSec(profiler, "finalStuffInVirtualTet");
+
+  if (verbose)
+    cout << "Begin virtual tets..." << endl;
+  if (newTetVtxID2OldTetVtxID) newTetVtxID2OldTetVtxID->clear();
+  if (newTetID2OldTetID) newTetID2OldTetID->clear();
+
+  // check whether the input triangle mesh is manifold
+  if (areTrianglesManifold(triMesh.triangles()) == false)
+  {
+    cout << "Error: input triangle mesh for virtual tets algorithm is not manifold" << endl;
+    throw 1;
+  }
+  TriangleNeighbor triangleNeighbor(triMesh.triangles());
+
+  // compute normals of each input triangle
+  // we use exact arithmetic to compute normals only when the precision is not enough
+  vector<Vec3d> triNormals(triMesh.numTriangles());
+  for(int triID = 0; triID < triMesh.numTriangles(); triID++)
+  {
+    triNormals[triID] = triMesh.ref().computeTriangleNormal(triID);
+    if (triNormals[triID].hasNaN())
+    {
+      Vec3ER pa = cuttingData.cutVtxPosER[triMesh.triVtxID(triID, 0)];
+      Vec3ER pb = cuttingData.cutVtxPosER[triMesh.triVtxID(triID, 1)];
+      Vec3ER pc = cuttingData.cutVtxPosER[triMesh.triVtxID(triID, 2)];
+      Vec3ER pNormal = cross(pb - pa, pc- pa);
+      Vec3d dNormal(0.0);
+      for(int i = 0; i < 3; i++)
+        dNormal[i] = ER_toDouble(pNormal[i]);
+      dNormal.normalize();
+      assert(dNormal.hasNaN() == false);
+      triNormals[triID] = dNormal;
+      // nanNormalTriIDs.push_back(triID);
+    }
+  }
+  // TODO: we can input triMeshNormal from caller to reduce exact-arithmetic calls
+  TriMeshPseudoNormal triMeshNormal(triMesh, triNormals.data());
+
+  auto triangleBoundaryLoops = triangleNeighbor.findBoundaryLoops(triMesh.triangles());
+  if (triangleBoundaryLoops.size() > 0)
+  {
+    cout << "Error: the input mesh has boundary" << endl;
+    throw 1;
+  }
+
+  // #vtx in the input triangle mesh.
+  // the cut mesh is assumed that the first numUncutVtx vertices are the same vertices from the input triangle mesh
+  // the rest vertices are those added due to cuts
+  int numUncutVtx = triMesh.numVertices();
+
+  vector<bool> tetOutside; // if removeAirTets, stores the mapping: tetID -> whether the tet is outside the triangle mesh
+  if (removeAirTets)
+  {
+    ProfilerSection airTetsSec(profiler, "airTets");
+    airTetsSec.start();
+
+    auto isTetBou = [&](int tetID) // return true if this tet intersects the triangle mesh
+    {
+      return cuttingData.cutTriGroups[tetID].tri.size() > 0;
+    };
+
+    // winding number tree from Jacobson 2013, Robust Inside-Outside Segmentation using Generalized Winding Numbers
+    // it detects whether a point is inside/outside a triangle mesh
+    WindingNumberTree wnTree;
+    wnTree.build(triMesh);
+    auto isTetOuter = [&](int tetID) // return true if this tet is outside the triangle mesh
+    {
+      double wn = wnTree.windingNumber(triMesh, tetMesh.ref().computeTetCenter(tetID));
+      return wn < 0.5; // winding number = 0: outside, = 1: inside
+    };
+
+    // build the neighboring structure of the input tet mesh
+    TetNeighbor tetNeighbor(tetMesh.tets());
+
+    tetOutside = labelOuterTets(tetMesh, tetNeighbor, isTetBou, isTetOuter);
+    airTetsSec.stop();
+  }
+
+  const vector<Vec3d> & cutPositions = cuttingData.cutVtxPos;
+  const vector<Vec3ER> & cutPositionsER = cuttingData.cutVtxPosER;
+
+#ifdef USE_VT_MULTITHREADING
+  vector<mutex> cutVtxPosVecMutex(cuttingData.cutVtxPos.size());
+  vector<mutex> tetPosVecMutex(cuttingData.tetPosER.size());
+  cuttingData.multiThreading = true;
+  cuttingData.cutVtxPosVecMutex = &cutVtxPosVecMutex;
+  cuttingData.tetPosVecMutex = &tetPosVecMutex;
+#endif
+
+  // TetTriPiece: represent a piece in our paper
+  // Pieces can be merged through the algorithm to represent all the boundaries of a '-' region in a tet
+  // In the end, each such '-' region is assigned a unique tet
+  vector<TetTriPiece> tetPieces;
+  vector<vector<int>> piecesInTet(tetMesh.numTets()); // tetID -> pieces inside the tet, with pieceIDs in tetPieces
+
+  buildCopiesSec.start();
+  if (verbose)
+    cout << "Begin to visit each tet for building comps" << endl;
+
+  // go to each input tet, compute the pieces inside them, build the region graph as explained in our paper,
+  // and stores the boundaries of each computed '-' region into tetPieces
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    if (removeAirTets && tetOutside[tetID]) continue; // if it's an outer tet, skip
+    if (verbose && tetID % 1000 == 0)
+      cout << "process comps in tetID " << tetID << "/" << tetMesh.numTets() << endl;
+
+    TetShape tetShape(tetMesh, tetID); // class to do geometry query on the tet
+    const auto & cutTriGroup = cuttingData.cutTriGroups[tetID];
+    vector<vector<int>> triCCs = getConnectedComponentsByVertex(cutTriGroup.tri);
+    for (const auto & cc : triCCs)
+      assert(cc.size() > 0);
+
+    // if no triangles inside this tet, then the entire volume of the tet is one region
+    if (triCCs.size() == 0)
+    {
+      piecesInTet[tetID].push_back(tetPieces.size());
+      tetPieces.emplace_back(tetID);
+      continue;
+    }
+    // there is only one piece, then it is simple to process
+    if (triCCs.size() == 1)
+    {
+      piecesInTet[tetID].push_back(tetPieces.size());
+      tetPieces.emplace_back(tetID, triCCs[0], cutTriGroup, cuttingData, triMeshNormal, tetShape);
+      continue;
+    }
+
+    // sample points on each piece
+    // a sample point is used to do pseudo-normal test for building the region graph
+    vector<Vec3d> samplePoint(triCCs.size()); // sample point position on each piece
+    vector<Vec3ER> samplePointER(triCCs.size()); // exact arithmetric sample point position
+
+    // find an appropriate sample point for each piece
+    // to avoid degeneracy, sample points should be away from the tet boundary
+    for(size_t i = 0; i < triCCs.size(); i++)
+    {
+      const vector<int> & cc = triCCs[i]; // local triangleIDs in cutTriGroup.tri
+      bool found = false;
+      for(int triID : cc)
+      {
+        Vec3i t = cutTriGroup.tri[triID];
+        for(int j = 0; j < 3; j++)
+        {
+          int v = t[j];          // get cutVtxID
+          if (v < numUncutVtx) // cutVtxID < numUncutVtx means this vtx is an original vertex in the input triangle mesh
+          {
+            samplePoint[i] = cutPositions[v];
+            samplePointER[i] = cutPositionsER[v];
+            found = true; // then we pick this vertex
+            break;
+          }
+        }
+        if (found) break;
+      }
+      // Here we choose the sample point as one original vertex because generally, original vertex is on the interior of the tet
+      // We can make it more robust by finding an original vertex that's not on the tet faces exactly
+      if (found) continue;
+      // else, found == false, we then use the center of a triangle as the sample point
+      // again, we don't consider the case where this triangle lies completely inside a tet face
+      // we can change this to make the code more robust
+      Vec3i t = cutTriGroup.tri[triCCs[i][0]];
+      samplePoint[i] = (cutPositions[t[0]] + cutPositions[t[1]] + cutPositions[t[2]]) / 3.0;
+      samplePointER[i] = (cutPositionsER[t[0]] + cutPositionsER[t[1]] + cutPositionsER[t[2]]) / ER(3.0);
+    } // end for i on triCC
+
+    vector<TetTriPiece> pieces; // pieces of this tet
+    for(size_t i = 0; i < triCCs.size(); i++)
+    {
+      pieces.emplace_back(tetID, triCCs[i], cutTriGroup, cuttingData, triMeshNormal, tetShape);
+    }
+//    for (const auto & c : pieces)
+//      assert(c.mesh(cuttingData).numTriangles() > 0);
+
+    auto debugPieces = [&]()
+    {
+      auto tetSurfaceMesh = createTetSurfaceMesh(tetMesh.pos(tetID, 0), tetMesh.pos(tetID, 1), tetMesh.pos(tetID, 2), tetMesh.pos(tetID, 3));
+      tetSurfaceMesh.save("tet.obj");
+      for(size_t i = 0; i < pieces.size(); i++)
+      {
+        TriMeshGeo pieceMesh = removeIsolatedVertices(pieces[i].mesh(cuttingData));
+        ostringstream os;
+        os << "comp" << i << ".obj";
+        pieceMesh.save(os.str());
+      }
+    };
+
+    // if #pieces == 2, the region graph building algorithm can be simplied to
+    // doing pseudo-normal inside/outside test only once:
+    if (triCCs.size() == 2)
+    {
+      // check the sample point on pieces[1] against pieces[0]
+      int ret = pieces[0].inOutTest(samplePoint[1], samplePointER[1], cuttingData, triMeshNormal, tetShape, profiler);
+      assert(ret != 0);
+
+      if (ret > 0) // pieces[1] is outside pieces[0]
+      {
+        // then because the input triangle mesh is closed manifold, free of self-intersection,
+        // there must be two '-' regions in the tet
+        piecesInTet[tetID].push_back(tetPieces.size());
+        tetPieces.emplace_back(move(pieces[0]));
+        piecesInTet[tetID].push_back(tetPieces.size());
+        tetPieces.emplace_back(move(pieces[1]));
+      }
+      else // pieces[1] is inside pieces[0]
+      {
+        // then because the input triangle mesh is closed manifold, free of self-intersection,
+        // there must be one '-' region which has both pieces as its boundaries
+        piecesInTet[tetID].push_back(tetPieces.size());
+        tetPieces.emplace_back(mergePiece(pieces[0], pieces[1], tetID, cutTriGroup, cuttingData, triMeshNormal, tetShape));
+      }
+      continue; // go to next iteration in the loop of tetID
+    }
+
+    int debugTetID = -1; // used to trigger debugging information
+    if (tetID == debugTetID)
+    {
+      for(size_t i = 0; i < triCCs.size(); i++)
+        cout << "sample pt " << samplePoint[i] << endl;
+    }
+
+    // more than three pieces in the tet, complicated situation
+    // we have to run the full region graph algorithm
+
+    // first initialize the region with one piece (pieces[0]) and two regions
+    // it stores: regionID -> list of pairs: <pieceID as its boundary, whether the triangle normals on the piece pointing outward of this region>
+    vector<vector<pair<int, bool>>> regions;
+    regions.push_back({{0, true}});  // region 0 is inside the pieces[0]
+    regions.push_back({{0, false}}); // region 1 is outside the pieces[0]
+
+
+    auto debugRegion = [&]()
+    {
+      debugPieces();
+      cout << "Dump data for debugging tetID " << tetID << "..." << endl;
+      for(size_t i = 0; i < regions.size(); i++)
+      {
+        cout << "region " << i << ": ";
+        for(auto p : regions[i])
+          cout << "(" << p.first << ", " << p.second << ") ";
+        cout << endl;
+      }
+    };
+
+    // do the iteration of the region graph algorithm
+    // at each iteration, a new piece is added to the graph, splitting an old region in the graph
+    for(size_t newPieceID = 1; newPieceID < triCCs.size(); newPieceID++)
+    {
+      const auto & samplePt = samplePoint[newPieceID];
+      const auto & samplePtER = samplePointER[newPieceID];
+      const int UNINIT = -999; // lable for uninitialized result
+      // vector inOutTest: pieceID of the existing pieces in the graph in the iteration so far -> in/out test result from pieces[newPieceID]
+      vector<int> inOutTest(newPieceID, UNINIT);
+      int regIDToCut = -1; // the regionID that will be cut by the new piece
+      for(size_t regID = 0; regID < regions.size(); regID++)
+      {
+        bool sampleIsOutside = false;
+        for(auto p : regions[regID]) // for each piece serving as the boundary of this region
+        {
+          int pieceID = p.first;
+          if (inOutTest[pieceID] == UNINIT) // check whether the new piece is in/out of the pieces[pieceID]
+            inOutTest[pieceID] = pieces[pieceID].inOutTest(samplePt, samplePtER, cuttingData, triMeshNormal, tetShape, profiler);
+          // if the newPiece is outside pieces[pieceID] and the normal of pieces[pieceID] is pointing outward the region
+          // or the newPiece is inside pieces[pieceID] and the normal of pieces[pieceID] is pointing inward the region
+          // then newPiece is outside the region
+          if ((inOutTest[pieceID] > 0) == p.second)
+          {
+            sampleIsOutside = true;
+            break;
+          }
+        }
+        if (sampleIsOutside == false) // in this case, the new piece is inside the region
+        {
+          // the newCCID is inside this region
+          regIDToCut = regID; // we shall split this region
+          break;
+        }
+      }
+      assert(regIDToCut >= 0);
+      if (tetID == debugTetID) // debugging
+        cout << "Found cutting CC " << newPieceID << " is in region " << regIDToCut << endl;
+      // now we split this region: regions[regIDToCut]
+      regions.emplace_back(); // add a new empty region to regions
+      vector<pair<int, bool>> regInsideNewPiece; // the boundary piece data for the newly generated region that is inside the new piece
+      vector<pair<int, bool>> & regOutsideNewPiece = regions.back(); // the new region that is outside the new piece
+      for(auto p : regions[regIDToCut]) // go through all the boundary pieces of the split region
+      {
+        int pieceID = p.first;
+        const auto & samplePt = samplePoint[pieceID];
+        const auto & samplePtER = samplePointER[pieceID];
+
+        // check whether pieces[pieceID] is in/out of the new piece
+        int test = pieces[newPieceID].inOutTest(samplePt, samplePtER, cuttingData, triMeshNormal, tetShape, profiler);
+        assert(test != 0);
+        // place pieces[pieceID] to one of the new region based on the in/out result
+        if (test > 0) regOutsideNewPiece.emplace_back(pieceID, p.second);
+        else regInsideNewPiece.emplace_back(pieceID, p.second);
+      }
+      // finally, add the new piece to the two new regions
+      regInsideNewPiece.emplace_back(newPieceID, true);
+      regOutsideNewPiece.emplace_back(newPieceID, false);
+      // assign regInsideNewPiece back to the vector: regions
+      regions[regIDToCut] = move(regInsideNewPiece);
+
+      if (tetID == debugTetID) // debugging
+      {
+        cout << "After cut, regions are" << endl;
+        for(size_t i = 0; i < regions.size(); i++)
+        {
+          cout << "region " << i << ": ";
+          for(auto p : regions[i])
+            cout << "(" << p.first << ", " << p.second << ") ";
+          cout << endl;
+        }
+      }
+    } // end each newPieceID, end of the region graph iteraion
+
+    // now regions have been created, we will visit each region to check whether everything is fine
+    for(const auto & region : regions)
+    {
+      assert(region.size() > 0);
+      // after the region graph is built, every piece connecting the same region should have
+      // the same normal orientation relative to the region
+      // if this is broken, sth. is wrong
+      bool insideTriMesh = region[0].second;
+      for(auto p : region)
+      {
+        if (p.second != insideTriMesh) { debugRegion(); }
+        assert(p.second == insideTriMesh);
+      }
+    }
+
+    // in the final region graph, each piece belongs to only one '-' region
+    // the vector below: piecesVisited is used to check this
+    vector<bool> piecesVisited(pieces.size(), false);
+    for(const auto & region : regions)
+    {
+      bool insideTriMesh = region[0].second;
+
+      if (insideTriMesh == false) continue; // if this region is '+', we continue
+
+      // now this region is a '-' region, we should merge all its regions to create the
+      // final boundary for this region and store it in tetPieces
+
+      vector<int> groupTriIDs; // store all cut triangles of the merged boundary
+      for(auto p : region)
+      {
+        int pieceID = p.first;
+        assert(piecesVisited[pieceID] == false);
+        piecesVisited[pieceID] = true;
+        if (region.size() == 1)
+        {
+          piecesInTet[tetID].push_back(tetPieces.size());
+          tetPieces.emplace_back(move(pieces[pieceID]));
+          break;
+        }
+        const auto & IDs = pieces[pieceID].groupTriID;
+        groupTriIDs.insert(groupTriIDs.end(), IDs.begin(), IDs.end());
+      }
+      if (region.size() == 1) { continue; }
+
+      sort(groupTriIDs.begin(), groupTriIDs.end());
+//      assert(unique(groupTriIDs.begin(), groupTriIDs.end()) == groupTriIDs.end());
+      piecesInTet[tetID].push_back(tetPieces.size());
+      // TODO: Here we create a merged boundary by first find the union of all cut triangles from several regions,
+      // then build a new TetTriPiece from the union.
+      // But in this way, internal data built in individual regions is lost and will be recomputed again
+      // we can write a better implementation by reusing the data
+      tetPieces.emplace_back(tetID, groupTriIDs, cutTriGroup, cuttingData, triMeshNormal, tetShape);
+    } // end each region
+
+    // assert all pieces have been added or merged
+    assert(find(piecesVisited.begin(), piecesVisited.end(), false) == piecesVisited.end());
+  } // end each tetID
+  if (verbose)
+    cout << "Done." << endl;
+  buildCopiesSec.stop();
+
+  connectCopiesSec.start();
+
+  // now each piece stored in tetPieces corresponds to a virtualized tet to appear in the final tet mesh
+  // we will begin connect those tets
+
+  // first, we build a structure to find neighboring tets and their shared tet faces
+  vector<map<UTriKey, int>> tetNbrs(tetMesh.numTets()); // input tetID -> a tet face -> the neigbor tetID sharing this face
+  map<UTriKey, vector<int>> tetFaceMap; // tet face -> all tets sharing this tet face
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    Vec4i tet = tetMesh.tet(tetID);
+    UTetKey tetkey(&tet[0]);
+    for(int i = 0; i < 4; i++)
+    {
+      UTriKey face = tetkey.uFaceKey(i);
+      tetFaceMap[face].push_back(tetID);
+    }
+  }
+
+  for(const auto & p : tetFaceMap)
+  {
+    assert(p.second.size() <= 2);
+    const auto & tetIDs = p.second;
+    if (tetIDs.size() == 2)
+    {
+      // we only store tetIDs[0] -> tet face -> tetIDs[1] and don't store tetIDs[1] -> tet face -> tetIDs[0]
+      // because later we need to visit each tet connection only once
+      tetNbrs[tetIDs[0]].emplace(p.first, tetIDs[1]);
+    }
+  }
+
+  vector<Vec3d> tetCenters(tetMesh.numTets(), Vec3d(0.0));
+  for(int tetID = 0; tetID < tetMesh.numTets(); tetID++)
+  {
+    tetCenters[tetID] = tetMesh.ref().computeTetCenter(tetID);
+  }
+
+  // We build a DisjointSet for each input tet mesh vertex
+  // each tet copy is first assigned with four unique tet copy vertex IDs
+  // these unique tet copy vertex IDs are not explicitly built and stored on memory, because they are just:
+  // [tetCopyID * 4, tetCopyID * 4 + 3] for tetCopyID
+  // Then the unique tet copy vertex IDs belonging to the same input tet vertex will be thrown into the disjointSet
+  // structure of that input tet vertex
+  // if any tet copy face is merged at the input tet vertex, corresponding unique tet copy vertex IDs are merged in
+  // the disjoint set
+  vector<DisjointSetDynamic> dset(tetMesh.numVertices());
+
+  // merge the vertices of the two tet copies: copyID and nbrCopyID on the shared face
+  auto mergeVtxOnTetFace = [&](int copyID, int nbrCopyID, UTriKey face)
+  {
+    assert(copyID >= 0 && copyID < (int)tetPieces.size());
+    assert(nbrCopyID >= 0 && nbrCopyID < (int)tetPieces.size());
+    int tetID = tetPieces[copyID].tetID;
+    int nbrtetID = tetPieces[nbrCopyID].tetID;
+    Vec4i tet = tetMesh.tet(tetID);
+    Vec4i nbrtet = tetMesh.tet(nbrtetID);
+
+    for(int vtxID = 0; vtxID < 3; vtxID++)
+    {
+      int v = face[vtxID];
+      int i = tet.getInvertedIndex(v);
+      assert(i >= 0);
+      int nbri = nbrtet.getInvertedIndex(v);
+      assert(nbri >= 0);
+      // now the two tet copy vertex to be merged are: 4*copyID + i, 4*nbrCopyID + nbri
+      dset[v].unionSet(4*copyID + i, 4*nbrCopyID + nbri); // merge them in the disjoint set
+    }
+  };
+
+  // use to store the potential pairs of tet copy IDs and their shared faces for later processing
+  vector<tuple<int,int,UTriKey>> mergeCandList;
+  for(size_t copyID = 0; copyID < tetPieces.size(); copyID++) // for each virtualied tet: tet copy
+  {
+    int tetID = tetPieces[copyID].tetID; // tetID on the input tet mesh
+
+    UTetKey tetkey(tetMesh.tet(tetID));
+    for(int faceID = 0; faceID < 4; faceID++)
+    {
+      UTriKey face = tetkey.uFaceKey(faceID);
+      auto it = tetNbrs[tetID].find(face);
+      if (it == tetNbrs[tetID].end()) continue;
+      int nbrTetID = it->second; // find a neighboring input tet
+//      TetShape nbrTetShape(tetMesh, nbrTetID);
+
+      for(int nbrCopyID : piecesInTet[nbrTetID]) // for each tet copy on this nbrTetID
+      {
+        if (intersectSorted(tetPieces[copyID].vtx, tetPieces[nbrCopyID].vtx)) // if the tet copies share a cut vtx
+        {
+          // we shall merge tet copy vertices at this face
+          mergeVtxOnTetFace(copyID, nbrCopyID, face);
+          continue;
+        }
+        // although the two copies are not connected through cut triMesh vtx,
+        // they can still be connected through "inner space"
+
+        // if any piece vtx is on this face, then this face is not the part of the inner space of this region
+        // touchedTetFaces stores all the tet faces that the piece vtx lie on
+        if (setFind(tetPieces[copyID].touchedTetFaces, face)) continue;
+        if (setFind(tetPieces[nbrCopyID].touchedTetFaces, face)) continue;
+
+        // store them for later processing
+        mergeCandList.emplace_back(copyID, nbrCopyID, face);
+      } // end nbrCopyID
+    }
+  }
+
+  // stores whether the candidate nbring pair should be connected
+  // we use char as elements of the vector because it is thread-safe
+  // Note that vector<bool> is not thread-safe when modifying individual elements concurrently
+  vector<char> mergeCandResult(mergeCandList.size(), false);
+
+#ifdef USE_VT_MULTITHREADING
+  mutex inOutTestMutex;
+  vector<mutex> copyInitMutex(tetPieces.size());
+  // use tbb here
+#endif
+  for(size_t i = 0; i < mergeCandList.size(); i++)
+  {
+    int copyID = get<0>(mergeCandList[i]);
+    int nbrCopyID = get<1>(mergeCandList[i]);
+    UTriKey face = get<2>(mergeCandList[i]);
+    int tetID = tetPieces[copyID].tetID;
+    int nbrTetID = tetPieces[nbrCopyID].tetID;
+    TetShape tetShape(tetMesh, tetID);
+    TetShape nbrTetShape(tetMesh, nbrTetID);
+
+    // here we check whether the two tet copies can be connected via inner space
+    ProfilerExtraSection InOutTestInConnectionSec(profiler, "inOutTestInConnection");
+    InOutTestInConnectionSec.start();
+    // if this tet copy has no pieces, then its an inner tet, each of whose face is inner space
+    // otherwise, we use the pieces to do in/out check on the face center to determine
+    // whether this face is inside/outside the pieces, thus the region this tet copy represents
+    if (tetPieces[copyID].vtx.size() > 0)
+    {
+      Vec3d faceCenter = (tetMesh.pos(face[0]) + tetMesh.pos(face[1]) + tetMesh.pos(face[2])) / 3.0;
+      Vec3d queryPt = 1e-6 * norm(faceCenter - tetCenters[tetID]) + faceCenter;
+      // we offset queryPoint a little bit away from faceCenter, to account for numerical errors in inside/outside test
+      Vec3ER queryPtK(queryPt[0], queryPt[1], queryPt[2]);
+#ifdef USE_VT_MULTITHREADING
+      lock_guard<mutex> lock(copyInitMutex[copyID]);
+#endif
+      int test = tetPieces[copyID].inOutTest(queryPt, queryPtK, cuttingData, triMeshNormal, tetShape, profiler);
+      assert(test != 0);
+      if (test > 0) continue; // the tet face is outside, therefore not inner space
+    }
+    if (tetPieces[nbrCopyID].vtx.size() > 0)
+    {
+      Vec3d faceCenter = (tetMesh.pos(face[0]) + tetMesh.pos(face[1]) + tetMesh.pos(face[2])) / 3.0;
+      Vec3d queryPt = 1e-6 * norm(faceCenter - tetCenters[nbrTetID]) + faceCenter;
+      // we offset queryPoint a little bit away from faceCenter, to account for numerical errors in inside/outside test
+      Vec3ER queryPtK(queryPt[0], queryPt[1], queryPt[2]);
+#ifdef USE_VT_MULTITHREADING
+      lock_guard<mutex> lock(copyInitMutex[nbrCopyID]);
+#endif
+      int test = tetPieces[nbrCopyID].inOutTest(queryPt, queryPtK, cuttingData, triMeshNormal, nbrTetShape, profiler);
+      assert(test != 0);
+      if (test > 0) continue; // outside
+    }
+    InOutTestInConnectionSec.stop();
+    mergeCandResult[i] = true;
+  }
+
+  for(size_t i = 0; i < mergeCandList.size(); i++)
+  {
+    if (mergeCandResult[i])
+    {
+      int copyID = get<0>(mergeCandList[i]);
+      int nbrCopyID = get<1>(mergeCandList[i]);
+      UTriKey face = get<2>(mergeCandList[i]);
+      mergeVtxOnTetFace(copyID, nbrCopyID, face);
+    }
+  }
+
+
+  // now we will assign the new tet copy vertex ID to the merged unique tet copy vertex IDs
+  vector<Vec4i> outputTets(tetPieces.size()); // stores the new tet copy vertex IDs of each tet copy
+  vector<Vec3d> outputTetPositions;           // new tet copy vertex ID -> its position
+  // input tet mesh vtx ID -> the representive setID in the disjiont set ->  new tet copy vtx ID
+  vector<map<int, int>> repSetID2NewID(tetMesh.numVertices());
+  for(size_t tetCopyID = 0; tetCopyID < tetPieces.size(); tetCopyID++)
+  {
+    for(int j = 0; j < 4; j++)
+    {
+      int uniqueID = (int)tetCopyID * 4 + j; // the original unique tet copy vtx ID
+      int oldVtxID = tetMesh.tet(tetPieces[tetCopyID].tetID)[j]; // the input tet mesh vtx ID
+      DisjointSetDynamic & vdset = dset[oldVtxID];
+      int repSetID = vdset.findSet(uniqueID); // get the representive setID of this uniqueID
+      auto it = repSetID2NewID[oldVtxID].find(repSetID);
+      int newID = -1;
+      if (it == repSetID2NewID[oldVtxID].end()) // this is a new representive setID not met before
+      {
+        newID = outputTetPositions.size();      // we then create a new tet copy vtx ID
+        repSetID2NewID[oldVtxID].emplace(repSetID, newID);
+        outputTetPositions.push_back(tetMesh.pos(oldVtxID));
+      }
+      else
+      {
+        newID = it->second;
+      }
+      outputTets[tetCopyID][j] = newID;        // store the new tet copy vtx ID to outputTets
+
+      if (newTetVtxID2OldTetVtxID) // create the mapping: new tet copy vtx ID -> input tet vtx ID for optional output of this function
+      {
+        if ((*newTetVtxID2OldTetVtxID).size() <= (size_t)newID)
+        {
+          (*newTetVtxID2OldTetVtxID).resize(newID+1);
+        }
+        (*newTetVtxID2OldTetVtxID)[newID] = tetMesh.tet(tetPieces[tetCopyID].tetID)[j];
+      }
+    }
+  }
+
+  // After connecting neighboring tet copies, some tet copies might have the same set of tet copy vertices.
+  // This means that they should be merged.
+  // We will do this merge here.
+  map<Vec4i, int> tetCopyVtx2CopyID; // tet copy vertices -> tet copy IDs
+  for(size_t copyID = 0; copyID < tetPieces.size(); copyID++)
+  {
+    Vec4i tet = outputTets[copyID];
+    auto it = tetCopyVtx2CopyID.find(tet);
+    if (it == tetCopyVtx2CopyID.end())
+      tetCopyVtx2CopyID.emplace(tet, copyID);
+    else
+    { // merge the tet copy stored in tetCopyVtx2CopyID and this tet copy with index of copyID
+      int prevCopyID = it->second;
+      // naturally, the two copies to be merged should come from the same input tet
+      if (!(tetPieces[prevCopyID].tetID == tetPieces[copyID].tetID))
+      {
+        cout << "copyID " << copyID << " preCopyID " << prevCopyID << " tet " << tet << " tetID" << tetPieces[copyID].tetID
+            << " pre tetID " << tetPieces[prevCopyID].tetID << endl;
+      }
+      assert(tetPieces[prevCopyID].tetID == tetPieces[copyID].tetID);
+      int tetID = tetPieces[copyID].tetID;
+      TetShape tetShape(tetMesh, tetID);
+      const auto & cutTriGroup = cuttingData.cutTriGroups[tetID];
+      // merge the pieces of the two tet copies
+      tetPieces[prevCopyID] = mergePiece(tetPieces[prevCopyID], tetPieces[copyID], tetID, cutTriGroup,
+          cuttingData, triMeshNormal, tetShape);
+    }
+  }
+
+  // now we have merged pieces of those copies with same tet copy vtx,
+  // we should remove those redundant tet copies in outputTets and tetPieces
+  outputTets.clear();
+  vector<TetTriPiece> newTetPieces;
+  for(auto p : tetCopyVtx2CopyID)
+  {
+    outputTets.push_back(p.first);
+    if (newTetID2OldTetID) newTetID2OldTetID->push_back(tetPieces[p.second].tetID);
+    newTetPieces.emplace_back(move(tetPieces[p.second]));
+  }
+  // now newTetPieces stores the final tetPieces and outputTets stores the final tet copies
+
+  connectCopiesSec.stop();
+ //  cout << if (profiler) profiler->toString() << endl;
+
+  finalStuffSec.start();
+
+  // build the final tet mesh
+  TetMeshGeo newTetMesh(move(outputTetPositions), move(outputTets));
+
+  if (coord) // build optional data, coord
+  {
+    // compute barycentric weights for embedding the input traingle mesh into the new virtualized tet mesh
+    vector<double> weights(triMesh.numVertices() * 4);
+    vector<int> tetVtxIndices(triMesh.numVertices() * 4, -1);
+    vector<int> embeddingTetIndices(triMesh.numVertices());
+    for(size_t copyID = 0; copyID < newTetPieces.size(); copyID++)
+    {
+      int tetID = newTetPieces[copyID].tetID;
+      for(int v : newTetPieces[copyID].vtx)
+      {
+        if (v >= triMesh.numVertices()) continue; // this tri vtx is a cut vtx
+        if (tetVtxIndices[4*v] >= 0) continue; // this tri vtx has been assigned a weight before
+        getTetBarycentricWeights(triMesh.pos(v), tetMesh.pos(tetID, 0), tetMesh.pos(tetID, 1), tetMesh.pos(tetID, 2), tetMesh.pos(tetID, 3),
+            &weights[4*v]);
+        newTetMesh.tet(copyID).convertToArray(&tetVtxIndices[4*v]);
+        embeddingTetIndices[v] = copyID;
+      }
+    }
+
+    // if some input triangle vtx are outside the tet mesh,
+    // we will find the closet tets to embed them
+    vector<int> outerTriVtxIDs;
+    for(int triVtxID = 0; triVtxID < triMesh.numVertices(); triVtxID++)
+    {
+      if (tetVtxIndices[4*triVtxID] >= 0) continue;
+      outerTriVtxIDs.push_back(triVtxID);
+    }
+
+    if (outerTriVtxIDs.size() > 0)
+    {
+      TetMeshRef newTetMeshRef = newTetMesh.ref();
+  #ifdef USE_TBB
+      tbb::parallel_for(0, sizei(outerTriVtxIDs), [&](const int & i)
+  #else
+      for(int i = 0; i < sizei(outerTriVtxIDs); i++)
+  #endif
+      {
+        int triVtxID = outerTriVtxIDs[i];
+        // now this vtx is not embedded because it is out of tet mesh
+        Vec3d pos = triMesh.pos(triVtxID);
+
+        MinValueIndex vi;
+        for(int newTetID = 0; newTetID < newTetMesh.numTets(); newTetID++)
+        {
+          vi.update(newTetMeshRef.computeSquaredDistanceToTet(newTetID, pos), newTetID);
+        }
+
+        assert(vi.index >= 0);
+        newTetMeshRef.computeTetBarycentricWeights(vi.index, pos, &weights[4*triVtxID]);
+        memcpy(&tetVtxIndices[4*triVtxID], newTetMesh.tet(vi.index).data(), sizeof(int) * 4);
+        embeddingTetIndices[triVtxID] = vi.index;
+  #ifdef USE_TBB
+      });
+  #else
+      }
+  #endif
+    }
+
+    BarycentricCoordinates bary(triMesh.numVertices(), 4, tetVtxIndices.data(), weights.data(), embeddingTetIndices.data());
+    *coord = move(bary);
+  }
+
+  if (tetTris) // build optional data, tetTris
+  {
+    tetTris->resize(newTetMesh.numTets());
+    for(int tetCopyID = 0; tetCopyID < newTetMesh.numTets(); tetCopyID++)
+    {
+      auto tris = newTetPieces[tetCopyID].gnrOriTriID;
+      sortAndDeduplicate(tris);
+      (*tetTris)[tetCopyID] = move(tris);
+    }
+  }
+
+  finalStuffSec.stop();
+
+  return newTetMesh;
+}
diff --git a/libraries/virtualTets/virtualTets.h b/libraries/virtualTets/virtualTets.h
new file mode 100644
index 0000000000000000000000000000000000000000..451e37f99567c57c6b2e284cb6ab3c808d0721b7
--- /dev/null
+++ b/libraries/virtualTets/virtualTets.h
@@ -0,0 +1,108 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "virtualTets" library , Copyright (C) 2018 USC                        *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef VIRTUALTETS_H
+#define VIRTUALTETS_H
+
+#include "tetMeshGeo.h"
+#include "triMeshGeo.h"
+#include "barycentricCoordinates.h"
+#include "vec3ER.h" // 3-vector of exact reals
+#include "tetTriCuttingData.h"
+#include "profiler.h"
+
+/*
+  This file implements our "virtual tets" method, as described in:
+
+  Yijing Li, Jernej Barbic: Immersion of Self-Intersecting Solids and Surfaces,
+  ACM Transactions on Graphics 37(4) (SIGGRAPH 2018), Vancouver, Canada, 2018.
+
+  Creates a virtual tet mesh that embeds an intersection-free triangle mesh,
+  given an input tet mesh that covers the space occupied by the triangle mesh.
+
+  The triangle mesh is assumed to be manifold, free of self-intersections and have no degenerate triangles.
+  The method duplicates the tets and tet vertices to follow the topology of the triangle mesh.
+
+  Inputs: tetMesh, triMesh.
+
+  Outputs: the virtual tet mesh (the return value)
+           coord: barycentric coordinates embedding the input triangle mesh into the output tet mesh
+           newTetVtxID2OldTetVtxID: the mapping: new tet vtxID -> old tet vtxID
+           newTetID2OldTetID: the mapping: new tetID -> old tetID
+           tetTris: the mapping: new tetID -> triangleIDs that this tet embeds
+
+  If a pointer is provided as nullptr, then that output is not provided.
+*/
+
+// The simplest routine with minimal input.
+TetMeshGeo createVirtualTetsMesh(
+    const TetMeshGeo & tetMesh,
+    const TriMeshGeo & triMesh,
+    BarycentricCoordinates * coord = nullptr,
+    std::vector<int> * newTetVtxID2OldTetVtxID = nullptr,
+    std::vector<int> * newTetID2OldTetID = nullptr,
+    std::vector<std::vector<int>> * tetTris = nullptr,
+    bool verbose = false,
+    Profiler * profiler = nullptr);
+
+// If you have already previously computed the input triangle mesh vertex positions in exact arithmetic,
+// use this routine to avoid re-computing them.
+// exactTriPos: triangle positions in exact arithmetics
+TetMeshGeo createVirtualTetsMesh(
+    const TetMeshGeo & tetMesh,
+    const TriMeshGeo & triMesh,
+    const std::vector<Vec3ER> & exactTriPos,
+    BarycentricCoordinates * coord = nullptr,
+    std::vector<int> * newTetVtxID2OldTetVtxID = nullptr,
+    std::vector<int> * newTetID2OldTetID = nullptr,
+    std::vector<std::vector<int>> * tetTris = nullptr,
+    bool verbose = false, Profiler * profiler = nullptr);
+
+// This is an advanced routine. It is used as a subroutine by our immersion algorithm for self-intersecting inputs.
+// cuttingData: Detailed information on tet mesh and triangle mesh intersection.
+// removeAirTets: "Air tets" are tets that are completely outside the triangle mesh.
+//                When set to true, the method will find all air tets and remove them.
+//                When false, the method assumes that the input tet mesh is free of air tets.
+TetMeshGeo createVirtualTetsMesh(
+    const TetMeshGeo & tetMesh,
+    const TriMeshGeo & triMesh,
+    const TetTriCuttingData & cuttingData,
+    BarycentricCoordinates * coord = nullptr,
+    std::vector<int> * newTetVtxID2OldTetVtxID = nullptr,
+    std::vector<int> * newTetID2OldTetID = nullptr,
+    std::vector<std::vector<int>> * tetTris = nullptr,
+    bool removeAirTets = true,
+    bool verbose = false,
+    Profiler * profiler = nullptr);
+
+#endif
+
diff --git a/src/libvolumetricMesh/computeStiffnessMatrixNullspace.cpp b/libraries/volumetricMesh/computeStiffnessMatrixNullspace.cpp
similarity index 88%
rename from src/libvolumetricMesh/computeStiffnessMatrixNullspace.cpp
rename to libraries/volumetricMesh/computeStiffnessMatrixNullspace.cpp
index 930b305b158932d57266471a5dcb007821fa03b7..6fd0671cefe8c6571611a85ed8188d83fd934fd4 100644
--- a/src/libvolumetricMesh/computeStiffnessMatrixNullspace.cpp
+++ b/libraries/volumetricMesh/computeStiffnessMatrixNullspace.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,8 +40,6 @@
 #include "minivector.h"
 #include "computeStiffnessMatrixNullspace.h"
 
-namespace vega
-{
 void ComputeStiffnessMatrixNullspace::ComputeNullspace(int n, const double * vertexPos, double * basis, int includeRotationalNullspace, int generateOrthogonalBasis)
 {
   int basisSize = (includeRotationalNullspace ? 6 : 3);
@@ -119,4 +121,4 @@ void ComputeStiffnessMatrixNullspace::RemoveNullspaceComponent(int n, int nullsp
       x[j] -= dotp * nullspaceOrthonormalBasis[ELT(3*n, j, i)];
   }
 }
-}
+
diff --git a/src/libsparseSolver/ARPACKSolver.h b/libraries/volumetricMesh/computeStiffnessMatrixNullspace.h
similarity index 57%
rename from src/libsparseSolver/ARPACKSolver.h
rename to libraries/volumetricMesh/computeStiffnessMatrixNullspace.h
index ed61d375fd7fb0552b6078ae15c1b59e8e0d3d42..2b8ab860520428d31fa46913840b711c931fcf9b 100644
--- a/src/libsparseSolver/ARPACKSolver.h
+++ b/libraries/volumetricMesh/computeStiffnessMatrixNullspace.h
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,32 +35,29 @@
  *                                                                       *
  *************************************************************************/
 
-#ifndef _ARPACKSOLVER_H_
-#define _ARPACKSOLVER_H_
+#ifndef _COMPUTESTIFFNESSMATRIXNULLSPACE_H_
+#define _COMPUTESTIFFNESSMATRIXNULLSPACE_H_
 
-#include "sparseMatrix.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
 
-class ARPACKSolver
+/*
+  Computes the nullspace of the tangent stiffness matrix at the specified deformation. At the origin (u=0), stiffness matrix has nullspace of dimension 6, elsewhere, it has nullspace of dimension 3.
+
+  Use "includeRotationalNullspace" to set the desired nullspace size (3 when deformed, or 6 when undeformed).
+*/
+
+class ComputeStiffnessMatrixNullspace
 {
 public:
+  // n is the number of vertices
+  // vertexPos is a 3n array giving vertex positions
+  // (if undeformed, this will be the rest positions, otherwise the current world-coordinate deformed positions)
+  static void ComputeNullspace(int n, const double * vertexPos, double * basis, int includeRotationalNullspace, int generateOrthogonalBasis=0);
+
+  static void RemoveNullspaceComponent(int n, int nullspaceDimension, const double * nullspaceOrthonormalBasis, double *x);
 
-  // K * x = lambda * M * x
-  // returns the number of converged eigenvalues
-  // assumes that both K and M are symmetric, and that M > 0
-  // both matrices are given using the entire matrix (not just lower/upper triangle)
-  // mode is either "LM" or "SM" (with SM, must also have K > 0)
-  // uses mode 2 of ARPACK (regular generalized eigenvalue problem)
-  int SolveGenEigReg(SparseMatrix * K, SparseMatrix * M, int numEigenvalues, double * eigenvalues, double * eigenvectors, char * mode = "LM", int numLinearSolverThreads=0, int verbose=1);
-
-  // K * x = lambda * M * x
-  // solves for the smallest (in absolute sense) eigenvalues
-  // returns the number of converged eigenvalues
-  // assumes that both K and M are symmetric, and that M >= 0
-  // K can be singular
-  // uses mode 3 of ARPACK (shift-inverted generalized eigenvalue problem)
-  int SolveGenEigShInv(SparseMatrix * K, SparseMatrix * M, int numEigenvalues, double * eigenvalues, double * eigenvectors, double sigma=0.0, int numLinearSolverThreads=0, int verbose=1);
-
-protected:
 };
 
 #endif
diff --git a/src/libvolumetricMesh/cubicMesh.cpp b/libraries/volumetricMesh/cubicMesh.cpp
similarity index 75%
rename from src/libvolumetricMesh/cubicMesh.cpp
rename to libraries/volumetricMesh/cubicMesh.cpp
index 83129ff2f3f5048fa4350aac27d21c6d0ad7c1d6..1bb41bdd2d31e6d935df35feb415238826f4e3fe 100644
--- a/src/libvolumetricMesh/cubicMesh.cpp
+++ b/libraries/volumetricMesh/cubicMesh.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -35,14 +39,27 @@
 #include <algorithm>
 #include "cubicMesh.h"
 #include "matrixMacros.h"
+#include "matrixMultiplyMacros.h"
 #include "triple.h"
 using namespace std;
 
-namespace vega
-{
 const VolumetricMesh::elementType CubicMesh::elementType_ = CUBIC;
 
-CubicMesh::CubicMesh(char * filename, int verbose) : VolumetricMesh(filename, 8, verbose, &temp)
+CubicMesh::CubicMesh(const char * filename, fileFormatType fileFormat, int verbose) : VolumetricMesh(filename, fileFormat, 8, &temp, verbose), parallelepipedMode(0)
+{
+  if (temp != elementType_)
+  {
+    printf("Error: mesh is not a cubic mesh.\n");
+    throw 11;
+  } 
+
+  // set cube size
+  cubeSize = len(getVertex(0,1) - getVertex(0,0));
+  SetInverseCubeSize();
+}
+
+CubicMesh::CubicMesh(void * binaryStream, int memoryLoad) : 
+  VolumetricMesh(binaryStream, 8, &temp, memoryLoad), parallelepipedMode(0)
 {
   if (temp != elementType_)
   {
@@ -51,17 +68,21 @@ CubicMesh::CubicMesh(char * filename, int verbose) : VolumetricMesh(filename, 8,
   } 
 
   // set cube size
-  cubeSize = len(*getVertex(0,1) - *getVertex(0,0));
+  cubeSize = len(getVertex(0,1) - getVertex(0,0));
+  SetInverseCubeSize();
 }
 
 CubicMesh::CubicMesh(int numVertices, double * vertices,
                int numElements, int * elements,
-               double E, double nu, double density): VolumetricMesh(numVertices, vertices, numElements, 8, elements, E, nu, density)
+               double E, double nu, double density): 
+  VolumetricMesh(numVertices, vertices, numElements, 8, elements, E, nu, density), parallelepipedMode(0)
 {
   if (numElements > 0)
-    cubeSize = len(*getVertex(0,1) - *getVertex(0,0));
+    cubeSize = len(getVertex(0,1) - getVertex(0,0));
   else
     cubeSize = 0.0;
+
+  SetInverseCubeSize();
 }
 
 CubicMesh::CubicMesh(int numVertices, double * vertices,
@@ -69,15 +90,17 @@ CubicMesh::CubicMesh(int numVertices, double * vertices,
          int numMaterials, Material ** materials,
          int numSets, Set ** sets,
          int numRegions, Region ** regions): 
-  VolumetricMesh(numVertices, vertices, numElements, 8, elements, numMaterials, materials, numSets, sets, numRegions, regions)
+  VolumetricMesh(numVertices, vertices, numElements, 8, elements, numMaterials, materials, numSets, sets, numRegions, regions), parallelepipedMode(0)
 {
   if (numElements > 0)
-    cubeSize = len(*getVertex(0,1) - *getVertex(0,0));
+    cubeSize = len(getVertex(0,1) - getVertex(0,0));
   else
     cubeSize = 0.0;
+
+  SetInverseCubeSize();
 }
 
-CubicMesh::CubicMesh(const CubicMesh & source): VolumetricMesh(source), cubeSize(source.cubeSize) {}
+CubicMesh::CubicMesh(const CubicMesh & source): VolumetricMesh(source), cubeSize(source.cubeSize), invCubeSize(source.invCubeSize), parallelepipedMode(source.parallelepipedMode) {}
 
 VolumetricMesh * CubicMesh::clone()
 {
@@ -85,6 +108,14 @@ VolumetricMesh * CubicMesh::clone()
   return mesh;
 }
 
+void CubicMesh::SetInverseCubeSize()
+{
+  if (cubeSize > 0)
+    invCubeSize = 1.0 / cubeSize;
+  else
+    invCubeSize = 0;
+}
+
 CubicMesh * CubicMesh::createFromUniformGrid(int resolution, int numVoxels, int * voxels, double E, double nu, double density)
 {
   int numElementVertices = 8;
@@ -158,22 +189,44 @@ CubicMesh * CubicMesh::createFromUniformGrid(int resolution, int numVoxels, int
 CubicMesh::CubicMesh(const CubicMesh & cubeMesh, int numElements, int * elements, map<int,int> * vertexMap_): VolumetricMesh(cubeMesh, numElements, elements, vertexMap_)
 {
   cubeSize = cubeMesh.getCubeSize();
+  SetInverseCubeSize();
 }
 
 CubicMesh::~CubicMesh() {}
 
-int CubicMesh::save(char * filename) const
+int CubicMesh::saveToAscii(const char * filename) const
+{
+  return VolumetricMesh::saveToAscii(filename, elementType_);
+}
+
+int CubicMesh::saveToBinary(const char * filename, unsigned int * bytesWritten) const
+{
+  return VolumetricMesh::saveToBinary(filename, bytesWritten, elementType_);
+}
+
+int CubicMesh::saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten, bool countBytesOnly) const
 {
-  return VolumetricMesh::save(filename, elementType_);
+  return VolumetricMesh::saveToBinary(binaryOutputStream, bytesWritten, elementType_, countBytesOnly);
 }
 
 bool CubicMesh::containsVertex(int element, Vec3d pos) const
 {
-  Vec3d bmin = *(getVertex(element, 0));
-  Vec3d bmax = *(getVertex(element, 6));
+/*
+  AABB version
+  Vec3d bmin = (getVertex(element, 0));
+  Vec3d bmax = (getVertex(element, 6));
 
   return (pos[0] >= bmin[0]) && (pos[1] >= bmin[1]) && (pos[2] >= bmin[2]) &&
          (pos[0] <= bmax[0]) && (pos[1] <= bmax[1]) && (pos[2] <= bmax[2]);
+*/
+
+  // general, parallelepied version (supports cubes transformed by a linear transformation; although the class does not "officially" support this)
+  double alpha, beta, gamma;
+  computeAlphaBetaGamma(element, pos, &alpha, &beta, &gamma);
+  
+  return ( (0 <= alpha) && (alpha <= 1) &&
+           (0 <= beta) && (beta <= 1) &&
+           (0 <= gamma) && (gamma <= 1) );
 }
 
 // interpolates given cubic mesh vertex 3D data to the destination locations
@@ -209,10 +262,10 @@ int CubicMesh::interpolateData(double * vertexData, int numInterpolationLocation
       int assignedZero = 0;
       for(int ii=0; ii< numElementVertices; ii++)
       {
-        Vec3d * vpos = getVertex(element, ii);
-        if (len(*vpos-pos) < minDistance)
+        const Vec3d & vpos = getVertex(element, ii);
+        if (len(vpos-pos) < minDistance)
         {
-          minDistance = len(*vpos-pos);
+          minDistance = len(vpos-pos);
         }
       }
 
@@ -231,10 +284,10 @@ int CubicMesh::interpolateData(double * vertexData, int numInterpolationLocation
     }
 
     // compute barycentric coordinates
-    Vec3d w = pos - *(getVertex(element, 0));
-    double alpha = w[0] / cubeSize;
-    double beta = w[1] / cubeSize;
-    double gamma = w[2] / cubeSize;
+    Vec3d w = pos - getVertex(element, 0);
+    double alpha = w[0] * invCubeSize;
+    double beta = w[1] * invCubeSize;
+    double gamma = w[2] * invCubeSize;
 
     double f000 = (1-alpha)*(1-beta)*(1-gamma);
     double f100 = (alpha)*(1-beta)*(1-gamma);
@@ -315,10 +368,10 @@ int CubicMesh::normalCorrection(double * vertexData, int numInterpolationLocatio
       int assignedZero = 0;
       for(int ii=0; ii< numElementVertices; ii++)
       {
-        Vec3d * vpos = getVertex(element, ii);
-        if (len(*vpos-pos) < minDistance)
+        const Vec3d & vpos = getVertex(element, ii);
+        if (len(vpos-pos) < minDistance)
         {
-          minDistance = len(*vpos-pos);
+          minDistance = len(vpos-pos);
         }
       }
 
@@ -338,10 +391,10 @@ int CubicMesh::normalCorrection(double * vertexData, int numInterpolationLocatio
     }
 
     // compute barycentric coordinates
-    Vec3d w = pos - *(getVertex(element, 0));
-    double alpha = w[0] / cubeSize;
-    double beta = w[1] / cubeSize;
-    double gamma = w[2] / cubeSize;
+    Vec3d w = pos - getVertex(element, 0);
+    double alpha = w[0] * invCubeSize;
+    double beta = w[1] * invCubeSize;
+    double gamma = w[2] * invCubeSize;
 
     //double f000 = (1-alpha)*(1-beta)*(1-gamma);
     //double f100 = (alpha)*(1-beta)*(1-gamma);
@@ -362,15 +415,15 @@ int CubicMesh::normalCorrection(double * vertexData, int numInterpolationLocatio
     int v111 = getVertexIndex(element, 6);
     int v011 = getVertexIndex(element, 7);
 
-    Vec3d gradf000(1.0 / cubeSize * -(1-beta)*(1-gamma), 1.0 / cubeSize * -(1-alpha)*(1-gamma), 1.0 / cubeSize * -(1-alpha)*(1-beta));
-    Vec3d gradf100(1.0 / cubeSize * (1-beta)*(1-gamma), 1.0 / cubeSize * -alpha*(1-gamma), 1.0 / cubeSize * -alpha*(1-beta));
-    Vec3d gradf110(1.0 / cubeSize * beta*(1-gamma), 1.0 / cubeSize * alpha*(1-gamma), 1.0 / cubeSize * -alpha*beta);
-    Vec3d gradf010(1.0 / cubeSize * -beta*(1-gamma), 1.0 / cubeSize * (1-alpha)*(1-gamma), 1.0 / cubeSize * (1-alpha)*-beta);
+    Vec3d gradf000(invCubeSize * -(1-beta)*(1-gamma), invCubeSize * -(1-alpha)*(1-gamma), invCubeSize * -(1-alpha)*(1-beta));
+    Vec3d gradf100(invCubeSize * (1-beta)*(1-gamma), invCubeSize * -alpha*(1-gamma), invCubeSize * -alpha*(1-beta));
+    Vec3d gradf110(invCubeSize * beta*(1-gamma), invCubeSize * alpha*(1-gamma), invCubeSize * -alpha*beta);
+    Vec3d gradf010(invCubeSize * -beta*(1-gamma), invCubeSize * (1-alpha)*(1-gamma), invCubeSize * (1-alpha)*-beta);
 
-    Vec3d gradf001(1.0 / cubeSize * -(1-beta)*gamma, 1.0 / cubeSize * -(1-alpha)*gamma, 1.0 / cubeSize * (1-alpha)*(1-beta));
-    Vec3d gradf101(1.0 / cubeSize * (1-beta)*gamma, 1.0 / cubeSize * -alpha*gamma, 1.0 / cubeSize * alpha*(1-beta));
-    Vec3d gradf111(1.0 / cubeSize * beta*gamma, 1.0 / cubeSize * alpha*gamma, 1.0 / cubeSize * alpha*beta);
-    Vec3d gradf011(1.0 / cubeSize * -beta*gamma, 1.0 / cubeSize * (1-alpha)*gamma, 1.0 / cubeSize * (1-alpha)*beta);
+    Vec3d gradf001(invCubeSize * -(1-beta)*gamma, invCubeSize * -(1-alpha)*gamma, invCubeSize * (1-alpha)*(1-beta));
+    Vec3d gradf101(invCubeSize * (1-beta)*gamma, invCubeSize * -alpha*gamma, invCubeSize * alpha*(1-beta));
+    Vec3d gradf111(invCubeSize * beta*gamma, invCubeSize * alpha*gamma, invCubeSize * alpha*beta);
+    Vec3d gradf011(invCubeSize * -beta*gamma, invCubeSize * (1-alpha)*gamma, invCubeSize * (1-alpha)*beta);
 
     Vec3d normal = Vec3d(staticNormals[3*i+0], staticNormals[3*i+1], staticNormals[3*i+2]);
 
@@ -410,10 +463,10 @@ int CubicMesh::normalCorrection(double * vertexData, int numInterpolationLocatio
 void CubicMesh::interpolateGradient(int element, const double * U, int numFields, Vec3d pos, double * grad) const
 {
   // compute barycentric coordinates
-  Vec3d w = pos - *(getVertex(element, 0));
-  double alpha = w[0] / cubeSize;
-  double beta = w[1] / cubeSize;
-  double gamma = w[2] / cubeSize;
+  Vec3d w = pos - getVertex(element, 0);
+  double alpha = w[0] * invCubeSize;
+  double beta = w[1] * invCubeSize;
+  double gamma = w[2] * invCubeSize;
 
   //double f000 = (1-alpha)*(1-beta)*(1-gamma);
   //double f100 = (alpha)*(1-beta)*(1-gamma);
@@ -434,15 +487,15 @@ void CubicMesh::interpolateGradient(int element, const double * U, int numFields
   int v111 = getVertexIndex(element, 6);
   int v011 = getVertexIndex(element, 7);
 
-  Vec3d gradf000(1.0 / cubeSize * -(1-beta)*(1-gamma), 1.0 / cubeSize * -(1-alpha)*(1-gamma), 1.0 / cubeSize * -(1-alpha)*(1-beta));
-  Vec3d gradf100(1.0 / cubeSize * (1-beta)*(1-gamma), 1.0 / cubeSize * -alpha*(1-gamma), 1.0 / cubeSize * -alpha*(1-beta));
-  Vec3d gradf110(1.0 / cubeSize * beta*(1-gamma), 1.0 / cubeSize * alpha*(1-gamma), 1.0 / cubeSize * -alpha*beta);
-  Vec3d gradf010(1.0 / cubeSize * -beta*(1-gamma), 1.0 / cubeSize * (1-alpha)*(1-gamma), 1.0 / cubeSize * (1-alpha)*-beta);
+  Vec3d gradf000(invCubeSize * -(1-beta)*(1-gamma), invCubeSize * -(1-alpha)*(1-gamma), invCubeSize * -(1-alpha)*(1-beta));
+  Vec3d gradf100(invCubeSize * (1-beta)*(1-gamma), invCubeSize * -alpha*(1-gamma), invCubeSize * -alpha*(1-beta));
+  Vec3d gradf110(invCubeSize * beta*(1-gamma), invCubeSize * alpha*(1-gamma), invCubeSize * -alpha*beta);
+  Vec3d gradf010(invCubeSize * -beta*(1-gamma), invCubeSize * (1-alpha)*(1-gamma), invCubeSize * (1-alpha)*-beta);
 
-  Vec3d gradf001(1.0 / cubeSize * -(1-beta)*gamma, 1.0 / cubeSize * -(1-alpha)*gamma, 1.0 / cubeSize * (1-alpha)*(1-beta));
-  Vec3d gradf101(1.0 / cubeSize * (1-beta)*gamma, 1.0 / cubeSize * -alpha*gamma, 1.0 / cubeSize * alpha*(1-beta));
-  Vec3d gradf111(1.0 / cubeSize * beta*gamma, 1.0 / cubeSize * alpha*gamma, 1.0 / cubeSize * alpha*beta);
-  Vec3d gradf011(1.0 / cubeSize * -beta*gamma, 1.0 / cubeSize * (1-alpha)*gamma, 1.0 / cubeSize * (1-alpha)*beta);
+  Vec3d gradf001(invCubeSize * -(1-beta)*gamma, invCubeSize * -(1-alpha)*gamma, invCubeSize * (1-alpha)*(1-beta));
+  Vec3d gradf101(invCubeSize * (1-beta)*gamma, invCubeSize * -alpha*gamma, invCubeSize * alpha*(1-beta));
+  Vec3d gradf111(invCubeSize * beta*gamma, invCubeSize * alpha*gamma, invCubeSize * alpha*beta);
+  Vec3d gradf011(invCubeSize * -beta*gamma, invCubeSize * (1-alpha)*gamma, invCubeSize * (1-alpha)*beta);
 
   for(int j=0; j<numFields; j++)
   {
@@ -475,13 +528,65 @@ void CubicMesh::interpolateGradient(int element, const double * U, int numFields
   }
 }
 
-void CubicMesh::computeBarycentricWeights(int el, Vec3d pos, double * weights) const
+void CubicMesh::computeAlphaBetaGamma(int el, Vec3d pos, double * alpha, double * beta, double * gamma) const
+{
+  if (parallelepipedMode)
+  {
+    // general, parallelepied version (supports rotated cubes, and even cubes deformed via linear transformations; although the class "officially" only supports axis-aligned cubes)
+    const Vec3d & v0 = getVertex(el, 0);
+    const Vec3d & v1 = getVertex(el, 1);
+    const Vec3d & v3 = getVertex(el, 3);
+    const Vec3d & v4 = getVertex(el, 4);
+
+    Vec3d axis0 = v1 - v0;
+    Vec3d axis1 = v3 - v0;
+    Vec3d axis2 = v4 - v0;
+    Vec3d p = pos - v0;
+    Vec3d v;
+  
+    // OBB code
+    //double invCubeSize2 = invCubeSize * invCubeSize;
+    //*alpha = invCubeSize2 * dot(p, axis0);
+    //*beta  = invCubeSize2 * dot(p, axis1);
+    //*gamma = invCubeSize2 * dot(p, axis2);
+  
+    // parallelepiped code
+    double A[9], invA[9];
+    A[0] = axis0[0];
+    A[1] = axis1[0];
+    A[2] = axis2[0];
+  
+    A[3] = axis0[1];
+    A[4] = axis1[1];
+    A[5] = axis2[1];
+  
+    A[6] = axis0[2];
+    A[7] = axis1[2];
+    A[8] = axis2[2];
+  
+    inverse3x3(A, invA);
+    MATRIX_VECTOR_MULTIPLY3X3(invA, p, v);
+  
+    *alpha = v[0];
+    *beta = v[1];
+    *gamma = v[2];
+  }
+  else
+  {
+    // AABB mode
+    const Vec3d & v0 = getVertex(el, 0);
+    Vec3d p = pos - v0;
+    *alpha = p[0] * invCubeSize;
+    *beta = p[1] * invCubeSize;
+    *gamma = p[2] * invCubeSize;
+  }
+}
+
+void CubicMesh::computeBarycentricWeights(int el, const Vec3d & pos, double * weights) const
 {
   // compute barycentric coordinates
-  Vec3d w = pos - *(getVertex(el, 0));
-  double alpha = w[0] / cubeSize;
-  double beta = w[1] / cubeSize;
-  double gamma = w[2] / cubeSize;
+  double alpha, beta, gamma;
+  computeAlphaBetaGamma(el, pos, &alpha, &beta, &gamma);
 
   weights[0] = (1-alpha)*(1-beta)*(1-gamma); // f000
   weights[1] = (alpha)*(1-beta)*(1-gamma); // f100
@@ -588,7 +693,7 @@ void CubicMesh::subdivide()
   vector<Vec3d> newVertices;
   for(int el=0; el<numElements; el++)
   {
-    Vec3d * v0 = getVertex(el, 0);   
+    const Vec3d & v0 = getVertex(el, 0);   
 
     // create the 8 children cubes
     for(int child=0; child<8; child++)
@@ -598,7 +703,7 @@ void CubicMesh::subdivide()
       int childVtx[8];
       for(int vtx=0; vtx<8; vtx++)
       {
-        Vec3d pos = (*v0) + 0.5 * cubeSize * Vec3d(mask[child][vtx][0], mask[child][vtx][1], mask[child][vtx][2]);
+        Vec3d pos = v0 + 0.5 * cubeSize * Vec3d(mask[child][vtx][0], mask[child][vtx][1], mask[child][vtx][2]);
         //printf("%G %G %G\n", pos[0], pos[1], pos[2]);
         // search for vertex
         int found = -1;
@@ -627,15 +732,12 @@ void CubicMesh::subdivide()
   cubeSize *= 0.5;
 
   // deallocate old vertices
-  for(int i=0; i<numVertices; i++)
-    delete(vertices[i]);
-  free(vertices);
+  delete [] vertices;
 
   // copy new vertices into place
   numVertices = (int)newVertices.size();
-  vertices = (Vec3d**) malloc (sizeof(Vec3d*) * numVertices);
-  for(int i=0; i<numVertices; i++)
-    vertices[i] = new Vec3d(newVertices[i]);
+  vertices = new Vec3d [numVertices];
+  memcpy(vertices, newVertices.data(), sizeof(Vec3d) * numVertices);
 
   // deallocate old elements
   for(int i=0; i<numElements; i++)
@@ -662,6 +764,17 @@ void CubicMesh::subdivide()
     sets[setIndex] = newSet;
   }
 
-  PropagateRegionsToElements();
+  if(numElements > 0)
+  {
+    elementMaterial = (int *) realloc(elementMaterial, sizeof(int) * numElements);
+    memset(elementMaterial, 0, sizeof(int) * numElements);
+  }
+
+  propagateRegionsToElements();
 }
+
+void CubicMesh::setParallelepipedMode(int parallelepipedMode_)
+{
+  parallelepipedMode = parallelepipedMode_;
 }
+
diff --git a/libraries/volumetricMesh/cubicMesh.h b/libraries/volumetricMesh/cubicMesh.h
new file mode 100644
index 0000000000000000000000000000000000000000..06f97ca262df0207051a6b63fa415fc60df6605b
--- /dev/null
+++ b/libraries/volumetricMesh/cubicMesh.h
@@ -0,0 +1,173 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+
+  This class is a container for a cubic ("voxel") volumetric 3D mesh. 
+  See also volumetricMesh.h. 
+
+  All cubes are of equal size, aligned with coordinate system axes, and 
+  follow a grid pattern. They are typically obtained by voxelizing a 
+  given input triangle geometry.
+
+  To generate a CubicMesh from an input triangle mesh (optionally flood-filling 
+  interior chambers), you can use the "Large Modal Deformation Factory" 
+  application ( http://www.jernejbarbic.com/vega ) .
+
+*/
+
+// vtx order in CUBIC:
+//
+//     3 - - - 2
+//    /|      /|
+//   7 - - - 6 |       y
+//   | |     | |       |
+//   | 0 - - | 1       |_ _ _x
+//   |/      |/       /
+//   4 - - - 5       z
+
+#ifndef _CUBICMESH_H_
+#define _CUBICMESH_H_
+
+#include "volumetricMesh.h"
+// see also volumetricMesh.h for a description of the routines
+
+class CubicMesh : public VolumetricMesh
+{
+public:
+  // loads the mesh from a file 
+  // ASCII: .veg text input formut, see documentation and the provided examples
+  // BINARY: .vegb binary input format
+  CubicMesh(const char * filename, fileFormatType fileFormat = ASCII, int verbose=1); 
+
+  // load from a stream
+  // if memoryLoad is 0, binaryStream is FILE* (load from a file), otherwise, it is char* (load from a memory buffer)
+  CubicMesh(void * binaryStream, int memoryLoad = 0);
+
+  // constructs a mesh from the given vertices and elements, with a single region and material
+  // "vertices" is double-precision array of length 3 x numVertices 
+  // "elements" is an integer array of length 8 x numElements 
+  CubicMesh(int numVertices, double * vertices,
+            int numElements, int * elements,
+            double E=1E6, double nu=0.45, double density=1000);
+
+  // constructs a mesh from the given vertices and elements, 
+  // with an arbitrary number of sets, regions and materials
+  // "vertices" is double-precision array of length 3 x numVertices 
+  // "elements" is an integer array of length 8 x numElements
+  // "materials", "sets" and "regions" will be copied internally (deep copy), so they
+  // can be released after calling this constructor
+  CubicMesh(int numVertices, double * vertices,
+            int numElements, int * elements,
+            int numMaterials, Material ** materials,
+            int numSets, Set ** sets,
+            int numRegions, Region ** regions);
+
+  // constructs a voxel mesh with the given voxels, as a subset of a regular 3D grid
+  // 'voxels' gives the grid indices (3 per voxel) of the voxels that are to be included in the mesh
+  // 'voxels' has length 3 x numVoxels
+  static CubicMesh * createFromUniformGrid(int resolution, int numVoxels, int * voxels, double E=1E6, double nu=0.45, double density=1000);
+
+  // creates a mesh consisting of the specified element subset of the given CubicMesh
+  CubicMesh(const CubicMesh & mesh, int numElements, int * elements, std::map<int,int> * vertexMap = NULL);
+
+  CubicMesh (const CubicMesh & CubicMesh);
+  virtual VolumetricMesh * clone();
+  virtual ~CubicMesh();
+
+  // saves the mesh to a text file (.veg format, see examples and documentation)
+  virtual int saveToAscii(const char * filename) const;
+
+  // saves the mesh to binary format
+  // returns: 0 = success, non-zero = error
+  // output: if bytesWritten is non-NULL, it will contain the number of bytes written 
+  virtual int saveToBinary(const char * filename, unsigned int * bytesWritten = NULL) const;
+  virtual int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten = NULL, bool countBytesOnly = false) const;
+
+  // === misc queries ===
+
+  static VolumetricMesh::elementType elementType() { return elementType_; }
+  virtual VolumetricMesh::elementType getElementType() const { return elementType(); }
+
+  inline double getCubeSize() const { return cubeSize; }
+
+  virtual double getElementVolume(int el) const;
+  virtual void computeElementMassMatrix(int el, double * massMatrix) const;
+  virtual void getElementInertiaTensor(int el, Mat3d & inertiaTensor) const;
+
+  virtual bool containsVertex(int element, Vec3d pos) const; // true if given element contain given position, false otherwise
+
+  // edge queries
+  virtual int getNumElementEdges() const; 
+  virtual void getElementEdges(int el, int * edgeBuffer) const;
+
+  // subdivides the cube mesh
+  void subdivide();
+
+  // === interpolation ===
+
+  virtual void computeBarycentricWeights(int el, const Vec3d & pos, double * weights) const;
+
+  int interpolateData(double * volumetricMeshVertexData, int numLocations, int r, double * interpolationLocations, double * destMatrix, double zeroThreshold = -1.0) const;
+
+  // computes approximation to the normal correction for the given deformations
+  // vertexData size must a matrix of size 3 * nElements x r
+  // destMatrix must be a vector of length 3 * numLocations x r
+  // staticNormals must be a vector of length 3 * numLocations
+  // returns the number of vertices that were not contained inside any element
+  // vertices more than distanceThreshold away from any element vertex are assigned zero data
+  int normalCorrection(double * vertexData, int numLocations, int r, double * interpolationLocations, double * staticNormals, double * normalCorrection, double zeroThreshold = -1.0) const; // note: this routine could be promoted to volumetricMesh.h
+
+  virtual void interpolateGradient(int element, const double * U, int numFields, Vec3d pos, double * grad) const;
+
+  // advanced, to ensure computeBarycentricWeights, containsVertex, generateInterpolationWeights, generateContainingElements work even when elements are cubes, transformed via a general linear transformation
+  // parallelepiped=1 : the elements are cubes transformed via a linear transformation (i.e., they are parallelepipeds)
+  // parallelepiped=0 : (default) the elements are axis-aligned cubes 
+  void setParallelepipedMode(int parallelepipedMode);
+
+protected:
+  double cubeSize;
+  double invCubeSize;
+  static const VolumetricMesh::elementType elementType_;
+  CubicMesh(int numElementVertices): VolumetricMesh(numElementVertices) {}
+  void SetInverseCubeSize();
+  int parallelepipedMode; // normally this is 0; in advanced usage, it can be 1 (see above)
+
+  // computes the normalized location of "pos" inside el
+  // when inside the element, one has 0 <= alpha <= 1, 0 <= beta <= 1, 0 <= gamma <= 1
+  void computeAlphaBetaGamma(int el, Vec3d pos, double * alpha, double * beta, double * gamma) const;
+
+  friend class VolumetricMeshExtensions;
+};
+
+#endif
+
diff --git a/libraries/volumetricMesh/generateGradientMatrix.cpp b/libraries/volumetricMesh/generateGradientMatrix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e36341b5553813e7f285b4d7efaefdc83ddec85e
--- /dev/null
+++ b/libraries/volumetricMesh/generateGradientMatrix.cpp
@@ -0,0 +1,221 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <string.h>
+#include "matrixMacros.h"
+#include "generateGradientMatrix.h"
+
+void GenerateGradientMatrix::GenerateElementMatrixEntries(const TetMesh * tetMesh, int element, double * m)
+{
+  // grad is constant inside a tet
+  Vec3d vtx[4];
+  for(int i=0; i<4; i++)
+    vtx[i] = tetMesh->getVertex(element,i);
+
+  // form M =
+  // [b - a]
+  // [c - a]
+  // [d - a]
+
+  Mat3d M(vtx[1] - vtx[0], vtx[2] - vtx[0], vtx[3] - vtx[0]);
+  Mat3d MInvT = trans(inv(M));
+  //printf("MInvT=\n");
+  //MInvT.print();
+
+  for(int vtx=0; vtx<3; vtx++)
+//    for(int dof=0; dof<3; dof++)
+      for(int j=0; j<3; j++)
+        m[3 * (vtx + 1) + j] = MInvT[vtx][j];
+        //G[ELT(9, 3 * dof + j, vtx + 1)] = MInvT[vtx][j];
+
+  //for(int dof=0; dof<3; dof++)
+    for(int j=0; j<3; j++)
+    {
+      double entry = 0.0;
+      for(int k=0; k<3; k++)
+        entry -= MInvT[k][j];
+      //G[ELT(9, 3 * dof + j, 0)] = entry;
+      m[j] = entry;
+    }
+}
+
+void GenerateGradientMatrix::GenerateElementMatrix(const TetMesh * tetMesh, int element, double * G)
+{
+  double m[12];
+  GenerateElementMatrixEntries(tetMesh, element, m);
+
+  // G is 9 x 12:
+  //        [ m0       m1       m2       m3       ]
+  //  G =   [    m0       m1       m2       m3    ]
+  //        [       m0       m1       m2       m3 ]
+  //
+  // where mi are 3-vectors
+
+  memset(G, 0, sizeof(double) * 9 * 12);
+
+  for(int vtx=0; vtx<4; vtx++)
+    for(int dof=0; dof<3; dof++)
+      for(int j=0; j<3; j++)
+        G[ELT(9, 3 * dof + j, 3 * vtx + dof)] = m[3 * vtx + j]; 
+}
+
+void GenerateGradientMatrix::Generate(const TetMesh * tetMesh, SparseMatrix ** G, SparseMatrix ** GTG, double * GTGElementWeights)
+{
+  // matrix is 9 #tets x 3 #vertices
+
+  SparseMatrixOutline outline(9*tetMesh->getNumElements());
+  SparseMatrixOutline outlineGTG(3*tetMesh->getNumVertices());
+
+  for(int element=0; element<tetMesh->getNumElements(); element++)
+  {
+    if (element % 100 == 0)
+    {
+      printf("%d ", element);
+      fflush(NULL);
+    }
+
+    double m[12];
+    GenerateElementMatrixEntries(tetMesh, element, m);
+
+    //double dFduPacked[36]; // 9 x 12, only diagonal 3x1 blocks are non-zero
+    //GenerateElementMatrix(tetMesh, element, dFduPacked);
+
+    //// write dFduPacked in place
+    // write m in place
+    for(int vtx=0; vtx<4; vtx++)
+      for(int dof=0; dof<3; dof++)
+        for(int j=0; j<3; j++)
+        {
+          int row = 9 * element + 3 * dof + j;
+          int column = 3 * tetMesh->getVertexIndex(element, vtx) + dof;
+          //double entry = dFduPacked[ELT(9, 3 * dof + j, vtx)];
+          double entry = m[3 * vtx + j];
+          outline.AddEntry(row, column, entry);
+        }
+
+    if (GTG != NULL)
+    {
+      double factor = 1.0;
+      if (GTGElementWeights != NULL)
+        factor = GTGElementWeights[element] * GTGElementWeights[element];
+
+      for(int vtxA=0; vtxA<4; vtxA++)
+        for(int vtxB=0; vtxB<4; vtxB++)
+          for(int i=0; i<3; i++)
+          {
+            int row = 3 * tetMesh->getVertexIndex(element, vtxA) + i;
+            int column = 3 * tetMesh->getVertexIndex(element, vtxB) + i;
+            double entry = 0.0;
+            for(int k=0; k<3; k++)
+              //entry += dFduPacked[ELT(9, 3 * i + k, vtxA)] * dFduPacked[ELT(9, 3 * i + k, vtxB)];
+              entry += m[3 * vtxA + k] * m[3 * vtxB + k];  
+            entry *= factor;
+            outlineGTG.AddEntry(row, column, entry);
+          }
+    }
+  }
+
+  *G = new SparseMatrix(&outline);
+
+  if (GTG != NULL)
+    *GTG = new SparseMatrix(&outlineGTG);
+}
+
+/*
+    // old version
+    for(int vtx=0; vtx<3; vtx++)
+    {
+      for(int dof=0; dof<3; dof++)
+      {
+        for(int j=0; j<3; j++)
+        {
+          int row = 9*element + 3*dof + j;
+          int column = 3*tetMesh->getVertexIndex(element, vtx+1) + dof;
+          outline.AddEntry(row, column, MInvT[vtx][j]);
+        }
+      }
+    }
+
+    for(int dof=0; dof<3; dof++)
+    {
+      for(int j=0; j<3; j++)
+      {
+        int row = 9*element + 3*dof + j;
+        int column = 3*tetMesh->getVertexIndex(element, 0) + dof;
+        double entry = 0.0;
+        for(int k=0; k<3; k++)
+          entry -= MInvT[k][j];
+        outline.AddEntry(row, column, entry);
+      }
+    }
+*/
+
+void GenerateGradientMatrix::GenerateForScalarField(const TetMesh * tetMesh, SparseMatrix ** GTG, double * GTGElementWeights)
+{
+  SparseMatrixOutline outlineGTG(tetMesh->getNumVertices());
+
+  for(int element=0; element<tetMesh->getNumElements(); element++)
+  {
+    if (element % 100 == 0)
+    {
+      printf("%d ", element);
+      fflush(NULL);
+    }
+
+    double m[12];
+    GenerateElementMatrixEntries(tetMesh, element, m);
+
+    if (GTG != NULL)
+    {
+      double factor = 1.0;
+      if (GTGElementWeights != NULL)
+        factor = GTGElementWeights[element] * GTGElementWeights[element];
+
+      for(int vtxA=0; vtxA<4; vtxA++)
+        for(int vtxB=0; vtxB<4; vtxB++)
+          {
+            int row = tetMesh->getVertexIndex(element, vtxA);
+            int column = tetMesh->getVertexIndex(element, vtxB);
+            double entry = 0.0;
+            for(int k=0; k<3; k++)
+              //entry += dFduPacked[ELT(9, 3 * i + k, vtxA)] * dFduPacked[ELT(9, 3 * i + k, vtxB)];
+              entry += m[3 * vtxA + k] * m[3 * vtxB + k];
+            entry *= factor;
+            outlineGTG.AddEntry(row, column, entry);
+          }
+    }
+  }
+
+  if (GTG != NULL)
+    *GTG = new SparseMatrix(&outlineGTG);
+}
+
diff --git a/libraries/volumetricMesh/generateGradientMatrix.h b/libraries/volumetricMesh/generateGradientMatrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..cd9203e57d2510d52fd4626cacece2f2edcf49c6
--- /dev/null
+++ b/libraries/volumetricMesh/generateGradientMatrix.h
@@ -0,0 +1,61 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _GENERATEGRADIENTMATRIX_H_
+#define _GENERATEGRADIENTMATRIX_H_
+
+#include "sparseMatrix.h"
+#include "tetMesh.h"
+
+// creates the sparse matrix G that computes the gradient
+// of a 3D vector field on a tet mesh
+// optionally, computes G^T G as well
+
+class GenerateGradientMatrix
+{
+public:
+
+  // for a 3D vector field on a tet mesh
+  // weights serve as GTw^2G
+  static void Generate(const TetMesh * tetMesh, SparseMatrix ** G, SparseMatrix ** GTG = NULL, double * GTGElementWeights = NULL); // of the entire mesh 
+
+  static void GenerateElementMatrix(const TetMesh * tetMesh, int el, double * G); // of a single element; G is 9 x 12
+
+  // the version that operates on scalar fields
+  static void GenerateForScalarField(const TetMesh * tetMesh, SparseMatrix ** GTG, double * GTGElementWeights); // of the entire mesh
+
+protected:
+  static void GenerateElementMatrixEntries(const TetMesh * tetMesh, int el, double * m); // of a single element, condensed array "m" of 12 doubles
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/generateInterpolationMatrix.cpp b/libraries/volumetricMesh/generateInterpolationMatrix.cpp
similarity index 77%
rename from src/libvolumetricMesh/generateInterpolationMatrix.cpp
rename to libraries/volumetricMesh/generateInterpolationMatrix.cpp
index 6d6aaa67c02c209f45f75b6a7c8d646be259ab4f..682f473997ad5330b6986200a9fa5ef93835279e 100644
--- a/src/libvolumetricMesh/generateInterpolationMatrix.cpp
+++ b/libraries/volumetricMesh/generateInterpolationMatrix.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,9 +32,7 @@
 
 #include "generateInterpolationMatrix.h"
 
-namespace vega
-{
-void GenerateInterpolationMatrix::generate(int numTargetLocations, int numElementVertices, int * vertices, double * weights, SparseMatrix ** A, int numSourceVertices)
+void GenerateInterpolationMatrix::generate(int numTargetLocations, int numElementVertices, const int * vertices, const double * weights, SparseMatrix ** A, int numSourceVertices)
 {
   SparseMatrixOutline outline(3*numTargetLocations);
 
@@ -50,4 +52,3 @@ void GenerateInterpolationMatrix::generate(int numTargetLocations, int numElemen
   *A = new SparseMatrix(&outline);
 }
 
-}
diff --git a/libraries/volumetricMesh/generateInterpolationMatrix.h b/libraries/volumetricMesh/generateInterpolationMatrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..39a9a3c7a9c65fa3523ca59e470ba59816ea4e86
--- /dev/null
+++ b/libraries/volumetricMesh/generateInterpolationMatrix.h
@@ -0,0 +1,56 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _GENERATEINTERPOLATIONMATRIX_H_
+#define _GENERATEINTERPOLATIONMATRIX_H_
+
+#include "sparseMatrix.h"
+
+// creates the sparse matrix A that interpolates a quantity from volumetric 
+// mesh vertices to an embedded triangle mesh:
+// y = A * x
+// y ... triangle mesh quantity
+// x ... volumetric mesh quantity
+
+class GenerateInterpolationMatrix
+{
+public:
+  // If numSourceLocations is >= 0, matrix A will be right-padded with 
+  // appropriate zero columns. Typically, numSourceLocations should be the 
+  // number of volumetric mesh vertices.
+  // numTargetLocations, numElementVertices, vertices, weights, can be 
+  // generated using the interpolation capabilities of the VolumetricMesh class
+  static void generate(int numTargetLocations, int numElementVertices, const int * vertices, const double * weights, SparseMatrix ** A, int numSourceLocations=-1); 
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/generateMassMatrix.cpp b/libraries/volumetricMesh/generateMassMatrix.cpp
similarity index 82%
rename from src/libvolumetricMesh/generateMassMatrix.cpp
rename to libraries/volumetricMesh/generateMassMatrix.cpp
index db66e9db734fd6eec579109bd1027a1923abd344..94d38bbe0d2b7cfe9a8274fd0786c3b4a7fff9fb 100644
--- a/src/libvolumetricMesh/generateMassMatrix.cpp
+++ b/libraries/volumetricMesh/generateMassMatrix.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,10 +32,8 @@
 
 #include "generateMassMatrix.h"
 
-namespace vega
-{
 void GenerateMassMatrix::computeMassMatrix(
-  VolumetricMesh * volumetricMesh, SparseMatrix ** massMatrix, bool inflate3Dim)
+  const VolumetricMesh * volumetricMesh, SparseMatrix ** massMatrix, bool inflate3Dim)
 {
   int n = volumetricMesh->getNumVertices();
   int numElementVertices = volumetricMesh->getNumElementVertices();
@@ -76,13 +78,11 @@ void GenerateMassMatrix::computeMassMatrix(
   free(buffer);
 }
 
-void GenerateMassMatrix::computeVertexMasses(VolumetricMesh * volumetricMesh, double * masses)
+void GenerateMassMatrix::computeVertexMasses(const VolumetricMesh * volumetricMesh, double * masses, bool inflate3Dim)
 {
   SparseMatrix * massMatrix;
-  bool inflate3Dim = false;
   computeMassMatrix(volumetricMesh, &massMatrix, inflate3Dim);
   massMatrix->SumRowEntries(masses);
   delete(massMatrix);
 }
 
-}
diff --git a/libraries/volumetricMesh/generateMassMatrix.h b/libraries/volumetricMesh/generateMassMatrix.h
new file mode 100644
index 0000000000000000000000000000000000000000..86ff259007c89de019dbfa0c324947dadfef6c57
--- /dev/null
+++ b/libraries/volumetricMesh/generateMassMatrix.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Computes the mass matrix for the given volumetric mesh.
+  See also volumetricMesh.h .
+*/
+
+#ifndef _GENERATEMASSMATRIX_H_
+#define _GENERATEMASSMATRIX_H_
+
+#include "volumetricMesh.h"
+#include "sparseMatrix.h"
+
+class GenerateMassMatrix
+{
+public:
+
+  // If inflate3Dim flag is on, each matrix element z will be augmented 
+  // to a 3x3 z*I matrix (causing mtx dimensions to grow by a factor of 3).
+  // In order to get the true 3D mass matrix, set inflate3Dim to true (output 
+  // matrix will be 3*numVertices x 3*numVertices).
+  // In order to save some space, set it to false (output matrix will be 
+  // numVertices x numVertices).
+  static void computeMassMatrix(const VolumetricMesh * volumetricMesh, SparseMatrix ** massMatrix, bool inflate3Dim = false);
+  // computes the mass belonging to each vertex, by lumping the mass matrix
+  // masses has size of volumetricMesh->getNumVertices() if (inflat3Dim == false) or 3 * n if (inflate3Dim == true)
+  static void computeVertexMasses(const VolumetricMesh * volumetricMesh, double * masses, bool inflate3Dim = false);
+
+protected:
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/generateMeshGraph.cpp b/libraries/volumetricMesh/generateMeshGraph.cpp
similarity index 82%
rename from src/libvolumetricMesh/generateMeshGraph.cpp
rename to libraries/volumetricMesh/generateMeshGraph.cpp
index 03891a8efb44b9f2e01b102043208072a30d526b..c45e6ef1c8e2203965f645b5dd318de6e8649d77 100644
--- a/src/libvolumetricMesh/generateMeshGraph.cpp
+++ b/libraries/volumetricMesh/generateMeshGraph.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -30,9 +34,7 @@
 using namespace std;
 #include "generateMeshGraph.h"
 
-namespace vega
-{
-Graph * GenerateMeshGraph::Generate(VolumetricMesh * volumetricMesh)
+Graph * GenerateMeshGraph::Generate(const VolumetricMesh * volumetricMesh)
 {
   // Generate springs:
   typedef pair<int,int> edge;
@@ -74,4 +76,3 @@ Graph * GenerateMeshGraph::Generate(VolumetricMesh * volumetricMesh)
   return graph;
 }
 
-}
diff --git a/src/libvolumetricMesh/volumetricMeshLoader.h b/libraries/volumetricMesh/generateMeshGraph.h
old mode 100755
new mode 100644
similarity index 66%
rename from src/libvolumetricMesh/volumetricMeshLoader.h
rename to libraries/volumetricMesh/generateMeshGraph.h
index a10bb33df57a779aa8582e9bb9203dd9e3a0d807..525d8741a5de9169ba2982aac857ac50fa58b3fb
--- a/src/libvolumetricMesh/volumetricMeshLoader.h
+++ b/libraries/volumetricMesh/generateMeshGraph.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,23 +30,23 @@
  *                                                                       *
  *************************************************************************/
 
-/*
-  Loads a volumetric mesh from a text file. It automatically determines 
-  the type of the mesh (tet mesh, cube mesh).
-*/
-
-#ifndef _VOLUMETRICMESHLOADER_H_
-#define _VOLUMETRICMESHLOADER_H_
+#ifndef _GENERATEMESHGRAPH_H_
+#define _GENERATEMESHGRAPH_H_
 
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
 #include "volumetricMesh.h"
+#include "graph.h"
 
-namespace vega
-{
-class VolumetricMeshLoader
+// generates a graph of the vertices of a volumetric mesh
+// two vertices are connected if they share an edge
+
+class GenerateMeshGraph
 {
 public:
-  static VolumetricMesh * load(char * filename, int verbose=1);
+  static Graph * Generate(const VolumetricMesh * volumetricMesh);
 };
-}
+
 #endif
 
diff --git a/libraries/volumetricMesh/generateSurfaceMesh.cpp b/libraries/volumetricMesh/generateSurfaceMesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b2136850a9f78149646b347728de8cb0dd9d2e40
--- /dev/null
+++ b/libraries/volumetricMesh/generateSurfaceMesh.cpp
@@ -0,0 +1,263 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <float.h>
+#include "objMesh.h"
+#include "generateSurfaceMesh.h"
+#include "cubicMesh.h"
+#include "triKey.h"
+#include "rectKey.h"
+using namespace std;
+
+// the main routine
+ObjMesh * GenerateSurfaceMesh::ComputeMesh(const VolumetricMesh * mesh, bool triangulate, bool allElementFaces)
+{
+  int numElementVertices = mesh->getNumElementVertices();
+  int faceDegree = 0;
+
+  if (numElementVertices == 4)
+  {
+    faceDegree = 3;
+    triangulate = false;
+  }
+
+  if (numElementVertices == 8)
+    faceDegree = 4;
+
+  if (faceDegree == 0)
+  {
+    printf("Error: unsupported mesh type encountered.\n");
+    return nullptr;
+  }  
+
+  // create an empty surface mesh
+  ObjMesh * objMesh = new ObjMesh;
+  objMesh->addGroup(ObjMesh::Group());
+
+  // add all vertices
+  for(int i=0; i<mesh->getNumVertices(); i++)
+    objMesh->addVertexPosition(mesh->getVertex(i));
+
+  // build unique list of all surface faces
+
+  if (numElementVertices == 4) // tet mesh
+  {
+    map<OTriKey,int> surfaceFaces;
+    for (int i=0; i<mesh->getNumElements(); i++)
+    {
+      // compute determinant to establish orientation
+      double det = dot(mesh->getVertex(i, 1) - mesh->getVertex(i, 0), cross(mesh->getVertex(i, 2) - mesh->getVertex(i, 0), mesh->getVertex(i, 3) - mesh->getVertex(i, 0)));
+
+      auto processFace = [&](int q0, int q1, int q2)
+      {
+        OTriKey key(mesh->getVertexIndex(i,q0),mesh->getVertexIndex(i,q1),mesh->getVertexIndex(i,q2));
+        if (allElementFaces) // get all faces
+        {
+          objMesh->addFaceToGroup(ObjMesh::Face(key[0], key[1], key[2]), 0);
+          return;
+        }
+        auto it = surfaceFaces.find(key);
+        if (it != surfaceFaces.end())
+          it->second++;
+        else 
+        {
+          auto revKey = key.getReversedTriKey();
+          it = surfaceFaces.find(revKey);  
+          if (it != surfaceFaces.end()) 
+            it->second--;
+          else
+            surfaceFaces.emplace(key, 1);
+        }
+      };
+  
+      if (det >= 0)
+      {
+        processFace(1,2,3);
+        processFace(2,0,3);
+        processFace(3,0,1);
+        processFace(1,0,2);
+      }
+      else
+      {
+        processFace(3,2,1);
+        processFace(3,0,2);
+        processFace(1,0,3);
+        processFace(2,0,1);
+      }
+    }
+
+    if (allElementFaces == false) // we build surface mesh
+    {
+      for(const auto & p : surfaceFaces)
+      {
+        if (p.second == 0) continue; // inner face
+        auto key = p.first;
+        int numFaces = abs(p.second);
+        if (p.second < 0) key.reverse();
+        for(int i = 0; i < numFaces; i++)
+        {
+          objMesh->addFaceToGroup(ObjMesh::Face(key[0], key[1], key[2]), 0);
+        }
+      }
+    }
+  }
+  else if (numElementVertices == 8) // cubic mesh
+  {
+    map<ORectKey,int> surfaceFaces;
+    for (int i = 0; i < mesh->getNumElements(); i++)
+    {
+      auto processFace = [&](int q0, int q1, int q2, int q3)
+      {
+        ORectKey key(mesh->getVertexIndex(i,q0),mesh->getVertexIndex(i,q1),mesh->getVertexIndex(i,q2),mesh->getVertexIndex(i,q3));
+        if (allElementFaces)
+        {
+          objMesh->addFaceToGroup(ObjMesh::Face(key[0], key[1], key[2], key[3]), 0);
+          return;
+        }
+        auto it = surfaceFaces.find(key);
+        if (it != surfaceFaces.end())
+          it->second++;
+        else 
+        {
+          auto revKey = key.getReversedRectKey();
+          it = surfaceFaces.find(revKey);  
+          if (it != surfaceFaces.end()) 
+            it->second--;
+          else
+            surfaceFaces.emplace(key, 1);
+        }
+      };
+  
+      processFace(0,3,2,1);
+      processFace(4,5,6,7);
+      processFace(0,1,5,4);
+      processFace(3,7,6,2);
+      processFace(1,2,6,5);
+      processFace(0,4,7,3);
+    }
+    if (allElementFaces == false) // we build surface mesh
+    {
+      for(const auto & p : surfaceFaces)
+      {
+        if (p.second == 0) continue; // inner face
+        auto key = p.first;
+        int numFaces = abs(p.second);
+        if (p.second < 0) key.reverse();
+        for(int i = 0; i < numFaces; i++)
+        {
+          if (triangulate)
+          {
+            objMesh->addFaceToGroup(ObjMesh::Face(key[0], key[1], key[2]), 0);
+            objMesh->addFaceToGroup(ObjMesh::Face(key[2], key[3], key[0]), 0);
+          }
+          else
+            objMesh->addFaceToGroup(ObjMesh::Face(key[0], key[1], key[2], key[3]), 0);
+        }
+      }
+    }
+  }
+
+  if (mesh->getElementType() == CubicMesh::elementType())
+  {
+    // cubic mesh
+    objMesh->setNormalsToFaceNormals();
+  }
+  else
+  {
+    // other types of meshes (e.g., tet)
+    objMesh->computePseudoNormals();
+    objMesh->setNormalsToPseudoNormals();
+  }
+
+  objMesh->setSingleMaterial(ObjMesh::Material());
+
+  return objMesh;
+}
+
+// advanced routine, not used very often
+ObjMesh * GenerateSurfaceMesh::ComputeMesh(const VolumetricMesh * mesh, const ObjMesh * superMesh, bool triangulate)
+{
+  ObjMesh * surfaceMesh = ComputeMesh(mesh, false);
+
+  // for each volumetric mesh vertex, find the nearest obj file vertex
+  vector<int> closestObjVertex(mesh->getNumVertices());
+  for(int i=0; i<mesh->getNumVertices(); i++)
+  {
+    const Vec3d & pos = mesh->getVertex(i);
+    double dist;
+    closestObjVertex[i] = superMesh->getClosestVertex(pos, &dist);
+  }
+
+  // create a new objMesh
+  ObjMesh * objMesh = new ObjMesh();
+  objMesh->addGroup("Default");
+
+  // build the list of triangles from superMesh
+  set<UTriKey> superMeshFaces;
+  for(unsigned int i=0; i < superMesh->getNumGroups(); i++)
+  {
+    const ObjMesh::Group * groupHandle = superMesh->getGroupHandle(i);
+    for(unsigned int iFace = 0; iFace < groupHandle->getNumFaces(); iFace++)
+    {
+      const ObjMesh::Face * faceHandle = groupHandle->getFaceHandle(iFace);
+      if (faceHandle->getNumVertices() != 3)
+      {
+        printf("Error: input superMesh is not triangulated.\n");
+        return nullptr;
+      }
+
+      superMeshFaces.emplace(faceHandle->getVertexPositionIndex(0), faceHandle->getVertexPositionIndex(1), faceHandle->getVertexPositionIndex(2));
+    }
+  }
+
+  for(unsigned int i=0; i < surfaceMesh->getNumGroups(); i++)
+  {
+    const ObjMesh::Group * groupHandle = surfaceMesh->getGroupHandle(i);
+    for(unsigned int iFace = 0; iFace < groupHandle->getNumFaces(); iFace++)
+    {
+      const ObjMesh::Face * faceHandle = groupHandle->getFaceHandle(iFace);
+      if (faceHandle->getNumVertices() != 3) continue;
+      UTriKey key(closestObjVertex[faceHandle->getVertexPositionIndex(0)], 
+                  closestObjVertex[faceHandle->getVertexPositionIndex(1)], 
+                  closestObjVertex[faceHandle->getVertexPositionIndex(2)]);
+      if (superMeshFaces.find(key) == superMeshFaces.end()) continue;
+      // only include faces from superMesh
+      objMesh->addFaceToGroup(ObjMesh::Face(faceHandle->getVertexPositionIndex(0), 
+                                            faceHandle->getVertexPositionIndex(1), 
+                                            faceHandle->getVertexPositionIndex(2)), 0);
+    }
+  }
+  delete surfaceMesh;
+
+  return objMesh;
+}
+
diff --git a/libraries/volumetricMesh/generateSurfaceMesh.h b/libraries/volumetricMesh/generateSurfaceMesh.h
new file mode 100644
index 0000000000000000000000000000000000000000..cb7f60bb6a5bfa9476c8781b41961405447c7f6e
--- /dev/null
+++ b/libraries/volumetricMesh/generateSurfaceMesh.h
@@ -0,0 +1,66 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Creates the surface mesh of the given volumetric mesh.
+  Note: interior volumetric mesh vertices are kept in the surface mesh (as isolated vertices).
+        So, the vertex set of the volumetric mesh is identical to the surface mesh vertex set,
+        with the same order.
+*/
+
+#ifndef _GENERATESURFACEMESH_H_
+#define _GENERATESURFACEMESH_H_
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include "volumetricMesh.h"
+#include "objMesh.h"
+
+
+class GenerateSurfaceMesh
+{
+public:
+  // The output surface mesh is a triangle mesh for tet meshes and can also be a quad mesh for cubic meshes.
+  // All vertices of the volumetric mesh are included in the output surface mesh, even if they don't touch any surface triangle
+  //   (e.g., vertices in the interior of the volumetric mesh).
+  // triangulate: specifies whether output mesh should be quads or triangles (in the case of cubic volumetric meshes).
+  // allElementFaces = true: build ALL faces for ALL elements in the mesh
+  //                 = false: build only surface faces of the mesh (default)
+  static ObjMesh * ComputeMesh(const VolumetricMesh * volumetricMesh, bool triangulate=false, bool allElementFaces = false); 
+
+  // computes the surface of the mesh, but only the part which is also the outer surface of the given super mesh (advanced routine)
+  static ObjMesh * ComputeMesh(const VolumetricMesh * volumetricMesh, const ObjMesh * superMesh, bool triangulate=false);
+};
+
+#endif
+
diff --git a/libraries/volumetricMesh/generateTetMeshFromCubicMesh.cpp b/libraries/volumetricMesh/generateTetMeshFromCubicMesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d35c4abda11953d11aa80a5b9e9e3d49d9786e7
--- /dev/null
+++ b/libraries/volumetricMesh/generateTetMeshFromCubicMesh.cpp
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "generateTetMeshFromCubicMesh.h"
+
+TetMesh * GenerateTetMeshFromCubicMesh::Generate(CubicMesh * cubicMesh, double E, double nu, double density)
+{
+  int numVertices;
+  double * vertices;
+  int numCubicElements;
+  int numElementVertices;
+  int * cubicElements;
+
+  cubicMesh->exportMeshGeometry(&numVertices, &vertices, &numCubicElements, &numElementVertices, &cubicElements);
+
+  int numTetElements = 6 * numCubicElements;
+  int * tetElements = (int*) malloc (sizeof(int) * 4 * numTetElements);
+
+  int tetSubdivision[6][4] = {
+    { 0, 1, 3, 4 },
+    { 1, 3, 4, 5 },
+    { 3, 4, 5, 7 },
+    { 1, 2, 3, 5 },
+    { 2, 5, 6, 7 },
+    { 2, 3, 5, 7 }
+   };
+
+  for(int i=0; i<numCubicElements; i++)
+  {
+    int * cubicElement = &cubicElements[8*i];
+    for(int tet=0; tet<6; tet++)
+    {
+      int tetIndex = 6*i+tet;
+      for(int vtx=0; vtx<4; vtx++)
+      {
+        int localVertexIndex = tetSubdivision[tet][vtx];
+        tetElements[4*tetIndex+vtx] = cubicElement[localVertexIndex];
+      }
+    }
+  }
+
+  TetMesh * tetMesh = new TetMesh(numVertices, vertices, numTetElements, tetElements, E, nu, density);
+
+  free(tetElements);
+  free(cubicElements);
+  free(vertices);
+
+  return tetMesh;
+}
+
diff --git a/src/libvolumetricMesh/volumetricMeshExtensions.h b/libraries/volumetricMesh/generateTetMeshFromCubicMesh.h
similarity index 65%
rename from src/libvolumetricMesh/volumetricMeshExtensions.h
rename to libraries/volumetricMesh/generateTetMeshFromCubicMesh.h
index d1cd90ef83be4a40c81c7cc6f6b4c0fbeadd0c2e..bb6f13150050e8f9c58bb25439faff595c3daf8c 100644
--- a/src/libvolumetricMesh/volumetricMeshExtensions.h
+++ b/libraries/volumetricMesh/generateTetMeshFromCubicMesh.h
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,14 +30,20 @@
  *                                                                       *
  *************************************************************************/
 
-#ifndef _VOLUMETRICMESHEXTENSIONS_H_
-#define _VOLUMETRICMESHEXTENSIONS_H_
+#ifndef _GENERATETETMESHFROMCUBICMESH_H_
+#define _GENERATETETMESHFROMCUBICMESH_H_
 
-namespace vega
-{
-class VolumetricMeshExtensions
+#include "cubicMesh.h"
+#include "tetMesh.h"
+
+// converts a cubic mesh into a tet mesh
+// only supports uniform material properties
+
+class GenerateTetMeshFromCubicMesh
 {
+public:
+  static TetMesh * Generate(CubicMesh * cubicMesh, double E=1E9, double nu=0.45, double density=1000);
 };
-}
 
 #endif
+
diff --git a/libraries/volumetricMesh/interpolationWeightsMultiLoad.cpp b/libraries/volumetricMesh/interpolationWeightsMultiLoad.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..40267931a6867dd09b20f66098f3cecefaea50cf
--- /dev/null
+++ b/libraries/volumetricMesh/interpolationWeightsMultiLoad.cpp
@@ -0,0 +1,120 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Load/save many sets of interpolation weights from one file.
+*/
+
+#include <stdio.h>
+#include "interpolationWeightsMultiLoad.h"
+#include "volumetricMesh.h"
+#include "vegalong.h"
+
+// input: filename, numModels
+// output: numTargetLocations, numElementVertices, vertices, weights
+int multiLoadInterpolationWeightsBinary(const char * filename, int * numModels, int ** numTargetLocations, int ** numElementVertices, int *** vertices, double *** weights) // binary version; returns 0 on success
+{
+  FILE * fin = fopen(filename, "rb");
+  if (!fin)
+  {
+    printf("Error: unable to open file %s.\n", filename);
+    return 1;
+  }
+
+  int readItems = (int) fread(numModels, sizeof(int), 1, fin);
+  if (readItems < 1)
+  {
+    printf("Error: unable to read from file %s.\n", filename);
+    return 1;
+  }
+
+  (*numTargetLocations) = (int*) malloc (sizeof(int) * *numModels);
+  (*numElementVertices) = (int*) malloc (sizeof(int) * *numModels);
+  (*vertices) = (int**) malloc (sizeof(int*) * *numModels);
+  (*weights) = (double**) malloc (sizeof(double*) * *numModels);
+
+  vegaunsignedlong * header = (vegaunsignedlong*) malloc (sizeof(vegaunsignedlong) * *numModels); // we only read the header, but don't use it
+
+  readItems = (int) fread(header, sizeof(vegaunsignedlong), *numModels, fin);
+  if (readItems < *numModels)
+  {
+    printf("Error: unable to read from file %s.\n", filename);
+    return 1;
+  }
+  free(header);
+
+  int code = 0;
+  for(int i=0; i<*numModels; i++)
+    code = code | VolumetricMesh::loadInterpolationWeightsBinary(fin, &(*numTargetLocations)[i], &(*numElementVertices)[i], &(*vertices)[i], &(*weights)[i]);
+
+  fclose(fin);
+
+  return code;
+}
+
+// input: filename, numTargetLocations (array), numElementVertices (array), vertices, weights
+int multiSaveInterpolationWeightsBinary(const char * filename, int numModels, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights) // binary version; returns 0 on success
+{
+  FILE * fout = fopen(filename, "wb");
+  if (!fout)
+  {
+    printf("Error: unable to open file %s.\n", filename);
+    return 1;
+  }
+
+  int writtenItems = (int)fwrite(&numModels, sizeof(int), 1, fout);
+  if (writtenItems < 1)
+  {
+    printf("Error: unable to write to file %s.\n", filename);
+    return 1;
+  }
+
+  vegaunsignedlong numBytes = 0;
+  for(int i=0; i<numModels; i++)
+  {
+    writtenItems = (int)fwrite(&numBytes, sizeof(vegaunsignedlong), 1, fout);
+    if (writtenItems < 1)
+    {
+      printf("Error: unable to write to file %s.\n", filename);
+      return 1;
+    }
+    numBytes += 2 * sizeof(int) + (vegaunsignedlong) numElementVertices[i] * numTargetLocations[i] * (sizeof(int) + sizeof(double));
+  }
+
+  int code = 0;
+  for(int i=0; i<numModels; i++)
+    code = code | VolumetricMesh::saveInterpolationWeightsBinary(fout, numTargetLocations[i], numElementVertices[i], vertices[i], weights[i]);
+ 
+  fclose(fout);
+  return code;
+}
+
diff --git a/libraries/volumetricMesh/interpolationWeightsMultiLoad.h b/libraries/volumetricMesh/interpolationWeightsMultiLoad.h
new file mode 100644
index 0000000000000000000000000000000000000000..cfedfb2f026eb5a06ef8c9097071a90f768eeb20
--- /dev/null
+++ b/libraries/volumetricMesh/interpolationWeightsMultiLoad.h
@@ -0,0 +1,48 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Load/save many sets of interpolation weights from one file.
+*/
+
+#ifndef _INTERPOLATIONWEIGHTSMULTILOAD_H_
+#define _INTERPOLATIONWEIGHTSMULTILOAD_H_
+
+// input: filename, numModels
+// output: numTargetLocations, numElementVertices, vertices, weights
+int multiLoadInterpolationWeightsBinary(const char * filename, int * numModels, int ** numTargetLocations, int ** numElementVertices, int *** vertices, double *** weights); // binary version; returns 0 on success
+
+// input: filename, numModels, numTargetLocations, numElementVertices, vertices, weights
+int multiSaveInterpolationWeightsBinary(const char * filename, int numModels, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights); // binary version; returns 0 on success
+
+#endif
+
diff --git a/src/libvolumetricMesh/tetMesh.cpp b/libraries/volumetricMesh/tetMesh.cpp
similarity index 65%
rename from src/libvolumetricMesh/tetMesh.cpp
rename to libraries/volumetricMesh/tetMesh.cpp
index 9bb62a71b52654f35cd4aa260bda4eb96fbdf86a..ac9bd775c29d9be5656db593aaedbb0a77597d91 100644
--- a/src/libvolumetricMesh/tetMesh.cpp
+++ b/libraries/volumetricMesh/tetMesh.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -26,16 +30,22 @@
  *                                                                       *
  *************************************************************************/
 
-#include <cmath>
-
 #include "tetMesh.h"
 #include "volumetricMeshParser.h"
+#include "geometryQuery.h"
 
-namespace vega
-{
 const VolumetricMesh::elementType TetMesh::elementType_ = TET;
 
-TetMesh::TetMesh(char * filename, int verbose) : VolumetricMesh(filename, 4, verbose, &temp)
+TetMesh::TetMesh(const char * filename, fileFormatType fileFormat, int verbose) : VolumetricMesh(filename, fileFormat, 4, &temp, verbose)
+{
+  if (temp != elementType_)
+  {
+    printf("Error: mesh is not a tet mesh.\n");
+    throw 11;
+  }
+}
+
+TetMesh::TetMesh(void * binaryStream, int memoryLoad) : VolumetricMesh(binaryStream, 4, &temp, memoryLoad)
 {
   if (temp != elementType_)
   {
@@ -44,7 +54,7 @@ TetMesh::TetMesh(char * filename, int verbose) : VolumetricMesh(filename, 4, ver
   }
 }
 
-TetMesh::TetMesh(char * filename, int specialFileType, int verbose): VolumetricMesh(4)
+TetMesh::TetMesh(const char * filename, int specialFileType, int verbose): VolumetricMesh(4)
 {
   double E = 1E8;
   double nu = 0.45;
@@ -63,14 +73,14 @@ TetMesh::TetMesh(char * filename, int specialFileType, int verbose): VolumetricM
   sprintf(lineBuffer, "%s.node", filename);
   if (parser.open(lineBuffer) != 0)
     throw 2;
-
+  
   parser.getNextLine(lineBuffer, 1);
   int dim;
   sscanf(lineBuffer, "%d %d", &numVertices, &dim);
   if (dim != 3)
     throw 3;
 
-  vertices = (Vec3d**) malloc (sizeof(Vec3d*) * numVertices);
+  vertices = new Vec3d [numVertices];
 
   for(int i=0; i<numVertices; i++)
   {
@@ -80,9 +90,9 @@ TetMesh::TetMesh(char * filename, int specialFileType, int verbose): VolumetricM
     sscanf(lineBuffer, "%d %lf %lf %lf", &index, &x, &y, &z);
     if (index != (i+1))
       throw 3;
-    vertices[i] = new Vec3d(x,y,z);
+    vertices[i] = Vec3d(x,y,z);
   }
-
+  
   parser.close();
 
   // next, read the elements
@@ -113,10 +123,10 @@ TetMesh::TetMesh(char * filename, int specialFileType, int verbose): VolumetricM
     for(int j=0; j<4; j++) // vertices are 1-indexed in .ele files
     {
       v[j]--;
-      elements[i][j] = v[j];
+      elements[i][j] = v[j]; 
     }
   }
-
+  
   parser.close();
 
   numMaterials = 0;
@@ -129,18 +139,49 @@ TetMesh::TetMesh(char * filename, int specialFileType, int verbose): VolumetricM
   setSingleMaterial(E, nu, density);
 }
 
-TetMesh::TetMesh(int numVertices_, double * vertices_,
-               int numElements_, int * elements_,
-               double E, double nu, double density): VolumetricMesh(numVertices_, vertices_, numElements_, 4, elements_, E, nu, density) {}
+TetMesh::TetMesh(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2, const Vec3d & p3) : VolumetricMesh(4) {
+  double E = 1E8;
+  double nu = 0.45;
+  double density = 1000;
+  
+  numVertices = 4;
+  vertices = (Vec3d*) malloc (sizeof(Vec3d) * numVertices);
+
+  vertices[0] = p0;
+  vertices[1] = p1;
+  vertices[2] = p2;
+  vertices[3] = p3;
+
+  numElements = 1;
+  elements = (int**) malloc (sizeof(int*) * numElements);
+  elementMaterial = (int*) malloc (sizeof(int) * numElements);
+
+  elements[0] = (int*) malloc (sizeof(int) * 4);
+  for(int i = 0; i < 4; i++)
+    elements[0][i] = i;
+  
+  numMaterials = 0;
+  numSets = 0;
+  numRegions = 0;
+  materials = NULL;
+  sets = NULL;
+  regions = NULL;
+
+  setSingleMaterial(E, nu, density);
+}
+
+
+TetMesh::TetMesh(int numVertices_, double * vertices_, int numElements_, int * elements_, double E, double nu, double density)
+    : VolumetricMesh(numVertices_, vertices_, numElements_, 4, elements_, E, nu, density) {}
 
-TetMesh::TetMesh(int numVertices_, double * vertices_,
-         int numElements_, int * elements_,
-         int numMaterials_, Material ** materials_,
-         int numSets_, Set ** sets_,
-         int numRegions_, Region ** regions_):
+TetMesh::TetMesh(int numVertices_, double * vertices_, int numElements_, int * elements_,
+         int numMaterials_, Material ** materials_, int numSets_, Set ** sets_, int numRegions_, Region ** regions_): 
   VolumetricMesh(numVertices_, vertices_, numElements_, 4, elements_,
                  numMaterials_, materials_, numSets_, sets_, numRegions_, regions_) {}
 
+TetMesh::TetMesh(const std::vector<Vec3d> & vertices, const std::vector<Vec4i> & elements, double E, double nu, double density)
+    : TetMesh(vertices.size(), (double*)vertices.data(), elements.size(), (int*)elements.data(), E, nu, density) {}
+
 TetMesh::TetMesh(const TetMesh & source): VolumetricMesh(source) {}
 
 VolumetricMesh * TetMesh::clone()
@@ -149,13 +190,32 @@ VolumetricMesh * TetMesh::clone()
   return mesh;
 }
 
-TetMesh::TetMesh(const TetMesh & tetMesh, int numElements_, int * elements_, map<int,int> * vertexMap_): VolumetricMesh(tetMesh, numElements_, elements_, vertexMap_) {}
+TetMesh::TetMesh(const TetMesh & tetMesh, int numElements_, int * elements_, map<int,int> * vertexMap_)
+    : VolumetricMesh(tetMesh, numElements_, elements_, vertexMap_) {}
 
 TetMesh::~TetMesh() {}
 
-int TetMesh::save(char * filename) const
+int TetMesh::saveToAscii(const char * filename) const
+{
+  return VolumetricMesh::saveToAscii(filename, elementType_);
+}
+
+int TetMesh::saveToBinary(const char * filename, unsigned int * bytesWritten) const
+{
+  return VolumetricMesh::saveToBinary(filename, bytesWritten, elementType_);
+}
+
+int TetMesh::saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten, bool countBytesOnly) const
+{
+  return VolumetricMesh::saveToBinary(binaryOutputStream, bytesWritten, elementType_, countBytesOnly);
+}
+
+void TetMesh::exportMeshGeometry(vector<Vec3d> & vertices, vector<Vec4i> & tets) const
 {
-  return VolumetricMesh::save(filename, elementType_);
+  exportMeshGeometry(vertices);
+  tets.resize(getNumElements());
+  for(int i = 0; i < getNumElements(); i++)
+    tets[i] = Vec4i(getVertexIndices(i));
 }
 
 void TetMesh::computeElementMassMatrix(int el, double * massMatrix) const
@@ -174,7 +234,7 @@ void TetMesh::computeElementMassMatrix(int el, double * massMatrix) const
    Singiresu S. Rao: The finite element method in engineering, 2004)
 */
 
-  const double mtx[16] = { 2, 1, 1, 1,
+  const double mtx[16] = { 2, 1, 1, 1, 
                            1, 2, 1, 1,
                            1, 1, 2, 1,
                            1, 1, 1, 2 } ;
@@ -192,27 +252,32 @@ void TetMesh::computeElementMassMatrix(int el, double * massMatrix) const
 */
 }
 
-double TetMesh::getTetVolume(Vec3d * a, Vec3d * b, Vec3d * c, Vec3d * d)
+double TetMesh::getSignedTetVolume(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d)
 {
-  // volume = 1/6 * | (a-d) . ((b-d) x (c-d)) |
-  return (1.0 / 6 * fabs( dot(*a - *d, cross(*b - *d, *c - *d)) ));
+  return 1.0 / 6 * getTetDeterminant(a, b, c, d);
+}
+
+double TetMesh::getTetVolume(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d)
+{
+  // volume = 1/6 * | (d-a) . ((b-a) x (c-a)) |
+  return (1.0 / 6 * fabs(getTetDeterminant(a, b, c, d))); 
 }
 
 double TetMesh::getElementVolume(int el) const
 {
-  Vec3d * a = getVertex(el, 0);
-  Vec3d * b = getVertex(el, 1);
-  Vec3d * c = getVertex(el, 2);
-  Vec3d * d = getVertex(el, 3);
+  const Vec3d & a = getVertex(el, 0);
+  const Vec3d & b = getVertex(el, 1);
+  const Vec3d & c = getVertex(el, 2);
+  const Vec3d & d = getVertex(el, 3);
   return getTetVolume(a, b, c, d);
 }
 
 void TetMesh::getElementInertiaTensor(int el, Mat3d & inertiaTensor) const
 {
-  Vec3d a = *getVertex(el, 0);
-  Vec3d b = *getVertex(el, 1);
-  Vec3d c = *getVertex(el, 2);
-  Vec3d d = *getVertex(el, 3);
+  Vec3d a = getVertex(el, 0);
+  Vec3d b = getVertex(el, 1);
+  Vec3d c = getVertex(el, 2);
+  Vec3d d = getVertex(el, 3);
 
   Vec3d center = getElementCenter(el);
   a -= center;
@@ -250,73 +315,29 @@ bool TetMesh::containsVertex(int el, Vec3d pos) const // true if given element c
   return ((weights[0] >= 0) && (weights[1] >= 0) && (weights[2] >= 0) && (weights[3] >= 0));
 }
 
-void TetMesh::computeBarycentricWeights(int el, Vec3d pos, double * weights) const
+void TetMesh::computeBarycentricWeights(const Vec3d tetVtxPos[4], const Vec3d & pos, double weights[4])
 {
-/*
-       |x1 y1 z1 1|
-  D0 = |x2 y2 z2 1|
-       |x3 y3 z3 1|
-       |x4 y4 z4 1|
-
-       |x  y  z  1|
-  D1 = |x2 y2 z2 1|
-       |x3 y3 z3 1|
-       |x4 y4 z4 1|
-
-       |x1 y1 z1 1|
-  D2 = |x  y  z  1|
-       |x3 y3 z3 1|
-       |x4 y4 z4 1|
-
-       |x1 y1 z1 1|
-  D3 = |x2 y2 z2 1|
-       |x  y  z  1|
-       |x4 y4 z4 1|
-
-       |x1 y1 z1 1|
-  D4 = |x2 y2 z2 1|
-       |x3 y3 z3 1|
-       |x  y  z  1|
-
-  wi = Di / D0
-*/
+  return computeBarycentricWeights(tetVtxPos[0], tetVtxPos[1], tetVtxPos[2], tetVtxPos[3], pos, weights);
+}
+
+void TetMesh::computeBarycentricWeights(const Vec3d & tetVtxPos0, const Vec3d & tetVtxPos1, const Vec3d & tetVtxPos2, const Vec3d & tetVtxPos3, 
+    const Vec3d & pos, double weights[4])
+{
+  getTetBarycentricWeights(pos, tetVtxPos0, tetVtxPos1, tetVtxPos2, tetVtxPos3, weights);
+}
 
+void TetMesh::computeBarycentricWeights(int el, const Vec3d & pos, double * weights) const
+{
   Vec3d vtx[4];
   for(int i=0; i<4; i++)
-    vtx[i] = *getVertex(el,i);
-
-  double D[5];
-  D[0] = getTetDeterminant(&vtx[0], &vtx[1], &vtx[2], &vtx[3]);
+    vtx[i] = getVertex(el,i);
 
-  for(int i=1; i<=4; i++)
-  {
-    Vec3d buf[4];
-    for(int j=0; j<4; j++)
-      buf[j] = vtx[j];
-    buf[i-1] = pos;
-    D[i] = getTetDeterminant(&buf[0], &buf[1], &buf[2], &buf[3]);
-    weights[i-1] = D[i] / D[0];
-  }
+  computeBarycentricWeights(vtx, pos, weights);
 }
 
-double TetMesh::getTetDeterminant(Vec3d * a, Vec3d * b, Vec3d * c, Vec3d * d)
+double TetMesh::getTetDeterminant(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d)
 {
-  // computes the determinant of the 4x4 matrix
-  // [ a 1 ]
-  // [ b 1 ]
-  // [ c 1 ]
-  // [ d 1 ]
-
-  Mat3d m0 = Mat3d(*b, *c, *d);
-  Mat3d m1 = Mat3d(*a, *c, *d);
-  Mat3d m2 = Mat3d(*a, *b, *d);
-  Mat3d m3 = Mat3d(*a, *b, *c);
-
-  double determinant = -det(m0) + det(m1) - det(m2) + det(m3);
-  if(std::fabs(determinant) < 1e-12)
-      determinant = 0.0;
-
-  return determinant;
+  return ::getTetDeterminant(a, b, c, d);
 }
 
 void TetMesh::interpolateGradient(int element, const double * U, int numFields, Vec3d pos, double * grad) const
@@ -330,7 +351,7 @@ void TetMesh::computeGradient(int element, const double * U, int numFields, doub
   // grad is constant inside a tet
   Vec3d vtx[4];
   for(int i=0; i<4; i++)
-    vtx[i] = *getVertex(element,i);
+    vtx[i] = getVertex(element,i);
 
   // form M =
   // [b - a]
@@ -397,7 +418,7 @@ void TetMesh::getElementEdges(int el, int * edgeBuffer) const
     v[i] = getVertexIndex(el,i);
 
   int edgeMask[6][2] = {
-   { 0, 1 }, { 1, 2 }, { 2, 0 },
+   { 0, 1 }, { 1, 2 }, { 2, 0 }, 
    { 0, 3 }, { 1, 3 }, { 2, 3 } };
 
   for(int edge=0; edge<6; edge++)
@@ -411,7 +432,9 @@ void TetMesh::orient()
 {
   for(int el=0; el<numElements; el++)
   {
-    double det = dot(*(getVertex(el, 1)) - *(getVertex(el, 0)), cross(*(getVertex(el, 2)) - *(getVertex(el, 0)), *(getVertex(el, 3)) - *(getVertex(el, 0))));
+    // a, b, c, d
+    // dot(d - a, cross(b - a, c - a))
+    double det = getTetDeterminant(getVertex(el, 0), getVertex(el, 1), getVertex(el, 2), getVertex(el, 3));
 
     if (det < 0)
     {
@@ -425,4 +448,3 @@ void TetMesh::orient()
   }
 }
 
-}
diff --git a/libraries/volumetricMesh/tetMesh.h b/libraries/volumetricMesh/tetMesh.h
new file mode 100644
index 0000000000000000000000000000000000000000..c8c1a004413ad1259c1a00cf6dd39a82a7a1428b
--- /dev/null
+++ b/libraries/volumetricMesh/tetMesh.h
@@ -0,0 +1,148 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This class is a container for a tetrahedral volumetric 3D mesh. See 
+  also volumetricMesh.h. The tetrahedra can take arbitrary shapes (not 
+  limited to only a few shapes).
+*/
+
+#ifndef _TETMESH_H_
+#define _TETMESH_H_
+
+#include "volumetricMesh.h"
+#include "vec4i.h"
+
+// see also volumetricMesh.h for a description of the routines
+
+class TetMesh : public VolumetricMesh
+{
+public:
+  // loads the mesh from a file 
+  // ASCII: .veg text input format, see documentation and the provided examples
+  // BINARY: .vegb binary input format
+  TetMesh(const char * filename, fileFormatType fileFormat = ASCII, int verbose=1);
+
+  // load from a stream
+  // if memoryLoad is 0, binaryStream is FILE* (load from a file), otherwise, it is char* (load from a memory buffer)
+  TetMesh(void * binaryStream, int memoryLoad = 0);
+  
+  TetMesh(const Vec3d & p0, const Vec3d & p1, const Vec3d & p2, const Vec3d & p3);
+
+  // constructs a tet mesh from the given vertices and elements, 
+  // with a single region and material ("E, nu" material)
+  // "vertices" is double-precision array of length 3 x numVertices .
+  // "elements" is an integer array of length 4 x numElements
+  TetMesh(int numVertices, double * vertices,
+         int numElements, int * elements,
+         double E=1E6, double nu=0.45, double density=1000);
+  TetMesh(const std::vector<Vec3d> & vertices, const std::vector<Vec4i> & elements, 
+    double E=1E6, double nu=0.45, double density=1000);
+
+  // constructs a tet mesh from the given vertices and elements, 
+  // with an arbitrary number of sets, regions and materials
+  // "vertices" is double-precision array of length 3 x numVertices 
+  // "elements" is an integer array of length 4 x numElements
+  // "materials", "sets" and "regions" will be copied internally (deep copy), so you
+  // can release them after calling this constructor
+  TetMesh(int numVertices, double * vertices,
+         int numElements, int * elements,
+         int numMaterials, Material ** materials,
+         int numSets, Set ** sets,
+         int numRegions, Region ** regions);
+
+  // loads a file of a "special" (not .veg) type
+  // currently one such special format is supported:
+  // specialFileType=0: 
+  //   the ".ele" and ".node" format, used by TetGen, 
+  //   "filename" is the basename, e.g., passing "mesh" will load the mesh from "mesh.ele" and "mesh.node" 
+  // default material parameters will be used
+  TetMesh(const char * filename, int specialFileType, int verbose); 
+
+  // creates a mesh consisting of the specified element subset of the given TetMesh
+  TetMesh(const TetMesh & mesh, int numElements, int * elements, std::map<int,int> * vertexMap = NULL);
+
+  TetMesh(const TetMesh & tetMesh);
+  virtual VolumetricMesh * clone();
+  virtual ~TetMesh();
+
+  virtual int saveToAscii(const char * filename) const;
+  // saves the mesh to binary format
+  // returns: 0 = success, non-zero = error
+  // output: if bytesWritten is non-NULL, it will contain the number of bytes written 
+  virtual int saveToBinary(const char * filename, unsigned int * bytesWritten = NULL) const;
+  virtual int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten = NULL, bool countBytesOnly = false) const;
+
+  using VolumetricMesh::exportMeshGeometry;
+  void exportMeshGeometry(std::vector<Vec3d> & vertices, std::vector<Vec4i> & tets) const;
+
+ // === misc queries ===
+
+  static VolumetricMesh::elementType elementType() { return elementType_; }
+  virtual VolumetricMesh::elementType getElementType() const { return elementType(); }
+
+  static double getSignedTetVolume(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+  static double getTetVolume(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+  static double getTetDeterminant(const Vec3d & a, const Vec3d & b, const Vec3d & c, const Vec3d & d);
+
+  virtual double getElementVolume(int el) const;
+  virtual void getElementInertiaTensor(int el, Mat3d & inertiaTensor) const;
+  virtual void computeElementMassMatrix(int element, double * massMatrix) const;
+
+  virtual bool containsVertex(int element, Vec3d pos) const; // true if given element contain given position, false otherwise
+
+  // edge queries
+  virtual int getNumElementEdges() const;
+  virtual void getElementEdges(int el, int * edgeBuffer) const;
+
+ // === interpolation ===
+
+  static void computeBarycentricWeights(const Vec3d tetVertexPos[4], const Vec3d & pos, double weights[4]);
+  static void computeBarycentricWeights(const Vec3d & tetVtxPos0, const Vec3d & tetVtxPos1, const Vec3d & tetVtxPos2, const Vec3d & tetVtxPos3, 
+    const Vec3d & pos, double weights[4]);
+  virtual void computeBarycentricWeights(int el, const Vec3d & pos, double * weights) const;
+  void computeGradient(int element, const double * U, int numFields, double * grad) const; // for tet meshes, gradient is constant inside each tet, hence no need to specify position
+  virtual void interpolateGradient(int element, const double * U, int numFields, Vec3d pos, double * grad) const; // conforms to the virtual function in the base class, "pos" does not affect the computation
+
+  // === misc ===
+
+  void orient(); // orients the tets (re-orders vertices within each tet), so that each tet has positive orientation: ((v1 - v0) x (v2 - v0)) dot (v3 - v0) >= 0
+
+protected:
+  static const VolumetricMesh::elementType elementType_;
+  TetMesh(int numElementVertices): VolumetricMesh(numElementVertices) {}
+
+  friend class VolumetricMeshExtensions;
+};
+
+#endif
+
diff --git a/libraries/volumetricMesh/volumetricMesh.cpp b/libraries/volumetricMesh/volumetricMesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4959e1c26505510651114d95ad5f71007b601c24
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMesh.cpp
@@ -0,0 +1,3126 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <float.h>
+#include <string.h>
+#include <assert.h>
+#include <iostream>
+#include <map>
+#include "volumetricMeshParser.h"
+#include "volumetricMesh.h"
+#include "volumetricMeshENuMaterial.h"
+#include "volumetricMeshOrthotropicMaterial.h"
+#include "volumetricMeshMooneyRivlinMaterial.h"
+#include "range.h"
+using namespace std;
+
+double VolumetricMesh::density_default = 1000;
+double VolumetricMesh::E_default = 1E9;
+double VolumetricMesh::nu_default = 0.45;
+
+// parses the mesh, and returns the string corresponding to the element type
+VolumetricMesh::VolumetricMesh(const char * filename, fileFormatType fileFormat, int numElementVertices_, elementType * elementType_, int verbose): numElementVertices(numElementVertices_)
+{
+  if (verbose)
+  {
+    printf("Opening file %s.\n", filename); fflush(NULL);
+  }
+
+  switch (fileFormat)
+  {
+    case ASCII:
+      loadFromAscii(filename, elementType_);
+    break;
+
+    case BINARY:
+      loadFromBinary(filename, elementType_);
+    break;
+
+    default:
+      printf("Error in VolumetricMesh::VolumetricMesh: file format is unknown.\n");
+    break;
+  } 
+}
+
+// parses the mesh, and returns the string corresponding to the element type
+VolumetricMesh::VolumetricMesh(void * binaryInputStream, int numElementVertices_, elementType * elementType_, int memoryLoad): numElementVertices(numElementVertices_)
+{
+  if (memoryLoad)
+    loadFromMemory((unsigned char *)binaryInputStream, elementType_);
+  else
+    loadFromBinary((FILE *)binaryInputStream, elementType_);
+}
+
+VolumetricMesh::~VolumetricMesh()
+{
+  delete [] vertices;
+
+  for(int i=0; i<numElements; i++)
+    free(elements[i]);
+  free(elements);
+
+  for(int i=0; i<numMaterials; i++)
+    delete(materials[i]);
+  free(materials);
+  
+  for(int i=0; i<numSets; i++)
+    delete(sets[i]);
+  free(sets);
+
+  for(int i=0; i<numRegions; i++)
+    delete(regions[i]);
+  free(regions);
+
+  free(elementMaterial);
+}
+
+void VolumetricMesh::assignMaterialsToElements(int verbose)
+{
+  elementMaterial = (int*) malloc (sizeof(int) * numElements);
+  for(int el=0; el<numElements; el++)
+    elementMaterial[el] = numMaterials;
+
+  propagateRegionsToElements();
+
+  // seek for unassigned elements
+  set<int> unassignedElements;
+  for(int el=0; el<numElements; el++)
+  {
+    if (elementMaterial[el] == numMaterials)
+      unassignedElements.insert(el);
+  }
+
+  if (unassignedElements.size() > 0)
+  {
+    // assign set and region to the unassigned elements
+
+    // create a material if none exists
+    if (numMaterials == 0)
+    {
+      numMaterials++;
+      materials = (Material**) realloc (materials, sizeof(Material*) * numMaterials);
+      materials[numMaterials - 1] = new ENuMaterial("defaultMaterial", density_default, E_default, nu_default);
+    }
+
+    numSets++;
+    sets = (Set**) realloc (sets, sizeof(Set*) * numSets);
+    sets[numSets-1] = new Set("unassignedSet"); 
+    for(set<int>::iterator iter = unassignedElements.begin(); iter != unassignedElements.end(); iter++)
+      sets[numSets-1]->insert(*iter); // elements in sets are 0-indexed
+
+    // create a new region for the unassigned elements
+    numRegions++;
+    regions = (Region**) realloc (regions, sizeof(Region*) * numRegions);
+    regions[numRegions - 1] = new Region(numMaterials - 1, numSets - 1);
+
+    for(set<int>::iterator iter = unassignedElements.begin(); iter != unassignedElements.end(); iter++)
+      elementMaterial[*iter] = numMaterials - 1;
+
+    if (verbose)
+      printf("Warning: %d elements were not found in any of the regions. Using default material parameters for these elements.\n", (int)unassignedElements.size());
+  }
+}
+
+void VolumetricMesh::loadFromAscii(const char * filename, elementType * elementType_, int verbose)
+{
+  // create buffer for element vertices
+  vector<int> elementVerticesBuffer(numElementVertices);
+  int * v = &elementVerticesBuffer[0];
+
+
+  // parse the .veg file
+  VolumetricMeshParser volumetricMeshParser;
+
+  if (volumetricMeshParser.open(filename) != 0)
+  {
+    printf("Error: could not open file %s.\n",filename);
+    throw 1;
+  }
+
+  // === First pass: parse vertices and elements, and count the number of materials, sets and regions  ===
+
+  int countNumVertices = 0;
+  int countNumElements = 0;
+
+  numElements = -1;
+  numMaterials = 0;
+  numSets = 1; // set 0 is "allElements"
+  numRegions = 0;
+  *elementType_ = INVALID;
+  int parseState = 0;
+  char lineBuffer[1024];
+
+  int oneIndexedVertices = 1;
+  int oneIndexedElements = 1;
+  while (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
+  {
+    //lineBuffer now contains the next line
+    //printf("%s\n", lineBuffer);
+
+    // find *VERTICES
+    if ((parseState == 0) && (strncmp(lineBuffer, "*VERTICES", 9) == 0))
+    {
+      parseState = 1;
+
+      if (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
+      {
+        // format is numVertices, 3, 0, 0
+        sscanf(lineBuffer, "%d", &numVertices);  // ignore 3, 0, 0
+        vertices = new Vec3d [numVertices];
+      }
+      else
+      {
+        printf("Error: file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
+        throw 2;
+      }
+  
+      continue;
+    }
+
+    // find *ELEMENTS
+    if ((parseState == 1) && (strncmp(lineBuffer, "*ELEMENTS", 9) == 0))
+    {
+      parseState = 2;
+
+      // parse element type
+      if (volumetricMeshParser.getNextLine(lineBuffer) != NULL)
+      {
+        volumetricMeshParser.removeWhitespace(lineBuffer);
+
+        if (strncmp(lineBuffer, "TET", 3) == 0)
+          *elementType_ = TET;
+        else if (strncmp(lineBuffer, "CUBIC", 5) == 0)
+          *elementType_ = CUBIC;
+        else
+        {
+          printf("Error: unknown mesh type %s in file %s\n", lineBuffer, filename);
+          throw 3;
+        }
+      }
+      else
+      {
+        printf("Error: file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
+        throw 4;
+      }
+
+      // parse the number of elements
+      if (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
+      {
+        // format is: numElements, numElementVertices, 0
+        sscanf(lineBuffer, "%d", &numElements);  // only use numElements; ignore numElementVertices, 0
+        elements = (int**) malloc (sizeof(int*) * numElements);
+      }
+      else
+      {
+        printf("Error: file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
+        throw 5;
+      }
+
+      continue;
+    }
+
+    if ((parseState == 2) && (lineBuffer[0] == '*'))
+    {
+      parseState = 3; // end of elements
+    }
+
+    if (parseState == 1)
+    {
+      // read the vertex position
+      if (countNumVertices >= numVertices)
+      {
+        printf("Error: mismatch in the number of vertices in %s.\n", filename);
+        throw 6;
+      }
+
+      // ignore space, comma or tab
+      char * ch = lineBuffer;
+      while((*ch == ' ') || (*ch == ',') || (*ch == '\t'))
+        ch++;
+
+      int index;
+      sscanf(ch, "%d", &index);
+      // seek next separator
+      while((*ch != ' ') && (*ch != ',') && (*ch != '\t') && (*ch != 0))
+        ch++;
+
+      if (index == 0)
+        oneIndexedVertices = 0; // input mesh has 0-indexed vertices
+
+      double pos[3];
+      for(int i=0; i<3; i++)
+      {
+        // ignore space, comma or tab
+        while((*ch == ' ') || (*ch == ',') || (*ch == '\t'))
+          ch++;
+
+        if (*ch == 0)
+        {
+          printf("Error parsing line %s in file %s.\n", lineBuffer, filename);
+          throw 7;
+        }
+
+        sscanf(ch, "%lf", &pos[i]);
+ 
+        // seek next separator
+        while((*ch != ' ') && (*ch != ',') && (*ch != '\t') && (*ch != 0))
+          ch++;
+      }
+
+      vertices[countNumVertices] = Vec3d(pos);
+      countNumVertices++;
+    }
+
+    if (parseState == 2)
+    {
+      // read the element vertices
+      if (countNumElements >= numElements)
+      {
+        printf("Error: mismatch in the number of elements in %s.\n", filename);
+        throw 8;
+      }
+
+      // ignore space, comma or tab
+      char * ch = lineBuffer;
+      while((*ch == ' ') || (*ch == ',') || (*ch == '\t'))
+        ch++;
+
+      int index;
+      sscanf(ch, "%d", &index);
+
+      if (index == 0)
+        oneIndexedElements = 0; // input mesh has 0-indexed elements
+
+      // seek next separator
+      while((*ch != ' ') && (*ch != ',') && (*ch != '\t') && (*ch != 0))
+        ch++;
+
+      for(int i=0; i<numElementVertices; i++)
+      {
+        // ignore space, comma or tab
+        while((*ch == ' ') || (*ch == ',') || (*ch == '\t'))
+          ch++;
+
+        if (*ch == 0)
+        {
+          printf("Error parsing line %s in file %s.\n", lineBuffer, filename);
+          throw 9;
+        }
+
+        sscanf(ch, "%d", &v[i]);
+
+        // seek next separator
+        while((*ch != ' ') && (*ch != ',') && (*ch != '\t') && (*ch != 0))
+          ch++;
+      }
+      
+      // if vertices were 1-numbered in the .veg file, convert to 0-numbered
+      for (int k=0; k<numElementVertices; k++)
+        v[k] -= oneIndexedVertices;
+
+      elements[countNumElements] = (int*) malloc (sizeof(int) * numElementVertices);
+      for(int j=0; j<numElementVertices; j++)
+        elements[countNumElements][j] = v[j];
+
+      countNumElements++;
+    }
+
+    if (strncmp(lineBuffer, "*MATERIAL", 9) == 0)
+    {
+      numMaterials++;
+      continue;
+    }
+
+    if (strncmp(lineBuffer, "*SET", 4) == 0)
+    {
+      numSets++;
+      continue;
+    }
+
+    if (strncmp(lineBuffer, "*REGION", 7) == 0)
+    {
+      numRegions++;
+      continue;
+    }
+  }
+
+  if (numElements < 0)
+  {
+    printf("Error: incorrect number of elements.  File %s may not be in the .veg format.\n", filename);
+    throw 10;
+  }
+
+  // === Second pass: parse materials, sets and regions ===
+
+  volumetricMeshParser.rewindToStart();
+
+  if (verbose)
+  {
+    if (numMaterials == 0)
+      printf("Warning: no materials encountered in %s.\n", filename);
+
+    if (numRegions == 0)
+      printf("Warning: no regions encountered in %s.\n", filename);
+  }
+
+  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
+  sets = (Set**) malloc (sizeof(Set*) * numSets);
+  regions = (Region**) malloc (sizeof(Region*) * numRegions);
+
+  // create the "allElements" set, containing all the elements
+  sets[0] = new Set("allElements");
+  for(int el=0; el<numElements; el++)
+    sets[0]->insert(el);
+
+  int countNumMaterials = 0;
+  int countNumSets = 1; // set 0 is "allElements"
+  int countNumRegions = 0;
+
+  map<string,int> materialMap; // establishes a relationship between material's string name and its index in the "materials" array
+  map<string,int> setMap; // establishes a relationship between set's string name and its index in the "sets" array
+  setMap.insert(pair<string,int>(string("allElements"), 0));
+
+  parseState = 0;
+
+  while (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
+  {
+    //printf("%s\n", lineBuffer);
+
+    // exit parsing of comma-separated set elements upon the new * command
+    if ((parseState == 11) && (lineBuffer[0] == '*'))
+      parseState = 0;
+
+    // parse material
+
+    if ((parseState == 0) && (strncmp(lineBuffer, "*MATERIAL", 9) == 0))
+    {
+      volumetricMeshParser.removeWhitespace(lineBuffer);
+
+      // read material name
+      char materialNameC[4096];
+      strcpy(materialNameC, &lineBuffer[9]);
+
+      // read the material type
+      char materialSpecification[4096];
+      if (volumetricMeshParser.getNextLine(lineBuffer) != NULL)
+      {
+        volumetricMeshParser.removeWhitespace(lineBuffer);
+        sscanf(lineBuffer, "%s", materialSpecification);
+      }
+      else
+      {
+        printf("Error: incorrect material in file %s. Offending line:\n%s\n", filename, lineBuffer);
+        throw 11;
+      }
+
+      // seek for first comma in the material type specification
+      char * ch = materialSpecification; 
+      while((*ch != ',') && (*ch != 0)) 
+        ch++;
+
+      if (*ch == 0)
+      {
+        printf("Error parsing file %s. Offending line: %s.\n", filename, lineBuffer);
+        throw 12;
+      }
+
+      // ch now points to the first comma
+      // set the materialType (the string up to the first comma)
+      char materialType[4096];
+      unsigned int materialTypeLength = ch - materialSpecification;
+      memcpy(materialType, materialSpecification, sizeof(unsigned char) * materialTypeLength);
+      *(materialType + materialTypeLength) = 0;
+      // materialType is now set
+
+      ch++;
+      // now, ch points to the first character after the comma
+
+      if (strcmp(materialType, "ENU") == 0)
+      {
+        // material specified by E, nu, density
+        double density, E, nu;
+        sscanf(ch, "%lf,%lf,%lf", &density, &E, &nu);
+
+        if ((E > 0) && (nu > -1.0) && (nu < 0.5) && (density > 0))
+        {
+          // create new material
+          string name(materialNameC);
+          materials[countNumMaterials] = new ENuMaterial(name, density, E, nu);
+          materialMap.insert(pair<string,int>(name, countNumMaterials));
+        }
+        else
+        {
+          printf("Error: incorrect material specification in file %s. Offending line: %s\n", filename, lineBuffer);
+          throw 13;
+        }
+      }
+      else if (strncmp(materialType, "ORTHOTROPIC", 11) == 0)
+      {
+        // orthotropic material 
+        double density = 0.0, E1 = 0.0, E2 = 0.0, E3 = 0.0, nu12 = 0.0, nu23 = 0.0, nu31 = 0.0, G12 = 0.0, G23 = 0.0, G31 = 0.0;
+        double nu = 0.0, G = 1.0;
+        bool useNuAndG = false;
+        double R[9]; // rotation matrix, stored in row-major format
+        memset(R, 0, sizeof(R));
+        R[0] = R[4] = R[8] = 1.0; // default to identity
+
+        // find subtype
+        char * subType = materialType + 11;
+        bool enoughParameters = false;
+
+        if ((sscanf(ch,"%lf,%lf,%lf,%lf", &density, &E1, &E2, &E3) == 4)
+            && ((E1 > 0) && (E2 > 0) && (E3 > 0) && (density > 0)))
+        {
+          // move ch to the next parameters (skip over the four ones that were just read)
+          for(int i = 0; i < 4; i++)
+          {
+            while ((*ch != ',') && (*ch != 0)) 
+              ch++;
+            if (*ch == 0) 
+              break;
+            ch++;
+          }
+          // ch now points to the first character of the fifth parameter, or \0 if there is no fifth parameter
+          // subtype points to the fitst character of the subtype suffix, or \0 if there is no suffix
+
+          if ((*subType == 0) || (strcmp(subType, "_N3G3R9") == 0))
+          {
+            // there is no suffix (*ORTHOTROPIC), or suffix is *ORTHOTROPIC_N3G3R9
+            // parse nu12,nu23,nu31,G12,G23,G31,R0,R1,R2,R3,R4,R5,R6,R7,R8
+            if (sscanf(ch, "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
+                &nu12, &nu23, &nu31, &G12, &G23, &G31,
+                &R[0], &R[1], &R[2], &R[3], &R[4], &R[5], &R[6], &R[7], &R[8]) == 15)
+              enoughParameters = true;
+          }
+          else if (strcmp(subType,"_N3G3") == 0)
+          {
+            // *ORTHOTROPIC_N3G3
+            // parse nu12,nu23,nu31,G12,G23,G31
+            // R is default (identity)
+            if (sscanf(ch, "%lf,%lf,%lf,%lf,%lf,%lf", &nu12, &nu23, &nu31, &G12, &G23, &G31) == 6)
+              enoughParameters = true;
+          }
+          else if (strcmp(subType,"_N1G1R9") == 0)
+          {
+            // *ORTHOTROPIC_N1G1R9
+            // parse nu,G,R0,R1,R2,R3,R4,R5,R6,R7,R8
+            if (sscanf(ch, "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
+              &nu, &G, &R[0], &R[1], &R[2], &R[3], &R[4], &R[5], &R[6], &R[7], &R[8]) == 11)
+            {
+              useNuAndG = true;
+              enoughParameters = true;
+            }
+          }
+          else if (strcmp(subType, "_N1G1") == 0)
+          {
+            // *ORTHOTROPIC_N1G1
+            // parse nu,G
+            // R is default (identity)
+            if (sscanf(ch, "%lf,%lf",&nu,&G) == 2)
+            {
+              useNuAndG = true;
+              enoughParameters = true;
+            }
+          }
+          else if (strcmp(subType,"_N1R9") == 0)
+          {
+            // *ORTHOTROPIC_N1R9
+            // parse nu,R0,R1,R2,R3,R4,R5,R6,R7,R8
+            // G is default (1.0)
+            if (sscanf(ch,"%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
+                &nu, &R[0], &R[1], &R[2], &R[3], &R[4], &R[5], &R[6], &R[7], &R[8]) == 10)
+            {
+              useNuAndG = true;
+              enoughParameters = true;
+            }
+          }
+          else if (strcmp(subType,"_N1") == 0)
+          {
+            // *ORTHOTROPIC_N1
+            // parse nu
+            // G is default (1.0)
+            // R is default (identity)
+            if (sscanf(ch,"%lf", &nu) == 1)
+            {
+              useNuAndG = true;
+              enoughParameters = true;
+            }
+          }
+          else
+          {
+            printf("Error: incorrect orthortropic material type \"%s\" in file %s. Offending line: %s\n", subType, filename, lineBuffer);
+            throw 14;
+          }
+
+          if (useNuAndG && (nu > -1.0) && (nu < 0.5)) // if nu is not in this range, then G_ij is still 0, finally produce an error
+          {
+            // formulas from:
+            // Yijing Li, Jernej Barbic: Stable Corotational Materials, Symposium on Computer Animation 2014
+            nu12 = nu * sqrt(E1 / E2);
+            nu23 = nu * sqrt(E2 / E3);
+            nu31 = nu * sqrt(E3 / E1);
+            G12 = G * sqrt(E1 * E2) / (2.0 * (1.0 + nu));
+            G23 = G * sqrt(E2 * E3) / (2.0 * (1.0 + nu));
+            G31 = G * sqrt(E3 * E1) / (2.0 * (1.0 + nu));
+          }
+        } //end if (sscanf(ch,"%lf,%lf,%lf,%lf", &density, &E1, &E2, &E3) == 4 && (E1 > 0 && E2 > 0 && E3 > 0) )
+        // if the if clause failed, enoughParameters is now false
+
+        if (enoughParameters && (G12 > 0) && (G23 > 0) && (G31 > 0))
+        {
+          // create new material
+          string name(materialNameC);
+          materials[countNumMaterials] = new OrthotropicMaterial(string(materialNameC), density, E1, E2, E3, nu12, nu23, nu31, G12, G23, G31, R);
+          materialMap.insert(pair<string,int>(name,countNumMaterials));
+        }
+        else
+        {
+          printf("Error: incorrect material specification in file %s. Offending line: %s\n", filename, lineBuffer);
+          throw 14;
+        }
+      }
+      else if (strncmp(materialType, "MOONEYRIVLIN", 12) == 0)
+      {
+        // mu01, m10, v1, density
+        double density, mu01, mu10, v1;
+        sscanf(ch, "%lf,%lf,%lf,%lf", &density, &mu01, &mu10, &v1);
+
+        if (density > 0)
+        {
+          // create new material
+          string name(materialNameC);
+          materials[countNumMaterials] = new MooneyRivlinMaterial(name, density, mu01, mu10, v1);
+          materialMap.insert(pair<string,int>(name, countNumMaterials));
+        }
+        else
+        {
+          printf("Error: incorrect material specification in file %s. Offending line:\n%s\n", filename, lineBuffer);
+          throw 15;
+        }
+      }
+      else
+      {
+        printf("Error: incorrect material specification in file %s. Offending line:\n%s\n", filename, lineBuffer);
+        throw 16;
+      }
+
+      countNumMaterials++;
+    }
+
+    // parse region
+    if ((parseState == 0) && (strncmp(lineBuffer, "*REGION,", 7) == 0))
+    {
+      volumetricMeshParser.removeWhitespace(lineBuffer);
+
+      char setNameC[4096];
+      char materialNameC[4096];
+
+      if (volumetricMeshParser.getNextLine(lineBuffer) != NULL)
+      {
+        volumetricMeshParser.removeWhitespace(lineBuffer);
+
+        // format is set, material
+        // seek for first comma
+        char * ch = lineBuffer;
+        while((*ch != ',') && (*ch != 0))
+          ch++;
+
+        if (*ch == 0)
+        {
+          printf("Error parsing file %s. Offending line: %s.\n", filename, lineBuffer);
+          throw 17;
+        }
+
+        // now, ch points to the comma
+        *ch = 0;
+        strcpy(setNameC, lineBuffer);
+        *ch = ','; // restore the lineBuffer
+        ch++;
+        strcpy(materialNameC, ch);
+      }
+      else
+      {
+        printf("Error: file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
+        throw 18;
+      }
+
+      // seek for setNameC
+      int setNum = -1;
+      map<string,int>::iterator it = setMap.find(string(setNameC));
+      if (it != setMap.end())
+      {
+      	setNum = it->second;
+      }
+      else 
+      {
+        printf("Error: set %s not found among the sets.\n", setNameC);
+        throw 19;
+      }
+
+      // seek for materialNameC
+      int materialNum = -1;
+      it = materialMap.find(string(materialNameC));
+      if (it != materialMap.end())
+      {
+      	materialNum = it->second;
+      }
+      else 
+      {
+      	printf("Error: material %s not found among the materials.\n", materialNameC);
+      	throw 20;
+      }
+
+      // create a new region
+      regions[countNumRegions] = new Region(materialNum, setNum);
+      countNumRegions++;
+    }
+   
+    // parse set elements
+    if (parseState == 11)
+    {
+      // parse the next line of the comma-separated elements in the set
+      // we know that lineBuffer[0] != '*' (i.e., not the end of the list), as that case was already previously handled
+
+      volumetricMeshParser.removeWhitespace(lineBuffer);
+      //printf("%s\n", lineBuffer);
+
+      // parse the comma-separated line
+      char * pch;
+      pch = strtok(lineBuffer, ",");
+      while ((pch != NULL) && (isdigit(*pch)))
+      {
+        int newElement = atoi(pch);
+        int ind = newElement-oneIndexedElements;
+        if (ind >= numElements || ind < 0)
+        {
+          printf("Error: set element index: %d out of bounds.\n", newElement);
+          throw 21;
+        }
+        sets[countNumSets-1]->insert(ind); // sets are 0-indexed, but .veg files may be 1-indexed (oneIndexedElements == 1)
+        pch = strtok(NULL, ",");
+      }
+    }
+
+    // parse set
+    if ((parseState == 0) && (strncmp(lineBuffer, "*SET", 4) == 0))
+    {
+      volumetricMeshParser.removeWhitespace(lineBuffer);
+
+      string name(&lineBuffer[4]);
+      sets[countNumSets] = new Set(name);
+      setMap.insert(pair<string,int>(name, countNumSets));
+      countNumSets++;
+      parseState = 11;
+    }
+  }
+
+  // === assign materials to elements and seek for unassigned elements ===
+  assignMaterialsToElements(verbose);
+
+  volumetricMeshParser.close();
+}
+
+VolumetricMesh::VolumetricMesh(int numVertices_, double * vertices_,
+               int numElements_, int numElementVertices_, int * elements_,
+               double E, double nu, double density): numElementVertices(numElementVertices_)
+{
+  numElements = numElements_;
+  numVertices = numVertices_;
+
+  numMaterials = 1;
+  numSets = 1;
+  numRegions = 1;
+
+  vertices = new Vec3d [numVertices];
+  elements = (int**) malloc (sizeof(int*) * numElements);
+  elementMaterial = (int*) malloc (sizeof(int) * numElements);
+  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
+  sets = (Set**) malloc (sizeof(Set*) * numSets);
+  regions = (Region**) malloc (sizeof(Region*) * numRegions);
+
+  for(int i=0; i<numVertices; i++)
+    vertices[i] = Vec3d(vertices_[3*i+0], vertices_[3*i+1], vertices_[3*i+2]);
+
+  Material * material = new ENuMaterial("defaultMaterial", density, E, nu);
+  materials[0] = material;
+
+  Set * set = new Set("allElements");
+
+  int * v = (int*) malloc (sizeof(int) * numElementVertices);
+  for(int i=0; i<numElements; i++)
+  {
+    set->insert(i);
+    elements[i] = (int*) malloc (sizeof(int) * numElementVertices);
+    elementMaterial[i] = 0;
+    for(int j=0; j<numElementVertices; j++)
+    {
+      v[j] = elements_[numElementVertices * i + j];
+      elements[i][j] = v[j];
+    }
+  }
+  free(v);
+
+  sets[0] = set;
+  Region * region = new Region(0, 0);
+  regions[0] = region;
+}
+
+VolumetricMesh::VolumetricMesh(int numVertices_, double * vertices_,
+         int numElements_, int numElementVertices_, int * elements_,
+         int numMaterials_, Material ** materials_,
+         int numSets_, Set ** sets_,
+         int numRegions_, Region ** regions_): numElementVertices(numElementVertices_)
+{
+  numElements = numElements_;
+  numVertices = numVertices_;
+
+  numMaterials = numMaterials_;
+  numSets = numSets_;
+  numRegions = numRegions_;
+
+  vertices = new Vec3d [numVertices];
+  elements = (int**) malloc (sizeof(int*) * numElements);
+  elementMaterial = (int*) malloc (sizeof(int) * numElements);
+  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
+  sets = (Set**) malloc (sizeof(Set*) * numSets);
+  regions = (Region**) malloc (sizeof(Region*) * numRegions);
+
+  for(int i=0; i<numVertices; i++)
+    vertices[i] = Vec3d(vertices_[3*i+0], vertices_[3*i+1], vertices_[3*i+2]);
+
+  int * v = (int*) malloc (sizeof(int) * numElementVertices);
+  for(int i=0; i<numElements; i++)
+  {
+    elements[i] = (int*) malloc (sizeof(int) * numElementVertices);
+    for(int j=0; j<numElementVertices; j++)
+    {
+      v[j] = elements_[numElementVertices * i + j];
+      elements[i][j] = v[j];
+    }
+  }
+  free(v);
+
+  for(int i=0; i<numMaterials; i++)
+    materials[i] = materials_[i]->clone();
+
+  for(int i=0; i<numSets; i++)
+    sets[i] = new Set(*(sets_[i]));
+
+  for(int i=0; i<numRegions; i++)
+    regions[i] = new Region(*(regions_[i]));
+
+  // set elementMaterial:
+  propagateRegionsToElements();
+}
+
+void VolumetricMesh::loadFromMemory(unsigned char * binaryInputStream, elementType * elementType_)
+{
+  int memoryLoad = 1;
+  loadFromBinaryGeneric((void*)binaryInputStream, elementType_, memoryLoad);
+}
+
+void VolumetricMesh::loadFromBinary(FILE * binaryInputStream, elementType * elementType_)
+{
+  int memoryLoad = 0;
+  loadFromBinaryGeneric((void*)binaryInputStream, elementType_, memoryLoad);
+}
+
+void VolumetricMesh::loadFromBinaryGeneric(void * binaryInputStream_, elementType * elementType_, int memoryLoad)
+{
+  unsigned int (*genericRead)(void *, unsigned int, unsigned int, void *);
+
+  void * binaryInputStream;
+  if (memoryLoad)
+  {
+    genericRead = &VolumetricMesh::readFromMemory;
+    binaryInputStream = &(binaryInputStream_);
+  }
+  else
+  {
+    genericRead = &VolumetricMesh::readFromFile;
+    binaryInputStream = binaryInputStream_;
+  }
+
+  double version;
+  if ((int)genericRead(&version, sizeof(double), 1, binaryInputStream) != 1)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read version.\n");
+    throw 0;
+  };
+
+  int eleType;
+  if ((int)genericRead(&eleType, sizeof(int), 1, binaryInputStream) != 1)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read element type.\n");
+    throw 0;
+  };
+
+  switch (eleType)
+  {
+    case TET:
+      *elementType_ = TET;
+      break;
+    case CUBIC:
+      *elementType_ = CUBIC;
+      break;
+    default:
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: unknown mesh type %d in file stream\n", eleType);
+      throw 2;
+      break;
+  }
+
+  // input the number of vertices
+  if ((int)genericRead(&numVertices, sizeof(int), 1, binaryInputStream) != 1)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read numVertices.\n");
+    throw 0;
+  }
+
+  if (numVertices < 0)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: incorrect number of vertices.\n");
+    throw 3; 
+  }
+
+  vertices = new Vec3d [numVertices];
+
+  // input all the vertices
+  double * doubleTempVec = (double *) malloc (sizeof(double) * 3 * numVertices);
+  if ((int)genericRead(doubleTempVec, sizeof(double), 3 * numVertices, binaryInputStream) != 3 * numVertices)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read vertex coordinates.\n");
+    throw 0;
+  }
+
+  for(int vertexIndex=0; vertexIndex<numVertices; vertexIndex++)
+  {
+    vertices[vertexIndex] = Vec3d(&doubleTempVec[vertexIndex*3]);
+  }
+  free(doubleTempVec);
+
+  // input the number of elements
+  if ((int)genericRead(&numElements, sizeof(int), 1, binaryInputStream) != 1)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read numElements.\n");
+    throw 0;
+  }
+
+  if (numElements < 0)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: incorrect number of elements.\n");
+    throw 4; 
+  }
+  // input the number of vertices of every element
+  int numEleVer;
+  if ((int)genericRead(&numEleVer, sizeof(int), 1, binaryInputStream) != 1)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read number of vertices in every element.\n");
+    throw 0;
+  }
+
+  if (numElementVertices > 0 && numEleVer != numElementVertices)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: mismatch in the number of vertices of every element from file stream.\n");
+    throw 5;
+  }
+
+  // input all elements
+  elements = (int**) malloc (sizeof(int*) * numElements);
+  int * intTempVec = (int *) malloc (sizeof(int) * numElementVertices * numElements);
+  if ((int)genericRead(intTempVec, sizeof(int), numElementVertices * numElements, binaryInputStream) != numElementVertices * numElements)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the elements.\n");
+    throw 0;
+  }
+
+  for(int elementIndex=0; elementIndex<numElements; elementIndex++)
+  {
+    elements[elementIndex] = (int*) malloc (sizeof(int) * numElementVertices);
+    memcpy(elements[elementIndex], &intTempVec[elementIndex * numElementVertices], sizeof(int) * numElementVertices);
+  }
+  free(intTempVec);
+
+  // input number of materials
+  if ((int)genericRead(&numMaterials, sizeof(int), 1, binaryInputStream) != 1)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the numMaterials.\n");
+    throw 0;
+  }
+
+  if (numMaterials < 0)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: incorrect number of materials.\n");
+    throw 6; 
+  }
+  // input all the materials
+  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
+  for(int materialIndex=0; materialIndex<numMaterials; materialIndex++)
+  {
+    // input material name
+    char materialName[4096];
+    int length;
+    if ((int)genericRead(&length, sizeof(int), 1, binaryInputStream) != 1)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the length of material name.\n");
+      throw 0;
+    }
+    if ((int)genericRead(materialName, sizeof(char), length, binaryInputStream) != length)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the material name.\n");
+      throw 0;
+    }
+    materialName[length] = '\0';
+
+    // input material type
+    int matType;
+    if ((int)genericRead(&matType, sizeof(int), 1, binaryInputStream) != 1)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the length of material type.\n");
+      throw 0;
+    }
+
+    switch (matType)
+    {
+      case Material::ENU:
+      {
+        double materialProperty[Material::ENU_NUM_PROPERTIES];
+        if ((int)genericRead(materialProperty, sizeof(double), Material::ENU_NUM_PROPERTIES, binaryInputStream) != Material::ENU_NUM_PROPERTIES)
+        {
+          printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the material properties (ENU).\n");
+          throw 0;
+        }
+
+        if ((materialProperty[Material::ENU_E] > 0) && (materialProperty[Material::ENU_NU] > -1.0) && (materialProperty[Material::ENU_NU] < 0.5) && (materialProperty[Material::ENU_DENSITY] > 0))
+        {
+          // create new material
+          materials[materialIndex] = new ENuMaterial(materialName, materialProperty[Material::ENU_DENSITY], materialProperty[Material::ENU_E], materialProperty[Material::ENU_NU]);
+        }
+        else
+        {
+          printf("Error in VolumetricMesh::loadFromBinaryGeneric: incorrect material specification in file stream.\n");
+          throw 7;
+        }
+      }
+      break;
+
+      case Material::ORTHOTROPIC:
+      {
+        double materialProperty[Material::ORTHOTROPIC_NUM_PROPERTIES];
+        if ((int)genericRead(materialProperty, sizeof(double), Material::ORTHOTROPIC_NUM_PROPERTIES, binaryInputStream) != Material::ORTHOTROPIC_NUM_PROPERTIES)
+        {
+          printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the material properties (ORTHOTROPIC).\n");
+          throw 0;
+        }
+        double R[9];
+        if ((int)genericRead(R, sizeof(double), 9, binaryInputStream) != 9)
+        {
+          printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the material properties (ORTHOTROPIC).\n");
+          throw 0;
+        }
+        if ((materialProperty[Material::ORTHOTROPIC_E1] > 0) && (materialProperty[Material::ORTHOTROPIC_E2] > 0) && (materialProperty[Material::ORTHOTROPIC_E3] > 0) && (materialProperty[Material::ORTHOTROPIC_G12] > 0) && (materialProperty[Material::ORTHOTROPIC_G23] > 0) && (materialProperty[Material::ORTHOTROPIC_G31] > 0) && (materialProperty[Material::ORTHOTROPIC_DENSITY] > 0))
+        {
+          // create new material
+          materials[materialIndex] = new OrthotropicMaterial(materialName, materialProperty[Material::ORTHOTROPIC_DENSITY], materialProperty[Material::ORTHOTROPIC_E1], materialProperty[Material::ORTHOTROPIC_E2], materialProperty[Material::ORTHOTROPIC_E3], materialProperty[Material::ORTHOTROPIC_NU12], materialProperty[Material::ORTHOTROPIC_NU23], materialProperty[Material::ORTHOTROPIC_NU31], materialProperty[Material::ORTHOTROPIC_G12], materialProperty[Material::ORTHOTROPIC_G23], materialProperty[Material::ORTHOTROPIC_G31], R);
+        }
+        else
+        {
+          printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the material properties (ORTHOTROPIC).\n");
+          throw 14;
+        }
+      }
+      break;
+
+      case Material::MOONEYRIVLIN:
+      {
+        double materialProperty[Material::MOONEYRIVLIN_NUM_PROPERTIES];
+        if ((int)genericRead(materialProperty, sizeof(double), Material::MOONEYRIVLIN_NUM_PROPERTIES, binaryInputStream) != Material::MOONEYRIVLIN_NUM_PROPERTIES)
+        {
+          printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the material properties (MOONEYRIVLIN).\n");
+          throw 0;
+        }
+        if (materialProperty[Material::MOONEYRIVLIN_DENSITY] > 0)
+        {
+          // create new material
+          materials[materialIndex] = new MooneyRivlinMaterial(materialName, materialProperty[Material::MOONEYRIVLIN_DENSITY], materialProperty[Material::MOONEYRIVLIN_MU01], materialProperty[Material::MOONEYRIVLIN_MU10], materialProperty[Material::MOONEYRIVLIN_V1]);
+        }
+        else
+        {
+          printf("Error in VolumetricMesh::loadFromBinaryGeneric: incorrect material specification in file stream. \n");
+          throw 8;
+        }
+      }
+      break;
+
+      default:
+        printf("Error in VolumetricMesh::loadFromBinaryGeneric: material type %d is unknown.\n", matType);
+        throw 9;
+      break;
+    }
+  }  // for materialIndex
+
+  // input the number of sets
+  if ((int)genericRead(&numSets, sizeof(int), 1, binaryInputStream) != 1)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the numSets.\n");
+    throw 0;
+  }
+
+  sets = (Set**) malloc (sizeof(Set*) * numSets);
+
+  // create the "allElements" set, containing all the elements
+  sets[0] = new Set("allElements");
+  for(int elementIndex=0; elementIndex<numElements; elementIndex++)
+    sets[0]->insert(elementIndex);
+
+  // input all the other sets (must start from set 1)
+  for(int setIndex = 1; setIndex < numSets; setIndex++)
+  {
+    // input the name of the set
+    char setName[4096];
+    int length;
+    if ((int)genericRead(&length, sizeof(int), 1, binaryInputStream) != 1)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the length of the set name.\n");
+      throw 0;
+    }
+
+    if ((int)genericRead(setName, sizeof(char), length, binaryInputStream) != length)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the set name.\n");
+      throw 0;
+    }
+    setName[length] = '\0';
+    sets[setIndex] = new Set(setName);
+
+    // input number of elements in the current set
+    int cardinality;
+    if ((int)genericRead(&cardinality, sizeof(int), 1, binaryInputStream) != 1)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the number of elements in current set.\n");
+      throw 0;
+    }
+    intTempVec = (int *) malloc (sizeof(int) * cardinality);
+
+    // input all the elements in the current set
+    if ((int)genericRead(intTempVec, sizeof(int), cardinality, binaryInputStream) != cardinality)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the elements in current set.\n"); throw 0;
+    }
+
+    for(int setElementIndex=0; setElementIndex < cardinality; setElementIndex++)
+      sets[setIndex]->insert(intTempVec[setElementIndex]);
+    free(intTempVec);
+  }
+
+  // input the number of regions
+  if ((int)genericRead(&numRegions, sizeof(int), 1, binaryInputStream) != 1)
+  {
+    printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the numRegions.\n");
+    throw 0;
+  }
+
+  // input all the regions, for each region (material, set)
+  regions = (Region**) malloc (sizeof(Region*) * numRegions);
+  for(int regionIndex=0; regionIndex < numRegions; regionIndex++)
+  {
+    int materialIndex;
+    if ((int)genericRead(&materialIndex, sizeof(int), 1, binaryInputStream) != 1)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the materialIndex.\n");
+      throw 0;
+    }
+    int setIndex;
+    if ((int)genericRead(&setIndex, sizeof(int), 1, binaryInputStream) != 1)
+    {
+      printf("Error in VolumetricMesh::loadFromBinaryGeneric: cannot read the setIndex.\n");
+      throw 0;
+    }
+    regions[regionIndex] = new Region(materialIndex, setIndex);
+  } // for regionIndex
+
+  // === assign materials to elements and handle the unassigned elements ===
+  int verbose = 0;
+  assignMaterialsToElements(verbose);
+}
+
+VolumetricMesh::VolumetricMesh(const VolumetricMesh & volumetricMesh)
+{
+  numVertices = volumetricMesh.numVertices;
+  vertices = new Vec3d [numVertices];
+  for(int i=0; i<numVertices; i++)
+    vertices[i] = volumetricMesh.vertices[i];
+
+  numElementVertices = volumetricMesh.numElementVertices;
+  numElements = volumetricMesh.numElements;
+  elements = (int**) malloc (sizeof(int*) * numElements);
+  for(int i=0; i<numElements; i++)
+  {
+    elements[i] = (int*) malloc (sizeof(int) * numElementVertices);
+    for(int j=0; j<numElementVertices; j++)
+      elements[i][j] = (volumetricMesh.elements)[i][j];
+  }
+
+  numMaterials = volumetricMesh.numMaterials;
+  numSets = volumetricMesh.numSets;
+  numRegions = volumetricMesh.numRegions;
+
+  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
+  for(int i=0; i<numMaterials; i++)
+    materials[i] = (volumetricMesh.materials)[i]->clone();
+
+  sets = (Set**) malloc (sizeof(Set*) * numSets);
+  for(int i=0; i<numSets; i++)
+    sets[i] = new Set(*((volumetricMesh.sets)[i]));
+
+  regions = (Region**) malloc (sizeof(Region*) * numRegions);
+  for(int i=0; i<numRegions; i++)
+    regions[i] = new Region((*(volumetricMesh.regions)[i]));
+
+  elementMaterial = (int*) malloc (sizeof(int) * numElements);
+  for(int i=0; i<numElements; i++)
+    elementMaterial[i] = (volumetricMesh.elementMaterial)[i];
+}
+
+void VolumetricMesh::loadFromBinary(const char * filename, elementType * elementType_)
+{
+  FILE * fin = fopen(filename, "rb");
+
+  if (fin == NULL)
+  {
+    printf("Error in VolumetricMesh::loadFromBinary: could not open file %s.\n",filename);
+    throw 1;
+  }
+  loadFromBinary(fin, elementType_);
+  
+  fclose(fin);
+}
+
+int VolumetricMesh::saveToAscii(const char * filename, elementType elementType_) const // saves the mesh to a .veg file
+{       
+  FILE * fout = fopen(filename, "w");
+  if (!fout)
+  {       
+    printf("Error: could not write to %s.\n",filename);
+    return 1;
+  }         
+
+  fprintf(fout, "# Vega mesh file.\n");
+  fprintf(fout, "# %d vertices, %d elements\n", numVertices, numElements);
+  fprintf(fout, "\n");
+          
+  // write vertices
+  fprintf(fout,"*VERTICES\n");
+  fprintf(fout,"%d 3 0 0\n", numVertices);
+          
+  for(int i=0; i < numVertices; i++)  
+  {
+    const Vec3d & v = getVertex(i);
+    fprintf(fout,"%d %.15G %.15G %.15G\n", i+1, v[0], v[1], v[2]);
+  }   
+  fprintf(fout, "\n");
+
+  // write elements
+  fprintf(fout,"*ELEMENTS\n");
+
+  char elementName[4096] = "INVALID";
+  if (elementType_ == TET)
+    strcpy(elementName, "TET");
+  if (elementType_ == CUBIC)
+    strcpy(elementName, "CUBIC");
+  fprintf(fout,"%s\n", elementName);
+
+  fprintf(fout,"%d %d 0\n", numElements, numElementVertices);
+
+  for(int el=0; el < numElements; el++)
+  {   
+    fprintf(fout,"%d ", el+1);
+    for(int j=0; j < numElementVertices; j++)
+    {   
+      fprintf(fout, "%d", getVertexIndex(el, j) + 1);
+      if (j != numElementVertices - 1)
+        fprintf(fout," ");
+    } 
+    fprintf(fout,"\n");
+  }     
+  fprintf(fout, "\n");
+
+  // write materials
+  for(int materialIndex=0; materialIndex < numMaterials; materialIndex++)
+  {
+    string name = materials[materialIndex]->getName();
+    fprintf(fout, "*MATERIAL %s\n", name.c_str());
+
+    if (materials[materialIndex]->getType() == Material::ENU)
+    {
+      ENuMaterial * material = downcastENuMaterial(materials[materialIndex]);
+      double density = material->getDensity();
+      double E = material->getE();
+      double nu = material->getNu();
+      fprintf(fout, "ENU, %.15G, %.15G, %.15G\n", density, E, nu);
+    }
+
+    if (materials[materialIndex]->getType() == Material::ORTHOTROPIC)
+    {
+      OrthotropicMaterial * material = downcastOrthotropicMaterial(materials[materialIndex]);
+      double density = material->getDensity();
+
+      double E1 = material->getE1();
+      double E2 = material->getE2();
+      double E3 = material->getE3();
+      double nu12 = material->getNu12();
+      double nu23 = material->getNu23();
+      double nu31 = material->getNu31();
+      double G12 = material->getG12();
+      double G23 = material->getG23();
+      double G31 = material->getG31();
+      double R[9];
+      material->getR(R);
+
+      fprintf(fout, "ORTHOTROPIC, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G, %.15G\n", density, E1, E2, E3, nu12, nu23, nu31, G12, G23, G31, R[0], R[1], R[2], R[3], R[4], R[5], R[6], R[7], R[8]);
+    }
+
+    if (materials[materialIndex]->getType() == Material::MOONEYRIVLIN)
+    {
+      MooneyRivlinMaterial * material = downcastMooneyRivlinMaterial(materials[materialIndex]);
+      double density = material->getDensity();
+      double mu01 = material->getmu01();
+      double mu10 = material->getmu10();
+      double v1 = material->getv1();
+      fprintf(fout, "MOONEYRIVLIN, %.15G, %.15G, %.20G %.15G\n", density, mu01, mu10, v1);
+    }
+    fprintf(fout, "\n");
+  }
+
+  // write sets (skip the allElements set)
+  for(int setIndex=1; setIndex < numSets; setIndex++)
+  {
+    string name = sets[setIndex]->getName();
+    fprintf(fout, "*SET %s\n", name.c_str());
+    set<int> setElements;
+    sets[setIndex]->getElements(setElements);
+    int count = 0;
+    for(set<int>::iterator iter = setElements.begin(); iter != setElements.end(); iter++)
+    {
+      fprintf(fout, "%d, ", *iter + 1); // .veg files are 1-indexed
+      count++;
+      if (count == 8)
+      {
+        fprintf(fout, "\n");
+        count = 0;
+      }
+    }
+    if (count != 0)
+      fprintf(fout, "\n");
+    fprintf(fout, "\n");
+  }
+
+  // write regions
+  for(int regionIndex=0; regionIndex < numRegions; regionIndex++)
+  {
+    int materialIndex = regions[regionIndex]->getMaterialIndex();
+    int setIndex = regions[regionIndex]->getSetIndex();
+
+    fprintf(fout, "*REGION\n");
+    fprintf(fout, "%s, %s\n", sets[setIndex]->getName().c_str(), materials[materialIndex]->getName().c_str());
+    fprintf(fout, "\n");
+  }
+        
+  fclose(fout);
+  return 0;
+}   
+
+int VolumetricMesh::save(const char * filename) const //  for backward compatibility
+{
+  return saveToAscii(filename);
+}
+
+int VolumetricMesh::saveToBinary(const char * filename, unsigned int * bytesWritten, elementType elementType_) const
+{
+  FILE * fout = fopen(filename, "wb");
+  if (!fout)
+  {       
+    printf("Error: could not write to %s.\n", filename);
+    return 1;
+  }         
+  int code = saveToBinary(fout, bytesWritten, elementType_);
+  fclose(fout);
+  return code;
+}
+
+unsigned int VolumetricMesh::readFromFile(void * buf, unsigned int elementSize, unsigned int numElements, void * fin)
+{
+  return fread(buf, elementSize, numElements, (FILE*)fin);
+}
+
+unsigned int VolumetricMesh::readFromMemory(void * buf, unsigned int elementSize, unsigned int numElements, void * memoryLocation_)
+{
+  unsigned char * memoryLocation = (unsigned char *)(*(void **)(memoryLocation_));
+  unsigned int numBytes = elementSize * numElements;
+  memcpy(buf, memoryLocation, numBytes);
+  (*(void **)(memoryLocation_)) = (void *)((unsigned char *)(*(void **)(memoryLocation_)) + numBytes);
+  return numElements;
+}
+
+int VolumetricMesh::saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten, elementType elementType_, bool countBytesOnly) const
+{
+  unsigned int totalBytesWritten = 0;
+  unsigned int itemsWritten;
+
+  // output the binary file version (1x double)
+  const double version = 1.0;
+  itemsWritten = 1;
+  if (!countBytesOnly)
+    itemsWritten = fwrite(&version, sizeof(double), 1, binaryOutputStream);
+  if (itemsWritten < 1)
+    return 1;
+  totalBytesWritten += itemsWritten * sizeof(double);
+
+  // output the element type (1x int)
+  int eleType = elementType_;
+  itemsWritten = 1;
+  if (!countBytesOnly)
+    itemsWritten = fwrite(&eleType, sizeof(int), 1, binaryOutputStream);
+  if (itemsWritten < 1)
+    return 1;
+  totalBytesWritten += itemsWritten * sizeof(int);
+
+  // output the number of vertices (1x int)
+  itemsWritten = 1;
+  if (!countBytesOnly)
+    itemsWritten = fwrite(&numVertices, sizeof(int), 1, binaryOutputStream);
+  if (itemsWritten < 1)
+    return 1;
+  totalBytesWritten += itemsWritten * sizeof(int);
+
+  // output the vertices (3 doubles x numVertices)
+  for(int vertexIndex = 0; vertexIndex < numVertices; vertexIndex++)
+  {
+    const Vec3d & v = getVertex(vertexIndex);
+    double v_array[3];
+    v.convertToArray(v_array);
+    itemsWritten = 3;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(v_array, sizeof(double), 3, binaryOutputStream); 
+    if (itemsWritten < 3)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(double);
+  }
+
+  // output the number of elements (1x int)
+  itemsWritten = 1;
+  if (!countBytesOnly)
+    itemsWritten = fwrite(&numElements, sizeof(int), 1, binaryOutputStream);
+  if (itemsWritten < 1)
+    return 1;
+  totalBytesWritten += itemsWritten * sizeof(int);
+
+  // output the number of vertices of every element (1x int)
+  itemsWritten = 1;
+  if (!countBytesOnly)
+    itemsWritten = fwrite(&numElementVertices, sizeof(int), 1, binaryOutputStream);
+  if (itemsWritten < 1)
+    return 1;
+  totalBytesWritten += itemsWritten * sizeof(int);
+
+  // output the vertex indices of every element
+  for(int elementIndex = 0; elementIndex < numElements; elementIndex++)
+  {
+    for(int vertexIndex = 0; vertexIndex < numElementVertices; vertexIndex++)
+    {
+      int temp = getVertexIndex(elementIndex, vertexIndex);
+      itemsWritten = 1;
+      if (!countBytesOnly)
+        itemsWritten = fwrite(&temp, sizeof(int), 1, binaryOutputStream);
+      if (itemsWritten < 1)
+        return 1;
+      totalBytesWritten += itemsWritten * sizeof(int);
+    } 
+  }
+
+  // output the number of materials
+  itemsWritten = 1;
+  if (!countBytesOnly)
+    itemsWritten = fwrite(&numMaterials, sizeof(int), 1, binaryOutputStream);
+  if (itemsWritten < 1)
+    return 1;
+  totalBytesWritten += itemsWritten * sizeof(int);
+
+  // output materials
+  for(int materialIndex = 0; materialIndex < numMaterials; materialIndex++)
+  {
+    string name = materials[materialIndex]->getName();
+    unsigned int length = name.size();
+
+    // output material name
+    itemsWritten = 1;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(&length, sizeof(int), 1, binaryOutputStream); // write the length of the material name
+    if (itemsWritten < 1)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(int);
+    
+    itemsWritten = length;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(name.c_str(), sizeof(char), length, binaryOutputStream);
+    if (itemsWritten < length)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(char);
+
+    // output material type (1x int)
+    int matType = materials[materialIndex]->getType();
+    
+    itemsWritten = 1;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(&matType, sizeof(int), 1, binaryOutputStream);
+    if (itemsWritten < 1)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(int);
+    switch (matType)
+    {
+      case Material::ENU:
+      {
+        ENuMaterial * material = downcastENuMaterial(materials[materialIndex]);
+        double materialProperty[Material::ENU_NUM_PROPERTIES];
+        materialProperty[Material::ENU_DENSITY] = material->getDensity();
+        materialProperty[Material::ENU_E] = material->getE();
+        materialProperty[Material::ENU_NU] = material->getNu();
+
+        itemsWritten = Material::ENU_NUM_PROPERTIES;
+        if (!countBytesOnly)
+          itemsWritten = fwrite(materialProperty, sizeof(double), Material::ENU_NUM_PROPERTIES, binaryOutputStream);
+        if (itemsWritten < Material::ENU_NUM_PROPERTIES)
+          return 1;
+        totalBytesWritten += itemsWritten * sizeof(double);
+      }
+      break;
+
+      case Material::MOONEYRIVLIN:
+      {
+        MooneyRivlinMaterial * material = downcastMooneyRivlinMaterial(materials[materialIndex]);
+        double materialProperty[Material::MOONEYRIVLIN_NUM_PROPERTIES];
+        materialProperty[Material::MOONEYRIVLIN_DENSITY] = material->getDensity();
+        materialProperty[Material::MOONEYRIVLIN_MU01] = material->getmu01();
+        materialProperty[Material::MOONEYRIVLIN_MU10] = material->getmu10();
+        materialProperty[Material::MOONEYRIVLIN_V1] = material->getv1();
+
+        itemsWritten = Material::MOONEYRIVLIN_NUM_PROPERTIES;
+        if (!countBytesOnly)
+          itemsWritten = fwrite(materialProperty, sizeof(double), Material::MOONEYRIVLIN_NUM_PROPERTIES, binaryOutputStream);
+        if (itemsWritten < Material::MOONEYRIVLIN_NUM_PROPERTIES)
+          return 1;
+        totalBytesWritten += itemsWritten * sizeof(double);
+      }
+      break;
+
+      case Material::ORTHOTROPIC:
+      {
+        OrthotropicMaterial * material = downcastOrthotropicMaterial(materials[materialIndex]);
+        double materialProperty[Material::ORTHOTROPIC_NUM_PROPERTIES];
+        materialProperty[Material::ORTHOTROPIC_DENSITY] = material->getDensity();
+        materialProperty[Material::ORTHOTROPIC_E1] = material->getE1();
+        materialProperty[Material::ORTHOTROPIC_E2] = material->getE2();
+        materialProperty[Material::ORTHOTROPIC_E3] = material->getE3();
+        materialProperty[Material::ORTHOTROPIC_NU12] = material->getNu12();
+        materialProperty[Material::ORTHOTROPIC_NU23] = material->getNu23();
+        materialProperty[Material::ORTHOTROPIC_NU31] = material->getNu31();
+        materialProperty[Material::ORTHOTROPIC_G12] = material->getG12();
+        materialProperty[Material::ORTHOTROPIC_G23] = material->getG23();
+        materialProperty[Material::ORTHOTROPIC_G31] = material->getG31();
+
+        itemsWritten = Material::ORTHOTROPIC_NUM_PROPERTIES;
+        if (!countBytesOnly)
+          itemsWritten = fwrite(materialProperty, sizeof(double), Material::ORTHOTROPIC_NUM_PROPERTIES, binaryOutputStream);
+        if (itemsWritten < Material::ORTHOTROPIC_NUM_PROPERTIES)
+          return 1;
+        totalBytesWritten += itemsWritten * sizeof(double);
+
+        double R[9];
+        material->getR(R);
+
+        itemsWritten = 9;
+        if (!countBytesOnly)
+          itemsWritten = fwrite(R, sizeof(double), 9, binaryOutputStream);
+        if (itemsWritten < 9)
+          return 1;
+        totalBytesWritten += itemsWritten * sizeof(double);
+      }
+      break;
+
+      default:
+      {
+        printf("Error: material type %d is unknown.\n", matType);
+          return 1;
+      }
+      break;
+    }
+
+  }  // for materialIndex
+
+  // output the number of sets
+  itemsWritten = 1;
+  if (!countBytesOnly)
+    itemsWritten = fwrite(&numSets, sizeof(int), 1, binaryOutputStream);
+  if (itemsWritten < 1)
+    return 1;
+  totalBytesWritten += itemsWritten * sizeof(int);
+
+  // output sets (skip the allElements set whose index is 0)
+  for(int setIndex = 1; setIndex < numSets; setIndex++)
+  {
+    char * name = (char*) malloc (sizeof(char) * (sets[setIndex]->getName().length()+1));
+    strcpy(name, sets[setIndex]->getName().c_str());
+
+    // output the name of the set
+    unsigned int length = strlen(name);
+    itemsWritten = 1;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(&length, sizeof(int), 1, binaryOutputStream);
+    if (itemsWritten < 1)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(int);
+
+    itemsWritten = length;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(name, sizeof(char), length, binaryOutputStream);
+    if (itemsWritten < length)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(char);
+
+    std::set<int> setElements;
+    sets[setIndex]->getElements(setElements);
+
+    // output the number of elements in the current set
+    unsigned int cardinality = setElements.size();
+    itemsWritten = 1;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(&cardinality, sizeof(int), 1, binaryOutputStream);
+    if (itemsWritten < 1)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(int);
+
+    int * elementGroup = (int *) malloc (sizeof(int) * cardinality);
+    int count = 0;
+    for(std::set<int>::iterator iter = setElements.begin(); iter != setElements.end(); iter++)
+    {
+      elementGroup[count] = *iter;
+      count++;
+    }
+    // output the current set
+    itemsWritten = cardinality;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(elementGroup, sizeof(int), cardinality, binaryOutputStream);
+    if (itemsWritten < cardinality)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(int);
+    free(elementGroup);
+
+    free(name);
+  }  // for setIndex
+
+  // output the number of regions
+  itemsWritten = 1;
+  if (!countBytesOnly)
+    itemsWritten = fwrite(&numRegions, sizeof(int), 1, binaryOutputStream);
+  if (itemsWritten < 1)
+    return 1;
+  totalBytesWritten += itemsWritten * sizeof(int);
+
+  // output regions (for each region, output material and set)
+  for(int regionIndex=0; regionIndex < numRegions; regionIndex++)
+  {
+    int materialIndex = regions[regionIndex]->getMaterialIndex();
+    itemsWritten = 1;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(&materialIndex, sizeof(int), 1, binaryOutputStream);
+    if (itemsWritten < 1)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(int);
+
+    int setIndex = regions[regionIndex]->getSetIndex();
+    itemsWritten = 1;
+    if (!countBytesOnly)
+      itemsWritten = fwrite(&setIndex, sizeof(int), 1, binaryOutputStream);
+    if (itemsWritten < 1)
+      return 1;
+    totalBytesWritten += itemsWritten * sizeof(int);
+  }
+
+  if (bytesWritten != NULL)
+    *bytesWritten = totalBytesWritten;
+
+  return 0;
+}
+
+VolumetricMesh::elementType VolumetricMesh::getElementTypeASCII(const char * filename)
+{
+  //printf("Parsing %s... (for element type determination)\n",filename);fflush(NULL);
+  elementType elementType_;
+
+  // parse the .veg file
+  VolumetricMeshParser volumetricMeshParser;
+  elementType_ = INVALID;
+
+  if (volumetricMeshParser.open(filename) != 0)
+  {
+    printf("Error: could not open file %s.\n",filename);
+    return elementType_;
+  }
+
+  char lineBuffer[1024];
+  while (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
+  {
+    //printf("%s\n", lineBuffer);
+
+    // seek for *ELEMENTS
+    if (strncmp(lineBuffer, "*ELEMENTS", 9) == 0)
+    {
+      // parse element type
+      if (volumetricMeshParser.getNextLine(lineBuffer) != NULL)
+      {
+        volumetricMeshParser.removeWhitespace(lineBuffer);
+
+        if (strncmp(lineBuffer, "TET", 3) == 0)
+          elementType_ = TET;
+        else if (strncmp(lineBuffer, "CUBIC", 5) == 0)
+          elementType_ = CUBIC;
+        else
+        {
+          printf("Error: unknown mesh type %s in file %s\n", lineBuffer, filename);
+          return elementType_;
+        }
+      }
+      else
+      {
+        printf("Error (getElementType): file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
+        return elementType_;
+      }
+    }
+  }
+
+  volumetricMeshParser.close();
+
+  if (elementType_ == INVALID)
+    printf("Error: could not determine the mesh type in file %s. File may not be in .veg format.\n", filename);
+
+  return elementType_;
+}
+
+VolumetricMesh::elementType VolumetricMesh::getElementTypeBinary(const char * filename)
+{
+  FILE * fin = fopen(filename, "rb");
+  if (fin == NULL)
+  {
+    printf("Error in VolumetricMesh::getElementTypeBinary: could not open file %s.\n",filename);
+    exit(0);
+  }
+
+  elementType elementType_ = getElementType(fin);
+  fclose(fin);
+
+  return (elementType_);
+}
+
+VolumetricMesh::elementType VolumetricMesh::getElementType(const char * filename, fileFormatType fileFormat) 
+{
+  switch (fileFormat)
+  {
+  case ASCII:
+    return VolumetricMesh::getElementTypeASCII(filename);
+  	break;
+
+  case BINARY:
+    return VolumetricMesh::getElementTypeBinary(filename);
+    break;
+
+  default:
+    printf("Error: the file format %d is unknown. \n", fileFormat);
+    exit(0);
+    break;
+  }
+}
+
+VolumetricMesh::elementType VolumetricMesh::getElementType(void * fin_, int memoryLoad)
+{
+  unsigned int (*genericRead)(void *, unsigned int, unsigned int, void *);
+
+  void * fin;
+  if (memoryLoad)
+  {
+    genericRead = &VolumetricMesh::readFromMemory;
+    fin = &(fin_);
+  }
+  else
+  {
+    genericRead = &VolumetricMesh::readFromFile;
+    fin = fin_;
+  }
+
+  double version;
+  if ((int)genericRead(&version, sizeof(double), 1, fin) != 1)
+  {
+    printf("Error in VolumetricMesh::getElementType: cannot read version.\n");
+    throw 0;
+  };
+
+  int eleType;
+  if ((int)genericRead(&eleType, sizeof(int), 1, fin) != 1)
+  {
+    printf("Error in VolumetricMesh::getElementType: cannot read the element type from the stream\n");
+    throw 1;
+  }
+  
+  // rewind the size of an integer and a double
+  if (memoryLoad)
+  {
+    fin = (void *)((unsigned char *)fin - ((int)sizeof(int)) - ((int)sizeof(double)));
+  }
+  else
+  {
+    int offset = -((int)sizeof(int)) - ((int)sizeof(double));
+    fseek((FILE *)fin, offset, SEEK_CUR);  
+  }
+
+  switch (eleType)
+  {
+    case TET:
+      return TET;
+    break;
+
+    case CUBIC:
+      return CUBIC;
+    break;
+
+    default:
+      printf("Error: could not determine the mesh type from the stream.\n");
+    break;
+  }
+
+  return INVALID;
+}
+
+double VolumetricMesh::getVolume() const
+{
+  double vol = 0.0;
+  for(int el=0; el<numElements; el++)
+    vol += getElementVolume(el);
+  return vol;
+}
+
+void VolumetricMesh::getVertexVolumes(double * vertexVolumes) const
+{
+  memset(vertexVolumes, 0, sizeof(double) * numVertices);
+  double factor = 1.0 / numElementVertices;
+  for(int el=0; el<numElements; el++)
+  {
+    double volume = getElementVolume(el);
+    for(int j=0; j<numElementVertices; j++)
+      vertexVolumes[getVertexIndex(el, j)] += factor * volume;
+  }
+}
+
+Vec3d VolumetricMesh::getElementCenter(int el) const
+{
+  Vec3d pos(0,0,0);
+  for(int i=0; i<numElementVertices; i++)
+    pos += getVertex(el,i);
+
+  pos *= 1.0 / numElementVertices;
+
+  return pos;
+}
+
+void VolumetricMesh::getVerticesInElements(vector<int> & elements_, vector<int> & vertices_) const
+{
+  set<int> ver;
+  for(unsigned int i=0; i< elements_.size(); i++)
+    for(int j=0; j< numElementVertices; j++)
+      ver.insert(getVertexIndex(elements_[i],j));
+
+  vertices_.clear();
+  set<int>::iterator iter;
+  for(iter = ver.begin(); iter != ver.end(); iter++)
+    vertices_.push_back(*iter);
+}
+
+void VolumetricMesh::getElementsTouchingVertices(vector<int> & vertices_, vector<int> & elements_) const
+{
+  set<int> ver;
+  for(unsigned int i=0; i<vertices_.size(); i++)
+    ver.insert(vertices_[i]);
+
+  elements_.clear();
+  for(int i=0; i< numElements; i++)
+  {
+    set<int> :: iterator iter;
+    for(int j=0; j<numElementVertices; j++)
+    {
+      iter = ver.find(getVertexIndex(i,j));
+      if (iter != ver.end())
+      {
+        elements_.push_back(i);
+        break;
+      }
+    }
+  }
+}
+
+void VolumetricMesh::getVertexNeighborhood(vector<int> & vertices_, vector<int> & neighborhood) const
+{
+  vector<int> elements_;
+  getElementsTouchingVertices(vertices_, elements_);
+  getVerticesInElements(elements_, neighborhood);
+}
+
+double VolumetricMesh::getMass() const
+{
+  double mass = 0.0;
+  for(int i=0; i< getNumRegions(); i++)
+  {
+    const Region * region = getRegion(i);
+    double density = getMaterial(region->getMaterialIndex())->getDensity();
+    set<int> setElements; // elements in the region
+    getSet(region->getSetIndex())->getElements(setElements);
+
+    // over all elements in the region
+    for(set<int> :: iterator iter = setElements.begin(); iter != setElements.end(); iter++)
+    {
+      int element = *iter;
+      double elementVolume = getElementVolume(element);
+      double elementMass = elementVolume * density;
+      mass += elementMass;
+    }
+  }
+
+  return mass;
+}
+
+void VolumetricMesh::getInertiaParameters(double & mass, Vec3d & centerOfMass, Mat3d & inertiaTensor) const
+{
+  mass = 0.0;
+  centerOfMass[0] = centerOfMass[1] = centerOfMass[2] = 0;
+  inertiaTensor[0][0] = inertiaTensor[0][1] = inertiaTensor[0][2] = 0;
+  inertiaTensor[1][0] = inertiaTensor[1][1] = inertiaTensor[1][2] = 0;
+  inertiaTensor[2][0] = inertiaTensor[2][1] = inertiaTensor[2][2] = 0;
+
+  // compute mass, center of mass, inertia tensor
+  for(int i=0; i< getNumRegions(); i++)
+  {
+    const Region * region = getRegion(i);
+    double density = getMaterial(region->getMaterialIndex())->getDensity();
+    set<int> setElements; // elements in the region
+    getSet(region->getSetIndex())->getElements(setElements);
+
+    // over all elements in the region
+    for(set<int> :: iterator iter = setElements.begin(); iter != setElements.end(); iter++)
+    {
+      int element = *iter;
+      double elementVolume = getElementVolume(element);
+      double elementMass = elementVolume * density;
+
+      mass += elementMass;
+      Vec3d elementCenter = getElementCenter(element);
+      centerOfMass += elementMass * elementCenter;
+
+      Mat3d elementITUnitDensity;
+      getElementInertiaTensor(element, elementITUnitDensity);
+
+      double a = elementCenter[0];
+      double b = elementCenter[1];
+      double c = elementCenter[2];
+
+      Mat3d elementITCorrection
+       (  b*b + c*c, -a*b, -a*c,
+         -a*b, a*a + c*c, -b*c,
+         -a*c, -b*c, a*a + b*b );
+
+      Mat3d elementIT = density * elementITUnitDensity + elementMass * elementITCorrection;
+
+      inertiaTensor += elementIT;
+    }
+  }
+
+  //printf("final mass: %G\n",mass);
+  centerOfMass /= mass;
+
+  // correct inertia tensor so it's around the center of mass
+  double a = centerOfMass[0];
+  double b = centerOfMass[1];
+  double c = centerOfMass[2];
+
+  Mat3d correction
+       ( b*b + c*c, -a*b, -a*c,
+         -a*b, a*a + c*c, -b*c,
+         -a*c, -b*c, a*a + b*b );
+
+  inertiaTensor -= mass * correction;
+}
+
+void VolumetricMesh::getMeshGeometricParameters(Vec3d & centroid, double * radius) const
+{
+  // compute centroid
+  centroid = Vec3d(0, 0, 0);
+  for(int i=0; i < numVertices ; i++)
+    centroid += getVertex(i);
+
+  centroid /= numVertices;
+
+  // compute radius
+  *radius = 0;
+  for(int i=0; i < numVertices; i++)
+  {
+    Vec3d vertex = getVertex(i);
+    double dist = len(vertex - centroid);
+    if (dist > *radius)
+      *radius = dist;
+  }
+}
+
+BoundingBox VolumetricMesh::getBoundingBox() const
+{
+  return BoundingBox(makeRange(vertices, vertices+numVertices));
+}
+
+int VolumetricMesh::getClosestVertex(Vec3d pos) const
+{
+  // linear scan
+  double closestDist = DBL_MAX;
+  int closestVertex = -1;
+
+  for(int i=0; i<numVertices; i++)
+  {
+    const Vec3d & vertexPosition = vertices[i];
+    double dist = len(pos - vertexPosition);
+    if (dist < closestDist)
+    {
+      closestDist = dist;
+      closestVertex = i;
+    }
+  }
+
+  return closestVertex;
+}
+
+int VolumetricMesh::getClosestElement(Vec3d pos) const
+{
+  // linear scan
+  double closestDist = DBL_MAX;
+  int closestElement = 0;
+  for(int element=0; element < numElements; element++)
+  {
+    Vec3d center = getElementCenter(element);
+    double dist = len(pos - center);
+    if (dist < closestDist)
+    {
+      closestDist = dist;
+      closestElement = element;
+    }
+  }
+
+  return closestElement;
+}
+
+int VolumetricMesh::getContainingElement(Vec3d pos) const
+{
+  // linear scan
+  for(int element=0; element < numElements; element++)
+  {
+    if (containsVertex(element, pos))
+      return element;
+  }
+
+  return -1;
+}
+
+void VolumetricMesh::setSingleMaterial(double E, double nu, double density)
+{
+  // erase previous materials
+  for(int i=0; i<numMaterials; i++)
+    delete(materials[i]);
+  free(materials);
+
+  for(int i=0; i<numSets; i++)
+    delete(sets[i]);
+  free(sets);
+
+  for(int i=0; i<numRegions; i++)
+    delete(regions[i]);
+  free(regions);
+
+  // add a single material
+  numMaterials = 1;
+  numSets = 1;
+  numRegions = 1;
+
+  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
+  sets = (Set**) malloc (sizeof(Set*) * numSets);
+  regions = (Region**) malloc (sizeof(Region*) * numRegions);
+
+  Material * material = new ENuMaterial("defaultMaterial", density, E, nu);
+  materials[0] = material;
+
+  Set * set = new Set("allElements");
+  for(int i=0; i<numElements; i++)
+  {
+    set->insert(i);
+    elementMaterial[i] = 0;
+  }
+  sets[0] = set;
+
+  Region * region = new Region(0, 0);
+  regions[0] = region;
+}
+
+void VolumetricMesh::getDefaultMaterial(double * E, double * nu, double * density)
+{
+  *E = E_default;
+  *nu = nu_default;
+  *density = density_default;
+}
+
+void VolumetricMesh::propagateRegionsToElements()
+{
+  for(int regionIndex=0; regionIndex < numRegions; regionIndex++)
+  {
+    Region * region = regions[regionIndex];
+    int materialIndex = region->getMaterialIndex();
+
+    set<int> & setElements = sets[region->getSetIndex()]->getElements();
+
+    for(set<int> :: iterator iter = setElements.begin(); iter != setElements.end(); iter++)
+    {
+      int elt = *iter;
+      elementMaterial[elt] = materialIndex;
+    }
+  }
+}
+
+int VolumetricMesh::generateInterpolationWeights(int numTargetLocations, 
+        const double * targetLocations, int * elements, int ** vertices_, double ** weights, 
+        double zeroThreshold, int verbose) const
+{
+  // allocate interpolation arrays  
+  *vertices_ = (int*) malloc (sizeof(int) * numElementVertices * numTargetLocations);
+  *weights = (double*) malloc (sizeof(double) * numElementVertices * numTargetLocations);
+
+  double * barycentricWeights = (double*) malloc (sizeof(double) * numElementVertices);
+
+  for (int i=0; i < numTargetLocations; i++) // over all interpolation locations
+  {
+    if ((verbose) && (i % 100 == 0))
+    {
+      printf("%d ", i); fflush(NULL);
+    }
+
+    Vec3d pos = Vec3d(targetLocations[3*i+0],
+                      targetLocations[3*i+1],
+                      targetLocations[3*i+2]);
+
+    int element = elements[i];
+    if (element < 0)
+    {
+      printf("Error: invalid element index %d.\n", element);
+      return 1;
+    }
+
+    computeBarycentricWeights(element, pos, barycentricWeights);
+
+    if (zeroThreshold > 0)
+    {
+      // check whether vertex is close enough to the mesh
+      double minDistance = DBL_MAX;
+      int numElementVertices = getNumElementVertices();
+      int assignedZero = 0;
+      for(int ii=0; ii< numElementVertices; ii++)
+      {
+        const Vec3d & vpos = getVertex(element, ii);
+        if (len(vpos-pos) < minDistance)
+        {
+          minDistance = len(vpos-pos);
+        }
+      }
+
+      if (minDistance > zeroThreshold)
+      {
+        // assign zero weights
+        for(int ii=0; ii < numElementVertices; ii++)
+          barycentricWeights[ii] = 0.0;
+        assignedZero++;
+        continue;
+      }
+    }
+
+    for(int ii=0; ii<numElementVertices; ii++)
+    {
+      (*vertices_)[numElementVertices * i + ii] = getVertexIndex(element, ii);
+      (*weights)[numElementVertices * i + ii] = barycentricWeights[ii];
+    }
+  }
+
+  free(barycentricWeights);
+
+  return 0;
+}
+
+int VolumetricMesh::generateContainingElements(int numTargetLocations, const double * targetLocations, int ** elements, int useClosestElementIfOutside, int verbose) const
+{
+  int numExternalVertices = 0;
+
+  (*elements) = (int*) malloc (sizeof(int) * numTargetLocations);
+
+  // determine containing (or closest) elements
+  for (int i=0; i < numTargetLocations; i++) // over all interpolation locations
+  {
+    Vec3d pos = Vec3d(targetLocations[3*i+0], targetLocations[3*i+1], targetLocations[3*i+2]);
+
+    // find element containing pos
+    int element = getContainingElement(pos);
+
+    // use closest element if outside
+    if (useClosestElementIfOutside && (element < 0))
+    {
+      element = getClosestElement(pos);
+      numExternalVertices++;
+    }
+
+    (*elements)[i] = element;
+  }
+
+  return numExternalVertices;
+}
+
+int VolumetricMesh::generateInterpolationWeights(int numTargetLocations, const double * targetLocations, int ** vertices_, double ** weights, double zeroThreshold, int ** containingElements, int verbose) const
+{  
+  int * elements = NULL;
+   
+  int useClosestElementIfOutside = 1;
+  int numExternalVertices = generateContainingElements(numTargetLocations, targetLocations, &elements, useClosestElementIfOutside);
+
+  // allocate interpolation arrays  
+  *vertices_ = (int*) malloc (sizeof(int) * numElementVertices * numTargetLocations);
+  *weights = (double*) malloc (sizeof(double) * numElementVertices * numTargetLocations);
+  int code = generateInterpolationWeights(numTargetLocations, targetLocations, elements, vertices_, weights, zeroThreshold, verbose);
+
+  if (containingElements == NULL)
+    free(elements);
+  else
+    *containingElements = elements;
+    
+  return (code == 0) ? numExternalVertices : -1;
+}
+
+int VolumetricMesh::getNumInterpolationElementVertices(const char * filename)
+{
+  FILE * fin = fopen(filename, "r");
+  if (!fin)
+  {
+    printf("Error: unable to open file %s.\n", filename);
+    return -1;
+  }
+
+  char s[1024];
+  if (fgets(s, 1024, fin) == NULL)
+  {
+    printf("Error: incorrect first line of file %s.\n", filename);
+    return -2;
+  }
+  fclose(fin);
+
+  VolumetricMeshParser::beautifyLine(s, 1);
+
+  int slen = strlen(s);
+  int count = 0;
+  for(int i=0; i<slen; i++)
+    if (s[i] == ' ')
+      count++;
+
+  if (count % 2 == 1)
+  {
+    printf("Error: odd number of whitespaces in the first line of file %s.\n", filename);
+    return -3;
+  }
+
+  return count / 2;
+}
+
+int VolumetricMesh::loadInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices_, int ** vertices_, double ** weights)
+{
+  FILE * fin = fopen(filename, "r");
+  if (!fin)
+  {
+    printf("Error: unable to open file %s.\n", filename);
+    return 2;
+  }
+
+  // allocate interpolation arrays
+  *vertices_ = (int*) malloc (sizeof(int) * numElementVertices_ * numTargetLocations);
+  *weights = (double*) malloc (sizeof(double) * numElementVertices_ * numTargetLocations);
+
+  int numReadTargetLocations = -1;
+  int currentVertex;
+
+  // read the elements one by one and accumulate entries
+  while (numReadTargetLocations < numTargetLocations-1)
+  {
+    numReadTargetLocations++;
+
+    if (feof(fin))
+    {
+      printf("Error: interpolation file is too short. Num vertices in interp file: %d . Should be: %d .\n", numReadTargetLocations, numTargetLocations);
+      free(*vertices_);
+      free(*weights);
+      *vertices_ = NULL;
+      *weights = NULL;
+      fclose(fin);
+      return 1;
+    }
+
+    if (fscanf(fin, "%d", &currentVertex) < 1)
+      printf("Warning: bad file syntax. Unable to read interpolation info.\n");
+
+    if (currentVertex != numReadTargetLocations)
+    {
+      printf("Error: consecutive vertex index at position %d mismatch.\n", currentVertex);
+      free(*vertices_);
+      free(*weights);
+      *vertices_ = NULL;
+      *weights = NULL;
+      fclose(fin);
+      return 1;
+    }
+
+    for(int j=0; j<numElementVertices_; j++)
+    {
+      if (fscanf(fin,"%d %lf", &((*vertices_)[currentVertex * numElementVertices_ + j]), &((*weights)[currentVertex * numElementVertices_ + j]) ) < 2)
+        printf("Warning: bad file syntax. Unable to read interpolation info.\n");
+    }
+
+    if (fscanf(fin,"\n") < 0)
+    {
+      //printf("Warning: bad file syntax. Missing end of line in the interpolation file.\n");
+      //do nothing
+    }
+  }
+
+  fclose(fin);
+  return 0;
+}
+
+int VolumetricMesh::loadInterpolationWeightsBinary(const char * filename, int * numTargetLocations, int * numElementVertices_, int ** vertices_, double ** weights)
+{
+  FILE * fin = fopen(filename, "rb");
+  if (!fin)
+  {
+    printf("Error: unable to open file %s.\n", filename);
+    return 2;
+  }
+
+  int code = loadInterpolationWeightsBinary(fin, numTargetLocations, numElementVertices_, vertices_, weights);
+  fclose(fin);
+
+  if (code != 0)
+    printf("Error reading from file %s.\n", filename);
+
+  return code;
+}
+
+int VolumetricMesh::loadInterpolationWeightsBinary(FILE * fin, int * numTargetLocations, int * numElementVertices_, int ** vertices_, double ** weights)
+{
+  int buffer[2];
+  int readItems = (int)fread(buffer, sizeof(int), 2, fin);
+  if (readItems < 2)
+    return 1;
+
+  *numTargetLocations = buffer[0];
+  *numElementVertices_ = buffer[1];
+
+  // allocate interpolation arrays
+  *vertices_ = (int*) malloc (sizeof(int) * *numElementVertices_ * *numTargetLocations);
+  *weights = (double*) malloc (sizeof(double) * *numElementVertices_ * *numTargetLocations);
+
+  readItems = (int)fread(*vertices_, sizeof(int), *numElementVertices_ * *numTargetLocations, fin);
+  if (readItems < *numElementVertices_ * *numTargetLocations)
+    return 1;
+
+  readItems = (int)fread(*weights, sizeof(double), *numElementVertices_ * *numTargetLocations, fin);
+  if (readItems < *numElementVertices_ * *numTargetLocations)
+    return 1;
+
+  return 0;
+}
+
+int VolumetricMesh::saveInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices_, const int * vertices_, const double * weights)
+{
+  FILE * fout = fopen(filename, "w");
+  if (!fout)
+  {
+    printf("Error: unable to open file %s.\n", filename);
+    return 1;
+  }
+
+  for(int currentVertex=0; currentVertex < numTargetLocations; currentVertex++)
+  {
+    fprintf(fout, "%d", currentVertex);
+
+    for(int j=0; j<numElementVertices_; j++)
+      fprintf(fout," %d %lf", vertices_[currentVertex * numElementVertices_ + j], 
+        weights[currentVertex * numElementVertices_ + j]);
+
+    fprintf(fout,"\n");
+  }
+
+  fclose(fout);
+  return 0;
+}
+
+int VolumetricMesh::saveInterpolationWeightsBinary(const char * filename, int numTargetLocations, int numElementVertices_, const int * vertices_, const double * weights)
+{
+  FILE * fout = fopen(filename, "wb");
+  if (!fout)
+  {
+    printf("Error: unable to open file %s.\n", filename);
+    return 1;
+  }
+
+  int code = saveInterpolationWeightsBinary(fout, numTargetLocations, numElementVertices_, vertices_, weights);
+  fclose(fout);
+  if (code != 0)
+    printf("Error reading from file %s.\n", filename);
+  return code;
+}
+
+int VolumetricMesh::saveInterpolationWeightsBinary(FILE * fout, int numTargetLocations, int numElementVertices_, const int * vertices_, const double * weights)
+{
+  int buffer[2];
+  buffer[0] = numTargetLocations;
+  buffer[1] = numElementVertices_;
+
+  int writtenItems = (int)fwrite(buffer, sizeof(int), 2, fout);
+  if (writtenItems < 2)
+    return 1;
+
+  writtenItems = (int)fwrite(vertices_, sizeof(int), numElementVertices_ * numTargetLocations, fout);
+  if (writtenItems < numElementVertices_ * numTargetLocations)
+    return 1;
+
+  writtenItems = (int)fwrite(weights, sizeof(double), numElementVertices_ * numTargetLocations, fout);
+  if (writtenItems < numElementVertices_ * numTargetLocations)
+    return 1;
+
+  return 0;
+}
+
+void VolumetricMesh::interpolate(const double * u, double * uTarget, int numTargetLocations, int numElementVertices_, const int * vertices_, const double * weights)
+{
+  for(int i=0; i< numTargetLocations; i++)
+  {
+    Vec3d defo(0,0,0);
+    for(int j=0; j<numElementVertices_; j++)
+    {
+      int volumetricMeshVertexIndex = vertices_[numElementVertices_ * i + j];
+      Vec3d volumetricMeshVertexDefo = Vec3d(u[3*volumetricMeshVertexIndex+0], u[3*volumetricMeshVertexIndex+1], u[3*volumetricMeshVertexIndex+2]);
+      defo += weights[numElementVertices_ * i + j] * volumetricMeshVertexDefo;
+    }
+    uTarget[3*i+0] = defo[0];
+    uTarget[3*i+1] = defo[1];
+    uTarget[3*i+2] = defo[2];
+  }
+}
+
+int VolumetricMesh::interpolateGradient(const double * U, int numFields, Vec3d pos, double * grad) const
+{
+  // find the element containing "pos"
+  int externalVertex = 0;
+  int element = getContainingElement(pos);
+  if (element < 0)
+  {
+    element = getClosestElement(pos);
+    externalVertex = 1;
+  }
+
+  interpolateGradient(element, U, numFields, pos, grad);
+
+  return externalVertex;
+}
+
+void VolumetricMesh::exportMeshGeometry(int * numVertices_, double ** vertices_, int * numElements_, int * numElementVertices_, int ** elements_) const
+{
+  if (numVertices_ != NULL) 
+    *numVertices_ = numVertices;
+  if (numElements_ != NULL) 
+    *numElements_ = numElements;
+  if (numElementVertices_ != NULL) 
+    *numElementVertices_ = numElementVertices;
+
+  if (vertices_ != NULL)
+  {
+    *vertices_ = (double*) malloc (sizeof(double) * 3 * numVertices);
+    for(int i=0; i<numVertices; i++)
+    {
+      const Vec3d & v = getVertex(i);
+      (*vertices_)[3*i+0] = v[0];
+      (*vertices_)[3*i+1] = v[1];
+      (*vertices_)[3*i+2] = v[2];
+    }
+  }
+
+  if (elements_ != NULL)
+  {
+    *elements_ = (int*) malloc (sizeof(int) * numElementVertices * numElements);
+    for(int i=0; i<numElements; i++)
+    {
+      for(int j=0; j<numElementVertices; j++)
+        (*elements_)[numElementVertices * i + j] = elements[i][j];
+    }
+  }
+}
+
+void VolumetricMesh::exportMeshGeometry(std::vector<Vec3d> & vertexBuffer) const
+{
+  vertexBuffer.resize(numVertices);
+  memcpy(vertexBuffer.data(), vertices, sizeof(double) * 3 * numVertices);
+}
+
+void VolumetricMesh::computeGravity(double * gravityForce, double g, bool addForce) const
+{
+  if (!addForce)
+    memset(gravityForce, 0, sizeof(double) * 3 * numVertices);
+
+  double invNumElementVertices = 1.0 / getNumElementVertices();
+
+  for(int el=0; el < numElements; el++)
+  {
+    double volume = getElementVolume(el);
+    double density = getElementDensity(el);
+    double mass = density * volume;
+    for(int j=0; j<getNumElementVertices(); j++)
+      gravityForce[3 * getVertexIndex(el,j) + 1] -= invNumElementVertices * mass * g; // gravity assumed to act in negative y-direction
+  }  
+}
+
+void VolumetricMesh::applyDeformation(const double * u)
+{
+  for(int i=0; i<numVertices; i++)
+  {
+    Vec3d & v = getVertex(i);
+    v[0] += u[3*i+0];
+    v[1] += u[3*i+1];
+    v[2] += u[3*i+2];
+  }
+}
+
+// transforms every vertex as X |--> pos + R * X
+void VolumetricMesh::applyLinearTransformation(double * pos, double * R)
+{
+  for(int i=0; i<numVertices; i++)
+  {
+    Vec3d & v = getVertex(i);
+    
+    double newPos[3];
+    for(int j=0; j<3; j++)
+    {
+      newPos[j] = pos[j];
+      for(int k=0; k<3; k++)
+        newPos[j] += R[3*j+k] * v[k];
+    }
+
+    v[0] = newPos[0];
+    v[1] = newPos[1];
+    v[2] = newPos[2];
+  }
+}
+
+void VolumetricMesh::setMaterial(int i, const Material * material)
+{
+  delete(materials[i]);
+  materials[i] = material->clone();
+}
+
+VolumetricMesh::VolumetricMesh(const VolumetricMesh & volumetricMesh, int numElements_, int * elements_, map<int,int> * vertexMap_)
+{
+  // determine vertices in the submesh
+  numElementVertices = volumetricMesh.getNumElementVertices();
+  set<int> vertexSet;
+  for(int i=0; i<numElements_; i++)
+    for(int j=0; j < numElementVertices; j++)
+      vertexSet.insert(volumetricMesh.getVertexIndex(elements_[i],j));
+
+  // copy vertices into place and also into vertexMap
+  numVertices = vertexSet.size();
+  vertices = new Vec3d [numVertices]; 
+  set<int> :: iterator iter;
+  int vertexNo = 0;
+  map<int, int> vertexMap;
+  for(iter = vertexSet.begin(); iter != vertexSet.end(); iter++)
+  {
+    vertices[vertexNo] = volumetricMesh.getVertex(*iter);
+    vertexMap.insert(make_pair(*iter,vertexNo));
+    vertexNo++;
+  }
+
+  if (vertexMap_ != NULL)
+    *vertexMap_ = vertexMap;
+
+  // copy elements
+  numElements = numElements_;
+  elements = (int**) malloc (sizeof(int*) * numElements);
+  elementMaterial = (int*) malloc (sizeof(int) * numElements);
+  map<int,int> elementMap;
+  for(int i=0; i<numElements; i++)
+  {
+    elements[i] = (int*) malloc (sizeof(int) * numElementVertices);
+    for(int j=0; j< numElementVertices; j++)
+    {
+      map<int,int> :: iterator iter2 = vertexMap.find((volumetricMesh.elements)[elements_[i]][j]);
+      if (iter2 == vertexMap.end())
+      {
+        printf("Internal error 1.\n");
+        throw 1;
+      }
+      elements[i][j] = iter2->second;
+    }
+
+    elementMaterial[i] = (volumetricMesh.elementMaterial)[elements_[i]];
+    elementMap.insert(make_pair(elements_[i], i)); 
+  }
+
+  // copy materials
+  numMaterials = volumetricMesh.getNumMaterials();
+  numSets = volumetricMesh.getNumSets();
+  numRegions = volumetricMesh.getNumRegions();
+
+  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
+  for(int i=0; i < numMaterials; i++)
+    materials[i] = volumetricMesh.getMaterial(i)->clone();
+
+  // copy element sets; restrict element sets to the new mesh, also rename vertices to reflect new vertex indices
+  vector<Set*> newSets;
+  map<int,int> oldToNewSetIndex;
+  for(int oldSetIndex=0; oldSetIndex < volumetricMesh.getNumSets(); oldSetIndex++)
+  {
+    const Set * oldSet = volumetricMesh.getSet(oldSetIndex);
+    set<int> oldElements;
+    oldSet->getElements(oldElements);
+
+    for(set<int> :: iterator iter = oldElements.begin(); iter != oldElements.end(); iter++)
+    {
+      if (*iter < 0)
+      {
+        printf("Internal error 2.\n");
+        exit(1);
+      }
+    }
+
+    // construct the element list
+    vector<int> newElements;
+    for(set<int> :: iterator iter = oldElements.begin(); iter != oldElements.end(); iter++)
+    {
+      map<int,int> :: iterator iter2 = elementMap.find(*iter);
+      if (iter2 != elementMap.end())
+        newElements.push_back(iter2->second);
+    }
+
+    // if there is at least one element in the new set, create a set for it
+    if (newElements.size() > 0)
+    {
+      Set * newSet = new Set(oldSet->getName());
+      for(unsigned int j=0; j<newElements.size(); j++)
+      {
+        if (newElements[j] < 0)
+        {
+          printf("Internal error 3.\n");
+          exit(1);
+        }
+        newSet->insert(newElements[j]);
+      }
+      newSets.push_back(newSet);
+      oldToNewSetIndex.insert(make_pair(oldSetIndex, newSets.size() - 1));
+    }
+  }
+
+  numSets = newSets.size();
+  sets = (Set**) malloc (sizeof(Set*) * numSets);
+  for(int i=0; i<numSets; i++)
+    sets[i] = newSets[i];
+
+  //printf("numSets: %d\n", numSets);
+
+  // copy regions; remove empty ones
+  vector<Region*> vregions;
+  for(int i=0; i < numRegions; i++)
+  {
+    const Region * sregion = volumetricMesh.getRegion(i);
+    map<int,int> :: iterator iter = oldToNewSetIndex.find(sregion->getSetIndex());
+    if (iter != oldToNewSetIndex.end())
+    {
+      Region * newRegion = new Region(sregion->getMaterialIndex(),iter->second);
+      vregions.push_back(newRegion);
+    }
+  }
+
+  numRegions = vregions.size();
+  regions = (Region**) malloc (sizeof(Region*) * numRegions);
+  for(int j=0; j<numRegions; j++)
+    regions[j] = vregions[j];
+
+  // sanity check
+  // seek each element in all the regions
+  for(int el=0; el<numElements; el++)
+  {
+    int found = 0;
+    for(int region=0; region < numRegions; region++)
+    {
+      int elementSet = (regions[region])->getSetIndex();
+
+      // seek for element in elementSet
+      if (sets[elementSet]->isMember(el))
+      {
+        if (found != 0)
+          printf("Warning: element %d (1-indexed) is in more than one region.\n",el+1);
+        else
+          found = 1;
+      }
+    }
+    if (found == 0)
+      printf("Warning: element %d (1-indexed) is not in any of the regions.\n",el+1);
+  }
+
+  // sanity check: make sure all elements are between bounds
+  for(int i=0; i < numSets; i++)
+  {
+    set<int> elts;
+    sets[i]->getElements(elts);
+    for(set<int> :: iterator iter = elts.begin(); iter != elts.end(); iter++)
+    {
+      if (*iter < 0)
+        printf("Warning: encountered negative element index in element set %d.\n",i);
+      if (*iter >= numElements)
+        printf("Warning: encountered too large element index in element set %d.\n",i);
+    }
+  }
+}
+
+// if vertexMap is non-null, it also returns a renaming datastructure: vertexMap[big mesh vertex] is the vertex index in the subset mesh
+void VolumetricMesh::setToSubsetMesh(std::set<int> & subsetElements, int removeIsolatedVertices, std::map<int,int> * vertexMap)
+{
+  int numRemovedElements = 0;
+  for(int el=0; el<numElements; el++)
+  {
+    if (subsetElements.find(el) == subsetElements.end())
+    {
+      free(elements[el]);
+      elements[el] = NULL;
+      numRemovedElements++;
+    }
+  }
+
+  int head = 0;
+  int tail = 0;
+
+  int * lookupTable = (int *) malloc (sizeof(int) * numElements); 
+  for(int i=0; i<numElements; i++)
+    lookupTable[i] = i;
+
+  while (tail < numElements)
+  {
+    if (elements[tail] != NULL)
+    {
+      elements[head] = elements[tail];
+      elementMaterial[head] = elementMaterial[tail];
+      lookupTable[tail] = head;  // update to new index 
+      head++;
+    }
+    tail++;
+  }
+  numElements -= numRemovedElements;
+  elements = (int**) realloc (elements, sizeof(int*) * numElements);
+  elementMaterial = (int*) realloc (elementMaterial, sizeof(int) * numElements);
+
+  for(int setIndex=0; setIndex < numSets; setIndex++)
+  {
+    set<int> setElements;
+    sets[setIndex]->getElements(setElements);
+    sets[setIndex]->clear();
+    for(set<int>::iterator iter = setElements.begin(); iter != setElements.end(); iter++)
+    {
+      if (subsetElements.find(*iter) == subsetElements.end()) // not found!!
+        continue;
+      int newIndex = lookupTable[(*iter)];
+      sets[setIndex]->insert(newIndex);
+    }
+  }
+  free(lookupTable);
+
+  if (removeIsolatedVertices)
+  {
+    set<int> retainedVertices;
+    for(int el=0; el<numElements; el++)
+      for(int j=0; j < numElementVertices; j++)
+        retainedVertices.insert(getVertexIndex(el,j));
+
+    int head = 0;
+    int tail = 0;
+  
+    int * renamingFunction = (int*) malloc (sizeof(int) * numVertices);
+    Vec3d * newVertices = new Vec3d [retainedVertices.size()];
+    if (vertexMap != NULL)
+      vertexMap->clear();
+    while (tail < numVertices)
+    {
+      if (retainedVertices.find(tail) != retainedVertices.end())
+      {
+        renamingFunction[tail] = head;
+        if (vertexMap != NULL)
+          vertexMap->insert(make_pair(tail, head));
+        newVertices[head] = vertices[tail];
+        head++;
+      }
+      tail++;
+    }
+    assert(head == int(retainedVertices.size()));
+
+    // rename vertices inside the elements
+    for(int el=0; el<numElements; el++)
+      for(int j=0; j < numElementVertices; j++)
+        elements[el][j] = renamingFunction[getVertexIndex(el,j)];
+   
+    free(renamingFunction);
+    numVertices = retainedVertices.size();
+    delete [] vertices;
+    vertices = newVertices;
+  }
+}
+
+int VolumetricMesh::exportToEle(const char * baseFilename, int includeRegions) const
+{
+  char s[1024];
+  sprintf(s, "%s.ele", baseFilename);
+
+  FILE * fout = fopen(s, "w");
+  if (!fout)
+  {       
+    printf("Error: could not write to %s.\n",s);
+    return 1;
+  }         
+
+  int * elementRegion = NULL;
+  if (includeRegions)
+  {
+    elementRegion = (int*) malloc (sizeof(int) * getNumElements());
+    for(int el=0; el<getNumElements(); el++)
+    {
+      int found = 0;
+      for(int region=0; region < numRegions; region++)
+      {
+        int elementSet = (regions[region])->getSetIndex();
+
+        // seek for element in elementSet
+        if (sets[elementSet]->isMember(el))
+        {
+          if (found != 0)
+            printf("Warning: element %d (1-indexed) is in more than one region.\n",el+1);
+          else
+            found = region+1;
+        }
+      }
+      if (found == 0)
+        printf("Warning: element %d (1-indexed) is not in any of the regions.\n",el+1);
+      elementRegion[el] = found;
+    }
+  }
+
+  if (includeRegions)
+    fprintf(fout,"%d %d %d\n", numElements, numElementVertices, 1);
+  else
+    fprintf(fout,"%d %d %d\n", numElements, numElementVertices, 0);
+
+  for(int el=0; el < numElements; el++)
+  {   
+    fprintf(fout,"%d ",el+1);
+    for(int j=0; j < numElementVertices; j++)
+    {   
+      fprintf(fout,"%d", getVertexIndex(el,j)+1);
+      if (j != numElementVertices - 1)
+        fprintf(fout," ");
+    } 
+    if (includeRegions)
+    {
+      fprintf(fout," %d", elementRegion[el]);
+    }
+    fprintf(fout,"\n");
+  }     
+        
+  fprintf(fout,"# generated by the volumetricMesh class\n");
+
+  fclose(fout);
+
+  if (includeRegions)
+    free(elementRegion);
+
+  sprintf(s, "%s.node", baseFilename);
+
+  fout = fopen(s, "w");
+  if (!fout)
+  {       
+    printf("Error: could not write to %s.\n",s);
+    return 1;
+  }         
+          
+  fprintf(fout,"%d %d %d %d\n", numVertices, 3, 0, 0);
+  for(int v=0; v < numVertices; v++)
+  {   
+    fprintf(fout,"%d ",v+1);
+    const Vec3d & vtx = getVertex(v);
+    fprintf(fout,"%.15f %.15f %.15f\n", vtx[0], vtx[1], vtx[2]);
+  }     
+        
+  fprintf(fout,"# generated by the volumetricMesh class\n");
+
+  fclose(fout);
+
+  return 0;
+}
+
+void VolumetricMesh::renumberVertices(const vector<int> & permutation)
+{
+
+  // renumber vertices
+  Vec3d * newVertices = new Vec3d [numVertices];
+  for (int i = 0; i < numVertices; i++)
+    newVertices[permutation[i]] = vertices[i];
+  delete [] vertices;
+  vertices = newVertices;
+
+  // renumber tets
+  for (int i = 0; i < numElements; i++)
+    for (int j = 0; j < numElementVertices; j++)
+      elements[i][j] = permutation[elements[i][j]];
+}
+
+void VolumetricMesh::addMaterial(const Material * material, const Set & newSet, bool removeEmptySets, bool removeEmptyMaterials)
+{
+  // add new material to materials
+  numMaterials++;
+  materials = (Material**) realloc (materials, sizeof(Material*) * numMaterials);
+  materials[numMaterials - 1] = material->clone();
+
+  // remove indices in the sets that belong to the newSet
+  const set<int> & newElements = newSet.getElements();
+  vector<bool> eleCovered(numElements, false);
+  for(int i = 1; i < numSets; i++) // skip the first set, which is always allElements
+  {
+    set<int> & s = sets[i]->getElements();
+
+    for(set<int>::iterator it = s.begin(); it != s.end(); )
+    {
+      int ele = *it;
+      eleCovered[ele] = true;
+      if (newElements.find(ele) != newElements.end())
+      {
+        set<int>::iterator it2 = it;
+        it++;
+        s.erase(it2);
+      }
+      else
+        it++;
+    }
+  }
+
+  // we have to be careful here: if previously the entire mesh is in default set: allElements,
+  // adding a new set will invalidate the elements that are previously in allElements
+  // a newSet is needed to cover those elements
+  set<int> restSet;
+  for(int i = 0; i < numElements; i++)
+    if (eleCovered[i] == false) // this element is covered only by allElements
+      restSet.insert(i);
+  if (restSet.size() > 0)
+    numSets++;
+
+  // add the new Set
+  numSets++;
+  sets = (Set**) realloc(sets, sizeof(Set *) * numSets);
+  sets[numSets-1] = new Set(newSet);
+
+  if (restSet.size() > 0)
+    sets[numSets-2] = new Set("restElements", restSet);
+
+  // create a new Region
+  numRegions++;
+  regions = (Region**) realloc (regions, sizeof(Region*) * numRegions);
+  regions[numRegions - 1] = new Region(numMaterials - 1, numSets - 1);
+
+  if (restSet.size() > 0)
+  {
+    // first find the material that used for allElements
+    for(int i = 0; i < numRegions-1; i++)
+      if (regions[i]->getSetIndex() == 0) // this is the index of the allElements set
+        regions[i]->setSetIndex(numSets-2);
+  }
+
+  // modify elementMaterial
+  for(set<int>::const_iterator it = newElements.begin(); it != newElements.end(); it++)
+  {
+    int el = *it;
+    assert(el >= 0 && el < numElements);
+    elementMaterial[el] = numMaterials-1;
+  }
+
+  if (removeEmptySets)
+  {
+    bool hasEmptySet = false;
+    vector<int> setIndexChange(numSets, 0); // old set index -> new set index
+    int newIndex = 0;                       // store the next available set index
+    for(int i = 0; i < numSets; i++)
+    {
+      if (sets[i]->getNumElements() == 0)
+      {
+        setIndexChange[i] = -1; // this set will be deleted, so its new set index is -1
+        delete sets[i];
+        sets[i] = NULL;
+        hasEmptySet = true;
+      }
+      else
+      {
+        setIndexChange[i] = newIndex; // this set is remained, its new index is the next available index
+        if (newIndex != i)            // this means there's already at least one set deleted
+        {
+          assert(newIndex < i);
+          sets[newIndex] = sets[i];   // assign the pointer to the current set to the location at newIndex
+        }
+        newIndex++;
+      }
+    }
+
+    if (hasEmptySet)
+    {
+      assert(newIndex < numSets);
+      numSets = newIndex;
+      sets = (Set**) realloc(sets, sizeof(Set *) * numSets);
+
+      int newRegionIdx = 0;
+      for(int i = 0; i < numRegions; i++)
+      {
+        int oldSetIdx = regions[i]->getSetIndex();
+        assert((size_t)oldSetIdx < setIndexChange.size());
+        if (setIndexChange[oldSetIdx] == -1) // this set has been deleted
+        {
+          delete regions[i];
+          regions[i] = NULL;
+        }
+        else
+        {
+          regions[i]->setSetIndex(setIndexChange[oldSetIdx]);
+          if (newRegionIdx != i)
+            regions[newRegionIdx] = regions[i];
+          newRegionIdx++;
+        }
+      }
+      numRegions = newRegionIdx;
+      regions = (Region**) realloc (regions, sizeof(Region*) * numRegions);
+    } // end if (hasEmptySet)
+    else
+    {
+      assert(newIndex == numSets);
+    }
+  } // end if (removeEmptySets)
+
+  // remove material
+  if (removeEmptyMaterials)
+  {
+    // count #Elements for each material
+    vector<int> elementsWithMaterial(numMaterials, 0);
+
+    for(int i = 0; i < numElements; i++)
+    {
+      int matIdx = elementMaterial[i];
+      assert(matIdx >= 0 && matIdx < numMaterials);
+      elementsWithMaterial[matIdx]++;
+    }
+
+    int newMatIdx = 0;
+    vector<int> matIndexChange(numMaterials, 0); // old material index -> new material index
+    bool hasEmptyMat = false;
+    for(int i = 0; i < numMaterials; i++)
+    {
+      if (elementsWithMaterial[i] == 0)
+      {
+        matIndexChange[i] = -1;
+        delete materials[i];
+        materials[i] = NULL;
+        hasEmptyMat = true;
+      }
+      else
+      {
+        matIndexChange[i] = newMatIdx;
+        if (newMatIdx != i)
+          materials[newMatIdx] = materials[i];
+        newMatIdx++;
+      }
+    }
+
+    if (hasEmptyMat)
+    {
+      numMaterials = newMatIdx;
+      materials = (Material**) realloc (materials, sizeof(Material*) * numMaterials);
+
+      // we also need to modify and delete invalid regions
+      bool hasInvalidRegion = false;
+      int newRegionIdx = 0;
+      for(int i = 0; i < numRegions; i++)
+      {
+        int oldMatIndex = regions[i]->getMaterialIndex();
+        if (matIndexChange[oldMatIndex] < 0)
+        {
+          hasInvalidRegion = true;
+          delete regions[i];
+          regions[i] = NULL;
+        }
+        else
+        {
+          regions[i]->setMaterialIndex(matIndexChange[oldMatIndex]);
+          if (newRegionIdx != i)
+            regions[newRegionIdx] = regions[i];
+          newRegionIdx++;
+        }
+      }
+
+      if (hasInvalidRegion)
+      {
+        numRegions = newRegionIdx;
+        regions = (Region**) realloc (regions, sizeof(Region*) * numRegions);
+      }
+
+      // reassign the correct material index to each element
+      propagateRegionsToElements();
+    }
+  } // end if (removeEmptyMaterials)
+}
+
diff --git a/libraries/volumetricMesh/volumetricMesh.h b/libraries/volumetricMesh/volumetricMesh.h
new file mode 100644
index 0000000000000000000000000000000000000000..dcbcc13fbf546da80b20443fe3c2715e35142a58
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMesh.h
@@ -0,0 +1,425 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _VOLUMETRICMESH_H_
+#define _VOLUMETRICMESH_H_
+
+/*
+  This abstract class can store a generic volumetric 3D mesh. 
+  It stores the mesh geometric information (elements and vertices), 
+  and also the material parameters of each individual mesh element 
+  (Young's modulus, Poisson ratio, mass density). This is done by 
+  organizing elements with the same material parameters into a "region".
+  The class supports several geometric queries and interpolation to 
+  an embedded triangle mesh ("Free-Form Deformation").  It also 
+  supports exporting the mesh to an .ele or .node format (the format 
+  used by the Stellar and TetGen mesh generation packages).  Derived classes 
+  are the TetMesh (general tetrahedral meshes), and CubicMesh 
+  (axis-aligned cubic "voxel" meshes). See description in tetMesh.h and cubicMesh.h.
+
+  All quantities are 0-indexed, except the input mesh files where the 
+  elements and vertices are 1-indexed (same as in TetGen and Stellar).
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vector>
+#include <set>
+#include <string>
+#include <map>
+#include "minivector.h"
+#include "boundingBox.h"
+
+class VolumetricMesh
+{
+public:
+
+  // Note: This class is abstract and cannot be instantiated; use the constructors in the derived classes (TetMesh, CubicMesh) to initialize a mesh, or use the load routine in volumetricMeshLoader.h
+
+  // copy constructor, destructor
+  VolumetricMesh(const VolumetricMesh & volumetricMesh);
+  virtual VolumetricMesh * clone() = 0;
+  virtual ~VolumetricMesh();
+
+  // nested classes to store sets, materials and regions (declared later)
+  class Set; 
+  class Material; 
+  class Region;
+
+  // === save/export ===
+
+  // saves the mesh to a text file (.veg file format, see examples and documentation)
+  virtual int saveToAscii(const char * filename) const = 0; 
+  virtual int save(const char * filename) const; // for backward compatibility (just calls saveToAscii)
+
+  // saves the mesh to binary format
+  // returns: 0 = success, non-zero = error
+  // output: if bytesWritten is non-NULL, it will contain the number of bytes written 
+  virtual int saveToBinary(const char * filename, unsigned int * bytesWritten = NULL) const = 0;
+  // if countBytesOnly = true, user can pass NULL to binaryOutputStream
+  virtual int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten = NULL, bool countBytesOnly = false) const = 0;
+
+  // exports the mesh geometry to an .ele and .node file (TetGen and Stellar format)
+  // if includeRegions=1, an extra column is added to output, identifying the region of each element
+  int exportToEle(const char * baseFilename, int includeRegions=0) const;
+  // exports the mesh geometry to memory arrays (say, for external usage)
+  // all parameters are output parameters
+  // vertices and elements will be allocated inside the routine
+  void exportMeshGeometry(int * numVertices, double ** vertices, int * numElements = NULL, int * numElementVertices = NULL, int ** elements = NULL) const;
+  void exportMeshGeometry(std::vector<Vec3d> & vertices) const;
+
+  // === vertex and element access ===
+
+  typedef enum { INVALID, TET, CUBIC } elementType;
+  typedef enum { ASCII, BINARY, NUM_FILE_FORMATS } fileFormatType; // ASCII is the text .veg format, BINARY is the binary .vegb format
+  // opens the file and returns the element type of the volumetric mesh in the file; returns INVALID if no type information found
+  static elementType getElementType(const char * filename, fileFormatType fileFormat = ASCII); 
+  virtual elementType getElementType() const = 0; // calls the derived class to identify itself
+  // advanced usage: returns the element type of the volumetric mesh from a BINARY stream (does not modify fin)
+  //static elementType getElementType(FILE * fin);
+  static elementType getElementType(void * fin, int memoryLoad = 0);
+
+  inline int getNumVertices() const { return numVertices; }
+  inline Vec3d & getVertex(int i) { return vertices[i]; }
+  inline const Vec3d & getVertex(int i) const { return vertices[i]; }
+  inline Vec3d & getVertex(int element, int vertex) { return vertices[elements[element][vertex]]; }
+  inline const Vec3d & getVertex(int element, int vertex) const { return vertices[elements[element][vertex]]; }
+  inline int getVertexIndex(int element, int vertex) const { return elements[element][vertex]; }
+  inline const int * getVertexIndices(int element) const { return elements[element]; }
+  inline Vec3d * getVertices() { return vertices; } // advanced, internal datastructure
+  inline const Vec3d * getVertices() const { return vertices; }
+  inline int getNumElements() const { return numElements; }
+  inline int getNumElementVertices() const { return numElementVertices; } 
+  void renumberVertices(const std::vector<int> & permutation); // renumbers the vertices using the provided permutation
+  inline void setVertex(int i, const Vec3d & pos) { vertices[i] = pos; } // set the position of a vertex
+
+  // === materials access === 
+
+  inline int getNumMaterials() const { return numMaterials; }
+  inline const Material * getMaterial(int i) const { return materials[i]; }
+  inline const Material * getElementMaterial(int el) const { return materials[elementMaterial[el]]; }
+  static void getDefaultMaterial(double * E, double * nu, double * density);
+
+  inline int getNumSets() const { return numSets; }
+  inline const Set * getSet(int i) const { return sets[i]; }
+
+  inline int getNumRegions() const { return numRegions; }
+  inline const Region * getRegion(int i) const { return regions[i]; }
+
+  // === materials editing ===
+  inline Material * getMaterial(int i) { return materials[i]; }
+  inline Material * getElementMaterial(int el) { return materials[elementMaterial[el]]; }
+  void setMaterial(int i, const Material * material); // sets i-th material to "material"
+  void setSingleMaterial(double E, double nu, double density); // erases all materials and creates a single material for the entire mesh
+  void addMaterial(const Material * material, const Set & newSet, bool removeEmptySets, bool removeEmptyMaterials);
+
+  // mass density of an element
+  double getElementDensity(int el) const { return materials[elementMaterial[el]]->getDensity(); }
+  // computes the mass matrix of a single element
+  // note: to compute the mass matrix for the entire mesh, use generateMassMatrix.h
+  virtual void computeElementMassMatrix(int element, double * massMatrix) const = 0; // massMatrix is numElementVertices_ x numElementVertices_
+
+  // === geometric queries and transformations ===
+
+  Vec3d getElementCenter(int el) const;
+
+  // center of mass and inertia tensor
+  double getVolume() const;
+  virtual double getElementVolume(int el) const = 0;
+  void getVertexVolumes(double * vertexVolumes) const; // compute the volume "belonging" to each vertex
+  virtual void getElementInertiaTensor(int el, Mat3d & inertiaTensor) const = 0; // returns the inertia tensor of a single element, around its center of mass, with unit density
+  double getMass() const; // compute the total mass of the mesh, using the mass density material information
+  void getInertiaParameters(double & mass, Vec3d & centerOfMass, Mat3d & inertiaTensor) const ; // mass, center of mass and inertia tensor for the entire mesh
+
+  // centroid is the geometric center of all vertices; radius is the tightest fitting sphere centered at the centroid
+  void getMeshGeometricParameters(Vec3d & centroid, double * radius) const;
+  BoundingBox getBoundingBox() const;
+
+  // mesh 1-neighborhood queries
+  void getVerticesInElements(std::vector<int> & elements, std::vector<int> & vertices) const;
+  void getElementsTouchingVertices(std::vector<int> & vertices, std::vector<int> & elements) const;
+  void getVertexNeighborhood(std::vector<int> & vertices, std::vector<int> & neighborhood) const;
+
+  // proximity queries
+  int getClosestElement(Vec3d pos) const; // finds the closest element to the given position (using linear scan); distance to a element is defined as distance to its center
+  int getClosestVertex(Vec3d pos) const; // finds the closest vertex to the given position (using linear scan)
+  int getContainingElement(Vec3d pos) const; // finds the element that containts the given position (using linear scan); if such element does not exist, -1 is returned
+  virtual bool containsVertex(int element, Vec3d pos)  const = 0; // true if given element contain given position, false otherwise
+
+  // computes the gravity vector (different forces on different mesh vertices due to potentially varying mass densities)
+  // gravityForce must be a pre-allocated vector of length 3xnumVertices()
+  void computeGravity(double * gravityForce, double g=9.81, bool addForce=false) const;
+
+  // edge queries
+  virtual int getNumElementEdges() const = 0;
+  virtual void getElementEdges(int el, int * edgeBuffer) const = 0; // edgeBuffer must be pre-allocated, of size 2 x numElementEdges()
+
+  // (permanently) applies the deformation to the vertices of the mesh
+  void applyDeformation(const double * u);
+  void applyLinearTransformation(double * pos, double * R); // transforms every vertex as X |--> pos + R * X (R must be given row-major)
+
+  // === submesh creation ===
+
+  // (permanently) set this mesh to its submesh containing the specified elements (i.e., delete the mesh elements not on the given list of elements)
+  // if vertexMap is non-null, it also returns a renaming datastructure: vertexMap[big mesh vertex] is the vertex index in the subset mesh
+  void setToSubsetMesh(std::set<int> & subsetElements, int removeIsolatedVertices=1, std::map<int,int> * vertexMap = NULL);
+
+  // === interpolation ===
+
+  // the interpolant is a triple (numTargetLocations, vertices, weights)
+  // Generates interpolation weights to transfer quantities from volumetric mesh to (embedded) surface meshes.
+  // Input is a list of 3D target locations where the interpolant will be computed,
+  // e.g., those could be vertices of a triangle mesh embedded into the volumetric mesh.
+  // Each location is a 3-vector, i.e., 3 consecutive double-precision values.
+  // If zeroThreshold is set positive, than for any target location that is 
+  //   more than zeroThreshold away from the closest element, 
+  //   all weights will be set to zero; this is useful, e.g. to 
+  //   fix locations far away from your mesh.
+  // Output: vertices and weights arrays
+  // vertices: gives a list of integer indices of the vertices of the element
+  //   closest to the target location (numElementVertices entries per target location, one for each element vertex)
+  //   note: if target location is inside a voxel, that voxel will be chosen as closest
+  // weights: a list of numElementVertices_ weights, as per the numElementVertices_ vertices of each element (weights sum to 1)
+  // If zeroThreshold >= 0, then the points that are further than zeroThreshold away from any volumetric mesh vertex, are assigned weights of 0.
+  // If elements is not NULL, the closest elements for each target location will be returned in the integer list "*elements" (allocated inside the function)
+  // If elements is not NULL, the function will allocate an integer array *elements, and return the closest element to each target location in it.
+  // Returns the number of target points that do not lie inside any element.
+  int generateInterpolationWeights(int numTargetLocations, const double * targetLocations, int ** vertices, double ** weights, double zeroThreshold = -1.0, int ** elements = NULL, int verbose=0) const; // this is the "master" function, meant to be typically used to create the interpolant
+
+  // interpolates 3D vector data from vertices of the 
+  //   volumetric mesh (data given in u) to the target locations (output goes into uTarget)
+  //   e.g., use this to interpolate deformation from the volumetric mesh to a triangle mesh
+  static void interpolate(const double * u, double * uTarget, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights);
+
+  // the following are less often used, more specialized functions
+  // same as "generateInterpolationWeights" above, except here the elements that contain the target locations are assumed to be known, and are provided in array "elements"; returns 0 on success, 1 otherwise
+  int generateInterpolationWeights(int numTargetLocations, const double * targetLocations, int * elements, int ** vertices, double ** weights, double zeroThreshold = -1.0, int verbose=0) const; 
+  // generates the integer list "elements" of the elements that contain given vertices; if closestElementIfOutside==1, then vertices outside of the mesh are assigned the closest element, otherwise -1 is assigned; returns the number of target locations outside of the mesh
+  int generateContainingElements(int numTargetLocations, const double * targetLocations, int ** elements, int useClosestElementIfOutside=1, int verbose=0) const; 
+  static int getNumInterpolationElementVertices(const char * filename); // looks at the first line of "filename" to determine "numElementVertices" for this particular interpolant
+  static int loadInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices, int ** vertices, double ** weights); // ASCII version; returns 0 on success
+  static int saveInterpolationWeights(const char * filename, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // ASCII version
+  static int loadInterpolationWeightsBinary(const char * filename, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights); // binary version; returns 0 on success
+  static int saveInterpolationWeightsBinary(const char * filename, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // binary version
+  static int loadInterpolationWeightsBinary(FILE * fin, int * numTargetLocations, int * numElementVertices, int ** vertices, double ** weights); // binary version; returns 0 on success
+  static int saveInterpolationWeightsBinary(FILE * fout, int numTargetLocations, int numElementVertices, const int * vertices, const double * weights); // binary version
+
+  // computes barycentric weights of the given position with respect to the given element
+  virtual void computeBarycentricWeights(int element, const Vec3d & pos, double * weights) const = 0;
+
+  // computes the gradient of a 3D vector field (specified at the volumetric mesh vertices), at the location "pos"
+  // "numFields" fields can be interpolated simultaneously; each is given as one column of the U matrix
+  // U is a 3numVertices x numFields matrix; stored column-major
+  // output: grad is 9 x numFields matrix, stored column-major; each column gives the gradient (3x3 matrix), stored row-major format
+  // return: 0 if pos inside the mesh, 1 otherwise
+  int interpolateGradient(const double * U, int numFields, Vec3d pos, double * grad) const;
+  // in this version, the element containing the "pos" must be known, and prescribed directly
+  virtual void interpolateGradient(int element, const double * U, int numFields, Vec3d pos, double * grad) const = 0;
+
+  // === material-related nested classes ===
+
+  // a set of integers, with a name (used for example, to store elements that share the same material properties)
+  class Set
+  {
+  public:
+
+    Set(const std::string & name);
+    Set(const Set & set);
+    Set(const std::string & name, const std::set<int> & elements);
+
+    inline std::string getName() const;
+    inline int getNumElements() const;
+    inline void getElements(std::set<int> & elements) const;
+    inline const std::set<int> & getElements() const;
+    inline bool isMember(int element) const;
+
+    inline std::set<int> & getElements();
+    inline void insert(int element);
+    inline void clear();
+
+  protected:
+    std::string name;
+    std::set<int> elements;
+  };
+
+  // stores a material (abstract class)
+  class Material
+  {
+  public:
+    Material(const std::string name, double density);
+    Material(const Material & material);
+    virtual ~Material() {};
+    virtual Material * clone() const = 0;
+
+    inline std::string getName() const; // material name
+    inline double getDensity() const; // density
+    inline void setName(const std::string name);
+    inline void setDensity(double density);
+
+    // ENU = any isotropic material parameterized by E (Young's modulus), nu (Poisson's ratio)
+    // ORTHOTROPIC = orthotropic anisotropic material
+    // MOONEYRIVLIN = Mooney-Rivlin material
+    typedef enum { INVALID, ENU, ORTHOTROPIC, MOONEYRIVLIN } materialType;
+    virtual materialType getType() = 0;
+
+    typedef enum { ENU_DENSITY, ENU_E, ENU_NU, ENU_NUM_PROPERTIES } enuMaterialProperties;
+    typedef enum { ORTHOTROPIC_DENSITY, ORTHOTROPIC_E1, ORTHOTROPIC_E2, ORTHOTROPIC_E3, ORTHOTROPIC_NU12, ORTHOTROPIC_NU23, ORTHOTROPIC_NU31, ORTHOTROPIC_G12, ORTHOTROPIC_G23, ORTHOTROPIC_G31, ORTHOTROPIC_NUM_PROPERTIES } orthotropicMaterialProperties;
+    typedef enum { MOONEYRIVLIN_DENSITY, MOONEYRIVLIN_MU01, MOONEYRIVLIN_MU10, MOONEYRIVLIN_V1, MOONEYRIVLIN_NUM_PROPERTIES } mooneyrivlinMaterialProperties;
+
+  protected:
+    std::string name;
+    double density;
+  };
+
+  // material with E (Young's modulus), nu (Poisson's ratio) (defined in volumetricMeshENuMaterial.h)
+  class ENuMaterial;
+  // Mooney-Rivlin material (defined in volumetricMeshMooneyRivlinMaterial.h)
+  class MooneyRivlinMaterial;
+  // Orthotropic material (defined in volumetricMeshOrthotropicMaterial.h)
+  class OrthotropicMaterial;
+
+  // a volumetric mesh region, i.e., a set of elements sharing the same material
+  class Region
+  {
+  public:
+    Region(int materialIndex, int setIndex);
+    inline int getMaterialIndex() const;
+    inline int getSetIndex() const;
+    inline void setMaterialIndex(int index);
+    inline void setSetIndex(int index);
+
+  protected:
+    int setIndex, materialIndex;
+  };
+
+  static double E_default;
+  static double nu_default;
+  static double density_default;
+
+protected:
+  int numVertices;
+  Vec3d * vertices;
+
+  int numElementVertices;
+  int numElements;
+  int ** elements;
+
+  int numMaterials;
+  int numSets;
+  int numRegions;
+  Material ** materials;
+  Set ** sets; 
+  Region ** regions;
+  int * elementMaterial;  // material index of each element
+
+  // parses the mesh, and returns the mesh element type
+  VolumetricMesh(const char * filename, fileFormatType fileFormat, int numElementVertices, elementType * elementType_, int verbose);
+  // if memoryLoad is 0, binaryInputStream is FILE* (load from a file, via a stream), otherwise, it is char* (load from a memory buffer)
+  VolumetricMesh(void * binaryInputStream, int numElementVertices, elementType * elementType_, int memoryLoad = 0);
+  VolumetricMesh(int numElementVertices_) { numElementVertices = numElementVertices_; }
+  void propagateRegionsToElements();
+  void loadFromBinaryGeneric(void * binaryInputStream, elementType * elementType_, int memoryLoad);
+
+  // constructs a mesh from the given vertices and elements, 
+  // with a single region and material ("E, nu" material)
+  // "vertices" is double-precision array of length 3 x numVertices 
+  // "elements" is an integer array of length numElements x numElementVertices
+  VolumetricMesh(int numVertices, double * vertices, 
+         int numElements, int numElementVertices, int * elements,
+         double E=E_default, double nu=nu_default, double density=density_default); 
+
+  // constructs a mesh from the given vertices and elements, 
+  // with an arbitrary number of sets, regions and materials
+  // "vertices" is double-precision array of length 3 x numVertices 
+  // "elements" is an integer array of length numElements x numElementVertices
+  // "materials", "sets" and "regions" will be copied internally (deep copy), so they
+  // can be released after calling this constructor
+  VolumetricMesh(int numVertices, double * vertices, 
+         int numElements, int numElementVertices, int * elements,
+         int numMaterials, Material ** materials,
+         int numSets, Set ** sets,
+         int numRegions, Region ** regions);
+
+  // creates a submesh consisting of the specified elements of the given mesh
+  // if vertexMap is non-null, it also returns a renaming datastructure: vertexMap[big mesh vertex] is the vertex index in the subset mesh
+  VolumetricMesh(const VolumetricMesh & mesh, int numElements, int * elements, std::map<int,int> * vertexMap = NULL); 
+
+  int saveToAscii(const char * filename, elementType elementType_) const;
+  int saveToBinary(const char * filename, unsigned int * bytesWritten, elementType elementType_) const;
+  int saveToBinary(FILE * binaryOutputStream, unsigned int * bytesWritten, elementType elementType_, bool countBytesOnly = false) const;
+
+  void loadFromAscii(const char * filename, elementType * elementType_, int verbose = 0);
+  void loadFromBinary(const char * filename, elementType * elementType_);
+  void loadFromBinary(FILE * binaryInputStream, elementType * elementType_);
+  void loadFromMemory(unsigned char * binaryInputStream, elementType * elementType_);
+  void assignMaterialsToElements(int verbose);
+
+  static elementType getElementTypeASCII(const char * filename); 
+  static elementType getElementTypeBinary(const char * filename);
+
+  elementType temp; // auxiliary
+
+  friend class VolumetricMeshExtensions;
+  friend class VolumetricMeshLoader;
+
+  static unsigned int readFromFile(void * buf, unsigned int elementSize, unsigned int numElements, void * fin);
+  static unsigned int readFromMemory(void * buf, unsigned int elementSize, unsigned int numElements, void * memoryLocation);
+};
+
+inline VolumetricMesh::Set::Set(const std::string & name_) { name = name_; }
+inline VolumetricMesh::Set::Set(const Set & set) { elements = set.elements; name = set.getName(); }
+inline VolumetricMesh::Set::Set(const std::string & name_, const std::set<int> & elements_) : name(name_), elements(elements_) {}
+inline std::string VolumetricMesh::Set::getName() const { return name; }
+inline int VolumetricMesh::Set::getNumElements() const { return (int)(this->elements.size()); }
+inline void VolumetricMesh::Set::getElements(std::set<int> & elements) const { elements = this->elements; }
+inline const std::set<int> & VolumetricMesh::Set::getElements() const { return elements; }
+inline std::set<int> & VolumetricMesh::Set::getElements() { return elements; }
+inline bool VolumetricMesh::Set::isMember(int element) const {return (elements.find(element) != elements.end());}
+inline void VolumetricMesh::Set::insert(int element) { elements.insert(element); }
+inline void VolumetricMesh::Set::clear() { elements.clear(); }
+
+inline VolumetricMesh::Material::Material(const std::string name_, double density_): density(density_) { name = name_; }
+inline VolumetricMesh::Material::Material(const Material & material) : density(material.getDensity()) { name = material.getName(); }
+inline std::string VolumetricMesh::Material::getName() const { return name; }  // material name
+inline double VolumetricMesh::Material::getDensity() const { return density; } // density
+inline void VolumetricMesh::Material::setName(const std::string name_) { name = name_; }
+inline void VolumetricMesh::Material::setDensity(double density_) { density = density_; }
+
+inline VolumetricMesh::Region::Region(int materialIndex_, int setIndex_): setIndex(setIndex_), materialIndex(materialIndex_) {}
+inline int VolumetricMesh::Region::getMaterialIndex() const { return materialIndex; }
+inline int VolumetricMesh::Region::getSetIndex() const { return setIndex; }
+inline void VolumetricMesh::Region::setMaterialIndex(int index) { materialIndex = index; }
+inline void VolumetricMesh::Region::setSetIndex(int index) { setIndex = index; }
+
+#endif
+
diff --git a/libraries/volumetricMesh/volumetricMeshDeformationGradient.cpp b/libraries/volumetricMesh/volumetricMeshDeformationGradient.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..918badab32905f93c35c88adc0b93ab50081f207
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshDeformationGradient.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Yijing Li                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "float.h"
+#include "mat3d.h"
+#include "volumetricMeshDeformationGradient.h"
+
+// computes the deformation gradient for the deformation given by the displacement vector u (length 3n, where n=#vertices), for the element with index "elementIndex"
+// F is a 3x3 matrix, stored row-major into a vector of length 9
+void VolumetricMeshDeformationGradient::ComputeDeformationGradient(VolumetricMesh * mesh, double * u, int elementIndex, double * F)
+{
+  Vec3d centroid = mesh->getElementCenter(elementIndex);
+  const int numFields = 1;
+  mesh->interpolateGradient(elementIndex, u, numFields, centroid, F);
+
+  F[0] += 1.0;
+  F[4] += 1.0;
+  F[8] += 1.0;
+}
+
+// computes the three principal stretches (singular values of F)
+int VolumetricMeshDeformationGradient::ComputePrincipalStretches(double * F_, double * lambda)
+{
+  // perform modified SVD on the deformation gradient
+  Mat3d F(F_);
+  Mat3d U;
+  Mat3d V;
+  Vec3d Lambda;
+  int modifiedSVD = 1;
+  if (SVD(F, U, Lambda, V, modifiedSVD) != 0)
+  {
+    printf("Error in diagonalization\n");
+    return 1;
+  }
+
+  /*
+    SVD for the deformation gradient has now been computed.
+    It is available in U, Lambda, V
+  */
+
+  Lambda.convertToArray(lambda);
+
+  return 0;
+}
+
+// maximal stretch in the entire mesh
+double VolumetricMeshDeformationGradient::ComputeMaximalStretch(VolumetricMesh * mesh, double * u)
+{
+  int numElements = mesh->getNumElements();
+  double maxLambda = -DBL_MAX;
+
+  for(int el=0; el<numElements; el++)
+  {
+    double F[9];
+    double lambda[3] = { 0, 0, 0 };
+    ComputeDeformationGradient(mesh, u, el, F);
+    ComputePrincipalStretches(F, lambda);
+    if (lambda[0] > maxLambda)
+      maxLambda = lambda[0];
+  }
+
+  return maxLambda;
+}
+
+
diff --git a/libraries/volumetricMesh/volumetricMeshDeformationGradient.h b/libraries/volumetricMesh/volumetricMeshDeformationGradient.h
new file mode 100644
index 0000000000000000000000000000000000000000..3b07b8a28e9ec81cca5b0f0e15063ddec8fafe85
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshDeformationGradient.h
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+   Given a volumetric mesh and its deformations,
+   computes the deformation gradient, and other related quantities.
+*/
+
+#ifndef _VOLUMETRICMESHDEFORMATIONGRADIENT_H_
+#define _VOLUMETRICMESHDEFORMATIONGRADIENT_H_
+
+#include "volumetricMesh.h"
+
+class VolumetricMeshDeformationGradient
+{
+public:
+  // computes the deformation gradient for the deformation given by the displacement vector u (length 3n, where n=#vertices), for the element with index "elementIndex"
+  // F is a 3x3 matrix, stored row-major into a vector of length 9
+  static void ComputeDeformationGradient(VolumetricMesh * mesh, double * u, int elementIndex, double * F);
+
+  // computes the three principal stretches (singular values of F)
+  // lambdas are returned in descending order
+  // returns 0 on success, and non-zero on SVD failure
+  static int ComputePrincipalStretches(double * F, double * lambda);
+
+  // maximal stretch in the entire mesh
+  static double ComputeMaximalStretch(VolumetricMesh * mesh, double * u);
+
+protected:
+
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/volumetricMeshENuMaterial.cpp b/libraries/volumetricMesh/volumetricMeshENuMaterial.cpp
similarity index 80%
rename from src/libvolumetricMesh/volumetricMeshENuMaterial.cpp
rename to libraries/volumetricMesh/volumetricMeshENuMaterial.cpp
index ef74455a139cd17c0b9b64b0f4fbc2b992db9d04..f811e9d1836ebaf7574823fd382a2da5e0a7083e 100644
--- a/src/libvolumetricMesh/volumetricMeshENuMaterial.cpp
+++ b/libraries/volumetricMesh/volumetricMeshENuMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,8 +32,6 @@
 
 #include "volumetricMeshENuMaterial.h"
 
-namespace vega
-{
 VolumetricMesh::Material::materialType VolumetricMesh::ENuMaterial::getType() 
 { 
   return VolumetricMesh::Material::ENU; 
@@ -49,4 +51,3 @@ VolumetricMesh::ENuMaterial * downcastENuMaterial(VolumetricMesh::Material * mat
   return (VolumetricMesh::ENuMaterial*) material;
 }
 
-}
diff --git a/libraries/volumetricMesh/volumetricMeshENuMaterial.h b/libraries/volumetricMesh/volumetricMeshENuMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..47f440c0cffa5718938202e11fb3a7cd0dc76a9c
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshENuMaterial.h
@@ -0,0 +1,73 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _VOLUMETRICMESHENUMATERIAL_H_
+#define _VOLUMETRICMESHENUMATERIAL_H_
+
+#include "volumetricMesh.h"
+
+// stores an isotropic material specified by E (Young's modulus), nu (Poisson's ratio), and density
+// such a material specification is very common: (corotational) linear FEM, StVK, etc.
+class VolumetricMesh::ENuMaterial : public VolumetricMesh::Material
+{
+public:
+  ENuMaterial(std::string name, double density, double E, double nu);
+  ENuMaterial(const ENuMaterial & eNuMaterial);
+  virtual ~ENuMaterial() {}
+  virtual VolumetricMesh::Material * clone() const;
+  virtual VolumetricMesh::Material::materialType getType();
+
+  inline double getE() const; // Young's modulus
+  inline double getNu() const; // Poisson's ratio
+  inline double getLambda() const; // Lame's lambda coefficient
+  inline double getMu() const; // Lame's mu coefficient
+  inline void setE(double E);
+  inline void setNu(double nu);
+
+protected:
+  double E_, nu_;
+};
+
+inline VolumetricMesh::ENuMaterial::ENuMaterial(std::string name, double density, double E, double nu): VolumetricMesh::Material(name, density), E_(E), nu_(nu) {}
+inline VolumetricMesh::ENuMaterial::ENuMaterial(const ENuMaterial & eNuMaterial) : VolumetricMesh::Material(eNuMaterial.getName(), eNuMaterial.getDensity()),  E_(eNuMaterial.getE()), nu_(eNuMaterial.getNu()) {}
+inline double VolumetricMesh::ENuMaterial::getE() const { return E_; } 
+inline double VolumetricMesh::ENuMaterial::getNu() const { return nu_; }
+inline double VolumetricMesh::ENuMaterial::getLambda() const { return (nu_ * E_) / ((1 + nu_) * (1 - 2 * nu_)); }  
+inline double VolumetricMesh::ENuMaterial::getMu() const { return E_ / (2 * (1 + nu_)); }  
+inline void VolumetricMesh::ENuMaterial::setE(double E) { E_ = E; }
+inline void VolumetricMesh::ENuMaterial::setNu(double nu) { nu_ = nu; }
+
+// obtain pointer to ENuMaterial (necessary inside classes that assume ENu material)
+VolumetricMesh::ENuMaterial * downcastENuMaterial(VolumetricMesh::Material * material); // performs a check via getType and returns NULL if material is not ENU
+
+#endif
+
diff --git a/libraries/volumetricMesh/volumetricMeshLoader.cpp b/libraries/volumetricMesh/volumetricMeshLoader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a7f954baf9e5af288c3ce68ddd87bc878635dbbf
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshLoader.cpp
@@ -0,0 +1,239 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "volumetricMeshLoader.h"
+#include "cubicMesh.h"
+#include "tetMesh.h"
+
+// for faster parallel loading of multimesh binary files, enable the -DUSE_TBB macro line in the Makefile-header file (see also documentation)
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#else
+  #include "range.h"
+#endif
+
+using namespace std;
+
+
+VolumetricMesh * VolumetricMeshLoader::load(const char * filename, VolumetricMesh::fileFormatType fileFormat, int verbose)
+{
+  VolumetricMesh::elementType elementType_ = VolumetricMesh::getElementType(filename, fileFormat);
+  if (elementType_ == VolumetricMesh::INVALID)
+  {
+    return NULL;
+  }
+
+  VolumetricMesh * volumetricMesh = NULL;
+
+  switch (fileFormat)
+  {
+    case VolumetricMesh::ASCII:
+    {
+      if (elementType_ == TetMesh::elementType())
+        volumetricMesh = new TetMesh(filename, VolumetricMesh::ASCII, verbose); 
+
+      if (elementType_ == CubicMesh::elementType())
+        volumetricMesh = new CubicMesh(filename, VolumetricMesh::ASCII, verbose);
+    }
+  	break;
+
+    case VolumetricMesh::BINARY:
+    {
+      if (elementType_ == TetMesh::elementType())
+        volumetricMesh = new TetMesh(filename, VolumetricMesh::BINARY, verbose); 
+
+      if (elementType_ == CubicMesh::elementType())
+        volumetricMesh = new CubicMesh(filename, VolumetricMesh::BINARY, verbose);
+    }
+    break;
+
+    default:
+    {
+      printf("Error in VolumetricMeshLoader: invalid file format.\n");
+      return NULL;
+    }
+  }
+
+  return volumetricMesh;
+}
+
+VolumetricMesh * VolumetricMeshLoader::load(void * fin, int memoryLoad)
+{
+  VolumetricMesh::elementType elementType_ = VolumetricMesh::getElementType(fin, memoryLoad);
+
+  VolumetricMesh * volumetricMesh = NULL;
+
+  if (elementType_ == TetMesh::elementType())
+    volumetricMesh = new TetMesh(fin, memoryLoad); 
+
+  if (elementType_ == CubicMesh::elementType())
+    volumetricMesh = new CubicMesh(fin, memoryLoad);
+
+  return volumetricMesh;
+}
+
+int VolumetricMeshLoader::load(const char * filename, int * numVolumetricMeshes, VolumetricMesh *** volumetricMeshes, int verbose)
+{
+  FILE * fin = fopen(filename, "rb");
+  if (fin == NULL)
+  {
+    printf("Error in VolumetricMeshLoader::load: cannot open %s to read.\n", filename);
+    return 1;
+  }
+  int code = load(fin, numVolumetricMeshes, volumetricMeshes, verbose);
+  fclose(fin);
+
+  return code;
+}
+
+int VolumetricMeshLoader::load(FILE * fin, int * numVolumetricMeshes, VolumetricMesh *** volumetricMeshes, int verbose)
+{
+  // read the number of volumetric meshes
+  unsigned int items = fread(numVolumetricMeshes, sizeof(int), 1, fin);
+  if (items != 1)
+    return 1;
+
+  unsigned int numMeshes = *numVolumetricMeshes;
+
+  if (verbose)
+    printf("number of volumetric meshes to be read from binary: %u\n", numMeshes);
+  
+  // read how many bytes are stored for every volumetric mesh
+  vector<int> bytesWritten(numMeshes);
+  items = fread(bytesWritten.data(), sizeof(unsigned int), numMeshes, fin);
+  if (items != numMeshes)
+    return 1;
+
+  if (verbose)
+  {
+    printf("number of bytes for each volumetric mesh: \n");
+    for(unsigned int i=0; i<numMeshes; i++)
+      printf("%u, ", bytesWritten[i]);
+    printf("\n");
+  }
+
+  // compute the total bytes
+  unsigned int totalBytes = 0;
+  for(unsigned int i=0; i<numMeshes; i++)
+    totalBytes += bytesWritten[i];
+
+  // allocate memory for volumetric meshes
+  (*volumetricMeshes) = (VolumetricMesh **) calloc (numMeshes, sizeof(VolumetricMesh *));
+
+  // read entire block from the memory
+  vector<unsigned char> memory(totalBytes);
+  items = fread(memory.data(), sizeof(unsigned char), totalBytes, fin);
+  if (items != totalBytes)
+  {
+    free(*volumetricMeshes);
+    *volumetricMeshes = nullptr;
+    return 1;
+  }
+
+  if (verbose)
+    printf("total bytes excluding header: %u\n", totalBytes);
+
+  // compute the offset for every volumetric mesh
+  vector<unsigned int> offset(numMeshes, 0);
+  for(unsigned int i=1; i<numMeshes; i++)
+    offset[i] = offset[i-1] + bytesWritten[i-1];
+
+  // load every volumetric mesh from memory
+#ifdef USE_TBB
+  tbb::parallel_for(tbb::blocked_range<int>(0, numMeshes), [&](const tbb::blocked_range<int> & rng)
+  {
+#else
+    Range<int> rng(0, numMeshes);
+#endif
+    for (int i = rng.begin(); i != rng.end(); ++i)
+    {
+      if (bytesWritten[i] != 0)
+      {
+        unsigned char * location = &memory[offset[i]];
+        int memoryLoad = 1;
+        (*volumetricMeshes)[i] = load((void *)location, memoryLoad);
+      }
+    }
+#ifdef USE_TBB
+  });
+#endif
+  return 0;
+}
+
+int VolumetricMeshLoader::save(const char * filename, int numVolumetricMeshes, VolumetricMesh ** volumetricMeshes, int * saveVolumetricMeshFlag, int verbose)
+{
+  FILE * output = fopen(filename, "wb");
+  if (output == NULL)
+  {
+    printf("Error in VolumetricMeshLoader::save: cannot open %s to write.\n", filename);
+    return 1;
+  }
+
+  unsigned int * bytesWritten = (unsigned int*) calloc (numVolumetricMeshes, sizeof(unsigned int));
+  // count the number of bytes written to the disk for every volumetric mesh
+  for(int i=0; i<numVolumetricMeshes; i++)
+  {
+    if ((saveVolumetricMeshFlag != NULL) && (saveVolumetricMeshFlag[i] == 0))
+      continue;
+
+    bool countBytesOnly = true;
+    volumetricMeshes[i]->saveToBinary(NULL, &bytesWritten[i], countBytesOnly);
+  }
+
+  if (verbose)
+  {
+    printf("number of bytes for each volumetric mesh: \n");
+    for(int i=0; i<numVolumetricMeshes; i++)
+      printf("%u, ", bytesWritten[i]);
+    printf("\n");
+  }
+
+  // write the header to the disk
+  fwrite(&numVolumetricMeshes, sizeof(int), 1, output);
+  fwrite(bytesWritten, sizeof(unsigned int), numVolumetricMeshes, output);
+
+  // write the volumetric meshes to the disk
+  for(int i=0; i<numVolumetricMeshes; i++)
+  {
+    if ((saveVolumetricMeshFlag != NULL) && (saveVolumetricMeshFlag[i] == 0))
+      continue;
+
+    bool countBytesOnly = false;
+    volumetricMeshes[i]->saveToBinary(output, &bytesWritten[i], countBytesOnly);
+  }
+
+  free(bytesWritten);
+  fclose(output);
+
+  return 0;
+}
+
diff --git a/libraries/volumetricMesh/volumetricMeshLoader.h b/libraries/volumetricMesh/volumetricMeshLoader.h
new file mode 100644
index 0000000000000000000000000000000000000000..cbfb6dea1623de98ad4d5ea40c550dc690593c63
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshLoader.h
@@ -0,0 +1,68 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Loads a volumetric mesh from a text file or a binary file. It automatically determines 
+  the type of the mesh (tet mesh, cube mesh).
+*/
+
+#ifndef _VOLUMETRICMESHLOADER_H_
+#define _VOLUMETRICMESHLOADER_H_
+
+#include "volumetricMesh.h"
+
+class VolumetricMeshLoader
+{
+public:
+  // loads a volumetric mesh (ASCII (.veg format), or BINARY)
+  static VolumetricMesh * load(const char * filename, VolumetricMesh::fileFormatType fileFormat = VolumetricMesh::ASCII, int verbose=1);
+
+  // loads several volumetric meshes from a single binary file
+  static int load(const char * filename, int * numVolumetricMeshes, VolumetricMesh *** volumetricMeshes, int verbose=1);
+
+  // saves several volumetric meshes to a single binary file
+  // if you do not want to use "saveVolumetricMeshFlag", set it to NULL
+  // if saveVolumetricMeshFlag is not NULL, then:
+  // saveVolumetricMeshFlag[i] = 0: skip (do not save) volumetric mesh i
+  // saveVolumetricMeshFlag[i] != 0: save volumetric mesh i to disk
+  static int save(const char * filename, int numVolumetricMeshes, VolumetricMesh ** volumetricMeshes, int * saveVolumetricMeshFlag, int verbose=0);
+
+  // advanced usage: loads a volumetric mesh from the current position of the file stream (binary mode).
+  // if memoryLoad is 0, binaryStream is FILE* (load from a file), otherwise, it is char* (load from a memory buffer)
+  static VolumetricMesh * load(void * fin, int memoryLoad = 0);
+
+protected:
+  static int load(FILE * fin, int * numVolumetricMeshes, VolumetricMesh *** volumetricMeshes, int verbose=1);
+};
+
+#endif
+
diff --git a/src/libvolumetricMesh/volumetricMeshMooneyRivlinMaterial.cpp b/libraries/volumetricMesh/volumetricMeshMooneyRivlinMaterial.cpp
similarity index 81%
rename from src/libvolumetricMesh/volumetricMeshMooneyRivlinMaterial.cpp
rename to libraries/volumetricMesh/volumetricMeshMooneyRivlinMaterial.cpp
index f0f83d3972c46c6dc94661d6323bd3334d78ad66..bb0662292a775f753047dac0ef053873580b59f9 100644
--- a/src/libvolumetricMesh/volumetricMeshMooneyRivlinMaterial.cpp
+++ b/libraries/volumetricMesh/volumetricMeshMooneyRivlinMaterial.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,8 +32,6 @@
 
 #include "volumetricMeshMooneyRivlinMaterial.h"
 
-namespace vega
-{
 VolumetricMesh::Material::materialType VolumetricMesh::MooneyRivlinMaterial::getType() 
 { 
   return VolumetricMesh::Material::MOONEYRIVLIN; 
@@ -49,4 +51,3 @@ VolumetricMesh::MooneyRivlinMaterial * downcastMooneyRivlinMaterial(VolumetricMe
   return (VolumetricMesh::MooneyRivlinMaterial*) material;
 }
 
-}
diff --git a/libraries/volumetricMesh/volumetricMeshMooneyRivlinMaterial.h b/libraries/volumetricMesh/volumetricMeshMooneyRivlinMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..962023deee6a6e45cc1514deb34dd1dbd2040d64
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshMooneyRivlinMaterial.h
@@ -0,0 +1,83 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _VOLUMETRICMESHMOONEYRIVLINMATERIAL_H_
+#define _VOLUMETRICMESHMOONEYRIVLINMATERIAL_H_
+
+#include "volumetricMesh.h"
+
+// Mooney-Rivlin material
+
+/*
+   The implemented Mooney-Rivlin material model is described in:
+   BONET J., WOOD R. D.: Nonlinear Continuum Mechanics
+   for Finite Element Analysis, 2nd Ed. Cambridge University
+   Press, 2008, page 170
+*/
+
+// note that this class only stores material properties
+// the actual implementation of the Mooney-Rivlin material is in the isotropicHyperelasticFEM class
+
+class VolumetricMesh::MooneyRivlinMaterial : public VolumetricMesh::Material
+{
+public:
+  MooneyRivlinMaterial(std::string name, double density, double mu01, double mu10, double v1);
+  MooneyRivlinMaterial(const MooneyRivlinMaterial & mooneyRivlinMaterial);
+  virtual ~MooneyRivlinMaterial() {}
+  virtual VolumetricMesh::Material * clone() const;
+  virtual VolumetricMesh::Material::materialType getType();
+
+  inline double getmu01() const; 
+  inline double getmu10() const; 
+  inline double getv1() const; 
+  inline void setmu01(double mu01); 
+  inline void setmu10(double mu10); 
+  inline void setv1(double v1); 
+
+protected:
+  double mu01_, mu10_, v1_;
+};
+
+inline VolumetricMesh::MooneyRivlinMaterial::MooneyRivlinMaterial(std::string name, double density, double mu01, double mu10, double v1): VolumetricMesh::Material(name, density), mu01_(mu01), mu10_(mu10), v1_(v1) {}
+inline VolumetricMesh::MooneyRivlinMaterial::MooneyRivlinMaterial(const MooneyRivlinMaterial & mooneyRivlinMaterial) : VolumetricMesh::Material(mooneyRivlinMaterial.getName(), mooneyRivlinMaterial.getDensity()),  mu01_(mooneyRivlinMaterial.getmu01()), mu10_(mooneyRivlinMaterial.getmu10()), v1_(mooneyRivlinMaterial.getv1()) {}
+inline double VolumetricMesh::MooneyRivlinMaterial::getmu01() const { return mu01_; } 
+inline double VolumetricMesh::MooneyRivlinMaterial::getmu10() const { return mu10_; } 
+inline double VolumetricMesh::MooneyRivlinMaterial::getv1() const { return v1_; } 
+inline void VolumetricMesh::MooneyRivlinMaterial::setmu01(double mu01) { mu01_ = mu01; }
+inline void VolumetricMesh::MooneyRivlinMaterial::setmu10(double mu10) { mu10_ = mu10; }
+inline void VolumetricMesh::MooneyRivlinMaterial::setv1(double v1) { v1_ = v1; }
+
+// obtain pointer to MooneyRivlinMaterial (necessary inside classes that implement the Mooney-Rivlin material)
+VolumetricMesh::MooneyRivlinMaterial * downcastMooneyRivlinMaterial(VolumetricMesh::Material * material); // performs a check via getType and returns NULL if material is not Mooney Rivlin
+
+#endif
+
diff --git a/libraries/volumetricMesh/volumetricMeshOrthotropicMaterial.cpp b/libraries/volumetricMesh/volumetricMeshOrthotropicMaterial.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d53b3df552396d6dc2aae01948935e0bd17b21bb
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshOrthotropicMaterial.cpp
@@ -0,0 +1,82 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "volumetricMeshOrthotropicMaterial.h"
+
+VolumetricMesh::OrthotropicMaterial::OrthotropicMaterial(std::string name, double density, double E1, double E2, double E3, double nu12, double nu23, double nu31, double G12, double G23, double G31, double * R): VolumetricMesh::Material(name, density), E1_(E1), E2_(E2), E3_(E3), nu12_(nu12), nu23_(nu23), nu31_(nu31), G12_(G12), G23_(G23), G31_(G31)
+{
+  memcpy(R_, R, sizeof(double) * 9);
+}
+
+VolumetricMesh::OrthotropicMaterial::OrthotropicMaterial(const OrthotropicMaterial & orthotropicMaterial) : VolumetricMesh::Material(orthotropicMaterial.getName(), orthotropicMaterial.getDensity()),  
+  E1_(orthotropicMaterial.getE1()), 
+  E2_(orthotropicMaterial.getE2()), 
+  E3_(orthotropicMaterial.getE3()), 
+  nu12_(orthotropicMaterial.getNu12()), 
+  nu23_(orthotropicMaterial.getNu23()), 
+  nu31_(orthotropicMaterial.getNu31()), 
+  G12_(orthotropicMaterial.getG12()),
+  G23_(orthotropicMaterial.getG23()), 
+  G31_(orthotropicMaterial.getG31()) 
+{
+  orthotropicMaterial.getR(R_); 
+}
+
+VolumetricMesh::Material::materialType VolumetricMesh::OrthotropicMaterial::getType() 
+{ 
+  return VolumetricMesh::Material::ORTHOTROPIC; 
+}
+
+VolumetricMesh::Material * VolumetricMesh::OrthotropicMaterial::clone() const
+{ 
+  return new VolumetricMesh::OrthotropicMaterial(*this); 
+}
+
+// performs a check via getType and returns NULL if material is not ORTHOTROPIC
+VolumetricMesh::OrthotropicMaterial * downcastOrthotropicMaterial(VolumetricMesh::Material * material)
+{
+  if (material->getType() != VolumetricMesh::Material::ORTHOTROPIC)
+    return NULL;
+
+  return (VolumetricMesh::OrthotropicMaterial*) material;
+}
+
+void VolumetricMesh::OrthotropicMaterial::setR(double * R) 
+{
+  memcpy(R_, R, sizeof(double) * 9);
+}
+
+void VolumetricMesh::OrthotropicMaterial::getR(double * R) const
+{
+  memcpy(R, R_, sizeof(double) * 9);
+}
+
diff --git a/libraries/volumetricMesh/volumetricMeshOrthotropicMaterial.h b/libraries/volumetricMesh/volumetricMeshOrthotropicMaterial.h
new file mode 100644
index 0000000000000000000000000000000000000000..5feceba9b81c791522f21419066f8c221ee5ad7a
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshOrthotropicMaterial.h
@@ -0,0 +1,104 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Stores an orthotropic material.
+*/
+
+#ifndef _VOLUMETRICMESHORTHOTROPICMATERIAL_H_
+#define _VOLUMETRICMESHORTHOTROPICMATERIAL_H_
+
+#include "volumetricMesh.h"
+
+// stores an orthotropic material
+class VolumetricMesh::OrthotropicMaterial : public VolumetricMesh::Material
+{
+public:
+  OrthotropicMaterial(std::string name, double density, double E1, double E2, double E3, double nu12, double nu23, double nu31, double G12, double G23, double G31, double * R);
+  OrthotropicMaterial(const OrthotropicMaterial & orthotropicMaterial);
+  virtual ~OrthotropicMaterial() {}
+  virtual VolumetricMesh::Material * clone() const;
+  virtual VolumetricMesh::Material::materialType getType();
+
+  // Es, Nus and Gs are all given in the local coordinate system specified by R
+  inline double getE1() const; // Young's modulus in the x-direction  
+  inline double getE2() const; // Young's modulus in the y-direction  
+  inline double getE3() const; // Young's modulus in the z-direction  
+  inline double getNu12() const; // Poisson's ratio when pulling in x-direction, contraction in y
+  inline double getNu23() const; // Poisson's ratio when pulling in y-direction, contraction in z
+  inline double getNu31() const; // Poisson's ratio when pulling in z-direction, contraction in x
+  inline double getG12() const; // shear modulus in xy
+  inline double getG23() const; // shear modulus in yz
+  inline double getG31() const; // shear modulus in zx
+  void getR(double * R) const; // rotation (3x3 matrix) of the local coordinate system, in row-major order
+
+  inline void setE1(double E1); 
+  inline void setE2(double E2); 
+  inline void setE3(double E3); 
+  inline void setNu12(double nu12); 
+  inline void setNu23(double nu23); 
+  inline void setNu31(double nu31); 
+  inline void setG12(double G12); 
+  inline void setG23(double G23); 
+  inline void setG31(double G31); 
+  void setR(double * R); 
+
+protected:
+  double E1_, E2_, E3_, nu12_, nu23_, nu31_, G12_, G23_, G31_; 
+  double R_[9];
+};
+
+inline double VolumetricMesh::OrthotropicMaterial::getE1() const { return E1_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getE2() const { return E2_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getE3() const { return E3_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getNu12() const { return nu12_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getNu23() const { return nu23_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getNu31() const { return nu31_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getG12() const { return G12_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getG23() const { return G23_; } 
+inline double VolumetricMesh::OrthotropicMaterial::getG31() const { return G31_; } 
+
+inline void VolumetricMesh::OrthotropicMaterial::setE1(double E1) { E1_ = E1; }
+inline void VolumetricMesh::OrthotropicMaterial::setE2(double E2) { E2_ = E2; }
+inline void VolumetricMesh::OrthotropicMaterial::setE3(double E3) { E3_ = E3; }
+inline void VolumetricMesh::OrthotropicMaterial::setNu12(double nu12) { nu12_ = nu12; }
+inline void VolumetricMesh::OrthotropicMaterial::setNu23(double nu23) { nu23_ = nu23; }
+inline void VolumetricMesh::OrthotropicMaterial::setNu31(double nu31) { nu31_ = nu31; }
+inline void VolumetricMesh::OrthotropicMaterial::setG12(double G12) { G12_ = G12; }
+inline void VolumetricMesh::OrthotropicMaterial::setG23(double G23) { G23_ = G23; }
+inline void VolumetricMesh::OrthotropicMaterial::setG31(double G31) { G31_ = G31; }
+
+// obtain pointer to OrthotropicMaterial (necessary inside classes that assume ENu material)
+VolumetricMesh::OrthotropicMaterial * downcastOrthotropicMaterial(VolumetricMesh::Material * material); // performs a check via getType and returns NULL if material is not Orthotropic
+
+#endif
+
diff --git a/src/libvolumetricMesh/volumetricMeshParser.cpp b/libraries/volumetricMesh/volumetricMeshParser.cpp
similarity index 83%
rename from src/libvolumetricMesh/volumetricMeshParser.cpp
rename to libraries/volumetricMesh/volumetricMeshParser.cpp
index 80a74ce229c3d4bc965c8188e522a53445bbd69b..35417484f5ff3606f51c63a21851e5220fb06043 100644
--- a/src/libvolumetricMesh/volumetricMeshParser.cpp
+++ b/libraries/volumetricMesh/volumetricMeshParser.cpp
@@ -1,19 +1,23 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -29,12 +33,10 @@
 #include <string.h>
 #include "volumetricMeshParser.h"
 
-namespace vega
-{
-VolumetricMeshParser::VolumetricMeshParser(char * includeToken_)
+VolumetricMeshParser::VolumetricMeshParser(const char * includeToken_)
 {
   fin = NULL;
-  fileStackDepth = -1;
+  //fileStackDepth = -1;
   fileStack.empty();
   if (includeToken_ == NULL)
   {
@@ -48,15 +50,15 @@ VolumetricMeshParser::VolumetricMeshParser(char * includeToken_)
   }
 }
 
-int VolumetricMeshParser::open(char * filename)
+int VolumetricMeshParser::open(const char * filename)
 {
   // extract directory name and filename
   // seek for last '/' in filename
   // if no '/', then directory name is "."
   // else, everything before '/' is directory name, and everything after is filename
 
-  char * iter = filename;
-  char * lastPos = NULL;
+  const char * iter = filename;
+  const char * lastPos = NULL;
 
   while (*iter != '\0')
   {
@@ -91,7 +93,7 @@ int VolumetricMeshParser::open(char * filename)
   if (!fin)
     return 1;
 
-  fileStackDepth=0;
+  //fileStackDepth=0;
   fileStack.push_back(fin);
 
   return 0;
@@ -104,14 +106,13 @@ VolumetricMeshParser::~VolumetricMeshParser()
 
 void VolumetricMeshParser::rewindToStart()
 {
-  if (fileStackDepth < 0) // no files currently opened
+  if (fileStack.empty()) // no files currently opened
     return;
 
-  while (fileStackDepth > 0)
+  while (fileStack.size() > 1)
   {
-    fclose(fileStack[fileStackDepth]);
+    fclose(fileStack[fileStack.size() - 1]);
     fileStack.pop_back();
-    fileStackDepth--;
   }
 
   // now, we have fileStackDepth == 0
@@ -121,11 +122,10 @@ void VolumetricMeshParser::rewindToStart()
 
 void VolumetricMeshParser::close()
 {
-  while (fileStackDepth >= 0)
+  while (fileStack.size() > 0)
   {
-    fclose(fileStack[fileStackDepth]);
+    fclose(fileStack[fileStack.size() - 1]);
     fileStack.pop_back();
-    fileStackDepth--;
   }
 }
 
@@ -153,12 +153,11 @@ char * VolumetricMeshParser::getNextLine(char * s, int numRetainedSpaces, int re
   char * code;
   do
   {
-    while (((code = fgets(s, 4096, fin)) == NULL) && (fileStackDepth > 0)) // if EOF, pop previous file from stack
+    while (((code = fgets(s, 4096, fin)) == NULL) && (fileStack.size() > 1)) // if EOF, pop previous file from stack
     {
-      fileStackDepth--;
       fclose(fin);
       fileStack.pop_back();
-      fin = fileStack[fileStackDepth];
+      fin = fileStack[fileStack.size() - 1];
     }
 
     if (code == NULL) // reached end of main file
@@ -183,7 +182,9 @@ char * VolumetricMeshParser::getNextLine(char * s, int numRetainedSpaces, int re
     if (!finNew)
     {
       printf("Error: couldn't open include file %s.\n", newFileCompleteName);
-      exit(1);
+      //exit(1);
+      close();
+      throw -1;
     }
 
     if ((code = fgets(s,4096,finNew)) != NULL) // new file is not empty
@@ -191,7 +192,6 @@ char * VolumetricMeshParser::getNextLine(char * s, int numRetainedSpaces, int re
       beautifyLine(s, numRetainedSpaces, removeWhitespace_);
 
       // register the new file
-      fileStackDepth++;
       fileStack.push_back(finNew);
       fin = finNew;
     }
@@ -256,4 +256,3 @@ void VolumetricMeshParser::removeWhitespace(char * s, int numRetainedSpaces)
   }
 }
 
-}
diff --git a/libraries/volumetricMesh/volumetricMeshParser.h b/libraries/volumetricMesh/volumetricMeshParser.h
new file mode 100644
index 0000000000000000000000000000000000000000..b63940eda11afba1d66254a66f32cbc64079d5fa
--- /dev/null
+++ b/libraries/volumetricMesh/volumetricMeshParser.h
@@ -0,0 +1,79 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2018 USC *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Jernej Barbic                                            *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _VOLUMETRICMESHPARSER_H_
+#define _VOLUMETRICMESHPARSER_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+using namespace std;
+
+/*
+  A parser for the volumetric mesh text file format.
+  Note: the end user never needs to use this class directly.
+  See volumetricMesh.h .
+*/
+
+class VolumetricMeshParser
+{
+public:
+
+  VolumetricMeshParser(const char * includeToken = NULL); // pass NULL for normal usage
+  ~VolumetricMeshParser();
+
+  int open(const char * filename);
+
+  // return the next line, s must be externally allocated string
+  // if last line, return will be NULL
+  char * getNextLine(char * s, int numRetainedSpaces=0, int removeWhitespace=1);
+
+  void rewindToStart();
+  void close();
+
+  static void upperCase(char * s);
+  static void removeWhitespace(char * s, int numRetainedSpaces=0); // any whitespace equal in length or longer to "numRetainedSpaces" is shrunk to "numRetainedSpaces" and retained
+  static void beautifyLine(char * s, int numRetainedSpaces, int removeWhitespace=1); // strip whitespace + removes trailing "\n"
+
+protected:
+  FILE * fin;
+  vector< FILE* > fileStack;
+  //int fileStackDepth;
+
+  char directoryName[4096];
+
+  char includeToken[96]; // normally "*INCLUDE "
+  int includeTokenLength; // normally 9
+};
+
+#endif
+
diff --git a/libraries/windingNumber/windingNumber.cpp b/libraries/windingNumber/windingNumber.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..27fdf95271ad1192fdccc8ed61e04dc03d00a1f5
--- /dev/null
+++ b/libraries/windingNumber/windingNumber.cpp
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "windingNumber" library , Copyright (C) 2018 USC                      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "windingNumber.h"
+#include "macros.h"
+
+double WindingNumber::computeWindingNumber(ObjMesh * objMesh, const Vec3d & p) 
+{
+  assert(objMesh->isTriangularMesh());
+  double w = 0;
+  //  double maxO = -DBL_MAX;
+  //  double minO = DBL_MAX;
+
+  for(size_t i = 0; i < objMesh->getNumGroups(); i++) 
+  {
+    const ObjMesh::Group* group = objMesh->getGroupHandle(i);
+    for(size_t j = 0; j < group->getNumFaces(); j++) 
+    {
+      const ObjMesh::Face* face = group->getFaceHandle(j);
+      assert(face->getNumVertices() == 3);
+      Vec3d vtx[3];
+
+      for(int k = 0; k < 3; k++) 
+      {
+        const ObjMesh::Vertex* v = face->getVertexHandle(k);
+        //unsigned int posInd = v->getPositionIndex();
+        vtx[k] = objMesh->getPosition(*v);
+      }
+
+      Vec3d a = vtx[0] - p;
+      Vec3d b = vtx[1] - p;
+      Vec3d c = vtx[2] - p;
+      double la = len(a), lb = len(b), lc = len(c);
+      Mat3d mat(a,b,c);
+      double omega = 2*atan2(det(mat), (la * lb * lc + dot(a,b) * lc + dot(b,c) * la + dot(c,a) * lb));
+//      if(omega > maxO) maxO = omega;
+//      if(omega < minO) minO = omega;
+      w += omega;
+    }
+  }
+
+  w /= 4 * M_PI;
+  //PRINT(maxO / (4*M_PI));
+  //PRINT(minO / (4*M_PI));
+  return w;
+}
+
diff --git a/libraries/windingNumber/windingNumber.h b/libraries/windingNumber/windingNumber.h
new file mode 100644
index 0000000000000000000000000000000000000000..a8cb83d0e6e0ad5edba8f7f965f90f0dc8bdef77
--- /dev/null
+++ b/libraries/windingNumber/windingNumber.h
@@ -0,0 +1,59 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "windingNumber" library , Copyright (C) 2018 USC                      *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Computes the winding number of a 3D point with respect to a given 
+  closed manifold triangle mesh, as described in:
+
+  Alec Jacobson, Ladislav Kavan, Olga Sorkine-Hornung:
+  Robust Inside-Outside Segmentation using Generalized Winding Numbers, SIGGRAPH 2013
+
+  This implementation does not use the hierarchical acceleration presented
+  in the paper above; instead, it loops over all the triangles.
+*/
+
+#ifndef _WINDINGNUMBER_H_
+#define _WINDINGNUMBER_H_
+
+#include "objMesh.h"
+#include "minivector.h"
+
+class WindingNumber 
+{
+public:
+
+  // computes the winding number of point p with respect to the given mesh
+  static double computeWindingNumber(ObjMesh * objMesh, const Vec3d & p);
+};
+
+#endif
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
deleted file mode 100644
index e4519396cf0d2f9bfb49cdaf398786ffbb99b2e8..0000000000000000000000000000000000000000
--- a/src/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-foreach(mod ${VegaFEM_Modules})
-  add_subdirectory(lib${mod})
-endforeach()
-
-if(VegaFEM_BUILD_UTILITIES)
-  add_subdirectory(util)
-endif(VegaFEM_BUILD_UTILITIES)
diff --git a/src/libcamera/CMakeLists.txt b/src/libcamera/CMakeLists.txt
deleted file mode 100644
index 37204b82d248f3d3ad8a268d9231be576ce795f5..0000000000000000000000000000000000000000
--- a/src/libcamera/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-vega_add_library(camera
-  SOURCES camera.cpp
-  PUBLIC_HEADERS
-    camera.h
-)
-target_link_libraries(camera
-  PUBLIC
-    ${OPENGL_LIBRARIES}
-)
diff --git a/src/libclothBW/CMakeLists.txt b/src/libclothBW/CMakeLists.txt
deleted file mode 100644
index 4f988ee05fd3d00faec4d87c9d0de40f5c68cc37..0000000000000000000000000000000000000000
--- a/src/libclothBW/CMakeLists.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-set(clothbw_srcs
-  clothBW.cpp
-  clothBWFromObjMesh.cpp
-)
-
-set(clothbw_hdrs
-  clothBW.h
-  clothBWFromObjMesh.h
-)
-
-if(VegaFEM_ENABLE_PTHREADS_SUPPORT)
-  list(APPEND clothbw_hdrs clothBWMT.h)
-  list(APPEND clothbw_srcs clothBWMT.cpp)
-endif()
-
-vega_add_library(clothBW
-  SOURCES
-    ${clothbw_srcs}
-  PUBLIC_HEADERS
-    ${clothbw_hdrs}
-)
-
-target_link_libraries(clothBW
-  PUBLIC
-    objMesh
-    minivector
-    sparseMatrix
-    Threads::Threads
-)
diff --git a/src/libclothBW/clothBW.cpp b/src/libclothBW/clothBW.cpp
deleted file mode 100644
index 296f4513432d87801f264de58e16690d7f11748d..0000000000000000000000000000000000000000
--- a/src/libclothBW/clothBW.cpp
+++ /dev/null
@@ -1,1456 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "clothBW" library , Copyright (C) 2013 USC                            *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Andy Pierce, Yu Yu Xu, Jernej Barbic                     *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <map>
-using namespace std;
-#include "clothBW.h"
-#include "macros.h"
-#include "matrixMultiplyMacros.h"
-#include "minivector.h"
-//#include "performanceCounter.h"
-
-namespace vega
-{
-// constructor without triangleUVs
-ClothBW::ClothBW(int numParticles_, double * masses_, double * restPositions_, int numTriangles_, int * triangles_, int * triangleGroups_, int numMaterialGroups_, double * groupTensileStiffness_, double * groupShearStiffness_, double * groupBendStiffnessU_, double * groupBendStiffnessV_, double * groupDamping_, int addGravity_) : bu(1.0), bv(1.0), addGravity(addGravity_), g(9.81)
-{
-  double * triangleUVs_ = (double *) malloc( sizeof(double) * 3 * 2 * numTriangles_ );
-  
-  //default to computing everything
-  _computationConditions[0] = true; // stretch&shear force
-  _computationConditions[1] = true; // bend force
-  _computationConditions[2] = true; // stretch&shear bend stiffness
-  _computationConditions[3] = true; // bend stiffness matrix
-  
-  // default to taking rest angles into account
-  useRestAnglesForBendingForces = 1;
-  
-  for (int i = 0 ; i < numTriangles_; i++)
-  {	
-    int particleA = triangles_[3*i+0];
-    int particleB = triangles_[3*i+1];
-    int particleC = triangles_[3*i+2];
-    
-    triangleUVs_[6*i+0] = 0.0;		// for the vertex A u
-    triangleUVs_[6*i+1] = 0.0;		// for the vertex A v
-    
-    Vec3d x0; // vector from A to B (previously B to A)
-    x0[0] = restPositions_[3*particleA+0] - restPositions_[3*particleB+0];
-    x0[1] = restPositions_[3*particleA+1] - restPositions_[3*particleB+1]; 
-    x0[2] = restPositions_[3*particleA+2] - restPositions_[3*particleB+2];  
-    
-    double lengthAB = len(x0);
-    
-    triangleUVs_[6*i+2] = lengthAB;	// for the vertex B u
-    triangleUVs_[6*i+3] = 0.0;		// for the vertex B v
-    
-    Vec3d xn0 = norm(x0); //vector from A to B normalized
-    
-    Vec3d x1; // vector from A to C (previously C to A)
-    x1[0] = restPositions_[3*particleA+0] - restPositions_[3*particleC+0];
-    x1[1] = restPositions_[3*particleA+1] - restPositions_[3*particleC+1]; 
-    x1[2] = restPositions_[3*particleA+2] - restPositions_[3*particleC+2];    
-    
-    double lengthAC = len(x1);
-    
-    triangleUVs_[6*i+4] = dot(xn0, x1); // for the vertex C u
-    triangleUVs_[6*i+5] = sqrt(lengthAC * lengthAC - triangleUVs_[6*i+4] * triangleUVs_[6*i+4]);	// for the vertex C v
-    
-  }
-  GenerateBW(numParticles_, masses_, restPositions_, numTriangles_, triangles_, triangleUVs_, triangleGroups_, numMaterialGroups_, groupTensileStiffness_, groupShearStiffness_, groupBendStiffnessU_, groupBendStiffnessV_, groupDamping_, addGravity_);
-}
-
-// constructor with trianglesUVs
-ClothBW::ClothBW(int numParticles_, double * masses_, double * restPositions_, int numTriangles_, int * triangles_, double * triangleUVs_, int * triangleGroups_, int numMaterialGroups_, double * groupTensileStiffness_, double * groupShearStiffness_, double * groupBendStiffnessU_, double * groupBendStiffnessV_, double * groupDamping_, int addGravity_) : bu(1.0), bv(1.0), addGravity(addGravity_), g(9.81)
-{
-  //default to computing everything
-  _computationConditions[0] = true; // stretch&shear force
-  _computationConditions[1] = true; // bend force
-  _computationConditions[2] = true; // stretch&shear bend stiffness
-  _computationConditions[3] = true; // bend stiffness matrix
-  
-  // default to taking rest angles into account
-  useRestAnglesForBendingForces = 1;
-  
-  GenerateBW(numParticles_, masses_, restPositions_, numTriangles_, triangles_, triangleUVs_, triangleGroups_, numMaterialGroups_, groupTensileStiffness_, groupShearStiffness_, groupBendStiffnessU_, groupBendStiffnessV_, groupDamping_, addGravity_);
-}
-
-// copy constructor
-ClothBW::ClothBW(ClothBW & ClothBW)
-{
-  _computationConditions[0] = ClothBW._computationConditions[0]; // stretch&shear force
-  _computationConditions[1] = ClothBW._computationConditions[1]; // bend force
-  _computationConditions[2] = ClothBW._computationConditions[2]; // stretch&shear bend stiffness
-  _computationConditions[3] = ClothBW._computationConditions[3]; // bend stiffness matrix
-  
-  // default to taking rest angles into account
-  useRestAnglesForBendingForces = ClothBW.useRestAnglesForBendingForces;
-  
-  numParticles = ClothBW.numParticles;
-  
-  masses = (double*) malloc (sizeof(double) * numParticles);
-  memcpy(masses, ClothBW.masses, sizeof(double) * numParticles);
-  restPositions = (double*) malloc (sizeof(double) * 3 * numParticles);
-  memcpy(restPositions, ClothBW.restPositions, sizeof(double) * 3 * numParticles);
-  
-  numQuads = ClothBW.numQuads;
-  restAngles = (double*) malloc (sizeof(double) * numQuads);
-  memcpy(restAngles, ClothBW.restAngles, sizeof(double) * numQuads);
-  
-  numTriangles = ClothBW.numTriangles;
-  triangles = (int*) malloc (sizeof(int) * 3 * numTriangles);
-  memcpy(triangles, ClothBW.triangles, sizeof(int) * 3 * numTriangles);
-  triangleGroups = (int*) malloc (sizeof(int) * numTriangles);
-  memcpy(triangleGroups, ClothBW.triangleGroups, sizeof(int) * numTriangles);
-  inverseIndicesStretchAndShear = (int*) malloc (sizeof(int) * 9 * numTriangles);
-  memcpy(inverseIndicesStretchAndShear, ClothBW.inverseIndicesStretchAndShear, sizeof(int) * 9 * numTriangles);
-  triangleUVs = (double *) malloc(sizeof(double) * 3 * 2 * numTriangles);
-  memcpy(triangleUVs, ClothBW.triangleUVs, sizeof(double) * 3 * 2 * numTriangles);
-  
-  
-  inverseIndicesQuad = (int*) malloc (sizeof(int) * 16 * numQuads);
-  memcpy(inverseIndicesQuad, ClothBW.inverseIndicesQuad, sizeof(int) * 16 * numQuads);
-  quadComponentIndices = (int*) malloc (sizeof(int) * 6 * numQuads);
-  memcpy(quadComponentIndices, ClothBW.quadComponentIndices, sizeof(int) * 6 * numQuads);
-  
-  numMaterialGroups = ClothBW.numMaterialGroups;
-  groupTensileStiffness = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupTensileStiffness, ClothBW.groupTensileStiffness, sizeof(double) * numMaterialGroups);
-  groupShearStiffness = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupShearStiffness, ClothBW.groupShearStiffness, sizeof(double) * numMaterialGroups);
-  groupBendStiffnessU = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupBendStiffnessU, ClothBW.groupBendStiffnessU, sizeof(double) * numMaterialGroups);
-  groupBendStiffnessV = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupBendStiffnessV, ClothBW.groupBendStiffnessV, sizeof(double) * numMaterialGroups);
-  groupDamping = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupDamping, ClothBW.groupDamping, sizeof(double) * numMaterialGroups);
-  
-  bu = ClothBW.bu;
-  bv = ClothBW.bv;
-  addGravity = ClothBW.addGravity;
-  g = ClothBW.g;	
-}
-
-void ClothBW::GenerateBW(int numParticles_, double * masses_, double * restPositions_, int numTriangles_, int * triangles_, double * triangleUVs_, int * triangleGroups_, int numMaterialGroups_, double * groupTensileStiffness_, double * groupShearStiffness_, double * groupBendStiffnessU_, double * groupBendStiffnessV_, double * groupDamping_, int addGravity_) 
-{
-  numParticles = numParticles_;	
-  masses = (double*) malloc (sizeof(double) * numParticles);
-  memcpy(masses, masses_, sizeof(double) * numParticles);
-  restPositions = (double*) malloc (sizeof(double) * 3 * numParticles);
-  memcpy(restPositions, restPositions_, sizeof(double) * 3 * numParticles);
-  
-  numTriangles = numTriangles_;
-  triangles = (int*) malloc (sizeof(int) * 3 * numTriangles);
-  memcpy(triangles, triangles_, sizeof(int) * 3 * numTriangles);
-  triangleUVs = (double*) malloc(sizeof(double) * 3 * 2 * numTriangles);
-  memcpy(triangleUVs, triangleUVs_, sizeof(double) * 3 * 2 * numTriangles);
-  triangleGroups = (int*) malloc (sizeof(int) * numTriangles);
-  memcpy(triangleGroups, triangleGroups_, sizeof(int) * numTriangles);
-  
-  numMaterialGroups = numMaterialGroups_;
-  groupTensileStiffness = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupTensileStiffness, groupTensileStiffness_, sizeof(double) * numMaterialGroups);
-  groupShearStiffness = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupShearStiffness, groupShearStiffness_, sizeof(double) * numMaterialGroups);
-  groupBendStiffnessU = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupBendStiffnessU, groupBendStiffnessU_, sizeof(double) * numMaterialGroups);
-  groupBendStiffnessV = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupBendStiffnessV, groupBendStiffnessV_, sizeof(double) * numMaterialGroups);
-  groupDamping = (double*) malloc (sizeof(double) * numMaterialGroups);
-  memcpy(groupDamping, groupDamping_, sizeof(double) * numMaterialGroups);
-  
-  // computing bending information
-  
-  // with these maps we parse all edges and find which ones 'belong' to
-  // two separate triangles like the one pointed out below:
-  /* 
-             *----------*
-            / \        /
-           /-> \      /
-          /  -> \    /
-         /     ->\  /
-        *----------*
-   */
-  // these edges are important because they can bend
-  
-  // SORTED ensures that edges are paired in ascending order
-  #define SORTED(i,j) ( (i) <= (j) ? make_pair((i),(j)) : make_pair((j),(i)) )
-  
-  // edgeInfo data structure: maps an edge (represented by a pair of vertex indices)
-  // to its corresponding triangle index as well as the index of the non-edge vertex.
-  // it is a vector because an edge may belong to either 1 or 2 triangles:
-  // if it belongs to two triangles, the order of the index vector is as follows:
-  // [index of triangle 1] [index of non-edge vertex 1] [index of triangle 2] [index of non-edge vertex 2]
-  // if it belongs to only 1 triangle, then the vector contains only the first 2 entries shown above
-  std::map<std::pair<int,int>, std::vector<int> > edgeInfo;
-  std::map<std::pair<int,int>, std::vector<int> >::iterator iter;
-  for( int i = 0 ; i < numTriangles; i++ )
-  {
-    int particleA = triangles_[i*3+0];
-    int particleB = triangles_[i*3+1];
-    int particleC = triangles_[i*3+2];
-    for( int j = 0 ; j < 3; j++)
-    {
-      std::pair<int,int> edge;
-      int nonEdgeIndex;
-      if( j == 0 )	{ edge = (SORTED(particleA,particleB)); nonEdgeIndex = particleC; }
-      if( j == 1 )	{ edge = (SORTED(particleB,particleC)); nonEdgeIndex = particleA; }
-      if( j == 2 )	{ edge = (SORTED(particleA,particleC)); nonEdgeIndex = particleB; }
-      
-      // if edge is not already in the list, we insert it to end of list
-      // and map it to the appropriate triangle index and non-edge vertex index
-      if( (iter = edgeInfo.find( edge )) == edgeInfo.end() )
-      {
-        std::vector<int> indices;
-        indices.push_back(i);
-        indices.push_back(nonEdgeIndex);
-        edgeInfo.insert( make_pair( edge, indices ));
-      }
-      
-      // if we find it in the list (which means that edge has already been mapped
-      // to a triangle index and a non-edge index), then we insert the new triangle
-      // index and non-edge index to the back of the index vector
-      else 
-      {
-        iter->second.push_back(i);
-        iter->second.push_back(nonEdgeIndex);
-      }
-    }
-  }
-  
-  // delete edges with only one triangles
-  map<std::pair<int,int>, std::vector<int> >::iterator iter_backup;
-  iter = edgeInfo.begin();
-  iter_backup = iter;
-  int edgeError = 0;
-  while(iter != edgeInfo.end())
-  {
-    iter_backup++;
-    
-    // if edge is only mapped to one triangle and one non-edge vertex index (1+1 = 2)
-    // then we delete it
-    if( iter->second.size() == 2 ) 
-    { 
-      // edge on the border (only 1 neighboring triangle)
-      edgeInfo.erase(iter); 
-      iter = iter_backup; 
-    }
-    
-    // else if edge is mapped to two triangles and two non-edge vertex indices (2+2 = 4)
-    // then we advance past it
-    else if( iter->second.size() == 4 )	
-      iter++;
-    
-    // size must be either 2 or 4 because every edge should be mapped to either
-    // one triangle index and one non-edge index
-    // or two triangles indices and two non-edge indices (if it is an edge shared by 2 triangles)
-    else	
-    {
-      edgeError = 1;
-      printf( "Edge Error!\n" );
-      break;
-    }
-  }
-  if (edgeError != 0)
-    throw 1;
-  
-  numQuads = edgeInfo.size();
-
-  restAngles = (double*) malloc (sizeof(double) * numQuads);
-
-  quadComponentIndices = (int *)malloc(sizeof(int) * 6 * numQuads);
-  iter = edgeInfo.begin();
-  
-  // below we save the information about edges belonging to more than one
-  // triangle. we store the info in the data structure quadComponentIndices, organized
-  // with 6 entries for each of these edges as pictured below:
-  
-    /* Old ordering
-            0---------5
-           / \       /
-         /    \  4  /
-        /  2   \   /
-       /        \ /
-      3----------1 
-   */
-  
-    /* New ordering
-            1---------3
-           / \       /
-         /    \  5  /
-        /  4   \   /
-       /        \ /
-      0----------2 
-   */
-  
-  for(int i = 0 ; i < numQuads; i++)
-  {
-    // Old order below
-//    quadComponentIndices[ i * 6 + 0 ] = iter->first.first;		//  index of edge vertex 1
-//    quadComponentIndices[ i * 6 + 1 ] = iter->first.second;		//	index of edge vertex 2
-//    quadComponentIndices[ i * 6 + 2 ] = (iter->second)[0];		//	index of triangle 1
-//    quadComponentIndices[ i * 6 + 3 ] = (iter->second)[1];		//	index of triangle 1 vertex which is not on the edge
-//    quadComponentIndices[ i * 6 + 4 ] = (iter->second)[2];		//	index of triangle 2
-//    quadComponentIndices[ i * 6 + 5 ] = (iter->second)[3];		//	index of triangle 2 vertex which is not on the edge	
-    
-    // New order below
-    quadComponentIndices[ i * 6 + 0 ] = (iter->second)[1];		//	index of triangle 1 vertex which is not on the edge
-    quadComponentIndices[ i * 6 + 1 ] = iter->first.first;		//  index of edge vertex 1
-    quadComponentIndices[ i * 6 + 2 ] = iter->first.second;		//	index of edge vertex 2
-    quadComponentIndices[ i * 6 + 3 ] = (iter->second)[3];		//	index of triangle 2 vertex which is not on the edge
-    quadComponentIndices[ i * 6 + 4 ] = (iter->second)[0];		//	index of triangle 1
-    quadComponentIndices[ i * 6 + 5 ] = (iter->second)[2];		//	index of triangle 2
-    
-    iter++;
-  }
-  
-  // build the locations of non-zero entries in the stiffness matrix
-  SparseMatrixOutline skeletonOutline(numParticles);
-
-  for (int i=0; i<numParticles; i++)
-  {
-    // protecting against isolated vertices
-    skeletonOutline.AddEntry(i, i);
-  }
-  
-  for(int i=0; i<numTriangles; i++)
-  {
-    int particleA = triangles[3*i+0];
-    int particleB = triangles[3*i+1];
-    int particleC = triangles[3*i+2];
-    
-    skeletonOutline.AddEntry(particleA, particleA);
-    skeletonOutline.AddEntry(particleA, particleB);
-    skeletonOutline.AddEntry(particleA, particleC);
-    skeletonOutline.AddEntry(particleB, particleA);
-    skeletonOutline.AddEntry(particleB, particleB);
-    skeletonOutline.AddEntry(particleB, particleC);
-    skeletonOutline.AddEntry(particleC, particleA);
-    skeletonOutline.AddEntry(particleC, particleB);
-    skeletonOutline.AddEntry(particleC, particleC);
-  }
-
-  /* Old ordering below
-            A         D 
-            0---------5
-           / \       /
-         /    \  4  /
-        /  2   \   /
-       /        \ /
-      3----------1
-      C          B  
-   */
-  
-  /* New ordering below
-            B         D 
-            1---------3
-           / \       /
-         /    \  5  /
-        /  4   \   /
-       /        \ /
-      0----------2
-      A          C  
-   */
-  
-  for(int i = 0 ; i < numQuads; i++)
-  {
-
-    // particles for new ordering below
-    int particleA = quadComponentIndices[6*i+0];	// index of non-edge vertex for triangle 1
-    int particleB = quadComponentIndices[6*i+1];	// index of vertex 1 on the edge
-    int particleC = quadComponentIndices[6*i+2];	// index of vertex 2 on the edge
-    int particleD = quadComponentIndices[6*i+3];	// index of non-edge vertex for triangle 2
-    
-    skeletonOutline.AddEntry(particleA, particleB);
-    skeletonOutline.AddEntry(particleA, particleC); 
-    skeletonOutline.AddEntry(particleA, particleD); 
-    skeletonOutline.AddEntry(particleB, particleA);
-    skeletonOutline.AddEntry(particleB, particleC); 
-    skeletonOutline.AddEntry(particleB, particleD); 
-    skeletonOutline.AddEntry(particleC, particleA);
-    skeletonOutline.AddEntry(particleC, particleB);
-    skeletonOutline.AddEntry(particleC, particleD); 
-    skeletonOutline.AddEntry(particleD, particleA);
-    skeletonOutline.AddEntry(particleD, particleB);
-    skeletonOutline.AddEntry(particleD, particleC); 
-    
-    //--- computing Resting Angles
-    Vec3d restPositionA(&restPositions[3*particleA]);
-    Vec3d restPositionB(&restPositions[3*particleB]);
-    Vec3d restPositionC(&restPositions[3*particleC]);
-    Vec3d restPositionD(&restPositions[3*particleD]);
-    
-    Vec3d vecBA = restPositionB - restPositionA;
-    Vec3d vecCA = restPositionC - restPositionA;
-    Vec3d vecBD = restPositionB - restPositionD;
-    Vec3d vecCD = restPositionC - restPositionD;
-    Vec3d vecBC = restPositionB - restPositionC;
-    
-    // new math ordering below
-    Vec3d NA = cross(vecCA, vecBA); // normal for the first triangle
-    Vec3d NB = cross(vecBD, vecCD);	// normal for the second triangle
-    Vec3d E = vecBC; // edge vector
-    
-    // normalized normals and edge	
-    Vec3d NAn = norm(NA);
-    Vec3d NBn = norm(NB);
-    Vec3d En = norm(E);
-    
-    double cosTheta = dot(NAn, NBn);
-    double sinTheta;
-    
-    Vec3d NANB = cross(NAn, NBn); // storing the cross product of NAn and NBn
-    
-    sinTheta = dot(NANB, En);
-    
-    restAngles[i] = atan2(sinTheta, cosTheta);
-  }
-
-  // build inverse indices for stiffness matrix access
-  SparseMatrix skeleton(&skeletonOutline);
-  
-  inverseIndicesStretchAndShear = (int*) malloc (sizeof(int) * 9 * numTriangles);
-  for(int i=0; i<numTriangles; i++)
-  {
-    int particleA = triangles[3*i+0];
-    int particleB = triangles[3*i+1];
-    int particleC = triangles[3*i+2];
-    inverseIndicesStretchAndShear[9*i+0] = skeleton.GetInverseIndex(particleA, particleA);
-    inverseIndicesStretchAndShear[9*i+1] = skeleton.GetInverseIndex(particleA, particleB);
-    inverseIndicesStretchAndShear[9*i+2] = skeleton.GetInverseIndex(particleA, particleC);
-    inverseIndicesStretchAndShear[9*i+3] = skeleton.GetInverseIndex(particleB, particleA);
-    inverseIndicesStretchAndShear[9*i+4] = skeleton.GetInverseIndex(particleB, particleB);
-    inverseIndicesStretchAndShear[9*i+5] = skeleton.GetInverseIndex(particleB, particleC);
-    inverseIndicesStretchAndShear[9*i+6] = skeleton.GetInverseIndex(particleC, particleA);
-    inverseIndicesStretchAndShear[9*i+7] = skeleton.GetInverseIndex(particleC, particleB);
-    inverseIndicesStretchAndShear[9*i+8] = skeleton.GetInverseIndex(particleC, particleC);
-  }		
-  
-  inverseIndicesQuad = (int *) malloc (sizeof(int) * 16 * numQuads );
-  for(int i = 0 ; i < numQuads; i++)
-  {
-    
-    // particles for new ordering below
-    int particleA = quadComponentIndices[6*i+0];	// index of non-edge vertex for triangle 1
-    int particleB = quadComponentIndices[6*i+1];	// index of vertex 1 on the edge
-    int particleC = quadComponentIndices[6*i+2];	// index of vertex 2 on the edge
-    int particleD = quadComponentIndices[6*i+3];	// index of non-edge vertex for triangle 2
-    
-    
-    // old order: CABD
-    inverseIndicesQuad[16*i+0] = skeleton.GetInverseIndex(particleA, particleA);
-    inverseIndicesQuad[16*i+1] = skeleton.GetInverseIndex(particleA, particleB);
-    inverseIndicesQuad[16*i+2] = skeleton.GetInverseIndex(particleA, particleC);
-    inverseIndicesQuad[16*i+3] = skeleton.GetInverseIndex(particleA, particleD);
-    
-    inverseIndicesQuad[16*i+4] = skeleton.GetInverseIndex(particleB, particleA);
-    inverseIndicesQuad[16*i+5] = skeleton.GetInverseIndex(particleB, particleB);
-    inverseIndicesQuad[16*i+6] = skeleton.GetInverseIndex(particleB, particleC);
-    inverseIndicesQuad[16*i+7] = skeleton.GetInverseIndex(particleB, particleD);
-    
-    inverseIndicesQuad[16*i+8] = skeleton.GetInverseIndex(particleC, particleA);
-    inverseIndicesQuad[16*i+9] = skeleton.GetInverseIndex(particleC, particleB);
-    inverseIndicesQuad[16*i+10] = skeleton.GetInverseIndex(particleC, particleC);
-    inverseIndicesQuad[16*i+11] = skeleton.GetInverseIndex(particleC, particleD);
-    
-    inverseIndicesQuad[16*i+12] = skeleton.GetInverseIndex(particleD, particleA);
-    inverseIndicesQuad[16*i+13] = skeleton.GetInverseIndex(particleD, particleB);
-    inverseIndicesQuad[16*i+14] = skeleton.GetInverseIndex(particleD, particleC);
-    inverseIndicesQuad[16*i+15] = skeleton.GetInverseIndex(particleD, particleD);
-  }
-}
-
-ClothBW::~ClothBW()
-{
-  free(masses);
-  free(restPositions);
-  free(triangles);
-  free(triangleGroups);
-  free(groupTensileStiffness);
-  free(groupShearStiffness);
-  free(groupBendStiffnessU);
-  free(groupBendStiffnessV);
-  free(inverseIndicesStretchAndShear);
-  free(inverseIndicesQuad);
-  free(quadComponentIndices);
-  free(groupDamping);	
-}
-
-void ClothBW::GenerateMassMatrix(SparseMatrix **M, int expanded)
-{
-  SparseMatrixOutline outline(expanded * numParticles);
-  for(int i=0; i<numParticles; i++)
-    for(int j=0; j<expanded; j++)
-      outline.AddEntry(expanded*i+j, expanded*i+j, masses[i]); 
-  *M = new SparseMatrix(&outline);	
-}
-
-double ClothBW::GetTriangleSurfaceArea(double *p0, double *p1, double *p2)
-{
-  Vec3d s0(p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]);
-  Vec3d s1(p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]);
-  return 0.5 * len(cross(s0, s1));
-}
-
-void ClothBW::SetComputationMode(bool conditions[4])
-{
-  _computationConditions[0] = conditions[0];
-  _computationConditions[1] = conditions[1];
-  _computationConditions[2] = conditions[2];
-  _computationConditions[3] = conditions[3];
-}
-
-void ClothBW::ComputeGravity(double * f, bool addGravity)
-{
-  if (!addGravity)
-    memset(f, 0, sizeof(double) * 3 * numParticles);
-  
-  for(int i=0; i<numParticles; i++)
-    f[3*i+1] += -g * masses[i]; // *** MODIFICATION : changed gravity from
-                                //                    acting in -z direction to -y
-                                //                    i.e. from [3*i+2] -> [3*i+1]
-}
-
-void ClothBW::ComputeForce(double *u, double *f, bool addForce)
-{
-  if (!addForce)	
-    memset(f, 0, sizeof(double) * 3 * numParticles);
-  
-  AddForce(u, f, 0, numTriangles, 0, numQuads);
-  
-  if (addGravity)
-    ComputeGravity(f, true);	
-}
-
-void ClothBW::AddForce(const double * u, double * f, int startTriangle, int endTriangle, int startQuad, int endQuad)
-{
-  
-  if (_computationConditions[0])
-    AddStretchAndShearForce(u, f, startTriangle, endTriangle);
-  
-  if (_computationConditions[1])
-    AddBendForce(u, f, startQuad, endQuad);
-  
-}
-
-void ClothBW::AddStretchAndShearForce(const double * u, double * f, int startTriangle, int endTriangle)
-{
-  // basic variables for all the three forces
-  double Cu, Cv;					// for stretch C(x) matrix	1*1
-  double Cs;						// for shear C(x) matrix	1*1
-  double phiCstr[3][2][3];		// for stretch dC(x)/dxi, 3 row, 2 column, 3 numbers
-  double phiCsh[3][3];			// for shear dC(x)/dxi, 3 row, 1 column, 3 numbers
-  
-  for(int i = startTriangle; i < endTriangle; i++)
-  {
-    int group = triangleGroups[i];			// group index
-    
-    int particleA = triangles[3*i+0];		// triangle indices, clockwise as A, B, C
-    int particleB = triangles[3*i+1];
-    int particleC = triangles[3*i+2];
-    
-    //--- compute C(x) for Stretch and Shear
-    
-    Vec3d positionA(&restPositions[3*particleA]);
-    positionA += Vec3d(&u[3*particleA]);
-    Vec3d positionB(&restPositions[3*particleB]);
-    positionB += Vec3d(&u[3*particleB]);
-    Vec3d positionC(&restPositions[3*particleC]);
-    positionC += Vec3d(&u[3*particleC]);
-    
-    Vec3d vecBA = positionB - positionA;
-    Vec3d vecCA = positionC - positionA;  
-    
-    double delta_u1, delta_u2, delta_v1, delta_v2;	// distance of neighboring particles in planar coordinates. (delta_u1, delta_v1): planar vector from A to B; (delta_u2, delta_v2): planar vector from B to A.
-    delta_u1 = triangleUVs[6*i+2] - triangleUVs[6*i+0];
-    delta_v1 = triangleUVs[6*i+3] - triangleUVs[6*i+1]; 
-    delta_u2 = triangleUVs[6*i+4] - triangleUVs[6*i+0];
-    delta_v2 = triangleUVs[6*i+5] - triangleUVs[6*i+1]; 
-    
-    double scale_w = 1.0/(delta_u1*delta_v2-delta_u2*delta_v1);	 // this is a intermediate scale variable while computing wu and wv
-    
-    Vec3d wu = (delta_v2 * vecBA - delta_v1 * vecCA) * scale_w;
-    Vec3d wv = ( -delta_u2 * vecBA + delta_u1 * vecCA) * scale_w;
-    
-    Vec3d wun = norm(wu); //wu normalized
-    Vec3d wvn = norm(wv); // wv normalized
-    double length_wu = len(wu);
-    double length_wv = len(wv);
-    double area;		// area for specific triangle
-    
-    area = 0.5 * len(cross(vecBA, vecCA));
-    
-    Cu = area * (length_wu - bu);
-    Cv = area * (length_wv - bv);
-    Cs = area * dot(wu, wv);
-    
-    //--- compute dC(x)/dx for Stretch and Shear
-    
-    Vec3d dwudx;
-    Vec3d dwvdx;
-    dwudx[0] = (delta_v1 - delta_v2) * scale_w;
-    dwudx[1] = delta_v2 * scale_w;
-    dwudx[2] = -delta_v1 * scale_w;
-    dwvdx[0] = (delta_u2 - delta_u1) * scale_w;
-    dwvdx[1] = -delta_u2 * scale_w;
-    dwvdx[2] = delta_u1 * scale_w;
-    
-    for(int j = 0 ; j < 3; j++)		// per number for dC(x)/dxi vector (Stretch and Shear)
-      for(int m = 0 ; m < 3; m++)	// per particle in a triangle
-      {
-        phiCstr[m][0][j] = wun[j] * area * dwudx[m];
-        phiCstr[m][1][j] = wvn[j] * area * dwvdx[m];
-        phiCsh[m][j] = area * ( dwudx[m] * wv[j] + dwvdx[m] * wu[j] );
-      }
-    
-    //--- f = - k * C(x) * dC(x)/dx
-    
-    double force[3];					// storing the force per particle
-    for(int j = 0 ; j < 3; j++)			// loop of 3 particles inside a triangle
-      for(int m = 0 ; m < 3; m++)		// loop of 3 numbers inside a 3*1 vector
-      {
-        force[m] = - groupTensileStiffness[group] * ( phiCstr[j][0][m] * Cu + phiCstr[j][1][m] * Cv );
-        force[m] += (- groupShearStiffness[group]) * phiCsh[j][m] * Cs;
-        
-        // must use "-=" because the internal force is on the left-hand side of equation
-        if(j == 0)	
-          f[3*particleA+m] -= force[m];
-        if(j == 1)	
-          f[3*particleB+m] -= force[m];
-        if(j == 2)	
-          f[3*particleC+m] -= force[m];
-      }
-  }
-}
-
-void ClothBW::AddBendForce(const double * u, double * f, int startQuad, int endQuad)
-{
-  for(int i = startQuad ; i < endQuad; i++)
-  {
-    // new ordering below
-    int particleA = quadComponentIndices[6*i+0];	// index of non-edge vertex for triangle 1
-    int particleB = quadComponentIndices[6*i+1];	// index of vertex 1 on the edge
-    int particleC = quadComponentIndices[6*i+2];	// index of vertex 2 on the edge
-    int particleD = quadComponentIndices[6*i+3];	// index of non-edge vertex for triangle 2
-    
-    int group1 = triangleGroups[quadComponentIndices[i*6+4]];			// group for first triangle
-    int group2 = triangleGroups[quadComponentIndices[i*6+5]];			// group for second triangle
-    
-    double kBendU = (groupBendStiffnessU[group1] + groupBendStiffnessU[group2]) * 0.5;
-    double kBendV = (groupBendStiffnessV[group1] + groupBendStiffnessV[group2]) * 0.5;
-    double kBend = ( kBendU + kBendV ) * 0.5;
-    
-    // compute C(x)
-    
-    // first define some positions and accompanying vectors
-    // triangle pictured below
-    
-    /* 
-            1---------3
-           / \       /
-         /    \  5  /
-        /  4   \   /
-       /        \ /
-      0----------2 
-   */
-    
-    
-    Vec3d positionA(&restPositions[3*particleA]);
-    positionA += Vec3d(&u[3*particleA]);
-    Vec3d positionB(&restPositions[3*particleB]);
-    positionB += Vec3d(&u[3*particleB]);
-    Vec3d positionC(&restPositions[3*particleC]);
-    positionC += Vec3d(&u[3*particleC]);
-    Vec3d positionD(&restPositions[3*particleD]);
-    positionD += Vec3d(&u[3*particleD]);
-    
-    Vec3d vecBA = positionB - positionA;
-    Vec3d vecCA = positionC - positionA;
-    Vec3d vecBD = positionB - positionD;
-    Vec3d vecCD = positionC - positionD;
-    Vec3d vecBC = positionB - positionC;
-    
-    Vec3d vecCB = positionC - positionB;
-    Vec3d vecAC = positionA - positionC;
-    Vec3d vecDB = positionD - positionB;
-    
-    // now find normals for triangles 1 and 2
-    Vec3d n1 = cross(vecCA, vecBA);
-    Vec3d n2 = cross(vecBD, vecCD);
-    Vec3d edge = vecBC;
-    
-    // now normalize the... normals
-    Vec3d n1N = norm(n1);
-    Vec3d n2N = norm(n2);
-    Vec3d edgeNorm = norm(edge);
-    
-    // calculate sin theta and cos theta
-    // sin theta = (norm1 x norm2) * edgeVector BC
-    // cos theta = norm1 * norm2
-    double sinTheta = dot(cross(n1N, n2N), edgeNorm);
-    double cosTheta = dot(n1N, n2N);
-    
-    // calculate C(bend)
-    double cbend = atan2(sinTheta, cosTheta);
-    
-    // account for resting angles?
-    if (useRestAnglesForBendingForces)
-      cbend -= restAngles[i];
-      
-//    // test this
-//    while (cbend < -PI)
-//      cbend += PI;
-//    while (cbend > PI)
-//      cbend -= PI;
-//    // end test this
-    
-    double lengthN1 = len(n1);
-    double lengthN2 = len(n2);
-    double lengthE = len(edge);
-    
-    // calculate dn^a/dx, dn^b/dx, and dn^e/dx
-    // dna/dx = Ss(q^a) from Pritchard's formula
-    // dnb/dx = Ss(q^b)
-    // dne/dx = I_s(q^e)
-    
-    // defined in Pritchard's paper
-    // q^a = [BC][CA][BA][0]
-    // q^b = [0] [CD][BD][BC]
-    // q^e = [0] [1] [-1] [0]
-    
-    Vec3d qa[4];
-    qa[0] = vecCB;
-    qa[1] = vecAC;
-    qa[2] = vecBA;
-    qa[3] = 0.0;
-    
-    Vec3d qb[4];
-    qb[0] = 0.0;
-    qb[1] = vecCD;
-    qb[2] = vecDB;
-    qb[3] = vecBC;
-    
-    double dn1[4][3][3];
-    double dn2[4][3][3];
-    double dne[4][3][3];
-    
-    // each derivative conceptually structured as array of 3x3 matrices: 
-    // [3x3 matrix] [3x3 matrix] [3x3 matrix] [3x3 matrix]
-    for (int entry = 0; entry < 4; entry++)
-    {
-      //constructSkewSymmetric(qa[entry], dn1[entry], 1.0/lengthN1);
-      //constructSkewSymmetric(qb[entry], dn2[entry], 1.0/lengthN2);
-      
-      double * dn1p = &(dn1[entry][0][0]);
-      SKEW_MATRIX(qa[entry], dn1p);
-      double invLengthN1 = 1.0 / lengthN1;
-      for(int ii=0; ii<3; ii++)
-        for(int jj=0; jj<3; jj++)
-          dn1[entry][ii][jj] *= invLengthN1;
-      
-      double * dn2p = &(dn2[entry][0][0]);
-      SKEW_MATRIX(qb[entry], dn2p);
-      double invLengthN2 = 1.0 / lengthN2;
-      for(int ii=0; ii<3; ii++)
-        for(int jj=0; jj<3; jj++)
-          dn2[entry][ii][jj] *= invLengthN2;
-    }
-    for (int outer=0; outer<3; outer++)
-      for (int inner=0; inner<3; inner++)
-      {
-        dne[0][outer][inner] = 0.0;
-        dne[3][outer][inner] = 0.0;
-        
-        if (outer == inner)
-        {
-          dne[1][outer][inner] = 1.0/lengthE;
-          dne[2][outer][inner] = -1.0/lengthE;
-        }
-        else
-        {
-          dne[1][outer][inner] = 0.0;
-          dne[2][outer][inner] = 0.0;
-        }
-      }
-    
-    // calculate dSin/dxm_s, dCos/dxm_s, and f = -k * C(x) * dC(x)/dx
-    Vec3d dSin[4], dCos[4];
-    for (int particle=0; particle<4; particle++)
-      for (int xyz=0; xyz<3; xyz++)
-      {
-        dCos[particle][xyz] = dot(dn1[particle][xyz], n2N) + dot(n1N, dn2[particle][xyz]);
-        
-        dSin[particle][xyz] = dot ( cross(dn1[particle][xyz], n2N) + cross(n1N, dn2[particle][xyz]), edgeNorm );
-        dSin[particle][xyz] += dot ( cross(n1N, n2N), dne[particle][xyz] );
-        
-        double force = -kBend * cbend * (cosTheta * dSin[particle][xyz] - sinTheta * dCos[particle][xyz]);
-        
-        // must use "-=" because the internal force is on the left-hand side of equation
-        if( particle == 0 )         
-          f[3*particleA+xyz] -= force;
-        else if( particle == 1 )	
-          f[3*particleB+xyz] -= force;
-        else if( particle == 2 )	
-          f[3*particleC+xyz] -= force;
-        else                        
-          f[3*particleD+xyz] -= force;
-      }
-  }
-}
-
-void ClothBW::ComputeDampingForce(double *u, double *uvel, double *f, bool addForce)
-{
-  // unimplemented
-}
-
-void ClothBW::AddDampingForce(double *uvel, double *f, int startTriangle, int endTriangle)
-{
-  // unimplemented
-}
-
-void ClothBS_Sfunction( const Vec3d & in, Vec3d & out, int row, double scale )
-{
-  if( row == 0 )
-  {
-    out[0] = 0.0;
-    out[1] = -in[2];
-    out[2] = in[1];
-  }
-  if( row == 1 )
-  {
-    out[0] = in[2];
-    out[1] = 0.0;
-    out[2] = -in[0];
-  }
-  if( row == 2 )
-  {
-    out[0] = -in[1];
-    out[1] = in[0];
-    out[2] = 0.0;
-  }
-  out[0] *= scale;
-  out[1] *= scale;
-  out[2] *= scale;
-}
-
-void ClothBW::GenerateStiffnessMatrixTopology(SparseMatrix **K)
-{
-  SparseMatrixOutline KOutline(3*numParticles);
-  
-  for(int vtx=0; vtx < numParticles; vtx++)
-  {
-    for(int j=0; j<3; j++)
-      for(int k=0; k<3; k++)
-        KOutline.AddEntry(3 * vtx + j, 3 * vtx + k);
-  }
-  
-  // for per triangle
-  for(int i=0; i<numTriangles; i++)
-  {
-    int particleA = triangles[3*i+0];
-    int particleB = triangles[3*i+1];
-    int particleC = triangles[3*i+2];
-    
-    if ((particleA < 0) || (particleA >= numParticles))
-    {
-      printf("Particle error.\n");
-      exit(1);
-    }
-    
-    if ((particleB < 0) || (particleB >= numParticles))
-    {
-      printf("Particle error.\n");
-      exit(1);
-    }
-    
-	if ((particleC < 0) || (particleC >= numParticles))
-    {
-      printf("Particle error.\n");
-      exit(1);
-    }
-    
-    for(int j=0; j<3; j++)
-      for(int k=0; k<3; k++)
-      {
-        KOutline.AddEntry(3 * particleA + j, 3 * particleA + k);
-        KOutline.AddEntry(3 * particleA + j, 3 * particleB + k);
-        KOutline.AddEntry(3 * particleA + j, 3 * particleC + k);
-        KOutline.AddEntry(3 * particleB + j, 3 * particleA + k);
-        KOutline.AddEntry(3 * particleB + j, 3 * particleB + k);
-        KOutline.AddEntry(3 * particleB + j, 3 * particleC + k);
-        KOutline.AddEntry(3 * particleC + j, 3 * particleA + k);
-        KOutline.AddEntry(3 * particleC + j, 3 * particleB + k);
-        KOutline.AddEntry(3 * particleC + j, 3 * particleC + k);
-      }
-  }
-  
-  // for per quad, in addition to per triangle topology
-  for( int i = 0 ; i < numQuads; i++ )
-  {
-    // new ordering below
-    int particleA = quadComponentIndices[6*i+0];	// index of non-edge vertex for triangle 1
-    int particleB = quadComponentIndices[6*i+1];	// index of vertex 1 on the edge
-    int particleC = quadComponentIndices[6*i+2];	// index of vertex 2 on the edge
-    int particleD = quadComponentIndices[6*i+3];	// index of non-edge vertex for triangle 2
-    
-    if ((particleA < 0) || (particleA >= numParticles))
-    {
-      printf("Particle error.\n");
-      exit(1);
-    }
-    
-    if ((particleB < 0) || (particleB >= numParticles))
-    {
-      printf("Particle error.\n");
-      exit(1);
-    }
-    
-	if ((particleC < 0) || (particleC >= numParticles))
-    {
-      printf("Particle error.\n");
-      exit(1);
-    }
-    
-	if ((particleD < 0) || (particleD >= numParticles))
-    {
-      printf("Particle error.\n");
-      exit(1);
-    }
-    
-    for(int j=0; j<3; j++)
-      for(int k=0; k<3; k++)
-      {
-        // new order below
-        KOutline.AddEntry(3 * particleA + j, 3 * particleB + k);
-        KOutline.AddEntry(3 * particleA + j, 3 * particleC + k);
-        KOutline.AddEntry(3 * particleA + j, 3 * particleD + k);
-        KOutline.AddEntry(3 * particleD + j, 3 * particleB + k);
-        KOutline.AddEntry(3 * particleD + j, 3 * particleC + k);
-        KOutline.AddEntry(3 * particleD + j, 3 * particleA + k);
-        KOutline.AddEntry(3 * particleB + j, 3 * particleA + k);
-        KOutline.AddEntry(3 * particleC + j, 3 * particleA + k);
-        KOutline.AddEntry(3 * particleB + j, 3 * particleD + k);
-        KOutline.AddEntry(3 * particleC + j, 3 * particleD + k);
-        
-        // old order below
-//        KOutline.AddEntry(3 * particleC + j, 3 * particleA + k);
-//        KOutline.AddEntry(3 * particleC + j, 3 * particleB + k);
-//        KOutline.AddEntry(3 * particleC + j, 3 * particleD + k);
-//        KOutline.AddEntry(3 * particleD + j, 3 * particleA + k);
-//        KOutline.AddEntry(3 * particleD + j, 3 * particleB + k);
-//        KOutline.AddEntry(3 * particleD + j, 3 * particleC + k);
-//        KOutline.AddEntry(3 * particleA + j, 3 * particleC + k);
-//        KOutline.AddEntry(3 * particleB + j, 3 * particleC + k);
-//        KOutline.AddEntry(3 * particleA + j, 3 * particleD + k);
-//        KOutline.AddEntry(3 * particleB + j, 3 * particleD + k);
-      }
-  }
-  
-  *K = new SparseMatrix(&KOutline);
-}
-
-void ClothBW::ComputeStiffnessMatrix(double *u, SparseMatrix *K, bool addMatrix)
-{
-  if (!addMatrix)	
-    K->ResetToZero();
-  
-  // in this single-threaded version, compute all triangles and all quads in 1 run
-  AddStiffnessMatrix(u, K, 0, numTriangles, 0, numQuads);
-}
-
-void ClothBW::AddStiffnessMatrix(double *u, SparseMatrix *K, int startTriangle, int endTriangle,
-                                 int startQuad, int endQuad)
-{
-  if (_computationConditions[2])
-    AddStretchAndShearStiffnessMatrix(u, K, startTriangle, endTriangle);
-  
-  if (_computationConditions[3])
-    AddBendStiffnessMatrix(u, K, startQuad, endQuad);
-  
-}
-
-void ClothBW::AddStretchAndShearStiffnessMatrix(double *u, SparseMatrix *K, int startTriangle, int endTriangle)
-{
-  // basic variables for all the three forces
-  double Cu, Cv;				// for stretch C(x) matrix
-  double Cs;					// for shear C(x) matrix
-  double phiCstr[3][2][3];		// for stretch dC(x)/dxi, 3 row, 2 column, 3 numbers
-  double phiCsh[3][3];			// for shear dC(x)/dxi, 3 row, 1 column, 3 numbers
-  
-  for(int i = startTriangle; i < endTriangle; i++)
-  {
-    int group = triangleGroups[i];			// group index
-    int particleA = triangles[3*i+0];		// triangle indices, clockwise as A, B, C
-    int particleB = triangles[3*i+1];
-    int particleC = triangles[3*i+2];
-	
-    //--- compute C(x) for Stretch and Shear
-    
-    Vec3d positionA(&restPositions[3*particleA]);
-    positionA += Vec3d(&u[3*particleA]);
-    Vec3d positionB(&restPositions[3*particleB]);
-    positionB += Vec3d(&u[3*particleB]);
-    Vec3d positionC(&restPositions[3*particleC]);
-    positionC += Vec3d(&u[3*particleC]);
-    
-    Vec3d vecBA = positionB - positionA;
-    Vec3d vecCA = positionC - positionA;  
-    
-    double delta_u1, delta_u2, delta_v1, delta_v2;	// distance of neighboring particles in planar coordinates. (delta_u1, delta_v1): planar vector from A to B; (delta_u2, delta_v2): planar vector from B to A.
-    delta_u1 = triangleUVs[6*i+2] - triangleUVs[6*i+0];
-    delta_v1 = triangleUVs[6*i+3] - triangleUVs[6*i+1]; 
-    delta_u2 = triangleUVs[6*i+4] - triangleUVs[6*i+0];
-    delta_v2 = triangleUVs[6*i+5] - triangleUVs[6*i+1]; 
-    
-    double scale_w = 1.0/(delta_u1*delta_v2-delta_u2*delta_v1);	 // this is a intermediate scale variable while computing wu and wv
-    
-    Vec3d wu = (delta_v2 * vecBA - delta_v1 * vecCA) * scale_w; 
-    Vec3d wv = ( -delta_u2 * vecBA + delta_u1 * vecCA) * scale_w; 
-    
-    Vec3d wun = norm(wu); //wu normalized
-    Vec3d wvn = norm(wv); // wv normalized
-    double length_wu = len(wu);
-    double length_wv = len(wv);
-    double area;		// area for specific triangle
-    
-    area = 0.5 * len(cross(vecBA, vecCA));
-    
-    // preventing unstable situation
-    //if(area > 0.5)	
-      //printf("Warning: large area\n" );
-    
-    Cu = area * (length_wu - bu);
-    Cv = area * (length_wv - bv);
-    Cs = area * dot(wu, wv);
-    
-    //--- compute dC(x)/dx for Stretch and Shear
-    
-    Vec3d dwudx;	// dwu/dx0_x, dwu/dx0_y, dwu/dx0_z
-    Vec3d dwvdx;	// dwv/dx0_x, dwv/dx0_y, dwv/dx0_z
-    dwudx[0] = (delta_v1 - delta_v2) * scale_w;
-    dwudx[1] = delta_v2 * scale_w;
-    dwudx[2] = -delta_v1 * scale_w;
-    dwvdx[0] = (delta_u2 - delta_u1) * scale_w;
-    dwvdx[1] = -delta_u2 * scale_w;
-    dwvdx[2] = delta_u1 * scale_w;
-    
-    for(int j = 0 ; j < 3; j++)		// per number for dC(x)/dxi Stretch
-      for(int m = 0 ; m < 3; m++)	// per particle in a triangle
-      {
-        phiCstr[m][0][j] = wun[j] * area * dwudx[m]; 
-        phiCstr[m][1][j] = wvn[j] * area * dwvdx[m]; 
-        phiCsh[m][j] = area * ( dwudx[m] * wv[j] + dwvdx[m] * wu[j] );
-      }
-    
-    //--- compute Stiffness Matrix for Stretch and Shear
-    // Kij = df(xi) / dxj = - k * ( (dC(x) / dxi) * transpose(dC(x) / dxj) + C(x) * dC(x) / dxidxj )
-    
-    for(int j = 0 ; j < 3 ; j++)		// Kij's i, 'i' is index inside per triangle. Here j = 0 1 2 represents particle A B C
-      for(int k = 0; k < 3; k++)		// Kij's j, 'j' is index inside per triangle. Here k = 0 1 2 represents particle A B C
-      {
-        for(int m = 0 ; m < 3; m++)			// loop inside per Kij 3*3 matrix
-          for(int n = 0 ; n < 3; n++)		// loop inside per Kij 3*3 matrix
-          {
-            double identity = 0.0;
-            double scale = 0.0;			// used as a scale factor for K-shear matrix
-            if( m == n ) {identity = 1; scale = 1;}
-            
-            // K stretch
-            double dFdz = area * dwudx[j] * dwudx[k] * (identity - wun[m] * wun[n]) * Cu / length_wu;				// (dCu2/dxm*dxn) * Cu
-            dFdz += area * dwvdx[j] * dwvdx[k] * (identity - wvn[m] * wvn[n]) * Cv / length_wv;						// +(dCv2/dxm*dxn) * Cv ---> dC2/dxm*dxn * C
-            dFdz += phiCstr[j][0][m] * phiCstr[k][0][n] + phiCstr[j][1][m] * phiCstr[k][1][n];						// +dC/dxm*transpose(dC/dxn)
-            dFdz *= ( -groupTensileStiffness[group] );
-            dFdz *= -1.0; // must flip because the internal force is on the left-hand side of equation
-            
-            // K shear
-            double dFdz1 = area * Cs * scale * (dwudx[j] * dwvdx[k] + dwudx[k] * dwvdx[j]);
-            dFdz1 += phiCsh[j][m] * phiCsh[k][n];
-            //dFdz1 += phiCsh[j][m] * phiCsh[m][j];
-            dFdz1 *= ( -groupShearStiffness[group] );
-            dFdz1 *= -1.0; // must flip because the internal force is on the left-hand side of equation
-            
-            // Adding it to K topology
-            int entry_row = 3 * triangles[3*i+j] + m; 
-            int entry_col =	3 * inverseIndicesStretchAndShear[9*i+j*3+k] + n;
-            K->AddEntry(entry_row, entry_col, dFdz);
-            K->AddEntry(entry_row, entry_col, dFdz1);
-          }
-      }
-  }
-}
-
-void ClothBW::AddBendStiffnessMatrix(double *u, SparseMatrix *K, int startQuad, int endQuad)
-{
-  for(int i = startQuad ; i < endQuad; i++)
-  {
-    // new ordering below
-    int particleA = quadComponentIndices[6*i+0];	// index of non-edge vertex for triangle 1
-    int particleB = quadComponentIndices[6*i+1];	// index of vertex 1 on the edge
-    int particleC = quadComponentIndices[6*i+2];	// index of vertex 2 on the edge
-    int particleD = quadComponentIndices[6*i+3];	// index of non-edge vertex for triangle 2
-    
-    int group1 = triangleGroups[quadComponentIndices[i*6+4]];			// group for first triangle
-    int group2 = triangleGroups[quadComponentIndices[i*6+5]];			// group for second triangle
-    
-    double kBendU = (groupBendStiffnessU[group1] + groupBendStiffnessU[group2]) * 0.5;
-    double kBendV = (groupBendStiffnessV[group1] + groupBendStiffnessV[group2]) * 0.5;
-    double kBend = ( kBendU + kBendV ) * 0.5;
-    
-    // compute C(x)
-    
-    // first define some positions and accompanying vectors
-    // triangle pictured below
-    
-    /* 
-     1---------3
-     / \       /
-     /    \  5  /
-     /  4   \   /
-     /        \ /
-     0----------2 
-     */
-    
-    
-    Vec3d positionA(&restPositions[3*particleA]);
-    positionA += Vec3d(&u[3*particleA]);
-    Vec3d positionB(&restPositions[3*particleB]);
-    positionB += Vec3d(&u[3*particleB]);
-    Vec3d positionC(&restPositions[3*particleC]);
-    positionC += Vec3d(&u[3*particleC]);
-    Vec3d positionD(&restPositions[3*particleD]);
-    positionD += Vec3d(&u[3*particleD]);
-    
-    Vec3d vecBA = positionB - positionA;
-    Vec3d vecCA = positionC - positionA;
-    Vec3d vecBD = positionB - positionD;
-    Vec3d vecCD = positionC - positionD;
-    Vec3d vecBC = positionB - positionC;
-    
-    Vec3d vecCB = positionC - positionB;
-    Vec3d vecAC = positionA - positionC;
-    Vec3d vecDB = positionD - positionB;
-    
-    // now find normals for triangles 1 and 2
-    Vec3d n1 = cross(vecCA, vecBA);
-    Vec3d n2 = cross(vecBD, vecCD);
-    Vec3d edge = vecBC;
-    
-    // now normalize the... normals
-    Vec3d n1N = norm(n1);
-    Vec3d n2N = norm(n2);
-    Vec3d edgeNorm = norm(edge);
-    
-    // calculate sin theta and cos theta
-    // sin theta = (norm1 x norm2) * edgeVector BC
-    // cos theta = norm1 * norm2
-    double sinTheta = dot(cross(n1N, n2N), edgeNorm);
-    double cosTheta = dot(n1N, n2N);
-    
-    // calculate C(bend)
-    double cbend = atan2(sinTheta, cosTheta);
-    
-    // account for resting angles?
-    if (useRestAnglesForBendingForces)
-      cbend -= restAngles[i];
-    
-//    // test this
-//    while (cbend < -PI)
-//      cbend += PI;
-//    while (cbend > PI)
-//      cbend -= PI;
-//    // end test this
-    
-    double lengthN1 = len(n1);
-    double lengthN2 = len(n2);
-    double lengthE = len(edge);
-    
-    // calculate dn^a/dx, dn^b/dx, and dn^e/dx
-    // dna/dx = Ss(q^a) from Pritchard's formula
-    // dnb/dx = Ss(q^b)
-    // dne/dx = I_s(q^e)
-    
-    // defined in Pritchard's paper
-    // q^a = [BC][CA][BA][0]
-    // q^b = [0] [CD][BD][BC]
-    // q^e = [0] [1] [-1] [0]
-    
-    Vec3d qa[4];
-    qa[0] = vecCB;
-    qa[1] = vecAC;
-    qa[2] = vecBA;
-    qa[3][0] = qa[3][1] = qa[3][2] = 0.0;
-    
-    Vec3d qb[4];
-    qb[0][0] = qb[0][1] = qb[0][2] =0.0;
-    qb[1] = vecCD;
-    qb[2] = vecDB;
-    qb[3] = vecBC;
-    
-    /*
-      dn1 is the first derivative of n1.
-
-      n1 is a 3x1 vector, and d(n1)/d(vertexA) is a 3x3 matrix.
-      Because we have 4 vertices, so dn1 is a 4x3x3 tensor.
-
-      dn1[0] = d(n1)/d(vertexA) which is a 3x3 matrix
-      dn1[1] = d(n1)/d(vertexB) which is a 3x3 matrix
-      dn1[2] = d(n1)/d(vertexC) which is a 3x3 matrix
-      dn1[3] = d(n1)/d(vertexD) which is a 3x3 matrix
-      dn1[0][0] = d(n1)/d(vertexA[0]) which is a 3x1 vector
-      (i.e., dn1[0][0] = [d(n1[0])/d(vertexA[0]), d(n1[1])/d(vertexA[0]), d(n1[2])/d(vertexA[0])] 
-             dn1[0][1] = [d(n1[0])/d(vertexA[1]), d(n1[1])/d(vertexA[1]), d(n1[2])/d(vertexA[1])] 
-             dn2[0][2] = [d(n1[0])/d(vertexA[2]), d(n1[1])/d(vertexA[2]), d(n1[2])/d(vertexA[2])] )
-
-      dn1[derivative w.r.t. which vertex][d n1 component][d vertex component]
-     */
-    Vec3d dn1[4][3];
-    Vec3d dn2[4][3];
-    Vec3d dne[4][3];
-    // each derivative conceptually structured as array of 3x3 matrices: 
-    // [3x3 matrix] [3x3 matrix] [3x3 matrix] [3x3 matrix]
-    for (int entry = 0; entry < 4; entry++)
-    {
-      //constructSkewSymmetric(qa[entry], dn1[entry], 1.0/lengthN1);
-      //constructSkewSymmetric(qb[entry], dn2[entry], 1.0/lengthN2);
-      
-      double * dn1p = &(dn1[entry][0][0]);
-      SKEW_MATRIX(qa[entry], dn1p);
-      double invLengthN1 = 1.0 / lengthN1;
-      for(int ii=0; ii<3; ii++)
-        for(int jj=0; jj<3; jj++)
-          dn1[entry][ii][jj] *= invLengthN1;
-      
-      double * dn2p = &(dn2[entry][0][0]);
-      SKEW_MATRIX(qb[entry], dn2p);
-      double invLengthN2 = 1.0 / lengthN2;
-      for(int ii=0; ii<3; ii++)
-        for(int jj=0; jj<3; jj++)
-          dn2[entry][ii][jj] *= invLengthN2;
-    }
-    for (int outer=0; outer<3; outer++)
-      for (int inner=0; inner<3; inner++)
-      {
-        dne[0][outer][inner] = 0.0;
-        dne[3][outer][inner] = 0.0;
-        
-        if (outer == inner)
-        {
-          dne[1][outer][inner] = 1.0/lengthE;
-          dne[2][outer][inner] = -1.0/lengthE;
-        }
-        else
-        {
-          dne[1][outer][inner] = 0.0;
-          dne[2][outer][inner] = 0.0;
-        }
-      }
-
-    
-    /*
-      sin and cos are scalars, so d(sin)/d(vertexA) is a 3x1 vector,
-      and dSin is a 4x3x1 tensor
-
-      dCos[0] = d(cosAngle)/d(vertexA) which is a 3x1 vector
-      dCos[1] = d(cosAngle)/d(vertexB) which is a 3x1 vector
-      dCos[2] = d(cosAngle)/d(vertexC) which is a 3x1 vector
-      dCos[3] = d(cosAngle)/d(vertexD) which is a 3x1 vector
-
-      dCos[derivative with respect to which vertex][and which x,y,z component]
-     */
-    // calculate dSin/dxm_s, dCos/dxm_s, and f = -k * C(x) * dC(x)/dx
-    Vec3d dSin[4], dCos[4], dCb[4];
-
-    for (int particle=0; particle<4; particle++)
-      for (int xyz=0; xyz<3; xyz++)
-      {
-        dCos[particle][xyz] = dot(dn1[particle][xyz], n2N) + dot(n1N, dn2[particle][xyz]);
-        
-        dSin[particle][xyz] = dot ( cross(dn1[particle][xyz], n2N) + cross(n1N, dn2[particle][xyz]), edgeNorm );
-        dSin[particle][xyz] += dot ( cross(n1N, n2N), dne[particle][xyz] );
-        
-        dCb[particle][xyz] = cosTheta * dSin[particle][xyz] - sinTheta * dCos[particle][xyz]; 
-      }
-    
-    
-    /*dqA=[0 -I  I  0;
-     *     I  0  -I 0;
-     *     -I I  0  0;
-     *      0 0  0  0]
-     
-     *dqB=[0  0  0  0;
-     *     0  0  I -I;
-     *     0  -I 0  I;
-     *     0  I  -I 0] */
-    
-    /*
-      dQA[m][n][t][s], where (m,s) is a pair and (n,t) is a pair
-
-      dQA = d(qA[m][s]) / d(x[n][t])
-
-      (i.e., nt is the t-th component of the n-th vertex.
-             For example, if n==1 and t==2, then it's the z-component of vertex 1.
-
-             ms refers to the m-th row and s-th column of qA,
-             like qA[m][s])
-
-      dqA[0][0]/dx0x  dqA[0][0]/dx0y  dqA[0][0]/dx0z  dqA[0][0]/d1x ... dqA[0][0]/dx3z
-      dqA[0][1]/dx0x  dqA[0][1]/dx0y  dqA[0][1]/dx0z  dqA[0][1]/d1x ... dqA[0][1]/dx3z
-      ...
-      dqA[3][2]/dx0x  dqA[3][2]/dx0y  dqA[3][2]/dx0z  dqA[3][2]/d1x ... dqA[3][2]/dx3z
-     */
-    double dQA[4][4][3][3], dQB[4][4][3][3];	// last 3 represents for the 3 value of a vector, dqA_m/dxn_t, dqB_m/dxn_t
-    for(int m = 0; m < 4; m++)					//m loop
-      for(int n = 0 ; n < 4; n++)				//n loop
-        for(int x = 0; x < 3; x++)			//t loop
-          for(int y = 0; y < 3; y++)		//3 values of a vector
-          {
-            dQA[m][n][x][y] = 0.0;
-            dQB[m][n][x][y] = 0.0;
-          }
-    
-    dQA[0][1][0][0] = -1.0; dQA[0][1][1][1] = -1.0; dQA[0][1][2][2] = -1.0;
-    dQA[0][2][0][0] = 1.0;  dQA[0][2][1][1] = 1.0;  dQA[0][2][2][2] = 1.0;
-
-    dQA[1][0][0][0] = 1.0;  dQA[1][0][1][1] = 1.0;  dQA[1][0][2][2] = 1.0;
-    dQA[1][2][0][0] = -1.0; dQA[1][2][1][1] = -1.0; dQA[1][2][2][2] = -1.0;
-
-    dQA[2][0][0][0] = -1.0; dQA[2][0][1][1] = -1.0; dQA[2][0][2][2] = -1.0;
-    dQA[2][1][0][0] = 1.0;  dQA[2][1][1][1] = 1.0;  dQA[2][1][2][2] = 1.0;
-    
-    dQB[1][2][0][0] = 1.0;  dQB[1][2][1][1] = 1.0;  dQB[1][2][2][2] = 1.0;
-    dQB[1][3][0][0] = -1.0; dQB[1][3][1][1] = -1.0; dQB[1][3][2][2] = -1.0;
-
-    dQB[2][1][0][0] = -1.0; dQB[2][1][1][1] = -1.0; dQB[2][1][2][2] = -1.0;
-    dQB[2][3][0][0] = 1.0;  dQB[2][3][1][1] = 1.0;  dQB[2][3][2][2] = 1.0;
-
-    dQB[3][1][0][0] = 1.0;  dQB[3][1][1][1] = 1.0;  dQB[3][1][2][2] = 1.0;
-    dQB[3][2][0][0] = -1.0; dQB[3][2][1][1] = -1.0; dQB[3][2][2][2] = -1.0;
-
-
-    /*
-      d2N1[m][n][s][t]
-     */    
-    Vec3d d2N1[4][4][3][3], d2N2[4][4][3][3];	// d2NA/dxm_s*dxn_t, d2N2/dxm_s*dxn_t
-    
-    //Vec3d d2N1Test[4][4][3][3], d2N2Test[4][4][3][3];
-    
-    for(int m = 0; m < 4; m++)					//m loop
-      for(int n = 0 ; n < 4; n++)				//n loop
-        for(int s = 0; s < 3; s++)			//s loop
-          for(int t = 0; t < 3; t++)		//t loop
-          {
-            ClothBS_Sfunction(dQA[m][n][t], d2N1[m][n][s][t], s, 1.0/lengthN1);
-            ClothBS_Sfunction(dQB[m][n][t], d2N2[m][n][s][t], s, 1.0/lengthN2);
-          }
-    
-    /*
-      d2Sin[m][n][s][t]
-     */
-    double d2Sin[4][4][3][3], d2Cos[4][4][3][3];
-    for(int m = 0; m < 4; m++)					//m loop
-      for(int n = 0 ; n < 4; n++)				//n loop
-        for(int s = 0; s < 3; s++)			//s loop
-          for(int t = 0; t < 3; t++)		//t loop
-          {
-            double temp_dCb = dot(d2N1[m][n][s][t], n2N); //changed to account for new indexing
-            temp_dCb += dot(dn2[n][t], dn1[m][s]);
-            temp_dCb += dot(dn1[n][t], dn2[m][s]);
-            temp_dCb += dot(n1N, d2N2[m][n][s][t]); 
-
-            ////////d2Cos[m][n][t][s] = temp_dCb; <- typo in Pritchard's note equation 31
-            d2Cos[m][n][s][t] = temp_dCb; 
-            
-            Vec3d temp1, temp2;
-            
-            temp1 = cross (d2N1[m][n][s][t], n2N); 
-            
-            temp2 = temp1;
-            
-            temp1 = cross(dn1[m][s], dn2[n][t]);
-            
-            temp2 += temp1;
-            
-            temp1 = cross(dn1[n][t], dn2[m][s]);
-            
-            temp2 += temp1;
-            
-            temp1 = cross(n1N, d2N2[m][n][s][t]); 
-            
-            temp2 += temp1;	
-            
-            temp_dCb = dot(temp2, edgeNorm);
-            
-            temp1 = cross(dn1[m][s], n2N);
-            
-            temp2 = temp1;	
-            
-            temp1 = cross(n1N, dn2[m][s]);
-            
-            temp2 += temp1;	
-            
-            temp_dCb += dot(temp2, dne[n][t]);
-            
-            temp1 = cross(dn1[n][t], n2N);
-            
-            temp2 = temp1;
-            
-            temp1 = cross(n1N, dn2[n][t]);
-            
-            temp2 += temp1;
-            
-            temp_dCb += dot(temp2, dne[m][s]);
-            
-            d2Sin[m][n][s][t] = temp_dCb;
-          }	
-    
-    for(int m = 0; m < 4; m++)					// Kij 'i
-      for(int n = 0; n < 4; n++)				// Kij 'j
-        for(int s = 0; s < 3; s++)			// 3*3 loop for K matrix block
-          for(int t = 0; t < 3; t++)		// 3*3 loop for K matrix block
-          {
-            //double dFdz = -kBend * (dCb[m][s] * dCb[n][t] + cbend * (dCos[n][t] * dSin[m][s] + cosTheta * d2Sin[m][n][s][t] - dSin[n][t] * dCos[m][s] - sinTheta * d2Cos[m][n][s][t]) ); // changed to account for new indexing
-            double dFdz = -kBend * ( dCb[m][s] * dCb[n][t] + cbend * (cosTheta * d2Sin[m][n][s][t] - sinTheta * d2Cos[m][n][s][t]) ); // observation that terms skew-symmetric terms are zero, Oct 2011
-            dFdz *= -1.0; // must flip because the internal force is on the left-hand side of the equation
-            int entry_row;
-            int entry_col;
-            // old order: CABD
-            if     ( m == 0 )	entry_row = 3*particleA+s;
-            else if( m == 1 )	entry_row = 3*particleB+s;
-            else if( m == 2 )	entry_row = 3*particleC+s;
-            else if( m == 3 )	entry_row = 3*particleD+s;
-            // ------------------------------------------
-            entry_col = 3 * inverseIndicesQuad[i*16+ m*4+ n] + t;
-            K->AddEntry(entry_row, entry_col, dFdz);
-          }   
-  }
-}
-}
diff --git a/src/libclothBW/clothBW.h b/src/libclothBW/clothBW.h
deleted file mode 100644
index aba19539a4f7f03a560917471511e6fd7b947f99..0000000000000000000000000000000000000000
--- a/src/libclothBW/clothBW.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "clothBW" library , Copyright (C) 2013 USC                            *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Andy Pierce, Yu Yu Xu, Jernej Barbic                     *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-/*
- This class implements the cloth model from [Baraff and Witkin 1998].
- It can compute both the internal elastic forces their gradient (tangent stiffness matrix).
- It computes stretch, shear, and bend forces.
- Damping is not implemented, but you can use the damping in the Vega integrator class.
-*/
-
-#ifndef _CLOTHBW_H_
-#define _CLOTHBW_H_
-
-#include "sparseMatrix.h"
-
-namespace vega
-{
-class ClothBW
-{
-public:
-  // creates the cloth elastic model, from a given triangle mesh
-  // "masses" is an array of length "numParticles"
-  // "restPositions" is an array of length 3x"numParticles"
-  // "triangles" is an integer array of length 3x"numTriangles" (giving integer indices of the three particle forming a triangle)
-  // "triangleGroups" is an integer array of length "numTriangles" (giving the integer index of the material group to which each triangle belongs)
-  // "groupTensileStiffness", "groupShearStiffness", "groupBendStiffness", and "groupDamping" are arrays that give the scalar stiffness and damping parameters for each material groups
-  // "triangleUVs" is an double array of length 3x2x"numTriangles", indicating the uv for every vertex; this array can be user-provided, or the constructor can compute it automatically
-  // all indices in this class are 0-indexed
-  // constructor that does not require triangleUVs input (it computes UVs automatically; note: the UVs are continuous only within each triangle; the UV map is not global (which is fine, provided one does not want to simulate anisotropic effects) )
-  ClothBW(int numParticles, double * masses, double * restPositions, int numTriangles, int * triangles, int * triangleGroups, int numMaterialGroups, double * groupTensileStiffness, double * groupShearStiffness, double * groupBendStiffnessU, double * groupBendStiffnessV, double * groupDamping, int addGravity=0);
-  
-  // constructor with triangleUVs input
-  ClothBW(int numParticles, double * masses, double * restPositions, int numTriangles, int * triangles, double * triangleUVs, int * triangleGroups, int numMaterialGroups, double * groupTensileStiffness, double * groupShearStiffness, double * groupBendStiffnessU, double * groupBendStiffnessV, double * groupDamping, int addGravity=0);	
-    
-  // copy constructor
-  ClothBW(ClothBW & ClothBW);
-  virtual ~ClothBW();
-    
-  void SetRestUVStretchValues(double bu, double bv) { this->bu = bu; this->bv = bv; } // these are the b_u and b_v stretch values from Equation (10) in the BW paper (default values are 1.0)
-    
-  int GetNumParticles() { return numParticles; }
-  int GetNumTriangles() { return numTriangles; }
-  int * GetTriangles() { return triangles; }
-  double * GetRestPositions() { return restPositions;  }
-    
-  // creates the mass matrix (which is diagonal); each diagonal entry is expanded into a diagonal submatrix of size 'expanded' (typically, for 3D simulations, expanded should be 3)
-  void GenerateMassMatrix(SparseMatrix ** M, int expanded=3);
-
-  // computes the gravitational force (result goes into f)
-  void SetGravity(bool addGravity, double g=9.81) { this->addGravity = addGravity; this->g = g; } // if addGravity is enabled, ComputeForces will add the gravity force to the elastic forces
-  void ComputeGravity(double * f, bool addForce=false);
-    
-  // === compute elastic forces ===    
-    
-  // compute the internal elastic force, under deformation u
-  // note: the force has the sign of the left side of the dynamic equation, Mu'' + Du' + f_int(u) = f_ext(t), i.e., f_int(u), that is, **opposite** to an external force f_ext(t) acting on the body 
-  virtual void ComputeForce(double * u, double * f, bool addForce=false); // if addForce is "true", f will be not be reset to zero prior to adding the forces
-  // compute the tangent stiffness matrix of the elastic force
-  void GenerateStiffnessMatrixTopology(SparseMatrix ** K); // call once to establish the location of sparse entries of the stiffness matrix
-  virtual void ComputeStiffnessMatrix(double * u, SparseMatrix * K, bool addMatrix=false);
-    
-  // compute the damping force
-  // unimplemented
-  // (use damping provided in the integrator class)
-  virtual void ComputeDampingForce(double * u, double * uvel, double * f, bool addForce=false); 
-
-  // this allows users to toggle on/off computation of the stretch/shear forces, bend forces,
-  // stretch/shear stiffness matrix, and bend stiffness matrix
-  // mode[0] = computeStretchAndShearForce
-  // mode[1] = computeBendForce
-  // mode[2] = computeStretchAndShearStiffnessMatrices
-  // mode[3] = computeBendStiffnessMatrices
-  void SetComputationMode(bool mode[4]);
-    
-  // allows user to toggle on/off the use of rest angles in bend force/stiffness matrix
-  // calculations; if set to 1, bend force/matrix will be computed in relation to the quad's rest
-  // angle. if set to 0, bend force/matrix will be computed in relation to a flat angle of 0.
-  // default is 1.
-  void UseRestAnglesForBendingForces(int useRestAnglesForBend)
-  { 
-    this->useRestAnglesForBendingForces = useRestAnglesForBend;
-  }
-
-  // === routines that only compute one type of a force ===
-    
-  void AddForce(const double * u, double * f, int startTriangle, int endTriangle, int startQuad, int endQuad);
-  void AddStretchAndShearForce(const double * u, double * f, int startTriangle, int endTriangle);
-  void AddBendForce(const double * u, double * f, int startQuad, int endQuad);
-    
-  void AddStiffnessMatrix(double * u, SparseMatrix * K, int startTriangle, int endTriangle, int startQuad, int endQuad);
-    
-  void AddStretchAndShearStiffnessMatrix(double * u, SparseMatrix * K, int startTriangle, int endTriangle);
-  void AddBendStiffnessMatrix(double * u, SparseMatrix * K, int startQuad, int endQuad);
-    
-  // unimplemented
-  // (use damping provided in the integrator class)
-  void AddDampingForce(double * uvel, double * f, int startTriangle, int endTriangle);
-        
-protected:
-  double GetTriangleSurfaceArea(double * p0, double * p1, double * p2);
-  void GenerateBW(int numParticles, double * masses, double * restPositions, int numTriangles, int * triangles, double * triangleUVs, int * triangleGroups, int numMaterialGroups, double * groupTensileStiffness, double * groupShearStiffness, double * groupBendStiffnessU, double * groupBendStiffnessV, double * groupDamping, int addGravity=0);
-    
-  int numParticles;
-  double * masses;
-  double * restPositions;
-  int numTriangles;
-  int * triangles;
-  double * triangleUVs;
-  int * inverseIndicesStretchAndShear;
-  int * triangleGroups;
-    
-  // Internal variables for bending computation:
-  // "numQuads" is number of 'quads' made up of two adjacent triangles. Important because each quad contains a bendable edge (the edge shared by the two triangles).
-  // "restAngles" is array of size numQuads containing resting angles of edges that can bend (indexed by numQuads)
-  // "inverseIndicesQuad" is numQuads x 16 interger array
-  // "quadComponentIndices" is numQuads x 6 interger array
-  int numQuads;
-  double * restAngles;
-  int * inverseIndicesQuad;
-  int * quadComponentIndices;
-    
-  int numMaterialGroups;
-  double * groupTensileStiffness; 
-  double * groupShearStiffness;
-  double * groupBendStiffnessU;
-  double * groupBendStiffnessV;
-  double * groupDamping;
-    
-  double bu; // stretch constraint u (default = 1.0)
-  double bv; // stretch constraint v (default = 1.0)
-    
-  int addGravity;
-  double g;
-    
-  bool _computationConditions[4];
-  int useRestAnglesForBendingForces;
-};
-}
-#endif
-
diff --git a/src/libclothBW/clothBWFromObjMesh.cpp b/src/libclothBW/clothBWFromObjMesh.cpp
deleted file mode 100644
index 3b0df7ed73c5f2ff3173050db8a8351059eec4f6..0000000000000000000000000000000000000000
--- a/src/libclothBW/clothBWFromObjMesh.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "clothBW" library , Copyright (C) 2013 USC                            *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Andy Pierce, Yu Yu Xu, Jernej Barbic                     *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include "macros.h"
-#include "clothBWFromObjMesh.h"
-using namespace std;
-
-namespace vega
-{
-int ClothBWFromObjMesh::GenerateClothBW(ObjMesh * mesh, ClothBW ** clothBW, double surfaceDensity, double tensileStiffness, double shearStiffness, double bendStiffnessU, double bendStiffnessV, double damping, int bu, int bv, int addGravity)
-{
-  int numMaterialGroups = mesh->getNumMaterials();
-  
-  // create arrays of density & stiffness values
-  double * surfaceDensityArray = (double*) malloc (sizeof(double) * numMaterialGroups);
-  double * tensileStiffnessArray = (double*) malloc (sizeof(double) * numMaterialGroups);
-  double * shearStiffnessArray = (double*) malloc (sizeof(double) * numMaterialGroups);
-  double * bendStiffnessUArray = (double*) malloc (sizeof(double) * numMaterialGroups);
-  double * bendStiffnessVArray = (double*) malloc (sizeof(double) * numMaterialGroups);
-  double * dampingArray = (double*) malloc (sizeof(double) * numMaterialGroups);
-  int * buArray = (int*) malloc (sizeof(int) * numMaterialGroups);
-  int * bvArray = (int*) malloc (sizeof(int) * numMaterialGroups);
-  
-  // fill arrays all with the same value
-  for (int i=0; i<numMaterialGroups; i++)
-  {
-    surfaceDensityArray[i] = surfaceDensity;
-    tensileStiffnessArray[i] = tensileStiffness;
-    shearStiffnessArray[i] = shearStiffness;
-    bendStiffnessUArray[i] = bendStiffnessU;
-    bendStiffnessVArray[i] = bendStiffnessV;
-    dampingArray[i] = damping;
-    buArray[i] = bu;
-    bvArray[i] = bv;
-  }
-  
-  // construct clothBW
-  GenerateClothBW(mesh, clothBW, numMaterialGroups, surfaceDensityArray, tensileStiffnessArray, shearStiffnessArray, bendStiffnessUArray, bendStiffnessVArray, dampingArray, buArray, bvArray, addGravity);
-  
-  // free memory we allocated
-  free(surfaceDensityArray);
-  free(tensileStiffnessArray);
-  free(shearStiffnessArray);
-  free(bendStiffnessUArray);
-  free(bendStiffnessVArray);
-  free(dampingArray);
-  free(buArray);
-  free(bvArray);
-
-  return 0;
-}
-
-int ClothBWFromObjMesh::GenerateClothBW(ObjMesh * mesh, ClothBW ** clothBW, int numMaterialGroups, double * surfaceDensity, double * tensileStiffness, double * shearStiffness, double * bendStiffnessU, double * bendStiffnessV, double * damping, int * bu, int * bv, int addGravity)
-{
-  mesh->triangulate();
-  
-  int numParticles;
-  double * restPositions;
-  int numTriangles;
-  int * triangles;
-  int numGroups;
-  int * triangleGroups;
-  
-  mesh->exportGeometry(&numParticles, &restPositions, &numTriangles, &triangles, &numGroups, &triangleGroups);
-
-  if (numGroups != numMaterialGroups)
-  {
-    printf("Mismatch in the number of groups. Mesh has %d groups.\n", numGroups);
-    return 1;
-  }
-  
-  // compute masses
-  vector<double> groupSurfaceMassDensity;
-  for(int i=0; i<numGroups; i++)
-    groupSurfaceMassDensity.push_back(surfaceDensity[i]);
-
-  vector<double> massesV;
-  mesh->computeMassPerVertex(groupSurfaceMassDensity, massesV);
-
-  double * masses = (double*) malloc (sizeof(double) * numParticles);
-  for(int i=0; i<numParticles; i++)
-    masses[i] = massesV[i];
-  
-  *clothBW = new ClothBW(numParticles,  masses, restPositions, numTriangles,
-                         triangles, triangleGroups, numGroups, tensileStiffness,
-                         shearStiffness, bendStiffnessU, bendStiffnessV,
-                         damping, addGravity);
-  
-  free(restPositions);
-  free(triangles);
-  free(triangleGroups);
-  free(masses);
-  
-  return 0;
-}
-
-}
diff --git a/src/libclothBW/clothBWMT.cpp b/src/libclothBW/clothBWMT.cpp
deleted file mode 100644
index 7fcf1e73b3ecb170b540745057bec4c98277aae3..0000000000000000000000000000000000000000
--- a/src/libclothBW/clothBWMT.cpp
+++ /dev/null
@@ -1,393 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "clothBW" library , Copyright (C) 2013 USC                            *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Andy Pierce, Yu Yu Xu, Jernej Barbic                     *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "clothBWMT.h"
-
-#include <pthread.h>
-
-namespace vega
-{
-ClothBWMT::ClothBWMT(int numParticles_, double * masses_, double * restPositions_, int numTriangles_,
-                      int * triangles_, int * triangleGroups_, int numMaterialGroups_,
-                       double * groupTensileStiffness_, double * groupShearStiffness_,
-                       double * groupBendStiffnessU_, double * groupBendStiffnessV_,
-                       double * groupDamping_, int addGravity_, int numThreads_) : 
-                            ClothBW(numParticles_, masses_, restPositions_, numTriangles_,
-                                    triangles_, triangleGroups_, numMaterialGroups_,
-                                    groupTensileStiffness_, groupShearStiffness_,
-                                    groupBendStiffnessU_, groupBendStiffnessV_,
-                                    groupDamping_, addGravity_), numThreads(numThreads_)
-                                                                                        
-{
-  Initialize();
-}
-
-// constructor with triangleUVs input
-ClothBWMT::ClothBWMT(int numParticles_, double * masses_, double * restPositions_, int numTriangles_,
-           int * triangles_, double * triangleUVs_, int * triangleGroups_, int numMaterialGroups_,
-           double * groupTensileStiffness_, double * groupShearStiffness_,
-           double * groupBendStiffnessU_, double * groupBendStiffnessV_,
-           double * groupDamping_, int addGravity_, int numThreads_) : 
-                          ClothBW(numParticles_, masses_, restPositions_, numTriangles_,
-                                  triangles_, triangleUVs_, triangleGroups_, numMaterialGroups_,
-                                  groupTensileStiffness_, groupShearStiffness_,
-                                  groupBendStiffnessU_, groupBendStiffnessV_,
-                                  groupDamping_, addGravity_), numThreads(numThreads_)
-{
-  Initialize();
-}
-
-ClothBWMT::ClothBWMT(ClothBW & clothBW, int numThreads_) : ClothBW(clothBW), numThreads(numThreads_)
-{
-  Initialize();
-}
-
-// destructor
-ClothBWMT::~ClothBWMT()
-{
-  free(startTriangle);
-  free(endTriangle);
-  
-  free(startQuad);
-  free(endQuad);
-  
-  free(internalForceBuffer);
-}
-
-// data structure to hold data for each thread
-struct ClothBWMT_threadArg
-{
-  ClothBWMT * clothBW_MT;
-  double * u;
-  double * uSecondary;
-  void * targetBuffer;
-  int rank;
-  enum ClothBWMT_computationTargetType computationTarget;
-};
-
-// global function
-void * ClothBWMT_WorkerThread(void * arg)
-{
-  // cast to struct
-  struct ClothBWMT_threadArg * threadArgp = (struct ClothBWMT_threadArg*) arg;
-  
-  // copy info into local vars
-  ClothBWMT * clothBW_MT = threadArgp->clothBW_MT;
-  double * u = threadArgp->u;
-  int rank = threadArgp->rank;
-  int startTriangle = clothBW_MT->GetStartTriangle(rank);
-  int endTriangle = clothBW_MT->GetEndTriangle(rank);
-  int startQuad = clothBW_MT->GetStartQuad(rank);
-  int endQuad = clothBW_MT->GetEndQuad(rank);
-  
-  // now put the thread to work
-  switch (threadArgp->computationTarget)
-  {
-    case STRETCH_SHEAR_FORCE:
-    {
-      double * targetBuffer = (double*)(threadArgp->targetBuffer);
-      clothBW_MT->AddStretchAndShearForce(u, targetBuffer, startTriangle, endTriangle);
-    }
-      break;
-      
-    case BEND_FORCE:
-    {
-      double * targetBuffer = (double*)(threadArgp->targetBuffer);
-      clothBW_MT->AddBendForce(u, targetBuffer, startQuad, endQuad);
-    }
-      break;
-      
-    case STRETCH_SHEAR_STIFFNESS:
-    {
-      SparseMatrix * targetBuffer = (SparseMatrix*)(threadArgp->targetBuffer);
-      clothBW_MT->AddStretchAndShearStiffnessMatrix(u, targetBuffer, startTriangle, endTriangle);
-    }
-      break;
-      
-    case BEND_STIFFNESS:
-    {
-      SparseMatrix * targetBuffer = (SparseMatrix*)(threadArgp->targetBuffer);
-      clothBW_MT->AddBendStiffnessMatrix(u, targetBuffer, startQuad, endQuad);
-    }
-      break;
-      
-    default:
-      printf("Error: unknown computation type.\n");
-      exit(1);
-      break;
-  }
-  
-  return NULL;
-  
-}
-
-// multi-threaded computations
-// compute the internal elastic force, under deformation u
-// note: the force has the same sign as an external force acting on the body (opposite sign as in the StVK class)
-// if addForce is "true", f will be not be reset to zero prior to adding the forces
-void ClothBWMT::ComputeForce(double * u, double * f, bool addForce)
-{
-  forceAlreadyCleared = false;
-  
-  if (_computationConditions[0])
-    ComputeHelper(STRETCH_SHEAR_FORCE, u, NULL, (void*)f, addForce);
-  if (_computationConditions[1])
-    ComputeHelper(BEND_FORCE, u, NULL, (void*)f, addForce);
-}
-
-// compute the damping force
-// unimplemented
-void ClothBWMT::ComputeDampingForce(double * u, double * uvel, double * f, bool addForce)
-{
-  
-}
-
-void ClothBWMT::ComputeStiffnessMatrix(double * u, SparseMatrix * K, bool addMatrix)
-{
-  //ComputeHelper(STIFFNESSMATRIX, u, NULL, (void*)K, addMatrix);
-  matrixAlreadyCleared = false;
-  
-  if (_computationConditions[2])
-    ComputeHelper(STRETCH_SHEAR_STIFFNESS, u, NULL, (void*)K, addMatrix);
-  
-  if (_computationConditions[3])
-    ComputeHelper(BEND_STIFFNESS, u, NULL, (void*)K, addMatrix);
-}
-
-// for stretch/shear
-int ClothBWMT::GetStartTriangle(int rank)
-{
-  return startTriangle[rank];
-}
-
-int ClothBWMT::GetEndTriangle(int rank)
-{
-  return endTriangle[rank];
-}
-
-// for bend
-int ClothBWMT::GetStartQuad(int rank)
-{
-  return startQuad[rank];
-}
-
-int ClothBWMT::GetEndQuad(int rank)
-{
-  return endQuad[rank];
-}
-
-void ClothBWMT::Initialize()
-{
-  // size of our force buffer (3 x numParticles for each thread)
-  internalForceBuffer = (double*) malloc (sizeof(double) * numThreads * 3 * numParticles);
-  
-  // generate skeleton matrices
-  sparseMatrixBuffer = (SparseMatrix**) malloc (sizeof(SparseMatrix*) * numThreads);
-  
-  SparseMatrix * sparseMatrix;
-  GenerateStiffnessMatrixTopology(&sparseMatrix);
-  for(int i=0; i<numThreads; i++)
-    sparseMatrixBuffer[i] = new SparseMatrix(*sparseMatrix);
-  
-  // split the workload
-  startTriangle = (int*) malloc (sizeof(int) * numThreads);
-  endTriangle = (int*) malloc (sizeof(int) * numThreads);
-  
-  startQuad = (int*) malloc (sizeof(int) * numThreads);
-  endQuad = (int*) malloc (sizeof(int) * numThreads);
-  
-  int remainderTriangles = numTriangles % numThreads;
-  int remainderQuads = numQuads % numThreads;
-  
-  // the first 'remainder' nodes will process one triangle + one quad more
-  
-  int jobSizeTriangles = numTriangles / numThreads;
-  int jobSizeQuads = numQuads / numThreads;
-  
-  for(int rank=0; rank < numThreads; rank++)
-  {
-    // triangles
-    if (rank < remainderTriangles)
-    {
-      startTriangle[rank] = rank * (jobSizeTriangles+1);
-      endTriangle[rank] = (rank+1) * (jobSizeTriangles+1);
-    }
-    else
-    {
-      startTriangle[rank] = remainderTriangles * (jobSizeTriangles+1) + (rank-remainderTriangles) * jobSizeTriangles;
-      endTriangle[rank] = remainderTriangles * (jobSizeTriangles+1) + ((rank-remainderTriangles)+1) * jobSizeTriangles;
-    }
-    
-    // quads
-    if (rank < remainderQuads)
-    {
-      startQuad[rank] = rank * (jobSizeQuads+1);
-      endQuad[rank] = (rank+1) * (jobSizeQuads+1);
-    }
-    else
-    {
-      startQuad[rank] = remainderQuads * (jobSizeQuads+1) + (rank-remainderQuads) * jobSizeQuads;
-      endQuad[rank] = remainderQuads * (jobSizeQuads+1) + ((rank-remainderQuads)+1) * jobSizeQuads;
-    }
-    
-  }
-  
-  printf("Total triangles: %d \n",numTriangles);
-  printf("Total quads: %d \n",numQuads);
-  
-  printf("Num threads: %d \n",numThreads);
-  printf("Canonical job size (triangles): %d \n",jobSizeTriangles);
-  printf("Canonical job size (quads): %d \n",jobSizeQuads);
-  
-  printf("Num threads with job size augmented by one triangle: %d \n",remainderTriangles);
-  printf("Num threads with job size augmented by one quad: %d \n",remainderQuads);
-  
-}
-
-void ClothBWMT::ComputeHelper(enum ClothBWMT_computationTargetType computationTarget, double * u, double * uSecondary, void * target, bool addQuantity)
-{
-  // launch threads
-  int numParticles3 = 3*numParticles;
-  struct ClothBWMT_threadArg * threadArgv = (struct ClothBWMT_threadArg*) malloc (sizeof(struct ClothBWMT_threadArg) * numThreads);
-  
-  pthread_t * tid = (pthread_t*) malloc (sizeof(pthread_t) * numThreads);
-  
-  for(int i=0; i<numThreads; i++)
-  {
-    threadArgv[i].clothBW_MT = this;
-    threadArgv[i].u = u;
-    threadArgv[i].rank = i;
-    threadArgv[i].computationTarget = computationTarget;
-  }
-  
-  switch(computationTarget)
-  {
-    // all force calcs need same size buffer, so we just fall through here
-    case STRETCH_SHEAR_FORCE:
-    case BEND_FORCE:
-    {
-      for(int i=0; i<numThreads; i++)
-        threadArgv[i].targetBuffer = (void*)(&internalForceBuffer[i * numParticles3]);
-      memset(internalForceBuffer, 0, sizeof(double) * numParticles3 * numThreads);
-    }
-      break;
-      
-    // likewise for the stiffness matrix calculations
-    case STRETCH_SHEAR_STIFFNESS:
-    case BEND_STIFFNESS:
-    {
-      for(int i=0; i<numThreads; i++)
-      {
-        threadArgv[i].targetBuffer = (void*)(sparseMatrixBuffer[i]);
-        sparseMatrixBuffer[i]->ResetToZero();
-      }
-    }
-      break;
-      
-    default:
-      printf("Error: unknown computation type.\n");
-      exit(1);
-      break;
-  }
-  
-  for(int i=0; i<numThreads; i++)  
-  {
-    if (pthread_create(&tid[i], NULL, ClothBWMT_WorkerThread, &threadArgv[i]) != 0)
-    {
-      printf("Error: unable to launch thread %d.\n", i);
-      exit(1);
-    }
-  }
-  
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_join(tid[i], NULL) != 0)
-    {
-      printf("Error: unable to join thread %d.\n", i);
-      exit(1);
-    }
-  }
-  
-  free(threadArgv);
-  free(tid);
-  
-  // assemble results
-  switch(computationTarget)
-  {
-    case STRETCH_SHEAR_FORCE:   
-    case BEND_FORCE:
-    {
-      double * f = (double*) target;
-      
-      if (!addQuantity && !forceAlreadyCleared)
-      {
-        memset(f, 0, sizeof(double) * numParticles3);
-        forceAlreadyCleared = true;
-      }
-      
-      for(int i=0; i<numThreads; i++)
-      {
-        double * source = &internalForceBuffer[i * numParticles3];
-        for(int j=0; j<numParticles3; j++)
-          f[j] += source[j];
-      }
-      
-      if (addGravity)
-        ComputeGravity(f, true);
-    }
-      break;
-      
-    case STRETCH_SHEAR_STIFFNESS:
-    case BEND_STIFFNESS:
-    {
-      SparseMatrix * targetK = (SparseMatrix*) target;
-      
-      if (!addQuantity && !matrixAlreadyCleared)
-      {
-        targetK->ResetToZero();
-        matrixAlreadyCleared = true;
-      }
-      
-      for(int i=0; i<numThreads; i++)
-      {
-        *targetK += *(sparseMatrixBuffer[i]);
-      }
-    }
-      break;
-      
-    default:
-      printf("Error: unknown computation type.\n");
-      exit(1);
-      break;
-  }
-}
-
-}
diff --git a/src/libclothBW/clothBWMT.h b/src/libclothBW/clothBWMT.h
deleted file mode 100644
index 923a5fd40f47b74b9cde2df58049c251d6447b7f..0000000000000000000000000000000000000000
--- a/src/libclothBW/clothBWMT.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "clothBW" library , Copyright (C) 2013 USC                            *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Andy Pierce, Yu Yu Xu, Jernej Barbic                     *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-/*
- Multi-threaded version of the ClothBW class. 
- It uses the POSIX threads ("pthreads").
-*/
-
-#ifndef _CLOTHBWMT_H_
-#define _CLOTHBWMT_H_
-
-#include "clothBW.h"
-
-namespace vega
-{
-enum ClothBWMT_computationTargetType { STRETCH_SHEAR_FORCE, BEND_FORCE, STRETCH_SHEAR_STIFFNESS, BEND_STIFFNESS };
-
-class ClothBWMT : public ClothBW
-{
-public:
-  
-  // constructor that does not require triangleUVs input (it computes UVs automatically; note: the UVs are continuous only within each triangle; the UV map is not global (which is fine, provided one does not want to simulate anisotropic effects) )
-  ClothBWMT(int numParticles, double * masses, double * restPositions, int numTriangles,
-          int * triangles, int * triangleGroups, int numMaterialGroups,
-          double * groupTensileStiffness, double * groupShearStiffness,
-          double * groupBendStiffnessU, double * groupBendStiffnessV,
-          double * groupDamping, int addGravity=0, int numThreads=1);
-  
-  // constructor with triangleUVs input
-  ClothBWMT(int numParticles, double * masses, double * restPositions, int numTriangles,
-          int * triangles, double * traingleUVs, int * triangleGroups, int numMaterialGroups,
-          double * groupTensileStiffness, double * groupShearStiffness,
-          double * groupBendStiffnessU, double * groupBendStiffnessV,
-          double * groupDamping, int addGravity=0, int numThreads=1);	
-  
-  // constructor from regular clothBW and thread count
-  ClothBWMT(ClothBW & clothBW, int numThreads_);
-  
-  // destructor
-  virtual ~ClothBWMT();
-  
-  // multi-threaded computations
-  // compute the internal elastic force, under deformation u
-  // note: the force has the same sign as an external force acting on the body (opposite sign as in the StVK class)
-  virtual void ComputeForce(double * u, double * f, bool addForce=true); // if addForce is "true", f will be not be reset to zero prior to adding the forces
-  
-  // compute the damping force
-  // unimplemented
-  // you can use the damping available in the integrator class
-  virtual void ComputeDampingForce(double * u, double * uvel, double * f, bool addForce=false);
-  
-  virtual void ComputeStiffnessMatrix(double * u, SparseMatrix * K, bool addMatrix=false);
-  
-  // for stretch/shear
-  int GetStartTriangle(int rank);
-  int GetEndTriangle(int rank);
-  
-  // for bend
-  int GetStartQuad(int rank);
-  int GetEndQuad(int rank);
-  
-protected:
-  int numThreads;
-  
-  int * startTriangle;
-  int * endTriangle;
-  
-  int * startQuad;
-  int * endQuad;
-  
-  bool forceAlreadyCleared; // need this to make sure we don't erase a force
-  bool matrixAlreadyCleared; // likewise with respect to a stiffness matrix
-  
-  double * internalForceBuffer;
-  SparseMatrix ** sparseMatrixBuffer;
-  
-  void Initialize();
-  void ComputeHelper(enum ClothBWMT_computationTargetType computationTarget, double * u, double * uSecondary, void * target, bool addQuantity);
-  
-};
-}
-#endif
-
diff --git a/src/libconfigFile/CMakeLists.txt b/src/libconfigFile/CMakeLists.txt
deleted file mode 100644
index 59b7e48094f061a4b49ac77fa845957630954870..0000000000000000000000000000000000000000
--- a/src/libconfigFile/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-vega_add_library(configFile
-  SOURCES
-    configFile.cpp
-  PUBLIC_HEADERS
-    configFile.h
-)
diff --git a/src/libconfigFile/configFile.cpp b/src/libconfigFile/configFile.cpp
deleted file mode 100644
index 9cda7c5f3ca8598b289354faa470b97b3d668e1a..0000000000000000000000000000000000000000
--- a/src/libconfigFile/configFile.cpp
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
-  * Copyright (c) 2007, Carnegie Mellon University
-  * All rights reserved.
-  *
-  * Redistribution and use in source and binary forms, with or without
-  * modification, are permitted provided that the following conditions are met:
-  *     * Redistributions of source code must retain the above copyright
-  *       notice, this list of conditions and the following disclaimer.
-  *     * Redistributions in binary form must reproduce the above copyright
-  *       notice, this list of conditions and the following disclaimer in the
-  *       documentation and/or other materials provided with the distribution.
-  *     * Neither the name of Carnegie Mellon University, nor the
-  *       names of its contributors may be used to endorse or promote products
-  *       derived from this software without specific prior written permission.
-  *
-  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
-  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-  * DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
-  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#include <string>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "configFile.h"
-
-namespace vega
-{
-#define OPT_INT 0
-#define OPT_BOOL 1
-#define OPT_FLOAT 2
-#define OPT_DOUBLE 3
-#define OPT_STR 10
-
-ConfigFile::ConfigFile(): suppressWarnings_(0) {}
-
-// convert string to uppercase
-void ConfigFile::upperCase(char * s)
-{
-  char diff = 'A' - 'a';
-  for(unsigned int i=0; i<strlen(s); i++)
-  {
-    if ((s[i] >= 'a') && (s[i] <= 'z'))
-      s[i] += diff;
-  }
-}
-
-void ConfigFile::removeTrailingCharacters(char * s, char ch)
-{
-  // remove trailing "ch"s
-  char * pos = s;
-  while( *pos != '\0' )
-    pos++;
-
-  // now pos points to '\0'
-  pos--;
-  while ((pos != s - 1) && (*pos == ch))
-  {
-    *pos = '\0';
-    pos--;
-  }
-}
-
-int ConfigFile::getTypeSize(int type)
-{
-  switch(type)
-  {
-    case OPT_INT:
-      return sizeof(int);
-
-    case OPT_BOOL:
-      return sizeof(bool);
-
-    case OPT_FLOAT:
-      return sizeof(float);
-
-    case OPT_DOUBLE:
-      return sizeof(double);
-
-    default:
-      printf("Error: invalid type requested (1)\n");
-      return -1;
-  }
-}
-
-// returns the printf format specifier string corresponding to a particular option type
-void ConfigFile::getTypeFormatSpecifier(int type, char * fsp)
-{
-  switch(type)
-  {
-    case OPT_INT:
-      strcpy(fsp,"%d");
-	  fsp[2] = 0;
-      break;
-
-    case OPT_BOOL:
-      strcpy(fsp,"%s");
-	  fsp[2] = 0;
-      break;
-
-    case OPT_FLOAT:
-      strcpy(fsp,"%f");
-	  fsp[2] = 0;
-      break;
-
-    case OPT_DOUBLE:
-      strcpy(fsp,"%lf");
-	  fsp[3] = 0;
-      break;
-
-    case OPT_STR:
-      strcpy(fsp,"%s");
-	  fsp[2] = 0;
-      break;
-
-    default:
-      printf("Error: invalid type requested (1)\n");
-  }
-}
-
-ConfigFile::~ConfigFile()
-{
-}
-
-// finds a particular option name among all specified options
-int ConfigFile::seekOption(const char * optionName)
-{
-  int slen = (int)(strlen(optionName));
-  char * upperOptionName = (char*) malloc (sizeof(char) * (slen + 1));
-  memcpy(upperOptionName, optionName, sizeof(char) * (slen + 1));
-
-  upperCase(upperOptionName);
-  for(unsigned int i=0; i<optionNames.size(); i++)
-  {
-    if (strcmp(upperOptionName,optionNames[i].c_str()) == 0)
-    {
-      free(upperOptionName);
-      return (int)i;
-    }
-  }
-  free(upperOptionName);
-  return -1;
-}
-
-void ConfigFile::printOptions()
-{
-  for(unsigned int i=0; i<optionNames.size(); i++)
-  {
-    switch(optionTypes[i])
-    {
-      case OPT_INT:
-        printf("%s: %d\n",optionNames[i].c_str(), *(int*)(destLocations[i]));
-        break;
-
-      case OPT_BOOL:
-        printf("%s: %d\n",optionNames[i].c_str(), *(bool*)(destLocations[i]));
-        break;
-
-      case OPT_FLOAT:
-        printf("%s: %G\n",optionNames[i].c_str(), *(float*)(destLocations[i]));
-        break;
-
-      case OPT_DOUBLE:
-        printf("%s: %G\n",optionNames[i].c_str(), *(double*)(destLocations[i]));
-        break;
-
-      case OPT_STR:
-        printf("%s: %s\n",optionNames[i].c_str(), (char*)(destLocations[i]));
-        break;
-
-      default:
-        printf("Error: invalid type requested (1)\n");
-    }
-
-  }
-
-}
-
-// a generic routine to add a new option entry to the list of all options
-template<class T>
-int ConfigFile::addOptionHelper(const char * optionName, T * destLocation)
-{
-  if (seekOption(optionName) != -1)
-  {
-    if (!suppressWarnings_)
-      printf("Warning: option %s already exists. Ignoring request to re-add it.\n",optionName);
-    return 1;
-  }
-
-  std::string optionName_(optionName);
-
-  // convert to uppercase
-  for(unsigned int i=0; i< optionName_.size(); i++)
-    optionName_[i] = toupper(optionName_[i]);
-
-  optionNames.push_back(optionName_);
-
-  destLocations.push_back((void*)destLocation);
-
-  optionSet.push_back(false);
-
-  return 0;
-}
-
-int ConfigFile::addOption(const char * optionName, int * destLocation)
-{
-  int code;
-  if ((code = addOptionHelper(optionName, destLocation)) != 0)
-    return code;
-
-  optionTypes.push_back(OPT_INT);
-
-  return 0;
-}
-
-int ConfigFile::addOption(const char * optionName, bool * destLocation)
-{
-  int code;
-  if ((code = addOptionHelper(optionName,destLocation)) != 0)
-    return code;
-
-  optionTypes.push_back(OPT_BOOL);
-
-  return 0;
-}
-
-int ConfigFile::addOption(const char * optionName, double * destLocation)
-{
-  int code;
-  if ((code = addOptionHelper(optionName,destLocation)) != 0)
-    return code;
-
-  optionTypes.push_back(OPT_DOUBLE);
-
-  return 0;
-}
-
-int ConfigFile::addOption(const char * optionName, float * destLocation)
-{
-  int code;
-  if ((code = addOptionHelper(optionName,destLocation)) != 0)
-    return code;
-
-  optionTypes.push_back(OPT_FLOAT);
-
-  return 0;
-}
-
-int ConfigFile::addOption(const char * optionName, char * destLocation)
-{
-  int code;
-  if ((code = addOptionHelper(optionName,destLocation)) != 0)
-    return code;
-
-  optionTypes.push_back(OPT_STR);
-
-  return 0;
-}
-
-template<class T>
-int ConfigFile::addOptionOptional(const char * optionName, T * destLocation, T defaultValue)
-{
-  int code = addOption(optionName,destLocation);
-  *destLocation = defaultValue;
-  optionSet[optionSet.size()-1] = true;
-  return code;
-}
-
-int ConfigFile::addOptionOptional(const char * optionName, char * destLocation, const char * defaultValue)
-{
-  int code = addOption(optionName,destLocation);
-  // must use memmove because strings may overlap
-  memmove(destLocation, defaultValue, strlen(defaultValue) + 1);
-  optionSet[optionSet.size()-1] = true;
-  return code;
-}
-
-int ConfigFile::parseOptions(const char * filename)
-{
-  FILE * fin = fopen(filename,"r");
-  if (!fin)
-  {
-    printf("Error: could not open option file %s\n",filename);
-    return 1;
-  }
-
-  int count = 0;
-  char line[4096];
-  while (fgets(line,4096,fin) != NULL)
-  {
-    count++;
-
-    removeTrailingCharacters(line,'\n');
-    removeTrailingCharacters(line,' ');
-
-    // ignore blank lines and comments
-    if ((line[0] == '#') || (line[0] == '\0'))
-      continue;
-
-    if (line[0] != '*')
-    {
-      printf("Error: invalid line %d: %s\n",count,line);
-      fclose(fin);
-      return 1;
-    }
-
-    int index = seekOption(&line[1]);
-    //printf("Read entry: %s . Option index: %d .\n", &line[1], index);
-    if (index == -1)
-    {
-      if (!suppressWarnings_)
-        printf("Warning: unknown option on line %d: %s\n",count,&line[1]);
-
-      // eat next line
-      do
-      {
-        if (fgets(line,4096,fin) == NULL)
-        {
-          printf("Error: EOF reached without specifying option value.\n");
-          fclose(fin);
-          return 1;
-        }
-        count++;
-      }
-      while (line[0] == '#'); // ignore comments
-
-      continue;
-    }
-
-    // parse next line to get the data
-    char dataEntry[4096];
-    do
-    {
-      if (fgets(dataEntry,4096,fin) == NULL)
-      {
-        printf("Error: EOF reached without specifying option value.\n");
-        fclose(fin);
-        return 1;
-      }
-      count++;
-    }
-    while (dataEntry[0] == '#'); // ignore comments
-
-    char typeFormatSpecifier[4];
-    getTypeFormatSpecifier(optionTypes[index],typeFormatSpecifier);
-
-    // remove any trailing line feed and carriage return
-    if (dataEntry[strlen(dataEntry)-1] == 10)
-      dataEntry[strlen(dataEntry)-1] = 0;
-    if (dataEntry[strlen(dataEntry)-1] == 13)
-      dataEntry[strlen(dataEntry)-1] = 0;
-
-    if (optionTypes[index] != OPT_STR)
-    {
-      char buffer[4096];
-      if (sscanf(dataEntry,typeFormatSpecifier,buffer) == 0)
-      {
-        printf("Error: invalid dataline for option %s: %s\n", optionNames[index].c_str(), dataEntry);
-        fclose(fin);
-        return 1;
-      }
-
-      // convert from string to true/false
-      if (optionTypes[index] == OPT_BOOL)
-      {
-        //printf("Option type: boolean.\n");
-        if (strncmp(buffer,"true",4) == 0)
-        {
-          bool * target = (bool*) buffer;
-          *target = true;
-        }
-        else
-          if (strncmp(buffer,"false",5) == 0)
-          {
-            bool * target = (bool*) buffer;
-            *target = false;
-          }
-          else
-          {
-            bool * target = (bool*) buffer;
-            *target = true;
-            printf("Error: invalid boolean specification: line %d: %s\n",count,dataEntry);
-            fclose(fin);
-            return 1;
-          }
-      }
-      memcpy(destLocations[index], buffer, getTypeSize(optionTypes[index]));
-    }
-    else
-    {
-      // this is a string option
-      strcpy((char*)destLocations[index], dataEntry);
-    }
-
-    optionSet[index] = true;
-  }
-
-  fclose(fin);
-
-  for(unsigned int i=0; i<optionNames.size(); i++)
-  {
-    if(!optionSet[i])
-    {
-      printf("Error: option %s didn't have an entry in the config file.\n",optionNames[i].c_str());
-      return 1;
-    }
-  }
-
-  return 0;
-}
-
-template int ConfigFile::addOptionOptional<bool>(const char * optionName, bool * destLocation, bool defaultValue);
-template int ConfigFile::addOptionOptional<int>(const char * optionName, int * destLocation, int defaultValue);
-template int ConfigFile::addOptionOptional<float>(const char * optionName, float * destLocation, float defaultValue);
-template int ConfigFile::addOptionOptional<double>(const char * optionName, double * destLocation, double defaultValue);
-}
diff --git a/src/libcorotationalLinearFEM/CMakeLists.txt b/src/libcorotationalLinearFEM/CMakeLists.txt
deleted file mode 100644
index b04c32fc835ac73d4ff82a240c4894f79d8a9022..0000000000000000000000000000000000000000
--- a/src/libcorotationalLinearFEM/CMakeLists.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-set(corotationallinearfem_srcs
-    corotationalLinearFEM.cpp)
-
-set(corotationallinearfem_hdrs
-    corotationalLinearFEM.h)
-
-if(VegaFEM_ENABLE_PTHREADS_SUPPORT)
-  list(APPEND corotationallinearfem_hdrs corotationalLinearFEMMT.h)
-  list(APPEND corotationallinearfem_srcs corotationalLinearFEMMT.cpp)
-endif()
-
-vega_add_library(corotationalLinearFEM
-  SOURCES
-    ${corotationallinearfem_srcs}
-  PUBLIC_HEADERS
-    ${corotationallinearfem_hdrs}
-)
-
-target_link_libraries(corotationalLinearFEM
-  PUBLIC
-    polarDecomposition
-    volumetricMesh
-    sparseMatrix
-    Threads::Threads
-  INTERFACE
-    minivector
-)
diff --git a/src/libcorotationalLinearFEM/corotationalLinearFEM.cpp b/src/libcorotationalLinearFEM/corotationalLinearFEM.cpp
deleted file mode 100644
index e7fc8925de51c92ca9a167bef48796084e975be0..0000000000000000000000000000000000000000
--- a/src/libcorotationalLinearFEM/corotationalLinearFEM.cpp
+++ /dev/null
@@ -1,572 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "corotational linear FEM" library , Copyright (C) 2013 USC            *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "corotationalLinearFEM.h"
-#include "polarDecomposition.h"
-#include "matrixMultiplyMacros.h"
-#include "mat3d.h"
-#include "volumetricMeshENuMaterial.h"
-
-namespace vega
-{
-CorotationalLinearFEM::CorotationalLinearFEM(TetMesh * tetMesh_) : tetMesh(tetMesh_) 
-{
-  numVertices = tetMesh->getNumVertices();
-
-  // store the undeformed positions
-  undeformedPositions = (double*) malloc (sizeof(double) * 3 * numVertices);
-  for(int i=0; i < numVertices; i++)
-  {
-    Vec3d * v = tetMesh->getVertex(i);
-    for(int j=0; j<3; j++)
-      undeformedPositions[3*i+j] = (*v)[j];
-  }
-
-  int numElements = tetMesh->getNumElements();
-
-  // set Lame's coefficients
-  lambdaLame = (double*) malloc (sizeof(double) * numElements);
-  muLame = (double*) malloc (sizeof(double) * numElements);
-  for(int el=0; el<numElements; el++)
-  {
-    VolumetricMesh::Material * material = tetMesh->getElementMaterial(el);
-    VolumetricMesh::ENuMaterial * eNuMaterial = downcastENuMaterial(material);
-    if (eNuMaterial == NULL)
-    {
-      printf("Error: CorotationalLinearFEM: mesh does not consist of E, nu materials.\n");
-      throw 1;
-    }
-
-    lambdaLame[el] = eNuMaterial->getLambda();
-    muLame[el] = eNuMaterial->getMu();
-  }
-
-  MInverse = (double**) malloc (sizeof(double*) * numElements);
-  for(int el = 0; el < numElements; el++)
-  {
-    // get the integer indices of the tet vertices
-    int vtxIndex[4];
-    for(int vtx=0; vtx<4; vtx++)
-      vtxIndex[vtx] = tetMesh->getVertexIndex(el, vtx);
-    /*
-       Form matrix: 
-       M = [ v0   v1   v2   v3 ]
-           [  1    1    1    1 ]
-    */
-    double M[16]; // row-major
-    for(int vtx=0; vtx<4; vtx++)
-      for(int dim=0; dim<3; dim++)
-        M[4 * dim + vtx] = undeformedPositions[3 * vtxIndex[vtx] + dim];
-    M[12] = M[13] = M[14] = M[15] = 1.0;
-
-    // invert M and cache inverse (see [Mueller 2004])
-    MInverse[el] = (double*) malloc (sizeof(double) * 16);
-    inverse4x4(M, MInverse[el]);
-  }
-
-  // build acceleration indices for fast writing to the global stiffness matrix
-  SparseMatrix * sparseMatrix;
-  GetStiffnessMatrixTopology(&sparseMatrix);
-  BuildRowColumnIndices(sparseMatrix);
-  delete(sparseMatrix);
-
-  // compute stiffness matrices for all the elements in the undeformed configuration
-  KElementUndeformed = (double**) malloc (sizeof(double*) * numElements);
-  for (int el = 0; el < numElements; el++)
-  {
-    double * MInv = MInverse[el];
-
-    // Form stiffness matrix of the element in the undeformed configuration.
-    // The procedure below is standard in FEM solid mechanics.
-    // This code implements the equations given in Ahmed A. Shabana: Theory of Vibration, Volume II: Discrete and Continuous Systems, Springer--Verlag, New York, NY, 1990.
-
-    double B[72] = 
-      { MInv[0], 0, 0, MInv[4], 0, 0, MInv[8], 0, 0, MInv[12], 0, 0,
-        0, MInv[1], 0, 0, MInv[5], 0, 0, MInv[9], 0, 0, MInv[13], 0,
-	0, 0, MInv[2], 0, 0, MInv[6], 0, 0, MInv[10], 0, 0, MInv[14],
-        MInv[1], MInv[0], 0, MInv[5], MInv[4], 0, MInv[9], MInv[8], 
-        0, MInv[13], MInv[12], 0, 0, MInv[2], MInv[1], 0, MInv[6], MInv[5], 
-        0, MInv[10], MInv[9], 0, MInv[14], MInv[13], MInv[2], 0, MInv[0], 
-        MInv[6], 0, MInv[4], MInv[10], 0, MInv[8], MInv[14], 0, MInv[12] };
-
-    double lambda = lambdaLame[el];
-    double mu = muLame[el];
-    double E[36] = { lambda + 2 * mu, lambda, lambda, 0, 0, 0,
-                     lambda, lambda + 2 * mu, lambda, 0, 0, 0,
-                     lambda, lambda, lambda + 2 * mu, 0, 0, 0,
-                     0, 0, 0, mu, 0, 0,
-                     0, 0, 0, 0, mu, 0,
-                     0, 0, 0, 0, 0, mu };
-
-    // EB = E * B
-    double EB[72];
-    memset(EB, 0, sizeof(double) * 72);
-    for (int i=0; i<6; i++)
-      for (int j=0; j<12; j++)
-	for (int k=0; k<6; k++)
-	  EB[12 * i + j] += E[6 * i + k] * B[12 * k + j];
- 
-    // KElementUndeformed[el] = B^T * EB
-    KElementUndeformed[el] = (double*) calloc (144, sizeof(double)); // element stiffness matrix
-    for (int i=0; i<12; i++)
-      for (int j=0; j<12; j++)
-	for (int k=0; k<6; k++)
-          KElementUndeformed[el][12 * i + j] += B[12 * k + i] * EB[12 * k + j];
-
-    // KElementUndeformed[el] *= volume
-    double volume = TetMesh::getTetVolume(tetMesh->getVertex(el,0), tetMesh->getVertex(el,1), tetMesh->getVertex(el,2), tetMesh->getVertex(el,3));
-    for(int i=0; i<144; i++)
-      KElementUndeformed[el][i] *= volume;
-  }
-}
-
-CorotationalLinearFEM::~CorotationalLinearFEM()
-{
-  free(undeformedPositions);
-  for(int el=0; el < tetMesh->getNumElements(); el++)
-  {
-    free(KElementUndeformed[el]);
-    free(MInverse[el]);
-  }
-  free(KElementUndeformed);
-  free(MInverse);
-
-  ClearRowColumnIndices();
-
-  free(lambdaLame);
-  free(muLame);
-}
-
-void CorotationalLinearFEM::GetStiffnessMatrixTopology(SparseMatrix ** stiffnessMatrixTopology)
-{
-  SparseMatrixOutline * emptyMatrix = new SparseMatrixOutline(3 * numVertices);
-
-  int numElements = tetMesh->getNumElements();
-  for (int el=0; el < numElements; el++)
-  {
-    int vtxIndex[4];
-    for(int vtx=0; vtx<4; vtx++)
-      vtxIndex[vtx] = tetMesh->getVertexIndex(el, vtx);
-
-    for (int i=0; i<4; i++)
-      for (int j=0; j<4; j++)
-      {
-        // add 3x3 block corresponding to pair of vertices (i,j)
-        for(int k=0; k<3; k++)
-          for(int l=0; l<3; l++)
-            emptyMatrix->AddEntry(3 * vtxIndex[i] + k, 3 * vtxIndex[j] + l, 0.0);
-      }
-  }
-
-  *stiffnessMatrixTopology = new SparseMatrix(emptyMatrix);
-  delete(emptyMatrix);
-}
-
-// compute RK = R * K and RKRT = R * K * R^T (block-wise)
-// input: K, R
-// output: RK, RKRT
-void CorotationalLinearFEM::WarpMatrix(double * K, double * R, double * RK, double * RKRT)
-{
-  memset(RK, 0, sizeof(double) * 144);
-  memset(RKRT, 0, sizeof(double) * 144);
-  for(int i=0; i<4; i++)
-    for(int j=0; j<4; j++)
-    {
-      // RK = R * K
-      for(int k=0; k<3; k++)
-         for(int l=0; l<3; l++)
-           for(int m=0; m<3; m++)
-             RK[12 * (3 * i + k) + (3 * j + l)] += R[3 * k + m] * K[12 * (3 * i + m) + (3 * j + l)];
-
-      // RKRT = RK * R^T
-      for(int k=0; k<3; k++)
-        for(int l=0; l<3; l++)
-          for(int m=0; m<3; m++)
-            RKRT[12 * (3 * i + k) + (3 * j + l)] += RK[12 * (3 * i + k) + (3 * j + m)] * R[3 * l + m];
-    }
-}
-
-void CorotationalLinearFEM::ComputeForceAndStiffnessMatrix(double * u, double * f, SparseMatrix * stiffnessMatrix, int warp)
-{
-  ComputeForceAndStiffnessMatrixOfSubmesh(u, f, stiffnessMatrix, warp, 0, tetMesh->getNumElements());
-}
-
-void CorotationalLinearFEM::ComputeForceAndStiffnessMatrixOfSubmesh(double * u, double * f, SparseMatrix * stiffnessMatrix, int warp, int elementLo, int elementHi)
-{
-  // clear f to zero
-  if (f != NULL)
-    memset(f, 0, sizeof(double) * 3 * numVertices);
-
-  // clear stiffness matrix to zero
-  if (stiffnessMatrix != NULL)
-    stiffnessMatrix->ResetToZero();
-
-  for (int el=elementLo; el < elementHi; el++)
-  {
-    int vtxIndex[4];
-    for (int vtx=0; vtx<4; vtx++)
-      vtxIndex[vtx] = tetMesh->getVertexIndex(el, vtx);
-
-    double KElement[144]; // element stiffness matrix, to be computed below; row-major
-
-    if (warp > 0)
-    {
-      double P[16]; // the current world-coordinate positions (row-major)
-      /*
-         P = [ v0   v1   v2   v3 ]
-             [  1    1    1    1 ]
-      */
-      // rows 1,2,3
-      for(int i=0; i<3; i++)
-        for(int j=0; j<4; j++)
-          P[4 * i + j] = undeformedPositions[3 * vtxIndex[j] + i] + u[3 * vtxIndex[j] + i];
-      // row 4
-      for(int j=0; j<4; j++)
-        P[12 + j] = 1;
-
-      // F = P * Inverse(M)
-      double F[9]; // upper-left 3x3 block
-      for(int i=0; i<3; i++) 
-        for(int j=0; j<3; j++) 
-        {
-          F[3 * i + j] = 0;
-          for(int k=0; k<4; k++)
-            F[3 * i + j] += P[4 * i + k] * MInverse[el][4 * k + j];
-	}
-
-      double R[9]; // rotation (row-major)
-      double S[9]; // symmetric (row-major)
-      double det = PolarDecomposition::Compute(F, R, S, 1E-6);
-      if (det < 0)
-      {
-        // flip R so that it becomes orthogonal
-        for(int i=0; i<9; i++)
-          R[i] *= -1.0;
-      }
-
-      // RK = R * K
-      // KElement = R * K * R^T
-      double RK[144]; // row-major
-      WarpMatrix(KElementUndeformed[el], R, RK, KElement);
-
-      // f = RK (RT x - x0)
-      double fElement[12];
-      for(int i=0; i<12; i++)
-      {
-        fElement[i] = 0;
-        for(int j=0; j<4; j++)
-          for(int l=0; l<3; l++)
-            fElement[i] += KElement[12 * i + 3 * j + l] * P[4 * l + j] - RK[12 * i + 3 * j + l] * undeformedPositions[3 * vtxIndex[j] + l];
-      }
-
-      // add fElement into the global f
-      if (f != NULL)
-      {
-        for(int j=0; j<4; j++)
-          for(int l=0; l<3; l++)
-            f[3 * vtxIndex[j] + l] += fElement[3 * j + l];
-      }
-
-      // compute exact stiffness matrix
-      if (warp == 2)
-      {
-        // compute G = (tr(S) I - S) R^T
-        double G[9]; 
-        double tr = S[0] + S[4] + S[8];
-        double temp[9];
-        for(int i=0; i<9; i++)
-          temp[i] = -S[i];
-        temp[0] += tr;
-        temp[4] += tr;
-        temp[8] += tr;
-        // G = temp * R^T
-        MATRIX_MULTIPLY3X3ABT(temp, R, G);
-
-        double invG[9]; // invG = G^{-1}
-        inverse3x3(G, invG);
-
-        double rhs[27]; // 3 x 9 matrix (column-major)
-        for(int i=0; i<3; i++)
-          for(int j=0; j<3; j++)
-          {
-            double temp[9];
-            for(int k=0; k<9; k++)
-              temp[k] = 0.0;
-            // copy i-th row of R into column j of temp      
-            for(int k=0; k<3; k++)
-              temp[3 * k + j] = R[3 * i + k];
-            // extract the skew-symmetric part
-            SKEW_PART(temp, &rhs[3 * (3 * i + j)]);
-          }
-        // must undo division by 2 from inside the SKEW_PART macro
-        for(int i=0; i<27; i++)
-          rhs[i] *= 2.0;
-
-        // solve G * omega = rhs
-        double omega[27]; // column-major
-        for(int i=0; i<9; i++)
-        {
-          MATRIX_VECTOR_MULTIPLY3X3(invG, &rhs[3 * i], &omega[3 * i]);
-        }
-
-        double dRdF[81]; // each column is skew(omega) * R ; column-major
-        for(int i=0; i<9; i++)
-        {
-          double skew[9];
-          SKEW_MATRIX(&omega[3 * i], skew);
-          MATRIX_MULTIPLY3X3(skew, R, &dRdF[9 * i]);
-        }
-
-        double B[3][3][9];
-        // re-arrange dRdF into B, for easier dRdF * dFdx multiplication (to exploit sparsity of dFdx)
-        for(int i=0; i<3; i++)
-          for(int j=0; j<3; j++)
-            for(int k=0; k<3; k++)
-              for(int l=0; l<3; l++)
-              {
-                int row = 3 * i + k;
-                int column = 3 * j + l;
-                B[i][j][3 * k + l] = dRdF[9 * column + row];
-              }
-
-        // four pointers to a 3-vector
-        double * minv[4] = { &MInverse[el][0], &MInverse[el][4], &MInverse[el][8], &MInverse[el][12] }; // the four rows of MInverse (last column ignored)
-
-        double dRdx[108]; // derivative of the element rotation matrix with respect to the positions of the tet vertices; column-major
-        for(int k=0; k<4; k++)
-          for(int i=0; i<3; i++)
-            for(int j=0; j<3; j++)
-            {
-              double temp[3];
-              MATRIX_VECTOR_MULTIPLY3X3(B[i][j], minv[k], temp);
-              int row = 3 * i;
-              int column = 3 * k + j;
-              VECTOR_SET3(&dRdx[9 * column + row], temp);
-            }
-
-        // add contribution of dRdx to KElement
-
-        // term 1: \hat{dR/dxl} K (R^T x - m)
-
-        // compute K (R^T x - m)
-        double tempVec[12]; // R^T x - m
-        for(int vtx=0; vtx<4; vtx++)
-        {
-          double pos[3];
-          for(int i=0; i<3; i++)
-            pos[i] = P[4 * i + vtx];
-          MATRIX_VECTOR_MULTIPLY3X3T(R, pos, &tempVec[3*vtx]);
-          // subtract m
-          for(int i=0; i<3; i++)
-            tempVec[3*vtx+i] -= undeformedPositions[3 * vtxIndex[vtx] + i];
-        }
-        double a[12]; // a = K * tempVec
-        for (int i=0; i<12; i++)
-        {
-          a[i] = 0.0;
-          for (int j=0; j<12; j++)
-            a[i] += KElementUndeformed[el][12 * i + j] * tempVec[j];
-        }
-
-        // add [\hat{dR/dxl} K R^T x]_l, l=1 to 12
-        for(int column=0; column<12; column++)
-        {
-          double b[12]; // b = \hat{dR/dxl} * a
-          for(int j=0; j<4; j++)
-          {
-            MATRIX_VECTOR_MULTIPLY3X3(&dRdx[9 * column], &a[3*j], &b[3*j]);
-          }
-          // write b into KElement (add b to i-th column)
-          for(int row=0; row<12; row++)
-            KElement[12 * row + column] += b[row]; // KElement is row-major
-        }
-
-        // term 2: (R K \hat{dRdxl}^T)x
-
-        // re-write positions into a
-        for(int vtx=0; vtx<4; vtx++)
-        {
-          for(int i=0; i<3; i++)
-            a[3 * vtx + i] = P[4 * i + vtx];
-        }
-
-        // compute [\hat{dRdxl}^T x)]_l, l=1 to 12
-        for(int column=0; column<12; column++)
-        {
-          double b[12]; // b = \hat{dRdxl}^T * a
-          for(int j=0; j<4; j++)
-          {
-            MATRIX_VECTOR_MULTIPLY3X3T(&dRdx[9 * column], &a[3*j], &b[3*j]);
-          }
-
-          // add RK * b to column of KElement
-          int rowStart = 0;
-          for (int row=0; row<12; row++)
-          {
-            double contrib = 0.0;
-            for (int j=0; j<12; j++)
-              contrib += RK[rowStart + j] * b[j];
-            KElement[rowStart + column] += contrib;
-            rowStart += 12;
-          }
-        }
-      }
-    }
-    else
-    {
-      // no warp
-      memcpy(KElement, KElementUndeformed[el], sizeof(double) * 144);
-      // f = K u
-      double fElement[12];
-      for(int i=0; i<12; i++)
-      {
-        fElement[i] = 0;
-        for(int j=0; j<4; j++)
-        {
-          fElement[i] += 
-            KElement[12 * i + 3 * j + 0] * u[3 * vtxIndex[j] + 0] +
-            KElement[12 * i + 3 * j + 1] * u[3 * vtxIndex[j] + 1] +
-            KElement[12 * i + 3 * j + 2] * u[3 * vtxIndex[j] + 2];
-        }
-      }
-
-      // add fElement into the global f
-      if (f != NULL)
-      {
-        for(int j=0; j<4; j++)
-        {
-          f[3 * vtxIndex[j] + 0] += fElement[3 * j + 0];
-          f[3 * vtxIndex[j] + 1] += fElement[3 * j + 1];
-          f[3 * vtxIndex[j] + 2] += fElement[3 * j + 2];
-        }
-      }
-    }
-
-    if (stiffnessMatrix != NULL)
-    {
-      int * rowIndex = rowIndices[el];
-      int * columnIndex = columnIndices[el];
-
-      // add KElement to the global stiffness matrix
-      for (int i=0; i<4; i++)
-        for (int j=0; j<4; j++)
-          for(int k=0; k<3; k++)
-            for(int l=0; l<3; l++)
-              stiffnessMatrix->AddEntry(3 * rowIndex[i] + k, 3 * columnIndex[4 * i + j] + l, KElement[12 * (3 * i + k) + 3 * j + l]);
-    }
-  }
-}
-
-void CorotationalLinearFEM::ClearRowColumnIndices()
-{
-  for (int el=0; el < tetMesh->getNumElements(); el++)
-  {
-    free(rowIndices[el]);
-    free(columnIndices[el]);
-  }
-
-  free(rowIndices);
-  free(columnIndices);
-}
-
-void CorotationalLinearFEM::BuildRowColumnIndices(SparseMatrix * sparseMatrix)
-{
-  int numElements = tetMesh->getNumElements();
-
-  rowIndices = (int**) malloc (sizeof(int*) * numElements);
-  columnIndices = (int**) malloc (sizeof(int*) * numElements);
-
-  for (int el=0; el < numElements; el++)
-  {
-    // the 4 rows corresponding to the 4 vertices
-    rowIndices[el] = (int*) malloc (sizeof(int) * 4);
-    for(int i=0; i<4; i++)
-      rowIndices[el][i] = tetMesh->getVertexIndex(el, i);
-
-    // the 4 columns corresponding to all 4 vertices, in row of each vertex
-    columnIndices[el] = (int*) malloc (sizeof(int) * 16);
-    // find index of vertex j in row of vertex i, and cache it
-    for(int i=0; i<4; i++)
-      for(int j=0; j<4; j++)
-        columnIndices[el][4 * i + j] = sparseMatrix->GetInverseIndex(3*rowIndices[el][i], 3*rowIndices[el][j]) / 3;
-  }
-}
-
-// inverse of a 3x3 matrix
-// row-major format
-void CorotationalLinearFEM::inverse3x3(double * A, double * AInv)
-{
-  // converted to C from Mathematica output   
-  AInv[0] = -A[5] * A[7] + A[4] * A[8]; 
-  AInv[1] = A[2] * A[7] - A[1] * A[8]; 
-  AInv[2] = -A[2] * A[4] + A[1] * A[5];
-  AInv[3] = A[5] * A[6] - A[3] * A[8]; 
-  AInv[4] = -A[2] * A[6] + A[0] * A[8]; 
-  AInv[5] = A[2] * A[3] - A[0] * A[5];
-  AInv[6] = -A[4] * A[6] + A[3] * A[7]; 
-  AInv[7] = A[1] * A[6] - A[0] * A[7];
-  AInv[8] = -A[1] * A[3] + A[0] * A[4];
-
-  double invDet = 1.0 / (-A[2] * A[4] * A[6] + A[1] * A[5] * A[6] + A[2] * A[3] * A[7] - A[0] * A[5] * A[7] - A[1] * A[3] * A[8] + A[0] * A[4] * A[8]);
-
-  for(int i=0; i<9; i++)
-    AInv[i] *= invDet;
-}
-
-// inverse of a 4x4 matrix
-// row-major format
-void CorotationalLinearFEM::inverse4x4(double * A, double * AInv)
-{
-  // converted to C from Mathematica output   
-  AInv[0] = -A[11] * A[14] * A[5] + A[10] * A[15] * A[5] + A[11] * A[13] * A[6] - A[10] * A[13] * A[7] - A[15] * A[6] * A[9] + A[14] * A[7] * A[9];
-  AInv[1] = A[1] * A[11] * A[14] - A[1] * A[10] * A[15] - A[11] * A[13] * A[2] + A[10] * A[13] * A[3] + A[15] * A[2] * A[9] - A[14] * A[3] * A[9];
-  AInv[2] = -A[15] * A[2] * A[5] + A[14] * A[3] * A[5] + A[1] * A[15] * A[6] - A[13] * A[3] * A[6] - A[1] * A[14] * A[7] + A[13] * A[2] * A[7];
-  AInv[3] = A[11] * A[2] * A[5] - A[10] * A[3] * A[5] - A[1] * A[11] * A[6] + A[1] * A[10] * A[7] + A[3] * A[6] * A[9] - A[2] * A[7] * A[9];
-  AInv[4] = A[11] * A[14] * A[4] - A[10] * A[15] * A[4] - A[11] * A[12] * A[6] + A[10] * A[12] * A[7] + A[15] * A[6] * A[8] - A[14] * A[7] * A[8];
-  AInv[5] = -A[0] * A[11] * A[14] + A[0] * A[10] * A[15] + A[11] * A[12] * A[2] - A[10] * A[12] * A[3] - A[15] * A[2] * A[8] + A[14] * A[3] * A[8];
-  AInv[6] = A[15] * A[2] * A[4] - A[14] * A[3] * A[4] - A[0] * A[15] * A[6] + A[12] * A[3] * A[6] + A[0] * A[14] * A[7] - A[12] * A[2] * A[7];
-  AInv[7] = -A[11] * A[2] * A[4] + A[10] * A[3] * A[4] + A[0] * A[11] * A[6] - A[0] * A[10] * A[7] - A[3] * A[6] * A[8] + A[2] * A[7] * A[8];
-  AInv[8] = -A[11] * A[13] * A[4] + A[11] * A[12] * A[5] - A[15] * A[5] * A[8] + A[13] * A[7] * A[8] + A[15] * A[4] * A[9] - A[12] * A[7] * A[9];
-  AInv[9] = -A[1] * A[11] * A[12] + A[0] * A[11] * A[13] + A[1] * A[15] * A[8] - A[13] * A[3] * A[8] - A[0] * A[15] * A[9] + A[12] * A[3] * A[9];
-  AInv[10] = -A[1] * A[15] * A[4] + A[13] * A[3] * A[4] + A[0] * A[15] * A[5] - A[12] * A[3] * A[5] + A[1] * A[12] * A[7] - A[0] * A[13] * A[7];
-  AInv[11] = A[1] * A[11] * A[4] - A[0] * A[11] * A[5] + A[3] * A[5] * A[8] - A[1] * A[7] * A[8] - A[3] * A[4] * A[9] + A[0] * A[7] * A[9]; 
-  AInv[12] = A[10] * A[13] * A[4] - A[10] * A[12] * A[5] + A[14] * A[5] * A[8] - A[13] * A[6] * A[8] - A[14] * A[4] * A[9] + A[12] * A[6] * A[9];
-  AInv[13] = A[1] * A[10] * A[12] - A[0] * A[10] * A[13] - A[1] * A[14] * A[8] + A[13] * A[2] * A[8] + A[0] * A[14] * A[9] - A[12] * A[2] * A[9]; 
-  AInv[14] = A[1] * A[14] * A[4] - A[13] * A[2] * A[4] - A[0] * A[14] * A[5] + A[12] * A[2] * A[5] - A[1] * A[12] * A[6] + A[0] * A[13] * A[6];
-  AInv[15] = -A[1] * A[10] * A[4] + A[0] * A[10] * A[5] - A[2] * A[5] * A[8] + A[1] * A[6] * A[8] + A[2] * A[4] *A[9] - A[0] * A[6] * A[9];
-
-  double invDet = 1.0 / (A[0] * AInv[0] + A[1] * AInv[4] + A[2] * AInv[8] + A[3] * AInv[12]);
-
-  for(int i=0; i<16; i++)
-    AInv[i] *= invDet;
-}
-
-}
diff --git a/src/libcorotationalLinearFEM/corotationalLinearFEM.h b/src/libcorotationalLinearFEM/corotationalLinearFEM.h
deleted file mode 100644
index 61700ca056e7f4e3cc73e9aa4e068f67f03057df..0000000000000000000000000000000000000000
--- a/src/libcorotationalLinearFEM/corotationalLinearFEM.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "corotational linear FEM" library , Copyright (C) 2013 USC            *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef _COROTATIONALLINEARFEM_H_
-#define _COROTATIONALLINEARFEM_H_
-
-/*
-  Corotational linear FEM deformable model.
-
-  This class implements the deformable model described in the following paper:
-
-  M. Mueller, M. Gross: Interactive Virtual Materials.
-  In Proc. of Graphics Interface 2004 (2004), pp. 239–246.
-
-  In [Mueller 2004], the tangent stiffness matrix is approximate (warp=1). 
-  This class can also compute the exact tangent stiffness matrix (warp=2).
-  The implementation is described in:
-  J. Barbic: Exact Corotational Linear FEM Stiffness Matrix, Technical Report, USC, 2012
-
-  It is also possible to turn warping off (warp=0). This gives fast linear FEM dynamics,
-  but large deformations are not well-represented.
-*/
-
-#include "tetMesh.h"
-#include "sparseMatrix.h"
-
-namespace vega
-{
-class CorotationalLinearFEM
-{
-public:
-
-  // initializes corotational linear FEM
-  // input: tetMesh
-  CorotationalLinearFEM(TetMesh * tetMesh);
-  virtual ~CorotationalLinearFEM();
-
-  void GetStiffnessMatrixTopology(SparseMatrix ** stiffnessMatrixTopology); // returns a zero matrix containing the locations of non-zero elements in the stiffness matrix
-
-  // computes the internal forces and (warped) stiffness matrix for the entire mesh
-  // vertex displacements (input) and internal forces (output) must be (pre-allocated) vectors of length 3 * numVertices
-  // the internal forces are returned with the sign corresponding to f_int(x) on the left side of the equation M * x'' + f_int(x) = f_ext
-  // i.e., the computed internal forces are *negatives* of the actual physical internal forces acting on the material
-  // warp:
-  //   0: no warping (linear FEM)
-  //   1: stiffness warping (corotational linear FEM with approximate stiffness matrix) [Mueller 2004]
-  //   2: corotational linear FEM with exact tangent stiffness matrix (see the technical report [Barbic 2012])
-  virtual void ComputeForceAndStiffnessMatrix(double * vertexDisplacements, double * internalForces, SparseMatrix * stiffnessMatrix, int warp=1);
-
-  // this routine is same as above, except that it only traverses elements from elementLo <= element <= elementHi - 1
-  void ComputeForceAndStiffnessMatrixOfSubmesh(double * vertexDisplacements, double * internalForces, SparseMatrix * stiffnessMatrix, int warp, int elementLo, int elementHi);
-
-  inline TetMesh * GetTetMesh() { return tetMesh; }
-
-protected:
-  int numVertices;
-  TetMesh * tetMesh;
-  double * undeformedPositions;
-  double ** MInverse;
-  double ** KElementUndeformed;
-
-  void WarpMatrix(double * K, double * R, double * RK, double * RKRT);
-  void inverse3x3(double * A, double * AInv); // inverse of a row-major 3x3 matrix
-  void inverse4x4(double * A, double * AInv); // inverse of a row-major 4x4 matrix
-
-  // acceleration indices
-  int ** rowIndices;
-  int ** columnIndices;
-  void ClearRowColumnIndices();
-  void BuildRowColumnIndices(SparseMatrix * sparseMatrix);
-
-  double * lambdaLame;
-  double * muLame;
-};
-}
-#endif
-
diff --git a/src/libcorotationalLinearFEM/corotationalLinearFEMMT.cpp b/src/libcorotationalLinearFEM/corotationalLinearFEMMT.cpp
deleted file mode 100644
index 53ea64f43eb0d1b8c09cbcb1bdd034dba84c11c0..0000000000000000000000000000000000000000
--- a/src/libcorotationalLinearFEM/corotationalLinearFEMMT.cpp
+++ /dev/null
@@ -1,188 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "corotational linear FEM" library , Copyright (C) 2013 USC            *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <pthread.h>
-#include <vector>
-#include <set>
-#include "macros.h"
-#include "corotationalLinearFEMMT.h"
-using namespace std;
-
-namespace vega
-{
-CorotationalLinearFEMMT::CorotationalLinearFEMMT(TetMesh * tetMesh, int numThreads_) : CorotationalLinearFEM(tetMesh), numThreads(numThreads_)
-{
-  Initialize();
-}
-
-CorotationalLinearFEMMT::~CorotationalLinearFEMMT()
-{
-  free(startElement);
-  free(endElement);
-  free(internalForceBuffer);
-  for(int i=0; i<numThreads; i++)
-    delete(stiffnessMatrixBuffer[i]);
-  free(stiffnessMatrixBuffer);
-}
-
-struct CorotationalLinearFEMMT_threadArg
-{
-  CorotationalLinearFEMMT * corotationalLinearFEMMT;
-  double * u;
-  double * f;
-  SparseMatrix * stiffnessMatrix;
-  int warp;
-  int rank;
-};
-
-void * CorotationalLinearFEMMT_WorkerThread(void * arg)
-{
-  struct CorotationalLinearFEMMT_threadArg * threadArgp = (struct CorotationalLinearFEMMT_threadArg*) arg;
-  CorotationalLinearFEMMT * corotationalLinearFEMMT = threadArgp->corotationalLinearFEMMT;
-  double * u = threadArgp->u;
-  double * f = threadArgp->f;
-  SparseMatrix * stiffnessMatrix = threadArgp->stiffnessMatrix;
-  int warp = threadArgp->warp;
-  int rank = threadArgp->rank;
-  int startElement = corotationalLinearFEMMT->GetStartElement(rank);
-  int endElement = corotationalLinearFEMMT->GetEndElement(rank);
-
-  //printf("%d %d\n", startElement, endElement);
-  corotationalLinearFEMMT->ComputeForceAndStiffnessMatrixOfSubmesh(u, f, stiffnessMatrix, warp, startElement, endElement);
-
-  return NULL;
-}
-
-void CorotationalLinearFEMMT::Initialize()
-{
-  internalForceBuffer = (double*) malloc (sizeof(double) * numThreads * 3 * tetMesh->getNumVertices());
-
-  // generate skeleton matrices
-  stiffnessMatrixBuffer = (SparseMatrix**) malloc (sizeof(SparseMatrix*) * numThreads);
-
-  SparseMatrix * sparseMatrix;
-  GetStiffnessMatrixTopology(&sparseMatrix);
-  for(int i=0; i<numThreads; i++)
-    stiffnessMatrixBuffer[i] = new SparseMatrix(*sparseMatrix);
-
-  // split the workload
-  int numElements = tetMesh->getNumElements();
-  startElement = (int*) malloc (sizeof(int) * numThreads);
-  endElement = (int*) malloc (sizeof(int) * numThreads);
-
-  int remainder = numElements % numThreads;
-  // the first 'remainder' nodes will process one edge more
-  int jobSize = numElements / numThreads;
-
-  for(int rank=0; rank < numThreads; rank++)
-  {
-    if (rank < remainder)
-    {
-      startElement[rank] = rank * (jobSize+1);
-      endElement[rank] = (rank+1) * (jobSize+1);
-    }
-    else
-    {
-      startElement[rank] = remainder * (jobSize+1) + (rank-remainder) * jobSize;
-      endElement[rank] = remainder * (jobSize+1) + ((rank-remainder)+1) * jobSize;
-    }
-  }
-
-  //printf("Total elements: %d \n", numElements);
-  //printf("Num threads: %d \n", numThreads);
-  //printf("Canonical job size: %d \n", jobSize);
-  //printf("Num threads with job size augmented by one edge: %d \n", remainder);
-}
-
-void CorotationalLinearFEMMT::ComputeForceAndStiffnessMatrix(double * u, double * f, SparseMatrix * stiffnessMatrix, int warp)
-{
-  // launch threads
-  struct CorotationalLinearFEMMT_threadArg * threadArgv = (struct CorotationalLinearFEMMT_threadArg*) malloc (sizeof(struct CorotationalLinearFEMMT_threadArg) * numThreads);
-
-  pthread_t * tid = (pthread_t*) malloc (sizeof(pthread_t) * numThreads);
-
-  int numVertices3 = 3 * tetMesh->getNumVertices();
-
-  for(int i=0; i<numThreads; i++)
-  {
-    threadArgv[i].corotationalLinearFEMMT = this;
-    threadArgv[i].u = u;
-    threadArgv[i].f = &internalForceBuffer[i * numVertices3];
-    threadArgv[i].stiffnessMatrix = stiffnessMatrixBuffer[i];
-    threadArgv[i].warp = warp;
-    threadArgv[i].rank = i;
-  }
-
-  for(int i=0; i<numThreads; i++)  
-  {
-    if (pthread_create(&tid[i], NULL, CorotationalLinearFEMMT_WorkerThread, &threadArgv[i]) != 0)
-    {
-      printf("Error: unable to launch thread %d.\n", i);
-      exit(1);
-    }
-  }
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_join(tid[i], NULL) != 0)
-    {
-      printf("Error: unable to join thread %d.\n", i);
-      exit(1);
-    }
-  }
-
-  free(threadArgv);
-  free(tid);
-
-  memset(f, 0, sizeof(double) * numVertices3);
-  stiffnessMatrix->ResetToZero();
-
-  for(int i=0; i<numThreads; i++)
-  {
-     double * source = &internalForceBuffer[i * numVertices3];
-     for(int j=0; j<numVertices3; j++)
-       f[j] += source[j];
-
-     *stiffnessMatrix += *(stiffnessMatrixBuffer[i]);
-  }
-}
-
-int CorotationalLinearFEMMT::GetStartElement(int rank)
-{
-  return startElement[rank];
-}
-
-int CorotationalLinearFEMMT::GetEndElement(int rank)
-{
-  return endElement[rank];
-}
-}
diff --git a/src/libelasticForceModel/CMakeLists.txt b/src/libelasticForceModel/CMakeLists.txt
deleted file mode 100644
index 4ac6d6148de81d9f32a7e6819d5e388957a4282c..0000000000000000000000000000000000000000
--- a/src/libelasticForceModel/CMakeLists.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-set(EFM_SRCS
-  corotationalLinearFEMForceModel.cpp
-  massSpringSystemForceModel.cpp
-  StVKForceModel.cpp
-  isotropicHyperelasticFEMForceModel.cpp
-  linearFEMForceModel.cpp
-)
-
-vega_add_library(elasticForceModel
-  SOURCES ${EFM_SRCS}
-  PUBLIC_HEADERS
-    StVKForceModel.h
-    corotationalLinearFEMForceModel.h
-    isotropicHyperelasticFEMForceModel.h
-    linearFEMForceModel.h
-    massSpringSystemForceModel.h
-)
-target_link_libraries(elasticForceModel
-  PUBLIC
-    forceModel
-    corotationalLinearFEM
-    massSpringSystem
-    stvk
-    isotropicHyperelasticFEM
-  INTERFACE
-    sparseMatrix
-    minivector
-    volumetricMesh
-)
diff --git a/src/libforceModel/CMakeLists.txt b/src/libforceModel/CMakeLists.txt
deleted file mode 100644
index 003498431afd9928aa7907cceb5c4b69940f3f18..0000000000000000000000000000000000000000
--- a/src/libforceModel/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-vega_add_library(forceModel
-  SOURCES
-    forceModel.cpp
-  PUBLIC_HEADERS
-    forceModel.h
-)
-target_link_libraries(forceModel
-  PUBLIC
-    sparseMatrix
-)
diff --git a/src/libforceModel/forceModel.cpp b/src/libforceModel/forceModel.cpp
deleted file mode 100644
index 5930d722a864ed9ae165a957d77837fa0b83eb81..0000000000000000000000000000000000000000
--- a/src/libforceModel/forceModel.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "forceModelBase" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "forceModel.h"
-
-namespace vega
-{
-ForceModel::~ForceModel()
-{
-}
-
-void ForceModel::GetForceAndMatrix(double * u, double * internalForces, SparseMatrix * tangentStiffnessMatrix)
-{
-  GetInternalForce(u, internalForces);
-  GetTangentStiffnessMatrix(u, tangentStiffnessMatrix);
-}
-
-void ForceModel::TestStiffnessMatrix(double * q, double * dq)
-{
-  double * q1 = (double*) malloc (sizeof(double) * r);
-  double * dqeps = (double*) malloc (sizeof(double) * r);
-  double * internalForce0 = (double*) malloc (sizeof(double) * r);
-  double * internalForce1 = (double*) malloc (sizeof(double) * r);
-  double * testV = (double*) malloc (sizeof(double) * r);
-
-  SparseMatrix * K0;
-  SparseMatrix * K1;
-  GetTangentStiffnessMatrixTopology(&K0);
-  GetTangentStiffnessMatrixTopology(&K1);
-  printf("r: %d K: %d %d\n", r, K0->GetNumRows(), K0->GetNumColumns());
-
-  GetForceAndMatrix(q, internalForce0, K0);
-  double KNorm0 = K0->GetMaxAbsEntry();
-
-  double fNorm0 = 0.0;
-  for(int j=0; j<r; j++)
-    if (fabs(internalForce0[j]) > fNorm0)
-      fNorm0 = fabs(internalForce0[j]);
-
-  double eps = 1.0;
-  do
-  {
-    for(int j=0; j<r; j++)
-    {
-      dqeps[j] = eps * dq[j];
-      q1[j] = q[j] + dqeps[j];
-    }
-
-    GetForceAndMatrix(q1, internalForce1, K1);
-
-    // internalForce1 - internalForce0 - K0 * dqeps should be O(eps^2)
-    K0->MultiplyVector(dqeps, testV);
-
-    for(int j=0; j<r; j++)
-      testV[j] = internalForce1[j] - internalForce0[j] - testV[j];
-
-    // compute largest abs value entry in testV
-    double maxEntry = 0.0;
-    for(int j=0; j<r; j++)
-      if (fabs(testV[j]) > maxEntry)
-        maxEntry = fabs(testV[j]);
-
-    double dfNorm = 0.0;
-    for(int j=0; j<r; j++)
-      if (fabs(internalForce1[j] - internalForce0[j]) > dfNorm)
-        dfNorm = fabs(internalForce1[j] - internalForce0[j]);
- 
-    printf("eps=%G: maxEntry=%G maxEntry/eps^2=%G dfNorm=%G f0Norm=%G K0Norm=%G\n", eps, maxEntry, maxEntry / eps / eps, dfNorm, fNorm0, KNorm0);
-    eps *= 0.1;
-  }
-  while (eps > 1e-15); 
-
-  delete(K0);
-
-  free(testV);
-  free(internalForce1);
-  free(internalForce0);
-  free(dqeps);
-  free(q1);
-}
-}
diff --git a/src/libglslPhong/CMakeLists.txt b/src/libglslPhong/CMakeLists.txt
deleted file mode 100644
index fc9f0424bf3c772fd223b73a74a235d4d57721c6..0000000000000000000000000000000000000000
--- a/src/libglslPhong/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-vega_add_library(glslPhong
-  SOURCES
-    glslPhong.cpp
-  PUBLIC_HEADERS
-    glslPhong.h
-)
-target_link_libraries(glslPhong
-  PUBLIC
-    ${OPENGL_LIBRARIES}
-    Threads::Threads
-)
diff --git a/src/libgraph/CMakeLists.txt b/src/libgraph/CMakeLists.txt
deleted file mode 100644
index 35262ad793193acb96283a4cbdbdc18d3fe309a1..0000000000000000000000000000000000000000
--- a/src/libgraph/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-vega_add_library(graph
-  SOURCES
-    graph.cpp
-  PUBLIC_HEADERS
-    graph.h
-)
-target_link_libraries(graph
-  PUBLIC
-    sparseMatrix
-    matrixIO
-)
diff --git a/src/libgraph/graph.cpp b/src/libgraph/graph.cpp
deleted file mode 100644
index daa5f6e5d95ed3331421c9ee04dbf791e9c9e43b..0000000000000000000000000000000000000000
--- a/src/libgraph/graph.cpp
+++ /dev/null
@@ -1,420 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "graph" library , Copyright (C) 2013 USC                              *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <limits.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-using namespace std;
-#include "matrixIO.h"
-#include "graph.h"
-
-namespace vega
-{
-Graph::Graph() 
-{ 
-  numVertices = 0; 
-  numEdges = 0; 
-}
-
-Graph::Graph(const Graph & graph) 
-{
-  (*this) = graph;
-}
-
-Graph & Graph::operator=(const Graph & graph) 
-{
-  numVertices = graph.numVertices;
-  numEdges = graph.numEdges;
-  edges = graph.edges;
-  vertexNeighbors = graph.vertexNeighbors;
-  vertexNeighborsVector = graph.vertexNeighborsVector;
-  return *this;
-}
-
-Graph::~Graph()
-{
-}
-
-Graph::Graph(int numVertices_, int numEdges_, int * edges_, int sortEdgeVertices): numVertices(numVertices_), numEdges(numEdges_)
-{
-  //printf("num vertices: %d\n", numVertices);
-  for(int i=0; i<numEdges; i++)
-  {
-    //printf("Edge: %d %d\n", edges_[2*i+0], edges_[2*i+1]);
-    if (sortEdgeVertices)
-    {
-      // keep the two indices in each edge sorted
-      if (edges_[2*i+0] < edges_[2*i+1])
-        edges.insert(make_pair(edges_[2*i+0], edges_[2*i+1])); 
-      else
-        edges.insert(make_pair(edges_[2*i+1], edges_[2*i+0])); 
-    }
-    else
-      edges.insert(make_pair(edges_[2*i+0], edges_[2*i+1])); 
-  }
-
-  BuildVertexNeighbors();
-}
-
-Graph::Graph(const char * filename, int sortEdgeVertices)
-{
-  FILE * fin;
-  OpenFile_(filename, &fin, "r");
-  int code = fscanf(fin, "%d %d\n", &numVertices, &numEdges);
-  if (code != 2)
-    throw 1;
-
-  //printf("num vertices: %d\n", numVertices);
-  for(int i=0; i<numEdges; i++)
-  {
-    int vtxA, vtxB;
-    int code = fscanf(fin, "%d %d\n", &vtxA, &vtxB);
-    if (code != 2)
-      throw 2;
-
-    //printf("Edge: %d %d\n", vtxA, vtxB);
-    // keep the two indices in each edge sorted
-    if (sortEdgeVertices)
-    {
-      if (vtxA < vtxB)
-        edges.insert(make_pair(vtxA, vtxB));
-      else
-        edges.insert(make_pair(vtxB, vtxA));
-    }
-    else
-      edges.insert(make_pair(vtxA, vtxB));
-  }
-
-  fclose(fin);
-
-  BuildVertexNeighbors();
-}
-
-void Graph::Save(const char * filename)
-{
-  FILE * fout;
-  OpenFile_(filename, &fout, "w");
-  fprintf(fout, "%d %d\n", numVertices, numEdges);
-
-  for(set<pair<int, int> > :: iterator iter = edges.begin(); iter != edges.end(); iter++)
-  {
-    int vtxA = iter->first;
-    int vtxB = iter->second;
-    fprintf(fout, "%d %d\n", vtxA, vtxB);
-  }
-
-  fclose(fout);
-}
-
-void Graph::BuildVertexNeighbors()
-{
-  vertexNeighbors.clear();
-
-  //printf("Building vertex neighbors.\n");
-  for(int i=0; i<numVertices; i++)
-    vertexNeighbors.push_back(map<int, int>());
-
-  for(set<pair<int,int> > :: iterator iter = edges.begin(); iter != edges.end(); iter++)
-  {
-    vertexNeighbors[iter->first].insert(make_pair(iter->second,0)); 
-    vertexNeighbors[iter->second].insert(make_pair(iter->first,0)); 
-  }
-
-  // number the neighbors
-  for(int i=0; i<numVertices; i++)
-  {
-    int count = 0;
-    for(map<int,int> :: iterator iter = vertexNeighbors[i].begin(); iter != vertexNeighbors[i].end(); iter++)
-    {
-      iter->second = count;
-      count++;
-    }
-  }
-
-  BuildVertexNeighborsVector();
-}
-
-void Graph::BuildVertexNeighborsVector()
-{
-  vertexNeighborsVector.clear();
-
-  // create a copy of the data (in a vector), so that can access ith element fast
-  for(int i=0; i<numVertices; i++)
-  {
-    vertexNeighborsVector.push_back(vector<int>());
-    for(map<int,int> :: iterator iter = vertexNeighbors[i].begin(); iter != vertexNeighbors[i].end(); iter++)
-      vertexNeighborsVector[i].push_back(iter->first);
-  }
-}
-
-int Graph::GetMaxDegree()
-{
-  int maxDegree = 0;
-  for(int vtx=0; vtx<numVertices; vtx++)
-    if ((int)vertexNeighbors[vtx].size() > maxDegree)
-      maxDegree = vertexNeighbors[vtx].size();
-  return maxDegree;
-}
-
-int Graph::GetMinDegree()
-{
-  int minDegree = INT_MAX;
-  for(int vtx=0; vtx<numVertices; vtx++)
-    if ((int)vertexNeighbors[vtx].size() < minDegree)
-      minDegree = vertexNeighbors[vtx].size();
-  return minDegree;
-}
-
-double Graph::GetAvgDegree()
-{
-  double avgDegree = 0;
-  for(int vtx=0; vtx<numVertices; vtx++)
-    avgDegree += vertexNeighbors[vtx].size();
-  return avgDegree / numVertices;
-}
-
-double Graph::GetStdevDegree()
-{
-  double avgDegree_ = GetAvgDegree();
-  double std = 0;
-  for(int vtx=0; vtx<numVertices; vtx++)
-    std += (vertexNeighbors[vtx].size() - avgDegree_) * (vertexNeighbors[vtx].size() - avgDegree_);
-  return sqrt(std / numVertices);
-}
-
-int Graph::IsNeighbor(int vtx1, int vtx2)
-{
-  map<int,int> :: iterator iter = vertexNeighbors[vtx1].find(vtx2);
-  if (iter == vertexNeighbors[vtx1].end())
-    return 0;
-  else
-    return iter->second + 1;
-}
-
-void Graph::ExpandNeighbors()
-{
-  // over all edges:
-  // insert neigbors of every vtx into the edges
-
-  set<pair<int, int> > expandedEdges = edges;
-
-  for(set<pair<int, int> > :: iterator iter = edges.begin(); iter != edges.end(); iter++)
-  {
-    int vtxA = iter->first; 
-    int vtxB = iter->second; 
-
-    // connect all neighbors of A to B
-    for(map<int,int> :: iterator mapIter = vertexNeighbors[vtxA].begin(); mapIter != vertexNeighbors[vtxA].end(); mapIter++)
-    {
-      if (vtxB < mapIter->first)
-        expandedEdges.insert(make_pair(vtxB, mapIter->first)); 
-    }
-
-    // connect all neigbhors of B to A
-    for(map<int,int> :: iterator mapIter = vertexNeighbors[vtxB].begin(); mapIter != vertexNeighbors[vtxB].end(); mapIter++)
-      if (vtxA < mapIter->first)
-        expandedEdges.insert(make_pair(vtxA, mapIter->first)); 
-  }
- 
-  edges = expandedEdges;
-  numEdges = edges.size();
-  BuildVertexNeighbors();
-}
-
-void Graph::PrintInfo()
-{
-  printf("Graph vertices: %d\n", numVertices);
-  printf("Graph edges: %d\n", numEdges);
-  printf("Graph min degree: %d\n", GetMinDegree());
-  printf("Graph max degree: %d\n", GetMaxDegree());
-  printf("Graph avg degree: %G\n", GetAvgDegree());
-  printf("Graph degree stdev: %G\n", GetStdevDegree());
-}
-
-void Graph::GetLaplacian(SparseMatrix ** L, int scaleRows)
-{
-  SparseMatrixOutline outline(3*numVertices);
-  for(int i=0; i<numVertices; i++)
-  {
-    int numNeighbors = (int)vertexNeighborsVector[i].size();
-    if (numNeighbors == 0)
-      continue;
-
-    for(int k=0; k<3; k++)
-      outline.AddEntry(3 * i + k, 3 * i + k, (scaleRows != 0) ? 1.0 : numNeighbors);
-
-    double weight;
-    if (scaleRows != 0)
-      weight = -1.0 / numNeighbors;
-    else
-      weight = -1.0;
-
-    for(int j=0; j<numNeighbors; j++)
-      for(int k=0; k<3; k++)
-        outline.AddEntry(3 * i + k, 3 * vertexNeighborsVector[i][j] + k, weight);
-  }
-
-  *L = new SparseMatrix(&outline);
-}
-
-Graph * Graph::CartesianProduct(Graph & graph2)
-{
-  int numProductVertices = numVertices * graph2.numVertices;
-  int numProductEdges = numEdges * graph2.numVertices + numVertices * graph2.numEdges;
-  int * productEdges = (int*) malloc (sizeof(int) * 2 * numProductEdges);
- 
-  printf("Num space-time graph vertices: %d\n", numProductVertices);
-  printf("Num space-time graph edges: %d\n", numProductEdges);
-
-  int edge = 0;
-  for(int j=0; j<graph2.numVertices; j++)
-  {
-    for(int i=0; i<numVertices; i++)
-    {
-      // connect every vertex of graph1 to its neighbors
-      //std::vector< std::vector<int> > vertexNeighborsVector;
-      for(int k=0; k<(int)vertexNeighborsVector[i].size(); k++)
-      {  
-        if (i > vertexNeighborsVector[i][k])
-        {
-          productEdges[2*edge+0] = GetCartesianProductVertexIndex(i, j);
-          productEdges[2*edge+1] = GetCartesianProductVertexIndex(vertexNeighborsVector[i][k], j);
-          edge++;
-        }
-      }
-      // connect every vertex of graph2 to its neighbors
-      for(int k=0; k<(int)(graph2.vertexNeighborsVector[j].size()); k++)
-      {  
-        if (j > graph2.vertexNeighborsVector[j][k])
-        {
-          productEdges[2*edge+0] = GetCartesianProductVertexIndex(i, j);
-          productEdges[2*edge+1] = GetCartesianProductVertexIndex(i, graph2.vertexNeighborsVector[j][k]);
-          edge++;
-        }
-      }
-    }
-  }
-
-  Graph * graph = new Graph(numProductVertices, numProductEdges, productEdges);
-  free(productEdges);
-  return graph;
-}
-
-// cluster given vertices into connected components
-void Graph::Cluster(std::set<int> & vertices, vector<set<int> > & clusters)
-{
-  clusters.clear();
-  for(set<int> :: iterator iter = vertices.begin(); iter != vertices.end(); iter++)
-  {
-    int vtx = *iter;
-    int found = -1;
-    for(int i=0; i<(int)clusters.size(); i++)
-    {
-      if (clusters[i].find(vtx) != clusters[i].end())
-      {
-        found = i;
-        break;
-      }
-      for(set<int> :: iterator iter2 = clusters[i].begin(); iter2 != clusters[i].end(); iter2++)
-      {
-        if (IsNeighbor(vtx, *iter2))
-        {
-          found = i;
-          break;
-        }
-      }
-    }
-
-    if (found == -1)
-    {
-     set<int> newCluster;
-     newCluster.insert(vtx);
-     clusters.push_back(newCluster);
-    }
-    else
-    {
-      clusters[found].insert(vtx);
-    }
-  }
-}
-
-int Graph::GetCartesianProductVertexIndex(int vertex1, int vertex2)
-{
-  return vertex2 * numVertices + vertex1;
-}
-
-void Graph::GetCartesianProductVertexIndexComponents(int productVertex, int * vertex1, int * vertex2)
-{
-  *vertex2 = productVertex / numVertices;
-  *vertex1 = productVertex % numVertices;
-}
-
-void Graph::ShortestPath(std::set<int> & seedVertices, std::vector<int> & distances)
-{
-  distances.clear();
-  distances.reserve(numVertices);
-  
-  for(set<int> :: iterator iter = seedVertices.begin(); iter != seedVertices.end(); iter++)
-    distances[*iter] = 0;
-
-  set<int> visitedVertices = seedVertices;
-
-  int distance = 0;
-  set<int> oldFront, front;
-  oldFront = seedVertices;
-  while ((int)visitedVertices.size() != numVertices)
-  {
-    distance++;
-
-    // create the front
-    front.clear();
-    for(set<int> :: iterator iter = oldFront.begin(); iter != oldFront.end(); iter++)
-    {
-      int node = *iter;
-      for(int i=0; i < (int)vertexNeighborsVector[node].size(); i++)
-      {
-        int neighbor = vertexNeighborsVector[node][i];
-        if (visitedVertices.find(neighbor) == visitedVertices.end())
-          front.insert(neighbor);
-      }
-    }
-
-    // write distance to front
-    for(set<int> :: iterator iter = front.begin(); iter != front.end(); iter++)
-    {
-      int node = *iter;
-      visitedVertices.insert(node);
-      distances[node] = distance;
-    }
-
-    oldFront = front; 
-  }
-}
-
-}
diff --git a/src/libgraph/graph.h b/src/libgraph/graph.h
deleted file mode 100755
index 75f93705fbbfe5510214a3f35b42ef5eadcc7615..0000000000000000000000000000000000000000
--- a/src/libgraph/graph.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "graph" library , Copyright (C) 2013 USC                              *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef _GRAPH_H_
-#define _GRAPH_H_
-
-#include <vector>
-#include <set>
-#include <map>
-#include "sparseMatrix.h"
-
-namespace vega
-{
-/*
-  A class to store a graph (nodes connected with edges).
-*/
-
-class Graph
-{
-public:
-
-  Graph(); 
-  Graph(const char * filename, int sortEdgeVertices=1); // load graph from file
-  // each edge is given by two integers; length of "edges" should be 2xnumEdges
-  Graph(int numVertices, int numEdges, int * edges, int sortEdgeVertices=1);
-  Graph(const Graph & graph);
-  Graph & operator=(const Graph & graph);
-  ~Graph();
-
-  void Save(const char * filename); // save graph to file
-
-  int GetNumVertices();
-  int GetNumEdges();
-
-  int GetNumNeighbors(int vertex);
-  int GetNeighbor(int vertex, int i);
-  int IsNeighbor(int vtx1, int vtx2);
-
-  int GetMinDegree();
-  int GetMaxDegree();
-  double GetAvgDegree();
-  double GetStdevDegree();
-
-  void ExpandNeighbors(); // connects every node to all the neighbors of every neighbor
-  void PrintInfo();
-
-  // if scaleRows == 1, each row will be scaled to sum to one
-  void GetLaplacian(SparseMatrix ** L, int scaleRows=0);
-
-  // returns the Cartesian graph product of "this" and graph2
-  Graph * CartesianProduct(Graph & graph2);
-  // return the index of vertex (vertex1, vertex2) in the cartesian product of "this" with another graph (which is not needed explicitly)
-  int GetCartesianProductVertexIndex(int vertex1, int vertex2);
-  // converts in the opposite direction
-  void GetCartesianProductVertexIndexComponents(int productVertex, int * vertex1, int * vertex2);
-
-  // clusters given vertices into connected components
-  void Cluster(std::set<int> & vertices, std::vector<std::set<int> > & clusters);
-
-  // computes the shortest distance from the given seed vertices (distance of zero) to all the graph vertices
-  // input: seed vertices
-  // output: distance to the set of seed vertices, for each mesh vertex
-  void ShortestPath(std::set<int> & seedVertices, std::vector<int> & distances);
-
-protected:
-  int numVertices, numEdges; // num vertices, num edges
-  std::set< std::pair<int, int> > edges;
-  std::vector< std::map<int, int> > vertexNeighbors; // (index of neighbor, index)
-  std::vector< std::vector<int> > vertexNeighborsVector;
-
-  void BuildVertexNeighbors();
-  void BuildVertexNeighborsVector();
-};
-
-inline int Graph::GetNumVertices()
-{
-  return numVertices;
-}
-
-inline int Graph::GetNumEdges()
-{
-  return numEdges;
-}
-
-inline int Graph::GetNumNeighbors(int vertex)
-{
-  return (int) vertexNeighborsVector[vertex].size();
-}
-
-inline int Graph::GetNeighbor(int vertex, int i)
-{
-  return vertexNeighborsVector[vertex][i];
-}
-}
-#endif
-
diff --git a/src/libhashTable/CMakeLists.txt b/src/libhashTable/CMakeLists.txt
deleted file mode 100644
index 27ff2bbd99923295e409439640a768e0757d65fa..0000000000000000000000000000000000000000
--- a/src/libhashTable/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-vega_add_library(hashTable
-  SOURCES
-    hashTable.cpp
-  PUBLIC_HEADERS
-    hashTable.h
-)
diff --git a/src/libimageIO/CMakeLists.txt b/src/libimageIO/CMakeLists.txt
deleted file mode 100644
index 0ce6866479097f38c55cbdaf8d57740a7551f8b6..0000000000000000000000000000000000000000
--- a/src/libimageIO/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-vega_add_library(imageIO
-  SOURCES
-    imageIO.cpp
-  PUBLIC_HEADERS
-    imageFormats.h
-    imageIO.h
-)
diff --git a/src/libinsertRows/CMakeLists.txt b/src/libinsertRows/CMakeLists.txt
deleted file mode 100644
index c88ecf5eddbd4db4b23a50f5b892cb759f3d449b..0000000000000000000000000000000000000000
--- a/src/libinsertRows/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-vega_add_library(insertRows
-  SOURCES
-    insertRows.cpp
-  PUBLIC_HEADERS
-    insertRows.h
-)
diff --git a/src/libinsertRows/insertRows.h b/src/libinsertRows/insertRows.h
deleted file mode 100755
index 210c3d8c7af0b7edaec5a9833a22949fe1332c12..0000000000000000000000000000000000000000
--- a/src/libinsertRows/insertRows.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "insertRows" library , Copyright (C) 2007 CMU, 2009 MIT               *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef _INSERT_ROWS_H_
-#define _INSERT_ROWS_H_
-
-namespace vega
-{
-/*
-  Insert or remove given components from a linear array.
-  The dynamic solver uses these routines to fix the specified vertices and remove
-  rows from mass and stiffness matrices, as necessary.
-*/
-
-// Note: these routines use the terminology "Rows" because they were mainly used to condense mass and stiffness matrices. They, however, operate on 1D arrays, not directly on 2D matrices
-
-// inserts zero entries into an array, at the specified locations
-// the locations must be given with respect to the full array 
-// input: xConstrained
-// output: x
-void InsertRows(int mFull, double * xConstrained, double * x, int numFixedRows, int * fixedRows, int oneIndexed=0); 
-
-// removes entries at the specified locations from an array
-// the locations must be given with respect to the full array 
-// input: x
-// output: xConstrained
-void RemoveRows(int mFull, double * xConstrained, double * x, int numFixedRows, int * fixedRows, int oneIndexed=0); 
-
-// translates the array indices from original indices to indices after removal of the specified entries
-// input: DOFs (must be sorted) (0-indexed)
-// output: DOFsConstrained (0-indexed)
-// oneIndexed applies only to fixedRows array, NOT to DOFsConstrained or DOFs
-void FullDOFsToConstrainedDOFs(int mFull, int numDOFs, int * DOFsConstrained, int * DOFs, int numFixedRows, int * fixedRows, int oneIndexed=0); 
-}
-#endif
-
diff --git a/src/libintegrator/CMakeLists.txt b/src/libintegrator/CMakeLists.txt
deleted file mode 100644
index 2c8de2909d699b4d3fd1e784ffe5037836ac6045..0000000000000000000000000000000000000000
--- a/src/libintegrator/CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-vega_add_library(integrator
-  SOURCES
-    integratorBase.cpp
-    getIntegratorSolver.cpp
-  PUBLIC_HEADERS
-    getIntegratorSolver.h
-    integratorBase.h
-    integratorSolverSelection.h
-)
-target_include_directories(integrator
-  PUBLIC
-    ${PARDISO_INCLUDE_DIRS}
-)
-target_link_libraries(integrator
-  PUBLIC
-    performanceCounter
-    insertRows
-    sparseSolver
-    forceModel
-  INTERFACE
-    sparseMatrix
-    insertRows
-)
diff --git a/src/libintegratorDense/CMakeLists.txt b/src/libintegratorDense/CMakeLists.txt
deleted file mode 100644
index 5b0ed8eba4e9ea1e2fd0c0a774d2c39e04363b5d..0000000000000000000000000000000000000000
--- a/src/libintegratorDense/CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-vega_add_library(integratorDense
-  SOURCES
-    centralDifferencesDense.cpp
-    implicitBackwardEulerDense.cpp
-    implicitNewmarkDense.cpp
-    integratorBaseDense.cpp
-  PUBLIC_HEADERS
-    IPIVC.h
-    centralDifferencesDense.h
-    implicitBackwardEulerDense.h
-    implicitNewmarkDense.h
-    integratorBaseDense.h
-)
-target_link_libraries(integratorDense
-  PUBLIC
-    matrix
-    integrator
-    performanceCounter
-    reducedForceModel
-    ${BLAS}
-    ${CBLAS_LIBRARY}
-    ${LAPACK_LIBRARIES}
-)
diff --git a/src/libintegratorSparse/CMakeLists.txt b/src/libintegratorSparse/CMakeLists.txt
deleted file mode 100644
index 3b22ed257c0f806a7cc5e13b558fa1ff3765b8b3..0000000000000000000000000000000000000000
--- a/src/libintegratorSparse/CMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-vega_add_library(integratorSparse
-  SOURCES
-    centralDifferencesSparse.cpp
-    eulerSparse.cpp
-    implicitBackwardEulerSparse.cpp
-    implicitNewmarkSparse.cpp
-    integratorBaseSparse.cpp
-  PUBLIC_HEADERS
-    centralDifferencesSparse.h
-    eulerSparse.h
-    implicitBackwardEulerSparse.h
-    implicitNewmarkSparse.h
-    integratorBaseSparse.h
-)
-target_link_libraries(integratorSparse
-  PUBLIC
-    integrator
-    performanceCounter
-    insertRows
-    sparseSolver
-    forceModel
-    matrixIO
-  INTERFACE
-    sparseMatrix
-)
diff --git a/src/libisotropicHyperelasticFEM/CMakeLists.txt b/src/libisotropicHyperelasticFEM/CMakeLists.txt
deleted file mode 100644
index 4b0571a7c5862492fab21c01cb1653f983aee999..0000000000000000000000000000000000000000
--- a/src/libisotropicHyperelasticFEM/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-set(isotropichyperelasticfem_srcs     
-    isotropicMaterial.cpp
-    MooneyRivlinIsotropicMaterial.cpp
-    neoHookeanIsotropicMaterial.cpp
-    StVKIsotropicMaterial.cpp
-    homogeneousMooneyRivlinIsotropicMaterial.cpp
-    homogeneousStVKIsotropicMaterial.cpp
-    homogeneousNeoHookeanIsotropicMaterial.cpp
-    isotropicHyperelasticFEM.cpp
-    isotropicMaterialWithCompressionResistance.cpp
-    )
-    
-set(isotropichyperelasticfem_hdrs 
-    MooneyRivlinIsotropicMaterial.h
-    StVKIsotropicMaterial.h
-    homogeneousMooneyRivlinIsotropicMaterial.h
-    homogeneousNeoHookeanIsotropicMaterial.h
-    homogeneousStVKIsotropicMaterial.h
-    isotropicHyperelasticFEM.h
-    isotropicMaterial.h
-    isotropicMaterialWithCompressionResistance.h
-    neoHookeanIsotropicMaterial.h
-    )
-
-    
-if(VegaFEM_ENABLE_PTHREADS_SUPPORT)
-  list(APPEND isotropichyperelasticfem_hdrs isotropicHyperelasticFEMMT.h)
-  list(APPEND isotropichyperelasticfem_srcs isotropicHyperelasticFEMMT.cpp)
-endif()
-
-vega_add_library(isotropicHyperelasticFEM
-  SOURCES
-    ${isotropichyperelasticfem_srcs}
-  PUBLIC_HEADERS
-    ${isotropichyperelasticfem_hdrs}
-)
-target_link_libraries(isotropicHyperelasticFEM
-  PUBLIC
-    minivector
-    volumetricMesh
-    sparseMatrix
-    Threads::Threads
-)
diff --git a/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEMMT.cpp b/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEMMT.cpp
deleted file mode 100644
index ea83be846f295d6f7a667bdb8d071fb46fe94637..0000000000000000000000000000000000000000
--- a/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEMMT.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <pthread.h>
-#include "isotropicHyperelasticFEMMT.h"
-
-namespace vega
-{
-IsotropicHyperelasticFEMMT::IsotropicHyperelasticFEMMT(TetMesh * tetMesh_, IsotropicMaterial * isotropicMaterial_, double principalStretchThreshold_, bool addGravity_, double g_, int numThreads_) :
-  IsotropicHyperelasticFEM(tetMesh_, isotropicMaterial_, principalStretchThreshold_, addGravity_, g_),
-  numThreads(numThreads_)
-{
-  Initialize();
-}
-
-IsotropicHyperelasticFEMMT::~IsotropicHyperelasticFEMMT()
-{
-  free(startElement);
-  free(endElement);
-  free(energyBuffer);
-  free(internalForceBuffer);
-  for(int i=0; i<numThreads; i++)
-    delete(tangentStiffnessMatrixBuffer[i]);
-  free(tangentStiffnessMatrixBuffer);
-}
-
-struct IsotropicHyperelasticFEMMT_threadArg
-{
-  IsotropicHyperelasticFEMMT * isotropicHyperelasticFEMMT;
-  double * u;
-  double * energy;
-  double * internalForces;
-  SparseMatrix *  tangentStiffnessMatrix;
-  int computationMode;
-  int rank;
-  int exitCode;
-};
-
-void * IsotropicHyperelasticFEMMT_WorkerThread(void * arg)
-{
-  struct IsotropicHyperelasticFEMMT_threadArg * threadArgp = (struct IsotropicHyperelasticFEMMT_threadArg*) arg;
-  IsotropicHyperelasticFEMMT * isotropicHyperelasticFEMMT = threadArgp->isotropicHyperelasticFEMMT;
-  double * u = threadArgp->u;
-  double * energy = threadArgp->energy;
-  double * internalForces = threadArgp->internalForces;
-  SparseMatrix * tangentStiffnessMatrix = threadArgp->tangentStiffnessMatrix;
-  int computationMode = threadArgp->computationMode;
-  int rank = threadArgp->rank;
-  int startElement = isotropicHyperelasticFEMMT->GetStartElement(rank);
-  int endElement = isotropicHyperelasticFEMMT->GetEndElement(rank);
-
-  //printf("%d %d\n", startElement, endElement);
-  int code = isotropicHyperelasticFEMMT->GetEnergyAndForceAndTangentStiffnessMatrixHelperWorkhorse(startElement, endElement, u, energy, internalForces, tangentStiffnessMatrix, computationMode); 
-  threadArgp->exitCode = code;
-
-  return NULL;
-}
-
-void IsotropicHyperelasticFEMMT::Initialize()
-{
-  energyBuffer = (double*) malloc (sizeof(double) * numThreads);
-  internalForceBuffer = (double*) malloc (sizeof(double) * numThreads * 3 * tetMesh->getNumVertices());
-
-  // generate skeleton matrices
-  tangentStiffnessMatrixBuffer = (SparseMatrix**) malloc (sizeof(SparseMatrix*) * numThreads);
-
-  SparseMatrix * sparseMatrix;
-  GetStiffnessMatrixTopology(&sparseMatrix);
-  for(int i=0; i<numThreads; i++)
-    tangentStiffnessMatrixBuffer[i] = new SparseMatrix(*sparseMatrix);
-
-  // split the workload
-  int numElements = tetMesh->getNumElements();
-  startElement = (int*) malloc (sizeof(int) * numThreads);
-  endElement = (int*) malloc (sizeof(int) * numThreads);
-
-  int remainder = numElements % numThreads;
-  // the first 'remainder' nodes will process one edge more
-  int jobSize = numElements / numThreads;
-
-  for(int rank=0; rank < numThreads; rank++)
-  {
-    if (rank < remainder)
-    {
-      startElement[rank] = rank * (jobSize+1);
-      endElement[rank] = (rank+1) * (jobSize+1);
-    }
-    else
-    {
-      startElement[rank] = remainder * (jobSize+1) + (rank-remainder) * jobSize;
-      endElement[rank] = remainder * (jobSize+1) + ((rank-remainder)+1) * jobSize;
-    }
-  }
-
-  printf("Total elements: %d \n", numElements);
-  printf("Num threads: %d \n", numThreads);
-  printf("Canonical job size: %d \n", jobSize);
-  printf("Num threads with job size augmented by one edge: %d \n", remainder);
-}
-
-int IsotropicHyperelasticFEMMT::GetEnergyAndForceAndTangentStiffnessMatrixHelper(double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix, int computationMode)
-{
-  GetEnergyAndForceAndTangentStiffnessMatrixHelperPrologue(u, energy, internalForces, tangentStiffnessMatrix, computationMode);
-
-  // launch threads
-  struct IsotropicHyperelasticFEMMT_threadArg * threadArgv = (struct IsotropicHyperelasticFEMMT_threadArg*) malloc (sizeof(struct IsotropicHyperelasticFEMMT_threadArg) * numThreads);
-
-  pthread_t * tid = (pthread_t*) malloc (sizeof(pthread_t) * numThreads);
-
-  int numVertices3 = 3 * tetMesh->getNumVertices();
-  for(int i=0; i<numThreads; i++)
-  {
-    threadArgv[i].isotropicHyperelasticFEMMT = this;
-    threadArgv[i].u = u;
-    threadArgv[i].energy = &energyBuffer[i];
-    threadArgv[i].internalForces = &internalForceBuffer[i * numVertices3];
-    threadArgv[i].tangentStiffnessMatrix = tangentStiffnessMatrixBuffer[i];
-    threadArgv[i].computationMode = computationMode;
-    threadArgv[i].rank = i;
-  }
-
-  // clear internal buffers
-  memset(energyBuffer, 0, sizeof(double) * numThreads);
-  memset(internalForceBuffer, 0, sizeof(double) * numThreads * numVertices3);
-  for(int i=0; i<numThreads; i++)  
-    tangentStiffnessMatrixBuffer[i]->ResetToZero();
-
-  for(int i=0; i<numThreads; i++)  
-  {
-    if (pthread_create(&tid[i], NULL, IsotropicHyperelasticFEMMT_WorkerThread, &threadArgv[i]) != 0)
-    {
-      printf("Error: unable to launch thread %d.\n", i);
-      return 1;
-    }
-  }
-
-  int code = 0;
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_join(tid[i], NULL) != 0)
-    {
-      printf("Error: unable to join thread %d.\n", i);
-      exit(1);
-    }
-    if (threadArgv[i].exitCode != 0)
-      code = 1;
-  }
-
-  free(threadArgv);
-  free(tid);
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (computationMode & COMPUTE_ENERGY)
-      *energy += energyBuffer[i];
-
-    if (computationMode & COMPUTE_INTERNALFORCES)
-    {
-      double * source = &internalForceBuffer[i * numVertices3];
-      for(int j=0; j<numVertices3; j++)
-        internalForces[j] += source[j];
-    }
-
-    if (computationMode & COMPUTE_TANGENTSTIFFNESSMATRIX)
-    {
-      *tangentStiffnessMatrix += *(tangentStiffnessMatrixBuffer[i]);
-    }
-  }
-
-  return code;
-}
-
-int IsotropicHyperelasticFEMMT::GetStartElement(int rank)
-{
-  return startElement[rank];
-}
-
-int IsotropicHyperelasticFEMMT::GetEndElement(int rank)
-{
-  return endElement[rank];
-}
-
-}
diff --git a/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEMMT.h b/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEMMT.h
deleted file mode 100644
index 41dcc61a66e94465b7d08a0915acae5db5529282..0000000000000000000000000000000000000000
--- a/src/libisotropicHyperelasticFEM/isotropicHyperelasticFEMMT.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "isotropic hyperelastic FEM" library , Copyright (C) 2013 USC         *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Jernej Barbic, Fun Shing Sin                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef _ISOTROPICHYPERELASTICFEMMT_H_
-#define _ISOTROPICHYPERELASTICFEMMT_H_
-
-#include "isotropicHyperelasticFEM.h"
-
-namespace vega
-{
-/*
-  This class is a multi-threaded version of the class "IsotropicHyperelasticFEM".
-  It uses POSIX threads ("pthreads") as the threading API.
-  Each thread assembles the internal force with respect to a subset of all the mesh elements. 
-  At the end, the individual results are added into a global internal force vector.
-
-  See also "isotropicHyperelasticFEM.h".
-*/
-
-class IsotropicHyperelasticFEMMT : public IsotropicHyperelasticFEM
-{
-public:
-  // see "isotropicHyperelasticFEM.h" for usage
-  // numThreads is the number of threads to use for the computation
-  IsotropicHyperelasticFEMMT(TetMesh * tetMesh, IsotropicMaterial * isotropicMaterial, double principalStretchThreshold=-DBL_MAX, bool addGravity=false, double g=9.81, int numThreads=1);
-  virtual ~IsotropicHyperelasticFEMMT();
-
-  // Computes strain energy, internal forces, and/or tangent stiffness matrix, as requested by computationMode. It returns 0 on success, and non-zero on failure.
-  // this is an advanced function; you normally do not need to use it
-  virtual int GetEnergyAndForceAndTangentStiffnessMatrixHelper(double * u, double * energy, double * internalForces, SparseMatrix * tangentStiffnessMatrix, int computationMode);
-
-  int GetStartElement(int rank);
-  int GetEndElement(int rank);
-
-protected:
-  int numThreads;
-  int * startElement, * endElement;
-  double * energyBuffer;
-  double * internalForceBuffer;
-  SparseMatrix ** tangentStiffnessMatrixBuffer;
-
-  void Initialize();
-};
-}
-#endif
-
diff --git a/src/liblighting/CMakeLists.txt b/src/liblighting/CMakeLists.txt
deleted file mode 100644
index f4d68b2b75975094db8f555ace515194938cd27f..0000000000000000000000000000000000000000
--- a/src/liblighting/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-vega_add_library(lighting
-  SOURCES
-    lighting.cpp
-  PUBLIC_HEADERS
-    lighting.h
-)
-target_link_libraries(lighting
-  PUBLIC
-    configFile
-    ${OPENGL_LIBRARIES}
-)
diff --git a/src/liblighting/lighting.cpp b/src/liblighting/lighting.cpp
deleted file mode 100644
index 11ac3fde84df6bc3fe75da550093ac9b6f576296..0000000000000000000000000000000000000000
--- a/src/liblighting/lighting.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
-  * Copyright (c) 2007, Carnegie Mellon University
-  * All rights reserved.
-  *
-  * Redistribution and use in source and binary forms, with or without
-  * modification, are permitted provided that the following conditions are met:
-  *     * Redistributions of source code must retain the above copyright
-  *       notice, this list of conditions and the following disclaimer.
-  *     * Redistributions in binary form must reproduce the above copyright
-  *       notice, this list of conditions and the following disclaimer in the
-  *       documentation and/or other materials provided with the distribution.
-  *     * Neither the name of Carnegie Mellon University, nor the
-  *       names of its contributors may be used to endorse or promote products
-  *       derived from this software without specific prior written permission.
-  *
-  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND ANY
-  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-  * DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR ANY
-  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <float.h>
-#include "configFile.h"
-#include "lighting.h"
-
-namespace vega
-{
-Lighting::Lighting(char * configFilename)
-{
-  if (glGetError() != GL_NO_ERROR)
-    printf("Warning: error in the OpenGL state at the beginning of lighting constructor.\n");
-
-  ConfigFile configFile;
-
-  configFile.addOptionOptional("globalAmbientIntensity", &ambientIntensity, 0.0f);
-  configFile.addOptionOptional("localViewer", &localViewer, true);
-  configFile.addOptionOptional("twoSidedLighting", &twoSidedLighting, false);
-  configFile.addOptionOptional("enableAmbientTerm", &enableAmbientTerm, true);
-  configFile.addOptionOptional("enableDiffuseTerm", &enableDiffuseTerm, true);
-  configFile.addOptionOptional("enableSpecularTerm", &enableSpecularTerm, true);
-
-  for(int light=0; light<8; light++)
-  {
-    char lightCh = '0' + light;
-    char optionName[128];
-
-    sprintf(optionName, "lightEnabled_%c", lightCh);
-    configFile.addOptionOptional(optionName, &lightEnabled[light], false);
-
-    sprintf(optionName, "position_%c_X", lightCh);
-    configFile.addOptionOptional(optionName, &lightPosition[4*light+0], 0.0f);
-    sprintf(optionName, "position_%c_Y", lightCh);
-    configFile.addOptionOptional(optionName, &lightPosition[4*light+1], 0.0f);
-    sprintf(optionName, "position_%c_Z", lightCh);
-    configFile.addOptionOptional(optionName, &lightPosition[4*light+2], 0.0f);
-    sprintf(optionName, "position_%c_DIR", lightCh);
-    configFile.addOptionOptional(optionName, &lightPosition[4*light+3], 1.0f);
-
-    sprintf(optionName, "lightIntensity_%c", lightCh);
-    configFile.addOptionOptional(optionName, &lightIntensity[light], 1.0f);
-  }
-  
-  if (configFile.parseOptions(configFilename) != 0)
-    throw 1;
-
-  //configFile.printOptions();
-
-  #define TURNONLIGHT(i)\
-   if(lightEnabled[i])\
-     glEnable(GL_LIGHT##i);\
-   else\
-     glDisable(GL_LIGHT##i);\
-
-  TURNONLIGHT(0);
-  TURNONLIGHT(1);
-  TURNONLIGHT(2);
-  TURNONLIGHT(3);
-  TURNONLIGHT(4);
-  TURNONLIGHT(5);
-  TURNONLIGHT(6);
-  TURNONLIGHT(7);
-
-  if (glGetError() != GL_NO_ERROR)
-    printf("Warning: error in the OpenGL state in the Lighting constructor.\n");
-
-  for(int light=0; light<8; light++)
-  {
-    if(!lightEnabled[light])
-      continue;
-
-    if (enableAmbientTerm) 
-    {
-      lKa[light*4+0] = lKa[light*4+1] = lKa[light*4+2] = lightIntensity[light];
-    }
-    else
-    {
-      lKa[light*4+0] = lKa[light*4+1] = lKa[light*4+2] = 0.0f;
-    }
-
-    if (enableDiffuseTerm)
-    {
-      lKd[light*4+0] = lKd[light*4+1] = lKd[light*4+2] = lightIntensity[light];
-    }
-    else
-    {
-      lKd[light*4+0] = lKd[light*4+1] = lKd[light*4+2] = 0.0f;
-    }
-
-    if (enableSpecularTerm)
-    {
-      lKs[light*4+0] = lKs[light*4+1] = lKs[light*4+2] = lightIntensity[light];
-    }
-    else
-    {
-      lKs[light*4+0] = lKs[light*4+1] = lKs[light*4+2] = 0.0f;
-    }
-
-    // set alpha to 1.0
-    lKa[light*4+3] = lKd[light*4+3] = lKs[light*4+3] = 1.0;
-  }
-
-  GLfloat aGa[] = { ambientIntensity, ambientIntensity, ambientIntensity, 1.0 };
-  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, aGa);
-  glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, localViewer);
-  glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, twoSidedLighting);
-}
-
-void Lighting::LightScene()
-{
-  #define LIGHTSETUP(i)\
-  if (lightEnabled[i])\
-  {\
-    glLightfv(GL_LIGHT##i, GL_POSITION, &lightPosition[4*i]);\
-    glLightfv(GL_LIGHT##i, GL_AMBIENT, &lKa[4*i]);\
-    glLightfv(GL_LIGHT##i, GL_DIFFUSE, &lKd[4*i]);\
-    glLightfv(GL_LIGHT##i, GL_SPECULAR, &lKs[4*i]);\
-  }
-
-  LIGHTSETUP(0);
-  LIGHTSETUP(1);
-  LIGHTSETUP(2);
-  LIGHTSETUP(3);
-  LIGHTSETUP(4);
-  LIGHTSETUP(5);
-  LIGHTSETUP(6);
-  LIGHTSETUP(7);
-
-  glEnable(GL_LIGHTING);
-}
-}
diff --git a/src/libloadList/CMakeLists.txt b/src/libloadList/CMakeLists.txt
deleted file mode 100644
index a49d5926c87f6747cbd3a800c65e78fe1abbe652..0000000000000000000000000000000000000000
--- a/src/libloadList/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-vega_add_library(loadList
-  SOURCES
-    loadList.cpp
-  PUBLIC_HEADERS
-    loadList.h
-)
diff --git a/src/libloadList/loadList.cpp b/src/libloadList/loadList.cpp
deleted file mode 100644
index 5b1f6a3f39a37fc8240608a53d4188f8f58e0fc6..0000000000000000000000000000000000000000
--- a/src/libloadList/loadList.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "loadList" library , Copyright (C) 2007 CMU, 2009 MIT                 *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include "loadList.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-namespace vega
-{
-// removes all whitespace characters from string s
-void LoadList::stripBlanks(char * s)
-{
-  char * w = s;
-  while (*w != '\0')
-  {
-    while (*w == ' ') // erase blank
-    {
-      char * u = w;
-      while (*u != '\0') // shift everything left one char
-      {
-        *u = *(u+1);
-        u++;
-      }
-    }
-    w++;
-  }
-}
-
-int compareLoadList(const void * a, const void * b)
-{
-  return ( *(int*)a - *(int*)b );
-}
-
-int LoadList::load(char * filename, int * numListEntries, int ** listEntries, int offset)
-{
-   // comma-separated text file of fixed vertices
-  FILE * fin;
-  fin = fopen(filename,"r");
-  if (!fin)
-  {
-    printf("Error: could not open file %s.\n",filename);
-    return 1;
-  }
-
-  *numListEntries = 0;
-
-  char s[4096];
-  while (fgets(s,4096,fin) != NULL)
-  { 
-    stripBlanks(s);
-
-    char * pch;
-    pch = strtok (s,",");
-    while ((pch != NULL) && (isdigit(*pch)))
-    {
-      (*numListEntries)++;
-      pch = strtok (NULL, ",");
-    } 
-  }
-
-  *listEntries = (int*) malloc (sizeof(int) * (*numListEntries));
-
-  rewind(fin);
-
-  (*numListEntries) = 0;
-
-  while (fgets(s,4096,fin) != NULL)
-  {
-    stripBlanks(s);
-    char * pch;
-    pch = strtok (s,",");
-    while ((pch != NULL) && (isdigit(*pch)))
-    {
-      (*listEntries)[*numListEntries] = atoi(pch) - offset;
-      (*numListEntries)++;
-      pch = strtok (NULL, ",");
-    }
-  }
-
-  // sort the list entries
-  qsort ((*listEntries), *numListEntries, sizeof(int), compareLoadList);
-
-  fclose(fin);
-
-  return 0;
-}
-
-int loadListComparator(const void * a, const void * b)
-{
-  if (*(int*)a < *(int*)b)
-    return -1;
-
-  if (*(int*)a == *(int*)b)
-    return 0;
-
-  return 1;
-}
-
-void LoadList::sort(int numListEntries, int * listEntries)
-{
-  qsort(listEntries,numListEntries,sizeof(int),loadListComparator);
-}
-
-int LoadList::save(char * filename, int numListEntries, int * listEntries, int offset)
-{
-  // comma-separated text file of fixed vertices
-  FILE * fout;
-  fout = fopen(filename, "w");
-  if (!fout)
-  {
-    printf("Error: could not open boundary vertices specification file %s.\n",filename);
-    return 1;
-  }
-
-  for(int nv=0; nv < numListEntries; nv++)
-  {
-    fprintf(fout, "%d,", listEntries[nv] + offset);
-    if (nv % 8 == 7)
-      fprintf(fout,"\n");
-  }
-  fprintf(fout,"\n");
-
-  fclose(fout);
-
-  return 0;
-}
-
-void LoadList::print(int numListEntries, int * listEntries)
-{
-  for(int nv=0; nv < numListEntries; nv++)
-  {
-    printf("%d,", listEntries[nv]);
-    if (nv % 8 == 7)
-      printf("\n");
-  }
-  printf("\n"); fflush(NULL);
-}
-
-}
diff --git a/src/libloadList/loadList.h b/src/libloadList/loadList.h
deleted file mode 100755
index 4915935d497ddeee052ea1ff183504b559da5bd4..0000000000000000000000000000000000000000
--- a/src/libloadList/loadList.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "loadList" library , Copyright (C) 2007 CMU, 2009 MIT                 *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef _LOADLIST_H_
-#define _LOADLIST_H_
-
-namespace vega
-{
-/*
-  A class to load an integer list from a text file into memory. 
-*/
-
-class LoadList
-{
-public:
-
-  // returns 0 on success and non-zero otherwise
-  static int load(char * filename, int * numListEntries, int ** listEntries, int offset=0);
-  static int save(char * filename, int numListEntries, int * listEntries, int offset=0);
-  static void sort(int numListEntries, int * listEntries);
-
-  static void print(int numListEntries, int * listEntries);
-
-  static void stripBlanks(char * s);
-
-protected:
-};
-}
-#endif
-
diff --git a/src/libmassSpringSystem/CMakeLists.txt b/src/libmassSpringSystem/CMakeLists.txt
deleted file mode 100644
index f9836000650cc4dcb41cb5df743a8b6d92ea87c7..0000000000000000000000000000000000000000
--- a/src/libmassSpringSystem/CMakeLists.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-set(massspringsystem_srcs
-  massSpringSystemFromObjMeshConfigFile.cpp
-  massSpringSystemFromObjMesh.cpp
-  massSpringSystemFromTetMeshConfigFile.cpp
-  massSpringSystemFromTetMesh.cpp
-  massSpringSystem.cpp
-  massSpringSystemFromCubicMesh.cpp
-  massSpringSystemFromCubicMeshConfigFile.cpp
-)
-set(massspringsystem_hdrs
-    massSpringSystem.h
-    massSpringSystemFromCubicMesh.h
-    massSpringSystemFromCubicMeshConfigFile.h
-    massSpringSystemFromObjMesh.h
-    massSpringSystemFromObjMeshConfigFile.h
-    massSpringSystemFromTetMesh.h
-    massSpringSystemFromTetMeshConfigFile.h
-)
-
-if(VegaFEM_ENABLE_PTHREADS_SUPPORT)
-  list(APPEND massspringsystem_srcs massSpringSystemMT.cpp)
-  list(APPEND massspringsystem_hdrs massSpringSystemMT.h)
-endif()
-
-if (VegaFEM_ENABLE_OpenGL_SUPPORT)
-  list(APPEND massspringsystem_srcs renderSprings.cpp)
-  list(APPEND massspringsystem_hdrs renderSprings.h)
-endif()
-
-vega_add_library(massSpringSystem
-  SOURCES
-    ${massspringsystem_srcs}
-  PUBLIC_HEADERS
-    ${massspringsystem_hdrs}
-)
-target_link_libraries(massSpringSystem
-  PUBLIC
-    objMesh
-    volumetricMesh
-    configFile
-    sparseMatrix
-    Threads::Threads
-  INTERFACE
-    minivector
-)
-if (VegaFEM_ENABLE_OpenGL_SUPPORT)
-  target_link_libraries(massSpringSystem
-    PUBLIC
-      ${OPENGL_gl_LIBRARY}
-      ${OPENGL_glu_LIBRARY}
-  )
-endif()
diff --git a/src/libmassSpringSystem/massSpringSystemMT.cpp b/src/libmassSpringSystem/massSpringSystemMT.cpp
deleted file mode 100644
index 0d50f3a3b283073503c975687b85bd116b99d92f..0000000000000000000000000000000000000000
--- a/src/libmassSpringSystem/massSpringSystemMT.cpp
+++ /dev/null
@@ -1,323 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Jernej Barbic, Daniel Schroeder                         *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <pthread.h>
-#include <vector>
-#include <set>
-#include "macros.h"
-#include "massSpringSystemMT.h"
-using namespace std;
-
-//#include "performanceCounter.h"
-
-namespace vega
-{
-MassSpringSystemMT::MassSpringSystemMT(int numParticles_, double * masses_, double * restPositions_, int numEdges_, int * edges_, int * edgeGroups_, int numMaterialGroups_, double * groupStiffness_, double * groupDamping_, int addGravity_, int numThreads_) : MassSpringSystem(numParticles_, masses_, restPositions_, numEdges_, edges_, edgeGroups_, numMaterialGroups_, groupStiffness_, groupDamping_, addGravity_), numThreads(numThreads_)
-{
-  Initialize();
-}
-
-MassSpringSystemMT::MassSpringSystemMT(int numParticles_, double * restPositions_, int numQuads, int * quads, double surfaceDensity, double tensileStiffness, double shearStiffness, double bendStiffness, double damping, int addGravity_, int numThreads_): MassSpringSystem(numParticles_, restPositions_, numQuads, quads, surfaceDensity, tensileStiffness, shearStiffness, bendStiffness, damping, addGravity_), numThreads(numThreads_)
-{
-  Initialize();
-}
-
-MassSpringSystemMT::MassSpringSystemMT(int numParticles_, double * restPositions_, MassSpringSystemElementType elementType, int numElements, int * elements, double density, double tensileStiffness, double damping, int addGravity_, int numThreads_): MassSpringSystem(numParticles_, restPositions_, elementType, numElements, elements, density, tensileStiffness, damping, addGravity_), numThreads(numThreads_)
-{
-  Initialize();
-}
-
-MassSpringSystemMT::MassSpringSystemMT(MassSpringSystem & massSpringSystem, int numThreads_): MassSpringSystem(massSpringSystem), numThreads(numThreads_)
-{
-  Initialize();
-}
-
-MassSpringSystemMT::~MassSpringSystemMT()
-{
-  free(startEdge);
-  free(endEdge);
-  free(internalForceBuffer);
-  for(int i=0; i<numThreads; i++)
-    delete(sparseMatrixBuffer[i]);
-  free(sparseMatrixBuffer);
-}
-
-struct MassSpringSystemMT_threadArg
-{
-  MassSpringSystemMT * massSpringSystemMT;
-  double * u;
-  double * uSecondary;
-  void * targetBuffer;
-  int rank;
-  enum MassSpringSystemMT_computationTargetType computationTarget;
-};
-
-void * MassSpringSystemMT_WorkerThread(void * arg)
-{
-  struct MassSpringSystemMT_threadArg * threadArgp = (struct MassSpringSystemMT_threadArg*) arg;
-  MassSpringSystemMT * massSpringSystemMT = threadArgp->massSpringSystemMT;
-  double * u = threadArgp->u;
-  int rank = threadArgp->rank;
-  int startEdge = massSpringSystemMT->GetStartEdge(rank);
-  int endEdge = massSpringSystemMT->GetEndEdge(rank);
-
-  switch (threadArgp->computationTarget)
-  {
-    case FORCE: 
-    {
-      double * targetBuffer = (double*)(threadArgp->targetBuffer);
-      massSpringSystemMT->AddForce(u, targetBuffer, startEdge, endEdge);
-    }
-    break;
-
-    case DAMPINGFORCE:
-    {
-      double * uvel = u;
-      double * targetBuffer = (double*)(threadArgp->targetBuffer);
-      massSpringSystemMT->AddDampingForce(uvel, targetBuffer, startEdge, endEdge);
-    }
-    break;
-
-    case STIFFNESSMATRIX:
-    {
-      SparseMatrix * targetBuffer = (SparseMatrix*)(threadArgp->targetBuffer);
-      massSpringSystemMT->AddStiffnessMatrix(u, targetBuffer, startEdge, endEdge);
-    }
-    break;
-
-    case HESSIANAPPROXIMATION:
-    {
-      SparseMatrix * targetBuffer = (SparseMatrix*)(threadArgp->targetBuffer);
-      double * uSecondary = threadArgp->uSecondary;
-      massSpringSystemMT->AddHessianApproximation(u, uSecondary, targetBuffer, startEdge, endEdge);
-    }
-    break;
-
-    default:
-      printf("Error: unknown computation type.\n");
-      exit(1);
-    break;
-  }
-
-  return NULL;
-}
-
-void MassSpringSystemMT::Initialize()
-{
-  internalForceBuffer = (double*) malloc (sizeof(double) * numThreads * 3 * numParticles);
-
-  // generate skeleton matrices
-  sparseMatrixBuffer = (SparseMatrix**) malloc (sizeof(SparseMatrix*) * numThreads);
-
-  SparseMatrix * sparseMatrix;
-  GetStiffnessMatrixTopology(&sparseMatrix);
-  for(int i=0; i<numThreads; i++)
-    sparseMatrixBuffer[i] = new SparseMatrix(*sparseMatrix);
-
-  // split the workload
-  startEdge = (int*) malloc (sizeof(int) * numThreads);
-  endEdge = (int*) malloc (sizeof(int) * numThreads);
-
-  int remainder = numEdges % numThreads;
-  // the first 'remainder' nodes will process one edge more
-  int jobSize = numEdges / numThreads;
-
-  for(int rank=0; rank < numThreads; rank++)
-  {
-    if (rank < remainder)
-    {
-      startEdge[rank] = rank * (jobSize+1);
-      endEdge[rank] = (rank+1) * (jobSize+1);
-    }
-    else
-    {
-      startEdge[rank] = remainder * (jobSize+1) + (rank-remainder) * jobSize;
-      endEdge[rank] = remainder * (jobSize+1) + ((rank-remainder)+1) * jobSize;
-    }
-  }
-
-  printf("Total edges: %d \n",numEdges);
-  printf("Num threads: %d \n",numThreads);
-  printf("Canonical job size: %d \n",jobSize);
-  printf("Num threads with job size augmented by one edge: %d \n",remainder);
-}
-
-void MassSpringSystemMT::ComputeHelper(enum MassSpringSystemMT_computationTargetType computationTarget, double * u, double * uSecondary, void * target, bool addQuantity)
-{
-  // launch threads
-  int numParticles3 = 3*numParticles;
-  struct MassSpringSystemMT_threadArg * threadArgv = (struct MassSpringSystemMT_threadArg*) malloc (sizeof(struct MassSpringSystemMT_threadArg) * numThreads);
-
-  pthread_t * tid = (pthread_t*) malloc (sizeof(pthread_t) * numThreads);
-
-  for(int i=0; i<numThreads; i++)
-  {
-    threadArgv[i].massSpringSystemMT = this;
-    threadArgv[i].u = u;
-    threadArgv[i].rank = i;
-    threadArgv[i].computationTarget = computationTarget;
-  }
-
-  switch(computationTarget)
-  {
-    case FORCE:
-    case DAMPINGFORCE:
-    {
-      for(int i=0; i<numThreads; i++)
-        threadArgv[i].targetBuffer = (void*)(&internalForceBuffer[i * numParticles3]);
-      memset(internalForceBuffer, 0, sizeof(double) * numParticles3 * numThreads);
-    }
-    break;
-
-    case STIFFNESSMATRIX:
-    case HESSIANAPPROXIMATION:
-    {
-      for(int i=0; i<numThreads; i++)
-      {
-        threadArgv[i].targetBuffer = (void*)(sparseMatrixBuffer[i]);
-        sparseMatrixBuffer[i]->ResetToZero();
-      }
-    }
-    break;
-
-    default:
-      printf("Error: unknown computation type.\n");
-      exit(1);
-    break;
-  }
-  
-  for(int i=0; i<numThreads; i++)  
-  {
-    if (pthread_create(&tid[i], NULL, MassSpringSystemMT_WorkerThread, &threadArgv[i]) != 0)
-    {
-      printf("Error: unable to launch thread %d.\n", i);
-      exit(1);
-    }
-  }
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_join(tid[i], NULL) != 0)
-    {
-      printf("Error: unable to join thread %d.\n", i);
-      exit(1);
-    }
-  }
-
-  free(threadArgv);
-  free(tid);
-
-  // assemble results
-  switch(computationTarget)
-  {
-    case FORCE:
-    case DAMPINGFORCE:
-    {
-      double * f = (double*) target;
-
-      if (!addQuantity)
-        memset(f, 0, sizeof(double) * numParticles3);
-
-      for(int i=0; i<numThreads; i++)
-      {
-        double * source = &internalForceBuffer[i * numParticles3];
-        for(int j=0; j<numParticles3; j++)
-          f[j] += source[j];
-      }
-
-      if ((computationTarget == FORCE) && addGravity)
-        ComputeGravity(f, true);
-    }
-    break;
-
-    case STIFFNESSMATRIX:
-    case HESSIANAPPROXIMATION:
-    {
-      SparseMatrix * targetK = (SparseMatrix*) target;
-
-      if (!addQuantity)
-        targetK->ResetToZero();
-
-      for(int i=0; i<numThreads; i++)
-      {
-        *targetK += *(sparseMatrixBuffer[i]);
-      }
-    }
-    break;
-
-    default:
-      printf("Error: unknown computation type.\n");
-      exit(1);
-    break;
-  }
-}
-
-void MassSpringSystemMT::ComputeForce(double * u, double * f, bool addForce)
-{
-  //PerformanceCounter forceCounter;
-  ComputeHelper(FORCE, u, NULL, (void*)f, addForce);
-
-  //forceCounter.StopCounter();
-  //printf("Internal forces: %G\n", forceCounter.GetElapsedTime());
-}
-
-void MassSpringSystemMT::ComputeStiffnessMatrix(double * u, SparseMatrix * K, bool addMatrix)
-{
-  //PerformanceCounter stiffnessCounter;
-  ComputeHelper(STIFFNESSMATRIX, u, NULL, (void*)K , addMatrix);
-
-  //stiffnessCounter.StopCounter();
-  //printf("Stiffness matrix: %G\n", stiffnessCounter.GetElapsedTime());
-}
-
-void MassSpringSystemMT::ComputeDampingForce(double * uvel, double * f, bool addForce)
-{
-  ComputeHelper(DAMPINGFORCE, uvel, NULL, (void*)f, addForce);
-}
-
-void MassSpringSystemMT::ComputeHessianApproximation(double * u, double * du, SparseMatrix * dK, bool addMatrix)
-{
-  ComputeHelper(HESSIANAPPROXIMATION, u, du, (void*) dK, addMatrix);
-}
-
-int MassSpringSystemMT::GetStartEdge(int rank)
-{
-  return startEdge[rank];
-}
-
-int MassSpringSystemMT::GetEndEdge(int rank)
-{
-  return endEdge[rank];
-}
-
-}
diff --git a/src/libmassSpringSystem/massSpringSystemMT.h b/src/libmassSpringSystem/massSpringSystemMT.h
deleted file mode 100644
index fa8720058814eea789a46637fdf498cddc3fd69c..0000000000000000000000000000000000000000
--- a/src/libmassSpringSystem/massSpringSystemMT.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "massSpringSystem" library, Copyright (C) 2007 CMU, 2009 MIT,         *
- *                                           2013 USC                    *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic, Daniel Schroeder                          *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef _MASS_SPRING_SYSTEM_MT_H_
-#define _MASS_SPRING_SYSTEM_MT_H_
-
-#include "massSpringSystem.h"
-
-namespace vega
-{
-/*
-   Multi-threaded version of the MassSpringSystem class. 
-   It uses the POSIX threads ("pthreads").
-   See also massSpringSystem.h
-*/
-
-enum MassSpringSystemMT_computationTargetType { FORCE, STIFFNESSMATRIX, DAMPINGFORCE, HESSIANAPPROXIMATION };
-
-class MassSpringSystemMT : public MassSpringSystem
-{
-public:
-
-  // creates a mass spring from scratch
-  MassSpringSystemMT(int numParticles, double * masses, double * restPositions,
-    int numEdges, int * edges, int * edgeGroups, int numMaterialGroups, double * groupStiffness, double * groupDamping, int addGravity=0, int numThreads=1);
-
-  MassSpringSystemMT(int numParticles, double * restPositions, int numQuads, int * quads, double surfaceDensity, double tensileStiffness, double shearStiffness, double bendStiffness, double damping, int addGravity=0, int numThreads=1); // creates the mass spring system from a quad surface mesh (cloth)
-
-  MassSpringSystemMT(int numParticles, double * restPositions, MassSpringSystemElementType elementType, int numElements, int * elements, double density, double tensileStiffness, double damping, int addGravity=0, int numThreads=1); // creates the mass spring system from a tet meh
-
-  MassSpringSystemMT(MassSpringSystem & massSpringSystem, int numThreads);
-
-  virtual ~MassSpringSystemMT();
-
-  virtual void ComputeForce(double * u, double * f, bool addForce=false); 
-  virtual void ComputeStiffnessMatrix(double * u, SparseMatrix * K, bool addMatrix=false);
-  virtual void ComputeDampingForce(double * uvel, double * f, bool addForce=false); 
-  // computes an approximation to dK, assuming the deformations change from u to u + du
-  virtual void ComputeHessianApproximation(double * u, double * du, SparseMatrix * dK, bool addMatrix=false);
-
-  int GetStartEdge(int rank);
-  int GetEndEdge(int rank);
-
-protected:
-  int numThreads;
-  int * startEdge, * endEdge;
-  double * internalForceBuffer;
-  SparseMatrix ** sparseMatrixBuffer;
-
-  void Initialize();
-  void ComputeHelper(enum MassSpringSystemMT_computationTargetType computationTarget, double * u, double * uSecondary, void * target, bool addQuantity);
-
-};
-}
-#endif
-
diff --git a/src/libmatrix/CMakeLists.txt b/src/libmatrix/CMakeLists.txt
deleted file mode 100644
index c481c0cbbf84ab7abd7b67beaf7192ec36d9cd7c..0000000000000000000000000000000000000000
--- a/src/libmatrix/CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-set(matrix_srcs
-  matrixProjection.cpp
-  matrixPCA.cpp
-  matrixBLAS.cpp
-  matrixLAPACK.cpp
-  matrix.cpp
-)
-set(matrix_hdrs
-    expokit_xgpadm.h
-    matrix.h
-    matrixBLAS.h
-    matrixLAPACK.h
-    matrixPCA.h
-    matrixProjection.h
-)
-if (VegaFEM_ENABLE_ExpoKit_SUPPORT)
-  set(matrix_srcs
-    ${matrix_srcs}
-    matrixExp.cpp
-  )
-  set(matrix_hdrs
-    ${matrix_hdrs}
-    matrixExp.h
-  )
-endif()
-
-vega_add_library(matrix
-  SOURCES
-    ${matrix_srcs}
-  PUBLIC_HEADERS
-    ${matrix_hdrs}
-)
-target_link_libraries(matrix
-  PUBLIC
-    matrixIO
-    ${BLAS}
-    ${CBLAS_LIBRARY}
-    ${LAPACK_LIBRARIES}
-)
diff --git a/src/libmatrix/matrixBLAS.cpp b/src/libmatrix/matrixBLAS.cpp
deleted file mode 100644
index fbc51f2b1b2ed3e670fdddc16bfce7dd177096ac..0000000000000000000000000000000000000000
--- a/src/libmatrix/matrixBLAS.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include "matrixMacros.h"
-#include "matrixBLAS.h"
-
-#define BLAS
-
-#ifdef BLAS
-  #include "matrixBLASOptimized.cpp"
-#else
-  #include "matrixBLASVanilla.cpp"
-#endif
-
-// transposes the matrix (without making a separate copy)
-template <class real>
-void InPlaceTransposeMatrix(int m, int n, real * mtx)
-{ 
-  real buffer;
-  #define SWAP_ELT(i,j)\
-    buffer = mtx[i];\
-    mtx[i] = mtx[j];\
-    mtx[j] = buffer;
-
-  long M = m;
-  long N = n;
-  long MN = M*N;
-  
-  for(long i=0; i< MN; i++)
-  {
-    long current = i;
-    do
-    {
-      // evaluate permutation on 'current'
-      long k = current / N;
-      long l = current % N;
-  
-      current = M * l + k;
-    } 
-    while(current < i);
-
-    if (current>i)
-    {
-      SWAP_ELT(i,current);
-      //printf("Swap: %d %d .\n",i,current);
-    }
-  }
-}
-
-template float * MultiplyMatrices<float>(int m, int p, int n, float * mtx1, float * mtx2, float * output);
-template double * MultiplyMatrices<double>(int m, int p, int n, double * mtx1, double * mtx2, double * output);
-
-template float * MultiplyMatricesT<float>(int m, int p, int n, float * mtx1, float * mtx2, float * output);
-template double * MultiplyMatricesT<double>(int m, int p, int n, double * mtx1, double * mtx2, double * output);
-
-template float * SumMatrices<float>(int m, int n, float * mtx1, float * mtx2, float * output);
-template double * SumMatrices<double>(int m, int n, double * mtx1, double * mtx2, double * output);
-
-template float * SubtractMatrices<float>(int m, int n, float * mtx1, float * mtx2, float * output);
-template double * SubtractMatrices<double>(int m, int n, double * mtx1, double * mtx2, double * output);
-
-template float * ScalarMultiplyMatrix<float>(int m, int n, float alpha, float * mtx, float * output);
-template double * ScalarMultiplyMatrix<double>(int m, int n, double alpha, double * mtx, double * output);
-
-template void InPlaceTransposeMatrix<double>(int m, int n, double * U);
-template void InPlaceTransposeMatrix<float>(int m, int n, float * U);
-
-template double VectorNorm<double>(int m, double * vec);
-template float VectorNorm<float>(int m, float * vec);
diff --git a/src/libmatrix/matrixBLAS.h b/src/libmatrix/matrixBLAS.h
deleted file mode 100644
index 2b141cd506675e89fc982266945defd9e1b8cb71..0000000000000000000000000000000000000000
--- a/src/libmatrix/matrixBLAS.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-/*
-  Wrappers to matrix BLAS routines.
-  See also matrix.h.
-*/
-
-#ifndef _MATRIX_BLAS_H_
-#define _MATRIX_BLAS_H_
-
-#include <stdlib.h>
-
-template<class real>
-real * SumMatrices(int m, int n, real * mtx1, real * mtx2, real * output = NULL);
-
-template<class real>
-real * SubtractMatrices(int m, int n, real * mtx1, real * mtx2, real * output = NULL);
-
-// output = mtx1 * mtx2
-// mtx1 is m x p
-// mtx2 is p x n
-template<class real>
-real * MultiplyMatrices(int m, int p, int n, real * mtx1, real * mtx2, real * output = NULL);
-
-// output = trans(mtx1) * mtx2
-// trans(mtx1) is m x p
-// mtx2 is p x n
-template<class real>
-real * MultiplyMatricesT(int m, int p, int n, real * mtx1, real * mtx2, real * output = NULL);
-
-template<class real>
-real * ScalarMultiplyMatrix(int m, int n, real alpha, real * mtx, real * output = NULL);
-
-// transposes the matrix (without making a separate copy)
-template <class real>
-void InPlaceTransposeMatrix(int m, int n, real * mtx);
-
-// computes Euclidean norm of a vector
-template <class real>
-real VectorNorm(int m, real * vec);
-
-#endif
-
diff --git a/src/libmatrixIO/CMakeLists.txt b/src/libmatrixIO/CMakeLists.txt
deleted file mode 100644
index f711fae90978797dbd10818736793b7a2daccf2d..0000000000000000000000000000000000000000
--- a/src/libmatrixIO/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-vega_add_library(matrixIO
-  SOURCES
-    matrixIO.cpp
-  PUBLIC_HEADERS
-    matrixIO.h
-    matrixMacros.h
-)
diff --git a/src/libmatrixIO/matrixMacros.h b/src/libmatrixIO/matrixMacros.h
deleted file mode 100644
index 9bd7e5083992736034e92d866fb4451973235a76..0000000000000000000000000000000000000000
--- a/src/libmatrixIO/matrixMacros.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "matrix" library , Copyright (C) 2007 CMU, 2009 MIT                   *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- * Research: Jernej Barbic, Doug L. James, Jovan Popovic                 *
- * Funding: NSF, Link Foundation, Singapore-MIT GAMBIT Game Lab          *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-/*
-  Commonly used macros in the matrix library.
-*/
-
-#ifndef _MATRIXMACROS_H_
-#define _MATRIXMACROS_H_
-
-/*
-   Returns the array offset index of element located in row i and column j (both 0-indexed). The matrix has "numRows" rows, and is stored in LAPACK-style column-major order.
-*/
-#ifndef ELT
-  #define ELT(numRows,i,j) (((long)j)*((long)numRows)+((long)i))
-#endif
-
-#define MIN(x,y) ((x)<(y) ? (x) : (y))
-#define MAX(x,y) ((x)>(y) ? (x) : (y))
-
-#endif
diff --git a/src/libminivector/CMakeLists.txt b/src/libminivector/CMakeLists.txt
deleted file mode 100644
index ac103c76586ab723ece97c1b1ed90d4713f85eee..0000000000000000000000000000000000000000
--- a/src/libminivector/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-vega_add_library(minivector
-  SOURCES
-    mat3d.cpp
-    vec3d.cpp
-    vec2d.cpp
-    eig3.cpp
-  PUBLIC_HEADERS
-    eig3.h
-    mat3d.h
-    minivector.h
-    vec2d.h
-    vec3d.h
-)
diff --git a/src/libmodalMatrix/CMakeLists.txt b/src/libmodalMatrix/CMakeLists.txt
deleted file mode 100644
index 9ae6444c541f0ce9cf6376c3bd95ae231a5388d0..0000000000000000000000000000000000000000
--- a/src/libmodalMatrix/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-vega_add_library(modalMatrix
-  SOURCES
-    modalMatrix.cpp
-  PUBLIC_HEADERS
-    modalMatrix.h
-)
-target_link_libraries(modalMatrix
-  PUBLIC
-    matrixIO
-     ${LAPACK_LIBRARIES}
-     ${BLAS_LIBRARY}
-)
diff --git a/src/libobjMesh/CMakeLists.txt b/src/libobjMesh/CMakeLists.txt
deleted file mode 100644
index f379c1655972cdba0de447892aa671100130580b..0000000000000000000000000000000000000000
--- a/src/libobjMesh/CMakeLists.txt
+++ /dev/null
@@ -1,60 +0,0 @@
-set(OBJ_SRCS
-  objMesh.cpp
-  objMesh-disjointSet.cpp
-  objMeshBinaryLoader.cpp
-  objMeshEncode.cpp
-  objMeshGraph.cpp
-  objMeshOrientable.cpp
-  simpleSphere.cpp
-  tribox3.cpp
-)
-set(OBJ_HDRS
-  objMesh-disjointSet.h
-  objMesh.h
-  objMeshBinaryLoader.h
-  objMeshEncode.h
-  objMeshGraph.h
-  objMeshOrientable.h
-  simpleSphere.h
-  tribox3.h
-)
-if (VegaFEM_ENABLE_OpenGL_SUPPORT)
-  set(OBJ_SRCS
-    ${OBJ_SRCS}
-    boundingBox.cpp
-    octree.cpp
-    triangle.cpp
-    objMeshOctree.cpp
-    objMeshOffsetVoxels.cpp
-    objMeshRender.cpp
-  )
-  set(OBJ_HDRS
-    ${OBJ_HDRS}
-    boundingBox.h
-    octree.h
-    triangle.h
-    objMeshOctree.h
-    objMeshOffsetVoxels.h
-    objMeshRender.h
-  )
-endif()
-
-vega_add_library(objMesh
-  SOURCES ${OBJ_SRCS}
-  PUBLIC_HEADERS ${OBJ_HDRS}
-)
-target_link_libraries(objMesh
-  PUBLIC
-    minivector
-    matrixIO
-    imageIO
-    sparseMatrix
-    graph
-)
-if (VegaFEM_ENABLE_OpenGL_SUPPORT)
-  target_link_libraries(objMesh
-    PUBLIC
-      ${OPENGL_gl_LIBRARY}
-      ${OPENGL_glu_LIBRARY}
-  )
-endif()
diff --git a/src/libobjMesh/boundingBox.h b/src/libobjMesh/boundingBox.h
deleted file mode 100644
index 61aec780641e5e5df6c34ab309799f3c05beac1a..0000000000000000000000000000000000000000
--- a/src/libobjMesh/boundingBox.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef __BOUNDINGBOX_H__
-#define __BOUNDINGBOX_H__
-
-//  Bounding Box
-//  Author: Jernej Barbic, CMU
-
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
-#include <vector>
-#include "minivector.h"
-
-namespace vega
-{
-class BoundingBox
-{
-public:
-
-  BoundingBox(Vec3d bmin_g=Vec3d(0,0,0), Vec3d bmax_g=Vec3d(1,1,1)): bmin_(bmin_g), bmax_(bmax_g) { updateData();}
-  template<class Triangle> BoundingBox(std::vector<Triangle> & tripool);
-
-  // accessors
-  Vec3d bmin() { return bmin_;}
-  Vec3d bmax() { return bmax_;}
-
-  Vec3d center() { return center_;}
-  Vec3d halfSides() { return halfSides_;}
-
-  double diameter() { return len(bmax_-bmin_); }
-
-  // mutators
-  void setbmin(Vec3d bmin_g) { bmin_=bmin_g; updateData();}
-  void setbmin(double x, double y, double z) { bmin_=Vec3d(x,y,z); updateData();}
-  void setbmax(Vec3d bmax_g) { bmax_=bmax_g; updateData();}
-  void setbmax(double x, double y, double z) { bmax_=Vec3d(x,y,z); updateData();}
-
-  void render();
-
-  double distanceToPoint(Vec3d point);
-
-  // sanity check bmin <= bmax
-  void verifyBox();
-
-  // expands from the center 
-  // factor of 1.0 indicates no expansion
-  void expand(double expansionFactor);
-  void regularize(); // converts the box into one with all sides equal
-
-  bool lineSegmentIntersection(Vec3d segmentStart, Vec3d segmentEnd, Vec3d * intersection);
-
-  void print();
-
-protected:
-
-  void updateData(); // updates center and half-sides
-  Vec3d center_, halfSides_;
-  Vec3d bmin_,bmax_;
-};
-}
-#endif
diff --git a/src/libobjMesh/objMesh-disjointSet.cpp b/src/libobjMesh/objMesh-disjointSet.cpp
deleted file mode 100644
index 198ca0ed28f28bba1aba064d0801fa20e60dbb7a..0000000000000000000000000000000000000000
--- a/src/libobjMesh/objMesh-disjointSet.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Yili Zhao, Jernej Barbic                                *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include "objMesh-disjointSet.h"
-
-namespace vega
-{
-DisjointSet::DisjointSet(int num):disjointSetParent(NULL),size(0)
-{
-  Resize(num);
-}
-
-DisjointSet::~DisjointSet(void)
-{
-  Destroy();
-}
-
-void DisjointSet::MakeSet(void)
-{
-  for(int i=0; i<size; i++)
-    disjointSetParent[i] = -1;  // each object is initially a root of a tree
-}
-
-void DisjointSet::Resize(int num)
-{
-  if (num < 0)
-  {
-    printf("Error in DisjointSet::Resize(int num): problem scale cannot be negtive.\n");
-    exit(0);
-  }
-  if (size >= num)
-    return;
-
-  if (size != 0)
-    free(disjointSetParent);
-  size = num;
-  disjointSetParent = (int *) malloc (sizeof(int) * size);
-}
-
-void DisjointSet::Destroy(void)
-{
-  if (size == 0)
-    return;
-  size = 0;
-  free(disjointSetParent);
-  disjointSetParent = NULL;
-}
-
-int DisjointSet::FindSet(int x)
-{
-  if (x < 0 || x >= size)
-  {
-    printf("Error in DisjointSet::FindSet(int x): the x value is illegal.\n");
-    exit(0);
-  }
-  int ancestor = x;
-  while(disjointSetParent[ancestor] >= 0) 
-    ancestor = disjointSetParent[ancestor];  // backtrack the ancestor
-  //path compression
-  while(x != ancestor)
-  {
-    int temp = disjointSetParent[x];
-    disjointSetParent[x] = ancestor;
-    x = temp;
-  }
-  return ancestor;  // here ancestor should be >=0
-}
-
-void DisjointSet::UnionSet(int x, int y)
-{
-  if (x < 0 || x >= size)
-  {
-    printf("Error in DisjointSet::UnionSet(int x, int y): the x value is illegal.\n");
-    exit(0);
-  }
-  if (y < 0 || y >= size)
-  {
-    printf("Error in DisjointSet::UnionSet(int x, int y): the y value is illegal.\n");
-    exit(0);
-  }
-  int ancestor1 = FindSet(x);
-  int ancestor2 = FindSet(y);
-  if (ancestor1 == ancestor2)
-    return; // they are already in the same set
-
-  if (disjointSetParent[ancestor1] < disjointSetParent[ancestor2]) // size of set 1 is larger
-  {
-    disjointSetParent[ancestor1] += disjointSetParent[ancestor2];
-    disjointSetParent[ancestor2] = ancestor1;
-  }
-  else  // size of set 2 is larger or equal to the size of set 1
-  {
-    disjointSetParent[ancestor2] += disjointSetParent[ancestor1];
-    disjointSetParent[ancestor1] = ancestor2;
-  }
-}
-
-}
diff --git a/src/libobjMesh/objMesh-disjointSet.h b/src/libobjMesh/objMesh-disjointSet.h
deleted file mode 100644
index 0e9105bcb9b8d826d5dbf598b20d5f5247127822..0000000000000000000000000000000000000000
--- a/src/libobjMesh/objMesh-disjointSet.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Yili Zhao, Jernej Barbic                                *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-/*
-  "Disjoint set", a union-find data structure 
-  that keeps track of a set of elements partitioned into a number of disjoint (non-overlapping) subsets.
-
-  Operations:
-  1) MakeSet: Make each set containing only a given element.
-  2) Find: Determine which set a particular element is in. Also useful for determining if two elements are in the same set.
-  3) Union: Combine or merge two sets into a single set.
-
-  Heuristics such as path compression etc. have been implemented.
-*/
-
-#ifndef _DISJOINT_SET_H_
-#define _DISJOINT_SET_H_
-
-namespace vega
-{
-class DisjointSet
-{
-public:
-
-  // constructor
-  DisjointSet(int num=0); // 'num' is the total number of elements
-
-  // destructor
-  ~DisjointSet();
-
-  // resizes the data structure to have size 'num'
-  void Resize(int num);
-
-  // makes each element be its own set (usually the first operation after the constructor)
-  void MakeSet(); 
-
-  // returns the representative of the set that x belongs to (path compression is implemented in this function as well)
-  int FindSet(int x);
-
-  // merge two sets (the smaller one will be absorbed by the larger one)
-  // x and y can be arbitrary elements, they need not be representatives
-  void UnionSet(int x, int y);
-
-protected:
-  int * disjointSetParent;
-  int size;
-
-  enum {NO_SUCCESSOR = -1};
-
-  // destroys the data structure
-  void Destroy();
-};
-}
-#endif
-
diff --git a/src/libobjMesh/objMeshEncode.cpp b/src/libobjMesh/objMeshEncode.cpp
deleted file mode 100644
index 1a626f79765e35211449f8cba59a528548589ad4..0000000000000000000000000000000000000000
--- a/src/libobjMesh/objMeshEncode.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include "objMeshEncode.h"
-
-namespace vega
-{
-void objMeshEncode(ObjMesh * mesh)
-{
-}
-
-void objMeshDecode(ObjMesh * mesh)
-{
-}
-}
diff --git a/src/libobjMesh/objMeshGraph.h b/src/libobjMesh/objMeshGraph.h
deleted file mode 100644
index abdc0fbfbefe85e98bf13efd1e55d310cf6af7a4..0000000000000000000000000000000000000000
--- a/src/libobjMesh/objMeshGraph.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef _OBJMESHGRAPH_H_
-#define _OBJMESHGRAPH_H_
-
-/*
-   Jernej Barbic, CMU, MIT, USC, 2007-2012
-
-   A graph where the nodes are obj mesh vertices, edges and faces.
-   Two nodes are connected if they are adjacent in the mesh.
-
-   There is also a static function, "GenerateVertexGraph", which computes a graph where the nodes
-   are obj mesh vertices, and two nodes are connected if they are adjacent in the mesh.
-*/
-
-#include "triple.h"
-#include "graph.h"
-#include "objMesh.h"
-
-namespace vega
-{
-class ObjMeshGraph : public Graph
-{
-public:
-  ObjMeshGraph(ObjMesh * objMesh); // will be triangulated if necessary
-
-  int GetVertexID(int meshVertex);
-  int GetEdgeID(int meshEdge);
-  int GetFaceID(int meshFace);
-   
-  //void meshID(int graphVertex, int & siteType, int & siteID);
-  // converts graph vertex to the integer indices of the particular site
-  // sitetpye: 0 = vtx, 1 = edge, 2 = face
-  void meshID(int graphVertex, int & siteType, int meshVtxData[3]);
-
-  // converts distance-field style (face, siteIndex) pair to the graph vertex ID
-  //  0: vertex0
-  //  1: vertex1
-  //  2: vertex2
-  //  3: edge among 01
-  //  4: edge among 12
-  //  5: edge among 20
-  //  6: the face itself
-  int graphID(int faceID, int siteIndex);
-
-  // Creates a graph where the nodes are obj mesh vertices. Two nodes are connected if they are adjacent in the mesh.
-  // Face clique:
-  // 0: connect every vertex to the next and previous vertex along the face perimeter (but not to other vertices of that face)
-  // 1: connect every vertex to all other face vertices
-  static Graph * GenerateVertexGraph(ObjMesh * objMesh, int faceClique=0);
-
-protected:
-  int nObj, eObj, fObj;
-  std::vector<std::pair<int, int> > meshEdgesVector; // maps each integer identifier to the corresponding edge 
-  std::vector<triple<int, int, int> > meshFaceVerticesVector; // for every mesh triangle, gives indices of its vertices
-  std::vector<triple<int, int, int> > meshFaceEdgesVector; // for every mesh triangle, gives indices of its edges
-  ObjMesh * objMesh;
-};
-
-inline int ObjMeshGraph::GetVertexID(int vertex)
-{
-  return vertex;
-}
-
-inline int ObjMeshGraph::GetEdgeID(int edge)
-{
-  return nObj + edge;
-}
-
-inline int ObjMeshGraph::GetFaceID(int face)
-{
-  return nObj + eObj + face;
-}
-}
-#endif
-
diff --git a/src/libobjMesh/objMeshRender.h b/src/libobjMesh/objMeshRender.h
deleted file mode 100644
index 181b071fc884297e69079537af7a5c77439ca0af..0000000000000000000000000000000000000000
--- a/src/libobjMesh/objMeshRender.h
+++ /dev/null
@@ -1,144 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC        *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder      *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-// Renders the obj mesh.
-// Written by Daniel Schroeder and Jernej Barbic, 2011
-
-#ifndef _OBJMESHRENDER_H_
-#define _OBJMESHRENDER_H_
-
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
-#include "openGL-headers.h"
-#include <vector>
-#include <assert.h>
-#include "objMesh.h"
-
-//flags for ObjMeshRender: 
-//geometry mode
-#define OBJMESHRENDER_TRIANGLES (1 << 0)
-#define OBJMESHRENDER_EDGES (1 << 1)
-#define OBJMESHRENDER_VERTICES (1 << 2)
-
-//rendering mode
-#define OBJMESHRENDER_NONE           (0)            /* render with only vertices */
-#define OBJMESHRENDER_FLAT           (1 << 0)       /* render with facet normals */
-#define OBJMESHRENDER_SMOOTH         (1 << 1)       /* render with vertex normals */
-#define OBJMESHRENDER_TEXTURE        (1 << 2)       /* render with texture coords */
-#define OBJMESHRENDER_COLOR          (1 << 3)       /* render with color materials */
-#define OBJMESHRENDER_MATERIAL       (1 << 4)       /* render with materials */
-#define OBJMESHRENDER_SELECTION      (1 << 5)       /* render with OpenGL selection (only applies to vertices, otherwise ignored) */
-#define OBJMESHRENDER_CUSTOMCOLOR    (1 << 6)       /* render with custom color (only applies to faces) */
-#define OBJMESHRENDER_TRANSPARENCY   (1 << 7)       /* render in two passes, to handle transparencies */
-
-//texture mode: replace vs modulate
-#define OBJMESHRENDER_LIGHTINGMODULATIONBIT 1
-#define OBJMESHRENDER_GL_REPLACE 0
-#define OBJMESHRENDER_GL_MODULATE 1
-
-//texture mode: mipmapping
-#define OBJMESHRENDER_MIPMAPBIT 2
-#define OBJMESHRENDER_GL_NOMIPMAP 0
-#define OBJMESHRENDER_GL_USEMIPMAP 2
-
-//texture mode: anisotropic filtering
-#define OBJMESHRENDER_ANISOTROPICFILTERINGBIT 4
-#define OBJMESHRENDER_GL_NOANISOTROPICFILTERING 0
-#define OBJMESHRENDER_GL_USEANISOTROPICFILTERING 4
-
-namespace vega
-{
-class ObjMeshRender
-{
-public:
-
-  class Texture
-  {
-  public:
-    Texture() : fullPath(std::string("__none")), texture(std::make_pair(false, 0)), textureMode(OBJMESHRENDER_GL_NOMIPMAP | OBJMESHRENDER_GL_MODULATE), bytesPerPixel(3) {}
-    virtual ~Texture() { if (texture.first) glDeleteTextures(1, &(texture.second)); }
-
-    static void loadTextureImage(std::string fullPath, int * width, int * height, int * bpp, unsigned char ** texData);
-
-    void loadTexture(std::string fullPath, int textureMode); // also sets up OpenGl
-
-    bool hasTexture() { return texture.first; }
-    unsigned int getTexture() { assert( texture.first ); return texture.second; }
-
-    std::string getFullPath() { return fullPath; }
-    int getTextureMode() { return textureMode; }
-    int getBytesPerPixel() { return bytesPerPixel; }
-
-  protected:
-    std::string fullPath;
-    std::pair< bool, unsigned int > texture; // OpenGL texture ID
-    int textureMode;
-    int bytesPerPixel;
-
-    static void flipImage(int width, int height, int bpp, unsigned char * image);
-  };
-
-  ObjMeshRender(ObjMesh * mesh);
-  ~ObjMeshRender();
-
-  void render(int geometryMode, int renderMode, int renderSingleGroup=-1);
-  unsigned int createDisplayList(int geometryMode, int renderMode);
-
-  // set custom colors, for OBJMESHRENDER_CUSTOMCOLOR mode
-  void setCustomColors(Vec3d color); // constant color for each mesh vertex
-  void setCustomColors(std::vector<Vec3d> colors); // specific color for every mesh vertex
-
-  void renderSpecifiedVertices(int * specifiedVertices, int numSpecifiedVertices);
-  void renderVertex(int index);
-
-  // the more specific rendering versions for various SceneObject functions
-  void renderGroup(unsigned int groupIndex, int geometryMode, int renderMode);
-  void renderGroup(char * groupName, int geometryMode, int renderMode);
-  void renderGroupEdges(char * groupName);
-
-  int numTextures();
-  int maxBytesPerPixelInTextures();
-  Texture * getTextureHandle(int textureIndex);
-  void loadTextures(int textureMode, std::vector<Texture*> * texturePool=NULL, int updatePool=0);
-
-  void renderNormals(double normalLength);
-
-  // outputs OpenGL code to render faces
-  void outputOpenGLRenderCode();
-
-protected:
-  ObjMesh * mesh;
-  std::vector<Vec3d> customColors;
-  std::vector< Texture* > textures;
-  std::vector<int> ownTexture;
-};
-}
-#endif
-
diff --git a/src/libobjMeshGPUDeformer/CMakeLists.txt b/src/libobjMeshGPUDeformer/CMakeLists.txt
deleted file mode 100644
index 4d6e4a25391d6db7e41fa2f9d7b3399350317ffb..0000000000000000000000000000000000000000
--- a/src/libobjMeshGPUDeformer/CMakeLists.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-vega_add_library(objMeshGPUDeformer
-  SOURCES
-    objMeshGPUDeformer.cpp
-    objMeshGPUDeformer_coarseToFine.cpp
-    objMeshGPUDeformer_coarseToFine_fbo.cpp
-    objMeshGPUDeformer_uUq.cpp
-    objMeshGPUDeformer_uUq_fbo.cpp
-    objMeshGPUDeformer_uUq_pbuffer.cpp
-    objMeshGPUDeformer_uUq_setLighting.cpp
-    vbo.cpp
-  PUBLIC_HEADERS
-    glh_extensions.h
-    glh_genext.h
-    objMeshGPUDeformer.h
-    objMeshGPUDeformer_coarseToFine.h
-    objMeshGPUDeformer_coarseToFine_fbo.h
-    objMeshGPUDeformer_uUq.h
-    objMeshGPUDeformer_uUq_fbo.h
-    objMeshGPUDeformer_uUq_pbuffer.h
-    objMeshGPUDeformer_uUq_setLighting.h
-    vbo.h
-)
-target_include_directories(objMeshGPUDeformer
-  PUBLIC
-    ${OPENGL_INCLUDE_DIR}
-    ${CG_INCLUDE_PATH}
-)
-if (APPLE)
-  # The nVidia installer puts the framework in /Library/Frameworks/Cg.framework
-  # but modern Xcode compilers expect frameworks to live in the SDK directory
-  # if one is specified... so add framework search paths to fix this:
-  target_compile_options(objMeshGPUDeformer
-    PUBLIC
-      -iframework "${Cg_FRAMEWORKS}/.."
-  )
-  set_target_properties(objMeshGPUDeformer
-    PROPERTIES
-    LINK_FLAGS "-F ${Cg_FRAMEWORKS}/.."
-  )
-endif()
-target_link_libraries(objMeshGPUDeformer
-  PUBLIC
-    objMesh
-    lighting
-    ${OPENGL_LIBRARIES}
-    ${CG_LIBRARY}
-    ${CG_GL_LIBRARY}
-)
diff --git a/src/libopenGLHelper/CMakeLists.txt b/src/libopenGLHelper/CMakeLists.txt
deleted file mode 100644
index 71ef522e7b4dd0b2704bf4835e2137a4531f61b2..0000000000000000000000000000000000000000
--- a/src/libopenGLHelper/CMakeLists.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-vega_add_library(openGLHelper
-  SOURCES
-    openGLHelper.cpp
-    printBitmap.cpp
-    saveScreenShot.cpp
-  PUBLIC_HEADERS
-    openGLHelper.h
-    printBitmap.h
-    saveScreenShot.h
-)
-target_link_libraries(openGLHelper
-  PUBLIC
-    matrixIO
-    imageIO
-    ${OPENGL_LIBRARIES}
-    ${GLUT_LIBRARY}
-)
diff --git a/src/libopenGLHelper/openGLHelper.h b/src/libopenGLHelper/openGLHelper.h
deleted file mode 100644
index bfcb11454b1c5011ae6aa2f7b565d15a654ffe14..0000000000000000000000000000000000000000
--- a/src/libopenGLHelper/openGLHelper.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "openGLHelper" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-/*
-  Assorted OpenGL "helper" routines.
-*/
-
-#ifndef _OPENGLHELPER_H_
-#define _OPENGLHELPER_H_
-
-#ifdef WIN32
-  #include <windows.h>
-#endif
-
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <float.h>
-#include <assert.h>
-
-#include "macros.h"
-#include "matrixIO.h"
-
-#include "openGL-headers.h"
-
-void OutputText(int x, int y, char *string);
-
-void UnitCube();
-void UnitCubeWireframe();
-void RenderWireframeBox(double bmin[3], double bmax[3]);
-
-void DrawArrow( GLfloat px, GLfloat py, GLfloat pz,
-    GLfloat nx, GLfloat ny, GLfloat nz,
-    double arrowEndWidth, double arrowEndLength );
-
-void DetermineCameraParameters(double centerX, double centerY, double centerZ, double modelRadius, double * focusX, double * focusY, double * focusZ, double * cameraRadius, double * zNear, double * zFar);
-
-void JetColorMap(double x, double color[3]);
-
-void RenderSphere(float x, float y, float z);
-void BuildSphereDisplayList(GLuint * solidSphereList, GLuint * wireSphereList);
-void TransparentSphere(GLuint solidSphereList, GLuint wireSphereList, double x, double y, double z, double radius);
-
-char * DuplicateString(char * s);
-
-void PrintGLerror( char *msg );
-
-#endif
-
diff --git a/src/libperformanceCounter/CMakeLists.txt b/src/libperformanceCounter/CMakeLists.txt
deleted file mode 100644
index ba7931cfbd889f4a637e0d2d0987872c6e758635..0000000000000000000000000000000000000000
--- a/src/libperformanceCounter/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-vega_add_library(performanceCounter
-  SOURCES
-    performanceCounter.cpp
-  PUBLIC_HEADERS
-    performanceCounter.h
-)
diff --git a/src/libperformanceCounter/performanceCounter.cpp b/src/libperformanceCounter/performanceCounter.cpp
deleted file mode 100644
index 4276cf6768b6b5076ab64092f5ddc6c95da49a3a..0000000000000000000000000000000000000000
--- a/src/libperformanceCounter/performanceCounter.cpp
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "performanceCounter.h"
-
-namespace vega {}
diff --git a/src/libpolarDecomposition/CMakeLists.txt b/src/libpolarDecomposition/CMakeLists.txt
deleted file mode 100644
index ae714f55f530eda7279fd198aa5f13dcec586059..0000000000000000000000000000000000000000
--- a/src/libpolarDecomposition/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-vega_add_library(polarDecomposition
-  SOURCES
-    polarDecomposition.cpp
-    polarDecompositionGradient.cpp
-  PUBLIC_HEADERS
-    polarDecomposition.h
-    polarDecompositionGradient.h
-)
-target_link_libraries(polarDecomposition
-  PUBLIC
-    minivector
-)
diff --git a/src/libquaternion/CMakeLists.txt b/src/libquaternion/CMakeLists.txt
deleted file mode 100644
index b84c7da3e21c65767cded5bf30f409ba83086854..0000000000000000000000000000000000000000
--- a/src/libquaternion/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-vega_add_library(quaternion
-  SOURCES
-    quaternion.cpp
-  PUBLIC_HEADERS
-    quaternion.h
-)
diff --git a/src/libreducedElasticForceModel/CMakeLists.txt b/src/libreducedElasticForceModel/CMakeLists.txt
deleted file mode 100644
index 4f5d7d04289d9a6a2b832e9fa4857f09eb367b50..0000000000000000000000000000000000000000
--- a/src/libreducedElasticForceModel/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-vega_add_library(reducedElasticForceModel
-  SOURCES
-    reducedLinearForceModel.cpp
-    reducedLinearStVKForceModel.cpp
-    reducedMassSpringSystemForceModel.cpp
-    reducedMassSpringSystemForceModelWithHessian.cpp
-    reducedSpringForceModel.cpp
-    reducedStVKForceModel.cpp
-    reducedStVKForceModelWithHessian.cpp
-    reducedSubspaceStVKForceModel.cpp
-  PUBLIC_HEADERS
-    reducedLinearForceModel.h
-    reducedLinearStVKForceModel.h
-    reducedMassSpringSystemForceModel.h
-    reducedMassSpringSystemForceModelWithHessian.h
-    reducedSpringForceModel.h
-    reducedStVKForceModel.h
-    reducedStVKForceModelWithHessian.h
-    reducedSubspaceStVKForceModel.h
-)
-target_link_libraries(reducedElasticForceModel
-  PUBLIC
-    reducedForceModel
-    reducedStvk
-    stvk
-    massSpringSystem
-    performanceCounter
-)
diff --git a/src/libreducedForceModel/CMakeLists.txt b/src/libreducedForceModel/CMakeLists.txt
deleted file mode 100644
index ecde42de65beab3f44e0e287728e057f5b286b4b..0000000000000000000000000000000000000000
--- a/src/libreducedForceModel/CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-vega_add_library(reducedForceModel
-  SOURCES
-    reducedForceModel.cpp
-    reducedForceModelWithHessian.cpp
-  PUBLIC_HEADERS
-    reducedForceModel.h
-    reducedForceModelWithHessian.h
-)
-target_link_libraries(reducedForceModel
-  PUBLIC
-#     ${LAPACK_LIBRARIES}
-#     ${BLAS_LIBRARIES}
-    modalMatrix
-)
diff --git a/src/libreducedStvk/CMakeLists.txt b/src/libreducedStvk/CMakeLists.txt
deleted file mode 100644
index b0555d33c52ef6c5995326a97bea6291f41212ee..0000000000000000000000000000000000000000
--- a/src/libreducedStvk/CMakeLists.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-set(reducedstvk_srcs
-    StVKReducedInternalForces.cpp
-    StVKReducedStiffnessMatrix.cpp
-    StVKReducedHessianTensor.cpp
-)
-set(reducedstvk_hdrs
-    StVKReducedHessianTensor.h
-    StVKReducedInternalForces.h
-    StVKReducedStiffnessMatrix.h
-)
-
-if(VegaFEM_ENABLE_PTHREADS_SUPPORT)
-  list(APPEND reducedstvk_srcs StVKReducedInternalForcesMT.cpp)
-  list(APPEND reducedstvk_hdrs StVKReducedInternalForcesMT.h)
-endif()
-
-vega_add_library(reducedStvk
-  SOURCES
-    ${reducedstvk_srcs}
-  PUBLIC_HEADERS
-    ${reducedstvk_hdrs}
-)
-target_link_libraries(reducedStvk
-  PUBLIC
-    stvk
-    modalMatrix
-    matrix
-    ${BLAS}
-    ${CBLAS_LIBRARY}
-    ${LAPACK_LIBRARIES}
-)
diff --git a/src/libreducedStvk/StVKReducedInternalForcesMT.cpp b/src/libreducedStvk/StVKReducedInternalForcesMT.cpp
deleted file mode 100644
index 15626b26b44412e885dd5a5497d0c2d89516dd16..0000000000000000000000000000000000000000
--- a/src/libreducedStvk/StVKReducedInternalForcesMT.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "reducedStvk" library , Copyright (C) 2007 CMU, 2009 MIT              *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <pthread.h>
-#include "lapack-headers.h"
-#include "matrixIO.h"
-#include "matrixMacros.h"
-#include "matrixProjection.h"
-#include "StVKReducedInternalForcesMT.h"
-
-struct StVKReducedInternalForcesMT_threadArg
-{
-  StVKReducedInternalForcesMT * stVKReducedInternalForcesMT;
-  double * targetBuffer;
-  int rank;
-};
-
-void * StVKReducedInternalForcesMT_WorkerThread(void * arg)
-{
-  struct StVKReducedInternalForcesMT_threadArg * threadArgp = (struct StVKReducedInternalForcesMT_threadArg*) arg;
-  StVKReducedInternalForcesMT * stVKReducedInternalForcesMT = threadArgp->stVKReducedInternalForcesMT;
-  double * targetBuffer = threadArgp->targetBuffer;
-  int rank = threadArgp->rank;
-  int r = stVKReducedInternalForcesMT->Getr();
-  int startElement = stVKReducedInternalForcesMT->GetStartElement(rank);
-  int endElement = stVKReducedInternalForcesMT->GetEndElement(rank);
-
-  double * target[3] = { &targetBuffer[0], &targetBuffer[r*StVKReducedInternalForces::GetLinearSize(r)], &targetBuffer[r*(StVKReducedInternalForces::GetLinearSize(r) + StVKReducedInternalForces::GetQuadraticSize(r))] };
-  stVKReducedInternalForcesMT->ProcessElements(startElement, endElement, target);
-
-  return NULL;
-}
-
-StVKReducedInternalForcesMT::StVKReducedInternalForcesMT(int r, double * U, VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity_, double g_, int numThreads_, int verbose_): 
-  StVKReducedInternalForces(r, U, volumetricMesh, precomputedABCDIntegrals, 1, addGravity_, g_, verbose_), numThreads(numThreads_)
-{
-  internalForceBuffer = (double*) malloc (sizeof(double) * numThreads * r * (linearSize + quadraticSize + cubicSize));
-
-  // split the workload
-  int numElements = volumetricMesh->getNumElements();
-  startElement = (int*) malloc (sizeof(int) * numThreads);
-  endElement = (int*) malloc (sizeof(int) * numThreads);
-
-  int remainder = numElements % numThreads;
-  // the first 'remainder' nodes will process one element more
-  int jobSize = numElements / numThreads;
-
-  for(int rank=0; rank < numThreads; rank++)
-  {
-    if (rank < remainder)
-    {
-      startElement[rank] = rank * (jobSize+1);
-      endElement[rank] = (rank+1) * (jobSize+1);
-    }
-    else
-    {
-      startElement[rank] = remainder * (jobSize+1) + (rank-remainder) * jobSize;
-      endElement[rank] = remainder * (jobSize+1) + ((rank-remainder)+1) * jobSize;
-    }
-  }
-
-  if (verbose)
-  {
-    printf("Reduced StVK coefficients computation info:\n");
-    printf("Total elements: %d \n", numElements);
-    printf("Num threads: %d \n", numThreads);
-    printf("Canonical job size: %d \n", jobSize);
-    printf("Num threads with job size augmented by one element: %d \n", remainder);
-  }
-
-  // launch threads 
-    
-  struct StVKReducedInternalForcesMT_threadArg * threadArgv = (struct StVKReducedInternalForcesMT_threadArg*) malloc (sizeof(struct StVKReducedInternalForcesMT_threadArg) * numThreads);
-
-  pthread_t * tid = (pthread_t*) malloc (sizeof(pthread_t) * numThreads);
-
-  for(int i=0; i<numThreads; i++)
-  {
-    threadArgv[i].stVKReducedInternalForcesMT = this;
-    threadArgv[i].targetBuffer = &internalForceBuffer[i * r * (linearSize + quadraticSize + cubicSize)];
-    threadArgv[i].rank = i;
-  }
-    
-  memset(internalForceBuffer, 0, sizeof(double) * numThreads * r * (linearSize + quadraticSize + cubicSize));
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_create(&tid[i], NULL, StVKReducedInternalForcesMT_WorkerThread, &threadArgv[i]) != 0)
-    {
-      printf("Error: unable to launch thread %d.\n", i);
-      exit(1);
-    }
-  }
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_join(tid[i], NULL) != 0)
-    {
-      printf("Error: unable to join thread %d.\n", i);
-      exit(1);
-    } 
-  }
-
-  free(threadArgv);
-  free(tid);
-
-  // assemble
-  memset(linearCoef_, 0, sizeof(double) * r * linearSize);
-  memset(quadraticCoef_, 0, sizeof(double) * r * quadraticSize);
-  memset(cubicCoef_, 0, sizeof(double) * r * cubicSize);
-  for(int i=0; i<numThreads; i++)
-  {
-    double * sourceLinear = &internalForceBuffer[i * r * (linearSize + quadraticSize + cubicSize)];
-    for(int j=0; j<r*linearSize; j++)
-      linearCoef_[j] += sourceLinear[j];
-
-    double * sourceQuadratic = &internalForceBuffer[i * r * (linearSize + quadraticSize + cubicSize) + r * linearSize];
-    for(int j=0; j<r*quadraticSize; j++)
-      quadraticCoef_[j] += sourceQuadratic[j];
-
-    double * sourceCubic = &internalForceBuffer[i * r * (linearSize + quadraticSize + cubicSize) + r * (linearSize + quadraticSize)];
-    for(int j=0; j<r*cubicSize; j++)
-      cubicCoef_[j] += sourceCubic[j];
-  }
-}
-
-StVKReducedInternalForcesMT::~StVKReducedInternalForcesMT()
-{
-  free(startElement);
-  free(endElement);
-  free(internalForceBuffer);
-}
-
-int StVKReducedInternalForcesMT::GetStartElement(int rank)
-{
-  return startElement[rank];
-}
-
-int StVKReducedInternalForcesMT::GetEndElement(int rank)
-{
-  return endElement[rank];
-}
-
-
diff --git a/src/librenderVolumetricMesh/CMakeLists.txt b/src/librenderVolumetricMesh/CMakeLists.txt
deleted file mode 100644
index 890c725800481554efcdf395af5729e0fc332cbb..0000000000000000000000000000000000000000
--- a/src/librenderVolumetricMesh/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-vega_add_library(renderVolumetricMesh
-  SOURCES
-    renderVolumetricMesh.cpp
-  PUBLIC_HEADERS
-    renderVolumetricMesh.h
-)
-target_link_libraries(renderVolumetricMesh
-  PUBLIC
-    volumetricMesh
-    openGLHelper
-)
diff --git a/src/librigidBodyDynamics/CMakeLists.txt b/src/librigidBodyDynamics/CMakeLists.txt
deleted file mode 100644
index 550d6ebaa8ba15f3368d708a259dac820b610bff..0000000000000000000000000000000000000000
--- a/src/librigidBodyDynamics/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-vega_add_library(rigidBodyDynamics
-  SOURCES
-    rigidBody.cpp
-    rigidBody_generalTensor.cpp
-  PUBLIC_HEADERS
-    rigidBody.h
-    rigidBody_generalTensor.h
-)
-target_link_libraries(rigidBodyDynamics
-  PUBLIC
-    quaternion
-)
diff --git a/src/libsceneObject/CMakeLists.txt b/src/libsceneObject/CMakeLists.txt
deleted file mode 100644
index 931ebe0873dcaaaaac28a391579f9dab564595f7..0000000000000000000000000000000000000000
--- a/src/libsceneObject/CMakeLists.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-set(SCOBJ_SRCS
-  sceneObject.cpp
-  sceneObject6DOF.cpp
-  sceneObjectDeformable.cpp
-  sceneObjectDeformable6DOF.cpp
-  sceneObjectWithRestPosition.cpp
-)
-set(SCOBJ_HDRS
-  sceneObject.h
-  sceneObject6DOF.h
-  sceneObjectDeformable.h
-  sceneObjectDeformable6DOF.h
-  sceneObjectWithRestPosition.h
-)
-if(CG_LIBS)
-	set(SCOBJS_SRCS
-		${SCOBJS_SRCS}
-		sceneObjectDeformableGPU.cpp
-		sceneObjectDeformableGPU6DOF.cpp
-  )
-  set(SCOBJS_HDRS
-    ${SCOBJS_HDRS}
-    sceneObjectDeformableGPU.h
-    sceneObjectDeformableGPU6DOF.h
-  )
-endif(CG_LIBS)
-
-vega_add_library(sceneObject
-  SOURCES ${SCOBJ_SRCS}
-  PUBLIC_HEADERS ${SCOBJ_HDRS}
-)
-target_link_libraries(sceneObject
-  PUBLIC
-    objMesh
-    lighting
-    minivector
-    ${GLUT_LIBRARIES}
-)
diff --git a/src/libsceneObjectReduced/CMakeLists.txt b/src/libsceneObjectReduced/CMakeLists.txt
deleted file mode 100644
index 300c7c1ab379dfd17a15693dc15758a8371db984..0000000000000000000000000000000000000000
--- a/src/libsceneObjectReduced/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-set(sceneobjreduced_srcs
-  sceneObjectReduced.cpp
-  sceneObjectReduced6DOF.cpp
-  sceneObjectReducedCPU.cpp
-  sceneObjectReducedCPU6DOF.cpp
-)
-set(sceneobjreduced_hdrs
-  sceneObjectReduced.h
-  sceneObjectReduced6DOF.h
-  sceneObjectReducedCPU.h
-  sceneObjectReducedCPU6DOF.h
-)
-if(VegaFEM_ENABLE_Cg_SUPPORT)
-  set(sceneobjreduced_srcs
-    ${sceneobjreduced_srcs}
-    sceneObjectReducedGPU.cpp
-    sceneObjectReducedGPU6DOF.cpp
-  )
-  set(sceneobjreduced_hdrs
-    ${sceneobjreduced_hdrs}
-    sceneObjectReducedGPU.h
-    sceneObjectReducedGPU6DOF.h
-  )
-endif(VegaFEM_ENABLE_Cg_SUPPORT)
-
-vega_add_library(sceneObjectReduced
-  SOURCES ${sceneobjreduced_srcs}
-  PUBLIC_HEADERS ${sceneobjreduced_hdrs}
-)
-target_link_libraries(sceneObjectReduced
-  PUBLIC
-    objMesh
-    lighting
-    minivector
-    modalMatrix
-    sceneObject
-)
-if (VegaFEM_ENABLE_Cg_SUPPORT)
-  target_link_libraries(sceneObjectReduced
-    PUBLIC
-      objMeshGPUDeformer
-  )
-endif()
diff --git a/src/libsparseMatrix/CMakeLists.txt b/src/libsparseMatrix/CMakeLists.txt
deleted file mode 100644
index cbb8758da97c06f1ed50da2a297fb8bebe063600..0000000000000000000000000000000000000000
--- a/src/libsparseMatrix/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-vega_add_library(sparseMatrix
-  SOURCES
-    sparseMatrix.cpp
-    sparseMatrixMT.cpp
-  PUBLIC_HEADERS
-    sparseMatrix.h
-    sparseMatrixMT.h
-)
-if(VegaFEM_ENABLE_OpenMP_SUPPORT)
-  target_compile_definitions(sparseMatrix
-    PUBLIC USE_OPENMP
-  )
-  target_compile_options(sparseMatrix
-    PUBLIC "${OpenMP_CXX_FLAGS}"
-  )
-endif()
diff --git a/src/libsparseSolver/CMakeLists.txt b/src/libsparseSolver/CMakeLists.txt
deleted file mode 100644
index 1ad9c372a28202dd084a2bdabfbbc957b2f8459a..0000000000000000000000000000000000000000
--- a/src/libsparseSolver/CMakeLists.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-vega_add_library(sparseSolver
-  SOURCES
-    #ARPACKSolver.cpp
-    linearSolver.cpp
-    PardisoSolver.cpp
-    SPOOLESSolver.cpp
-    SPOOLESSolverMT.cpp
-    CGSolver.cpp
-    invMKSolver.cpp
-  PUBLIC_HEADERS
-    #ARPACKSolver.h
-    CGSolver.h
-    PardisoSolver.h
-    SPOOLESSolver.h
-    SPOOLESSolverMT.h
-    invMKSolver.h
-    linearSolver.h
-    sparseSolverAvailability.h
-    sparseSolvers.h
-)
-target_link_libraries(sparseSolver
-  PUBLIC
-    sparseMatrix
-)
diff --git a/src/libsparseSolver/Makefile b/src/libsparseSolver/Makefile
deleted file mode 100755
index c9ae539a6e576a3639ceca0de9b662a82e77a236..0000000000000000000000000000000000000000
--- a/src/libsparseSolver/Makefile
+++ /dev/null
@@ -1,51 +0,0 @@
-ifndef SPARSESOLVER
-SPARSESOLVER=SPARSESOLVER
-
-ifndef CLEANFOLDER
-CLEANFOLDER=SPARSESOLVER
-endif
-
-include ../../Makefile-headers/Makefile-header
-R ?= ../..
-
-
-# the object files to be compiled for this library
-SPARSESOLVER_OBJECTS=linearSolver.o PardisoSolver.o SPOOLESSolver.o SPOOLESSolverMT.o CGSolver.o
-ifneq ($(ARPACK_LIB),)
-SPARSESOLVER_OBJECTS+=ARPACKSolver.o invMKSolver.o
-endif
-
-# the libraries this library depends on
-SPARSESOLVER_LIBS=sparseMatrix
-
-# the headers in this library
-SPARSESOLVER_HEADERS=linearSolver.h PardisoSolver.h SPOOLESSolver.h SPOOLESSolverMT.h CGSolver.h sparseSolverAvailability.h sparseSolvers.h
-ifneq ($(ARPACK_LIB),)
-SPARSESOLVER_HEADERS+=ARPACKSolver.h invMKSolver.h
-endif
-
-SPARSESOLVER_OBJECTS_FILENAMES=$(addprefix $(L)/sparseSolver/, $(SPARSESOLVER_OBJECTS))
-SPARSESOLVER_HEADER_FILENAMES=$(addprefix $(L)/sparseSolver/, $(SPARSESOLVER_HEADERS))
-SPARSESOLVER_LIB_MAKEFILES=$(call GET_LIB_MAKEFILES, $(SPARSESOLVER_LIBS))
-SPARSESOLVER_LIB_FILENAMES=$(call GET_LIB_FILENAMES, $(SPARSESOLVER_LIBS))
-
-include $(SPARSESOLVER_LIB_MAKEFILES)
-
-all: $(L)/sparseSolver/libsparseSolver.a
-
-$(L)/sparseSolver/libsparseSolver.a: $(SPARSESOLVER_OBJECTS_FILENAMES)
-	ar r $@ $^; cp $@ $(L)/lib; cp $(L)/sparseSolver/*.h $(L)/include
-
-$(SPARSESOLVER_OBJECTS_FILENAMES): %.o: %.cpp $(SPARSESOLVER_LIB_FILENAMES) $(SPARSESOLVER_HEADER_FILENAMES)
-	$(CXX) $(CXXFLAGS) -c $(INCLUDE) $(SPOOLES_INCLUDE) $(PARDISO_INCLUDE) $< -o $@
-
-ifeq ($(CLEANFOLDER), SPARSESOLVER)
-clean: cleansparseSolver
-endif
-
-deepclean: cleansparseSolver
-
-cleansparseSolver:
-	$(RM) $(SPARSESOLVER_OBJECTS_FILENAMES) $(L)/sparseSolver/libsparseSolver.a
-
-endif
diff --git a/src/libsparseSolver/PardisoSolver.cpp b/src/libsparseSolver/PardisoSolver.cpp
deleted file mode 100644
index a27991a31dee3b9e6ceb8d3f4a97be4f56abd40f..0000000000000000000000000000000000000000
--- a/src/libsparseSolver/PardisoSolver.cpp
+++ /dev/null
@@ -1,416 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-/*
-
-  Solves A * x = rhs, where A is sparse, usually large, and positive definite.
-  Uses a multi-threaded approach to perform the solve.
-
-  The solution is obtained using the the Pardiso library .
-
-  Jernej Barbic, MIT, 2007-2009
-
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "PardisoSolver.h"
-#include "sparseSolverAvailability.h"
-
-namespace vega
-{
-#ifdef PARDISO_SOLVER_IS_AVAILABLE
-
-// Pardiso solver is available
-
-#ifdef __APPLE__
-  #include "TargetConditionals.h"
-#endif
-
-#include "mkl.h"
-
-#if defined(_WIN32) || defined(_WIN64)
-  #define PARDISO pardiso
-#else
-  #define PARDISO pardiso
-#endif
-
-PardisoSolver::PardisoSolver(const SparseMatrix * A, int numThreads_, int positiveDefinite_, int directIterative_, int verbose_) : systemMatrix(A), numThreads(numThreads_), positiveDefinite(positiveDefinite_), directIterative(directIterative_), verbose(verbose_)
-{
-  mkl_set_num_threads(numThreads);
-
-  n = systemMatrix->Getn();
-
-  if (verbose >= 1)
-    printf("Converting matrix to Pardiso format...\n");
-
-  int numUpperTriangleEntries = systemMatrix->GetNumUpperTriangleEntries();
-
-  a = (double*) malloc (sizeof(double) * numUpperTriangleEntries);
-
-  ia = (int*) malloc (sizeof(int) * (systemMatrix->GetNumRows() + 1));
-
-  ja = (int*) malloc (sizeof(int) * numUpperTriangleEntries);
-
-  int upperTriangleOnly = 1;
-  int oneIndexed = 1;
-  systemMatrix->GenerateCompressedRowMajorFormat(a, ia, ja, upperTriangleOnly, oneIndexed);
-
-  if (verbose >= 2)
-    printf("numUpperTriEntries: %d\n", numUpperTriangleEntries);
-
-  // permute & do symbolic factorization
-
-  mtype = positiveDefinite ? 2 : -2;
-  nrhs = 1; /* Number of right hand sides. */
-  maxfct = 1; /* Maximum number of numerical factorizations. */
-  mnum = 1; /* Which factorization to use. */
-  msglvl = verbose >= 1 ? verbose-1 : 0; /* Print statistical information in file */
-  error = 0; /* Initialize error flag */
-
-  for (int i = 0; i < 64; i++)
-    iparm[i] = 0;
-  iparm[0] = 1; // No solver default
-  iparm[1] = 2; // 0=minimum degree ordering, 2=Fill-in reordering from METIS
-  iparm[2] = numThreads; // Numbers of processors, value of OMP_NUM_THREADS
-  iparm[3] = directIterative ? 62 : 0; //62; // No iterative-direct algorithm
-  iparm[4] = 0; // No user fill-in reducing permutation
-  iparm[5] = 0; // Write solution into x
-  iparm[6] = 0; // Not in use
-  iparm[7] = 2; // Max numbers of iterative refinement steps
-  iparm[8] = 0; // Not in use
-  iparm[9] = 8; //13; // Perturb the pivot elements with 1E-13
-  iparm[10] = 0; //1; // Use nonsymmetric permutation and scaling MPS
-  iparm[11] = 0; // Not in use
-  iparm[12] = 0; // matchings for highly indefinite symmetric matrices
-  iparm[13] = 0; // Output: Number of perturbed pivots
-  iparm[14] = 0; // Not in use
-  iparm[15] = 0; // Not in use
-  iparm[16] = 0; // Not in use
-  iparm[17] = -1; // Output: Number of nonzeros in the factor LU
-  iparm[18] = 0; // no Output: Mflops for LU factorization
-  iparm[19] = 0; // Output: Numbers of CG Iterations
-  iparm[20] = 1; // pivoting method
-
-/*
-  iparm[0] = 0;
-  iparm[2] = numThreads;
-*/
-
-  /* -------------------------------------------------------------------- */
-  /* .. Initialize the internal solver memory pointer. This is only */
-  /* necessary for the FIRST call of the PARDISO solver. */
-  /* -------------------------------------------------------------------- */
-  for (int i=0; i<64; i++)
-    pt[i] = 0;
-
-  if (verbose >= 1)
-    printf("Reordering matrix...\n");
-
-  /* -------------------------------------------------------------------- */
-  /* .. Reordering and Symbolic Factorization. This step also allocates */
-  /* all memory that is necessary for the factorization. */
-  /* -------------------------------------------------------------------- */
-  phase = 11;
-  PARDISO (pt, &maxfct, &mnum, &mtype, &phase,
-           &n, a, ia, ja, NULL, &nrhs,
-          iparm, &msglvl, NULL, NULL, &error);
-
-  if (error != 0)
-  {
-    printf("Error: Pardiso matrix re-ordering returned non-zero exit code %d.\n", error);
-    throw error;
-  }
-
-  if (verbose >= 2)
-  {
-    printf("\nReordering completed ...\n");
-    printf("Number of nonzeros in factors = %d\n", iparm[17]);
-    printf("Number of factorization MFLOPS = %d\n", iparm[18]);
-  }
-
-  this->solverType = "PARDISO";
-}
-
-PardisoSolver::~PardisoSolver()
-{
-  phase = -1;
-  PARDISO(pt, &maxfct, &mnum, &mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, NULL, NULL, &error);
-
-  if (error != 0)
-    printf("Error: Pardiso Cholesky dealloacation returned non-zero exit code %d.\n", error);
-
-  free(a);
-  free(ia);
-  free(ja);
-}
-
-void PardisoSolver::DisabledSolverError() {}
-
-MKL_INT PardisoSolver::ComputeCholeskyDecomposition(const SparseMatrix * A)
-{
-  if (directIterative)
-    return 0;
-
-  // compute the factorization
-  if (verbose >= 1)
-    printf("Factoring the %d x %d matrix (%d threads)...\n",n,n, numThreads);
-
-  int upperTriangleOnly = 1;
-  int oneIndexed = 1;
-  A->GenerateCompressedRowMajorFormat(a, NULL, NULL, upperTriangleOnly, oneIndexed);
-
-  // factor
-  phase = 22;
-  PARDISO(pt, &maxfct, &mnum, &mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, NULL, NULL,  &error);
-
-  if (error != 0)
-    printf("Error: Pardiso Cholesky decomposition returned non-zero exit code %d.\n", error);
-
-  if (verbose >= 1)
-    printf("Factorization completed.\n");
-
-  return error;
-}
-
-int PardisoSolver::SolveLinearSystem(double * x, const double * rhs)
-{
-  error = ComputeCholeskyDecomposition(this->systemMatrix);
-  if (error)
-    return error;
-
-  if (directIterative != 0)
-  {
-    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
-    return 101;
-  }
-
-  if (verbose >= 2)
-    printf("Solving linear system...(%d threads, using previously computed LU)\n", numThreads);
-
-  phase = 33;
-  PARDISO(pt, &maxfct, &mnum, &mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)rhs, x, &error);
-
-  if (error != 0)
-    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
-
-  if (verbose >= 2)
-    printf("Solve completed.\n");
-
-  return (int)error;
-}
-
-
-MKL_INT PardisoSolver::ForwardSubstitution(double * y, const double * rhs)
-{
-  if (directIterative != 0)
-  {
-    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
-    return 101;
-  }
-
-  if (verbose >= 2)
-    printf("Performing forward substitution...(%d threads, using previously computed LU)\n", numThreads);
-
-  int maxIterRefinementSteps = iparm[7];
-  iparm[7] = 0;
-
-  phase = 331;
-  PARDISO(pt, &maxfct, &mnum, &mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)rhs, y, &error);
-
-  iparm[7] = maxIterRefinementSteps;
-
-  if (error != 0)
-    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
-
-  if (verbose >= 2)
-    printf("Solve completed.\n");
-
-  return error;
-}
-
-MKL_INT PardisoSolver::DiagonalSubstitution(double * v, const double * y)
-{
-  if (directIterative != 0)
-  {
-    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
-    return 101;
-  }
-
-  if (positiveDefinite == 1)
-  {
-    printf("Error: diagonal substitution should not be used for positive-definite matrices.\n");
-    return 102;
-  }
-
-  if (verbose >= 2)
-    printf("Performing forward substitution...(%d threads, using previously computed LU)\n", numThreads);
-
-  int maxIterRefinementSteps = iparm[7];
-  iparm[7] = 0;
-
-  phase = 332;
-  PARDISO(pt, &maxfct, &mnum, &mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)y, v, &error);
-
-  iparm[7] = maxIterRefinementSteps;
-
-  if (error != 0)
-    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
-
-  if (verbose >= 2)
-    printf("Solve completed.\n");
-
-  return error;
-}
-
-MKL_INT PardisoSolver::BackwardSubstitution(double * x, const double * y)
-{
-  if (directIterative != 0)
-  {
-    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
-    return 101;
-  }
-
-  if (verbose >= 2)
-    printf("Performing forward substitution...(%d threads, using previously computed LU)\n", numThreads);
-
-  int maxIterRefinementSteps = iparm[7];
-  iparm[7] = 0;
-
-  phase = 333;
-  PARDISO(pt, &maxfct, &mnum, &mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)y, x, &error);
-
-  iparm[7] = maxIterRefinementSteps;
-
-  if (error != 0)
-    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
-
-  if (verbose >= 2)
-    printf("Solve completed.\n");
-
-  return error;
-}
-
-MKL_INT PardisoSolver::SolveLinearSystemMultipleRHS(double * x, const double * rhs, int numRHS)
-{
-  if (directIterative != 0)
-  {
-    printf("Error: direct-iterative flag was specified in the constructor (must use SolveLinearSystemDirectIterative routine).\n");
-    return 101;
-  }
-
-  if (verbose >= 2)
-    printf("Solving linear system...(%d threads, using previously computed LU)\n", numThreads);
-
-  phase = 33;
-
-  PARDISO(pt, &maxfct, &mnum, &mtype, &phase, &n, a, ia, ja, NULL, &numRHS, iparm, &msglvl, (double*)rhs, x, &error);
-
-  if (error != 0)
-    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
-
-  if (verbose >= 2)
-    printf("Solve completed.\n");
-
-  return error;
-}
-
-MKL_INT PardisoSolver::SolveLinearSystemDirectIterative(const SparseMatrix * A, double * x, const double * rhs)
-{
-  if (directIterative != 1)
-  {
-    printf("Error: direct-iterative flag was not specified in the constructor.\n");
-    return 102;
-  }
-
-  if (verbose >= 2)
-    printf("Solving linear system...(%d threads, direct-iterative)\n", numThreads);
-
-  int upperTriangleOnly = 1;
-  int oneIndexed = 1;
-  A->GenerateCompressedRowMajorFormat(a, NULL, NULL, upperTriangleOnly, oneIndexed);
-  phase = 23;
-
-  PARDISO(pt, &maxfct, &mnum, &mtype, &phase, &n, a, ia, ja, NULL, &nrhs, iparm, &msglvl, (double*)rhs, x, &error);
-
-  if (error != 0)
-    printf("Error: Pardiso solve returned non-zero exit code %d.\n", error);
-
-  if (verbose >= 2)
-    printf("Solve completed.\n");
-
-  return error;
-}
-
-#else
-
-// Pardiso Solver is not available
-
-PardisoSolver::PardisoSolver(const SparseMatrix * A, int numThreads_, int positiveDefinite_, int directIterative_, int verbose_) : numThreads(numThreads_), positiveDefinite(positiveDefinite_), directIterative(directIterative_), verbose(verbose_)
-{
-  DisabledSolverError();
-}
-
-PardisoSolver::~PardisoSolver()
-{
-  DisabledSolverError();
-}
-
-void PardisoSolver::DisabledSolverError()
-{
-  printf("Error: Pardiso solver called, but it has not been installed/compiled/enabled. After installation, enable it in \"sparseSolverAvailability.h\".\n");
-  throw 1;
-}
-
-MKL_INT PardisoSolver::ComputeCholeskyDecomposition(const SparseMatrix * A)
-{
-  DisabledSolverError();
-  return 1;
-}
-
-MKL_INT PardisoSolver::SolveLinearSystem(double * x, const double * rhs)
-{
-  DisabledSolverError();
-  return 1;
-}
-
-MKL_INT PardisoSolver::SolveLinearSystemMultipleRHS(double * x, const double * rhs, int numRHS)
-{
-  DisabledSolverError();
-  return 1;
-}
-
-MKL_INT PardisoSolver::SolveLinearSystemDirectIterative(const SparseMatrix * A, double * x, const double * rhs)
-{
-  DisabledSolverError();
-  return 1;
-}
-}
-#endif
-
diff --git a/src/libsparseSolver/PardisoSolver.h b/src/libsparseSolver/PardisoSolver.h
deleted file mode 100644
index 851aca762b7143102ab99dd61ef0557fef94c87d..0000000000000000000000000000000000000000
--- a/src/libsparseSolver/PardisoSolver.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "sparseSolver" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC   *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef _PARDISOSOLVER_H_
-#define _PARDISOSOLVER_H_
-
-/*
-
-  Solves A * x = rhs, where A is sparse, usually large, and positive definite.
-  Uses a multi-threaded approach to perform the solve.
-
-  The solution is obtained using the the Pardiso library .
-
-  Jernej Barbic, MIT, 2007-2009
-
-*/
-
-#include "linearSolver.h"
-#include "sparseMatrix.h"
-
-namespace vega
-{
-#define MKL_INT int
-
-class PardisoSolver : public LinearSolver
-{
-public:
-
-  // the constructor will compute the permutation to re-order A
-  // only the topology of A matters for this step
-  // A is not modified
-  PardisoSolver(const SparseMatrix * A, int numThreads, int positiveDefinite=0, int directIterative=0, int verbose=0);
-  virtual ~PardisoSolver();
-
-  MKL_INT ComputeCholeskyDecomposition(const SparseMatrix * A); // perform complete Cholesky factorization
-
-  // solve: A * x = rhs, using the previously computed Cholesky factorization
-  // rhs is not modified
-  virtual int SolveLinearSystem(double * x, const double * rhs);
-
-  MKL_INT SolveLinearSystemMultipleRHS(double * x, const double * rhs, int numRHS);
-
-  // solve: A * x = rhs, using the direct-iterative solver
-  MKL_INT SolveLinearSystemDirectIterative(const SparseMatrix * A, double * x, const double * rhs);
-
-  /*
-    For positiveDefinite=0, SolveLinearSystem is equivalent to: ForwardSubstitution + DiagonalSubstitution + BackwardSubstitution
-    For positiveDefinite=1, SolveLinearSystem is equivalent to: ForwardSubstitution + BackwardSubstitution (DiagonalSubstitution is not used)
-  */
-
-  MKL_INT ForwardSubstitution(double * y, const double * rhs); // L y = rhs
-  MKL_INT DiagonalSubstitution(double * v, const double * y);  // D v = y
-  MKL_INT BackwardSubstitution(double * x, const double * v);  // L^T x = v
-
-protected:
-  int n;
-  int numThreads;
-  int positiveDefinite;
-  int directIterative;
-  int verbose;
-  double * a;
-  int * ia, * ja;
-
-  void *pt[64];
-  MKL_INT iparm[64];
-  int mtype;
-  MKL_INT nrhs;
-  MKL_INT maxfct, mnum, phase, error, msglvl;
-
-  SparseMatrix *systemMatrix;
-
-  static void DisabledSolverError();
-};
-}
-#endif
-
diff --git a/src/libstvk/CMakeLists.txt b/src/libstvk/CMakeLists.txt
deleted file mode 100644
index 23637057f5413efca8e2d38098bf45eaaa06ef7f..0000000000000000000000000000000000000000
--- a/src/libstvk/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-set(stvk_srcs
-    StVKCubeABCD.cpp
-    StVKElementABCD.cpp
-    StVKElementABCDLoader.cpp
-    StVKHessianTensor.cpp
-    StVKInternalForces.cpp
-    StVKStiffnessMatrix.cpp
-    StVKTetABCD.cpp
-    StVKTetHighMemoryABCD.cpp
-)
-set(stvk_hdrs
-    StVKCubeABCD.h
-    StVKElementABCD.h
-    StVKElementABCDLoader.h
-    StVKHessianTensor.h
-    StVKInternalForces.h
-    StVKStiffnessMatrix.h
-    StVKTetABCD.h
-    StVKTetHighMemoryABCD.h
-)
-
-if(VegaFEM_ENABLE_PTHREADS_SUPPORT)
-  list(APPEND stvk_srcs
-    StVKInternalForcesMT.cpp
-    StVKStiffnessMatrixMT.cpp)
-  list(APPEND stvk_hdrs 
-    StVKInternalForcesMT.h
-    StVKStiffnessMatrixMT.h)
-endif()
-
-vega_add_library(stvk
-  SOURCES
-    ${stvk_srcs}
-  PUBLIC_HEADERS
-    ${stvk_hdrs}
-)
-target_link_libraries(stvk
-  PUBLIC
-    minivector
-    volumetricMesh
-    sparseMatrix
-    Threads::Threads
-)
diff --git a/src/libstvk/StVKInternalForcesMT.cpp b/src/libstvk/StVKInternalForcesMT.cpp
deleted file mode 100644
index b7e2201f8dce886228759743855d5237ec98ae98..0000000000000000000000000000000000000000
--- a/src/libstvk/StVKInternalForcesMT.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <pthread.h>
-#include "StVKInternalForcesMT.h"
-
-namespace vega
-{
-StVKInternalForcesMT::StVKInternalForcesMT(VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity_, double g_, int numThreads_): StVKInternalForces(volumetricMesh, precomputedABCDIntegrals, addGravity_, g_), numThreads(numThreads_) 
-{
-  internalForceBuffer = (double*) malloc (sizeof(double) * numThreads * 3 * volumetricMesh->getNumVertices());
-  energyBuffer = (double*) malloc (sizeof(double) * numThreads);
-  energyAuxBuffer = (double*) malloc (sizeof(double) * numThreads * 3 * volumetricMesh->getNumVertices());
-
-  // split the workload
-  int numElements = volumetricMesh->getNumElements();
-  startElement = (int*) malloc (sizeof(int) * numThreads);
-  endElement = (int*) malloc (sizeof(int) * numThreads);
-
-  int remainder = numElements % numThreads;
-  // the first 'remainder' nodes will process one element more
-  int jobSize = numElements / numThreads;
-
-  for(int rank=0; rank < numThreads; rank++)
-  {
-    if (rank < remainder)
-    { 
-      startElement[rank] = rank * (jobSize+1);
-      endElement[rank] = (rank+1) * (jobSize+1);
-    }      
-    else      
-    { 
-      startElement[rank] = remainder * (jobSize+1) + (rank-remainder) * jobSize;
-      endElement[rank] = remainder * (jobSize+1) + ((rank-remainder)+1) * jobSize;
-    }
-  }
-      
-  printf("Total elements: %d \n",numElements);
-  printf("Num threads: %d \n",numThreads);
-  printf("Canonical job size: %d \n",jobSize);      
-  printf("Num threads with job size augmented by one element: %d \n",remainder);
-}
-
-StVKInternalForcesMT::~StVKInternalForcesMT() 
-{
-  free(startElement);
-  free(endElement);
-  free(energyBuffer);
-  free(energyAuxBuffer);
-  free(internalForceBuffer);
-}
-
-int StVKInternalForcesMT::GetStartElement(int rank)
-{
-  return startElement[rank];
-}
-
-int StVKInternalForcesMT::GetEndElement(int rank)
-{
-  return endElement[rank];
-}
-
-struct StVKInternalForcesMT_threadArg
-{
-  StVKInternalForcesMT * stVKInternalForcesMT;
-  double * vertexDisplacements;
-  double * targetBuffer;
-  int rank;
-  int computationTarget; // 0 = force, 1 = energy
-  double * auxBuffer; // for energy computations
-};
-
-void * StVKInternalForcesMT_WorkerThread(void * arg)
-{
-  struct StVKInternalForcesMT_threadArg * threadArgp = (struct StVKInternalForcesMT_threadArg*) arg;
-  StVKInternalForcesMT * stVKInternalForcesMT = threadArgp->stVKInternalForcesMT;
-  double * vertexDisplacements = threadArgp->vertexDisplacements;
-  double * targetBuffer = threadArgp->targetBuffer;
-  int rank = threadArgp->rank;
-  int startElement = stVKInternalForcesMT->GetStartElement(rank);
-  int endElement = stVKInternalForcesMT->GetEndElement(rank);
-
-  if (threadArgp->computationTarget == 0)
-  {
-    stVKInternalForcesMT->AddLinearTermsContribution(vertexDisplacements, targetBuffer, startElement, endElement);
-    stVKInternalForcesMT->AddQuadraticTermsContribution(vertexDisplacements, targetBuffer, startElement, endElement);
-    stVKInternalForcesMT->AddCubicTermsContribution(vertexDisplacements, targetBuffer, startElement, endElement);
-  }
-
-  if (threadArgp->computationTarget == 1)
-  {
-    *targetBuffer = stVKInternalForcesMT->ComputeEnergyContribution(vertexDisplacements, startElement, endElement, threadArgp->auxBuffer);
-  }
-
-  return NULL;
-}
-
-void StVKInternalForcesMT::ComputeForces(double * vertexDisplacements, double * internalForces)
-{
-  //PerformanceCounter forceCounter;
-  Compute(0, vertexDisplacements, internalForces);
-
-  //forceCounter.StopCounter();
-  //printf("Internal forces: %G\n", forceCounter.GetElapsedTime());
-}
-
-double StVKInternalForcesMT::ComputeEnergy(double * vertexDisplacements)
-{
-  double result;
-  Compute(1, vertexDisplacements, &result);
-  return result;
-}
-
-void StVKInternalForcesMT::Compute(int computationTarget, double * vertexDisplacements, double * target)
-{
-  int numVertices3 = 3 * volumetricMesh->getNumVertices();
-  struct StVKInternalForcesMT_threadArg * threadArgv = (struct StVKInternalForcesMT_threadArg*) malloc (sizeof(struct StVKInternalForcesMT_threadArg) * numThreads);
-
-  pthread_t * tid = (pthread_t*) malloc (sizeof(pthread_t) * numThreads);
-
-  for(int i=0; i<numThreads; i++)
-  {
-    threadArgv[i].stVKInternalForcesMT = this;
-    threadArgv[i].vertexDisplacements = vertexDisplacements;
-    if (computationTarget == 0)
-      threadArgv[i].targetBuffer = &internalForceBuffer[i * numVertices3];
-    if (computationTarget == 1)
-    {
-      threadArgv[i].targetBuffer = &energyBuffer[i];
-      threadArgv[i].auxBuffer = &energyAuxBuffer[i * numVertices3];
-    }
-    threadArgv[i].rank = i;
-    threadArgv[i].computationTarget = computationTarget;
-  }
-    
-  if (computationTarget == 0)
-    memset(internalForceBuffer, 0, sizeof(double) * numVertices3 * numThreads);
-  if (computationTarget == 1)
-    memset(energyBuffer, 0, sizeof(double) * numThreads);
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_create(&tid[i], NULL, StVKInternalForcesMT_WorkerThread, &threadArgv[i]) != 0)
-    {
-      printf("Error: unable to launch thread %d.\n", i);
-      exit(1);
-    }
-  }
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_join(tid[i], NULL) != 0)
-    {
-      printf("Error: unable to join thread %d.\n", i);
-      exit(1);
-    } 
-  }
-
-  free(threadArgv);
-  free(tid);
-
-  // assemble
-  if (computationTarget == 0)
-  {
-    memset(target, 0, sizeof(double) * numVertices3);
-    for(int i=0; i<numThreads; i++)
-    {
-      double * source = &internalForceBuffer[i * numVertices3];
-      for(int j=0; j<numVertices3; j++)
-        target[j] += source[j];
-    }
-
-    if (addGravity)
-    {
-      int n = volumetricMesh->getNumVertices();
-      for(int i=0; i<3*n; i++)
-      {
-        target[i] -= gravityForce[i];
-      }
-    }
-  }
-
-  if (computationTarget == 1)
-  {
-    *target = 0;
-    for(int i=0; i<numThreads; i++)
-      *target += energyBuffer[i];
-  }
-}
-}
diff --git a/src/libstvk/StVKInternalForcesMT.h b/src/libstvk/StVKInternalForcesMT.h
deleted file mode 100644
index afc1f3db1a005bd7ec8c61eff7962fa2ebe29960..0000000000000000000000000000000000000000
--- a/src/libstvk/StVKInternalForcesMT.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#ifndef _STVKINTERNALFORCESMT_H_
-#define _STVKINTERNALFORCESMT_H_
-
-#include "StVKInternalForces.h"
-
-namespace vega
-{
-/*
-  This class is a multi-threaded version of the class "StVKInternalForces".
-  It uses POSIX threads ("pthreads") as the threading API.
-  Each thread assembles the internal force with respect to a subset of all the mesh elements. 
-  At the end, the individual results are added into a global internal force vector.
-
-  See also StVKInternalForces.h .
-*/
-
-class StVKInternalForcesMT : public StVKInternalForces
-{
-public:
-
-  // same usage as StVKInternalForces, except must specify the number of threads
-  StVKInternalForcesMT(VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity, double g, int numThreads);
-  virtual ~StVKInternalForcesMT();
-
-  virtual void ComputeForces(double * vertexDisplacements, double * internalForces);
-  virtual double ComputeEnergy(double * vertexDisplacements); 
-
-  // advanced function (tells what range of volumetric mesh elements is assigned to each thread)
-  int GetStartElement(int rank);
-  int GetEndElement(int rank);
-
-protected:
-  int numThreads;
-  int * startElement, * endElement;
-  double * internalForceBuffer;
-  double * energyBuffer;
-  double * energyAuxBuffer;
-
-  void Compute(int computationTarget, double * vertexDisplacements, double * internalForces);
-};
-}
-#endif
-
diff --git a/src/libstvk/StVKStiffnessMatrixMT.cpp b/src/libstvk/StVKStiffnessMatrixMT.cpp
deleted file mode 100644
index 285a73723c4029cf25d33a3c3a7ef9c226fa40a2..0000000000000000000000000000000000000000
--- a/src/libstvk/StVKStiffnessMatrixMT.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "StVK" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC           *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <pthread.h>
-#include "StVKStiffnessMatrixMT.h"
-
-namespace vega
-{
-StVKStiffnessMatrixMT::StVKStiffnessMatrixMT(StVKInternalForces *  stVKInternalForces, int numThreads_): StVKStiffnessMatrix(stVKInternalForces), numThreads(numThreads_) 
-{
-  SparseMatrix * stiffnessMatrixSkeleton;
-  GetStiffnessMatrixTopology(&stiffnessMatrixSkeleton);
-
-  // generate skeleton matrices
-  sparseMatrixBuffer = (SparseMatrix**) malloc (sizeof(SparseMatrix*) * numThreads);
-  for(int i=0; i<numThreads; i++)
-    sparseMatrixBuffer[i] = new SparseMatrix(*stiffnessMatrixSkeleton);
-
-  // split the workload
-  int numElements = volumetricMesh->getNumElements();
-  startElement = (int*) malloc (sizeof(int) * numThreads);
-  endElement = (int*) malloc (sizeof(int) * numThreads);
-
-  int remainder = numElements % numThreads;
-  // the first 'remainder' nodes will process one elements more
-  int jobSize = numElements / numThreads;
-
-  for(int rank=0; rank < numThreads; rank++)
-  {
-    if (rank < remainder)
-    {
-      startElement[rank] = rank * (jobSize+1);
-      endElement[rank] = (rank+1) * (jobSize+1);
-    }
-    else
-    {
-      startElement[rank] = remainder * (jobSize+1) + (rank-remainder) * jobSize;
-      endElement[rank] = remainder * (jobSize+1) + ((rank-remainder)+1) * jobSize;
-    }
-  }
-
-  delete(stiffnessMatrixSkeleton);
-
-  printf("Total elements: %d \n",numElements);
-  printf("Num threads: %d \n",numThreads);
-  printf("Canonical job size: %d \n",jobSize);
-  printf("Num threads with job size augmented by one elements: %d \n",remainder);
-}
-
-StVKStiffnessMatrixMT::~StVKStiffnessMatrixMT() 
-{
-  free(startElement);
-  free(endElement);
-  for(int i=0; i<numThreads; i++)
-    delete(sparseMatrixBuffer[i]);
-  free(sparseMatrixBuffer);
-}
-
-int StVKStiffnessMatrixMT::GetStartElement(int rank)
-{
-  return startElement[rank];
-}
-
-int StVKStiffnessMatrixMT::GetEndElement(int rank)
-{
-  return endElement[rank];
-}
-
-struct StVKStiffnessMatrixMT_threadArg
-{
-  StVKStiffnessMatrixMT * stVKStiffnessMatrixMT;
-  double * vertexDisplacements;
-  SparseMatrix * targetBuffer;
-  int rank;
-};
-
-void * StVKStiffnessMatrixMT_WorkerThread(void * arg)
-{
-  struct StVKStiffnessMatrixMT_threadArg * threadArgp = (struct StVKStiffnessMatrixMT_threadArg*) arg;
-  StVKStiffnessMatrixMT * stVKStiffnessMatrixMT = threadArgp->stVKStiffnessMatrixMT;
-  double * vertexDisplacements = threadArgp->vertexDisplacements;
-  SparseMatrix * targetBuffer = threadArgp->targetBuffer;
-  int rank = threadArgp->rank;
-  int startElement = stVKStiffnessMatrixMT->GetStartElement(rank);
-  int endElement = stVKStiffnessMatrixMT->GetEndElement(rank);
-
-  stVKStiffnessMatrixMT->AddLinearTermsContribution(vertexDisplacements, targetBuffer, startElement, endElement);
-  stVKStiffnessMatrixMT->AddQuadraticTermsContribution(vertexDisplacements, targetBuffer, startElement, endElement);
-  stVKStiffnessMatrixMT->AddCubicTermsContribution(vertexDisplacements, targetBuffer, startElement, endElement);
-
-  return NULL;
-}
-
-void StVKStiffnessMatrixMT::ComputeStiffnessMatrix(double * vertexDisplacements, SparseMatrix * sparseMatrix)
-{
-  //PerformanceCounter stiffnessCounter;
-  // launch the threads
-  struct StVKStiffnessMatrixMT_threadArg * threadArgv = (struct StVKStiffnessMatrixMT_threadArg*) malloc (sizeof(struct StVKStiffnessMatrixMT_threadArg) * numThreads);
-
-  pthread_t * tid = (pthread_t*) malloc (sizeof(pthread_t) * numThreads);
-
-  for(int i=0; i<numThreads; i++)
-  {
-    threadArgv[i].stVKStiffnessMatrixMT = this;
-    threadArgv[i].vertexDisplacements = vertexDisplacements;
-    threadArgv[i].targetBuffer = sparseMatrixBuffer[i];
-    threadArgv[i].rank = i;
-    sparseMatrixBuffer[i]->ResetToZero();
-  }
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_create(&tid[i], NULL, StVKStiffnessMatrixMT_WorkerThread, &threadArgv[i]) != 0)
-    {
-      printf("Error: unable to launch thread %d.\n", i);
-      exit(1);
-    }
-  }
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (pthread_join(tid[i], NULL) != 0)
-    {
-      printf("Error: unable to join thread %d.\n", i);
-      exit(1);
-    }
-  }
-
-  free(threadArgv);
-  free(tid);
-
-  // assemble results
-  sparseMatrix->ResetToZero();
-  for(int i=0; i<numThreads; i++)
-    *sparseMatrix += *(sparseMatrixBuffer[i]);
-
-  //stiffnessCounter.StopCounter();
-  //printf("Stiffness matrix: %G\n", stiffnessCounter.GetElapsedTime());
-}
-
-}
diff --git a/src/libvega-getopts/CMakeLists.txt b/src/libvega-getopts/CMakeLists.txt
deleted file mode 100644
index 864af5dd3211343d4805e90e37751177a2baccbb..0000000000000000000000000000000000000000
--- a/src/libvega-getopts/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-vega_add_library(vega-getopts
-  SOURCES
-    getopts.cpp
-  PUBLIC_HEADERS
-    getopts.h
-)
diff --git a/src/libvolumetricMesh/CMakeLists.txt b/src/libvolumetricMesh/CMakeLists.txt
deleted file mode 100644
index 06c9bc852bed44c1825594746acefc890eb492e5..0000000000000000000000000000000000000000
--- a/src/libvolumetricMesh/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-vega_add_library(volumetricMesh
-  SOURCES
-    volumetricMeshParser.cpp
-    generateInterpolationMatrix.cpp
-    generateMassMatrix.cpp
-    generateSurfaceMesh.cpp
-    generateMeshGraph.cpp
-    computeStiffnessMatrixNullspace.cpp
-    cubicMesh.cpp
-    tetMesh.cpp
-    volumetricMeshLoader.cpp
-    volumetricMesh.cpp
-    volumetricMeshENuMaterial.cpp
-    volumetricMeshMooneyRivlinMaterial.cpp
-    volumetricMeshExtensions.cpp
-  PUBLIC_HEADERS
-    computeStiffnessMatrixNullspace.h
-    cubicMesh.h
-    generateInterpolationMatrix.h
-    generateMassMatrix.h
-    generateMeshGraph.h
-    generateSurfaceMesh.h
-    tetMesh.h
-    volumetricMesh.h
-    volumetricMeshENuMaterial.h
-    volumetricMeshExtensions.h
-    volumetricMeshLoader.h
-    volumetricMeshMooneyRivlinMaterial.h
-    volumetricMeshParser.h
-)
-target_link_libraries(volumetricMesh
-  PUBLIC
-    sparseMatrix
-    graph
-    objMesh
-    minivector
-)
diff --git a/src/libvolumetricMesh/generateSurfaceMesh.cpp b/src/libvolumetricMesh/generateSurfaceMesh.cpp
deleted file mode 100644
index e1f470f30298ce4d24c0f2a9b62d760ea7a275f7..0000000000000000000000000000000000000000
--- a/src/libvolumetricMesh/generateSurfaceMesh.cpp
+++ /dev/null
@@ -1,495 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <float.h>
-#include "objMesh.h"
-#include "generateSurfaceMesh.h"
-#include "cubicMesh.h"
-using namespace std;
-
-// classes to disambiguate and sort faces
-
-namespace vega
-{
-class TopologicalFaceI
-{
-public:
-  inline TopologicalFaceI(int p1, int p2, int p3, int p4)
-    { vertices_.push_back(p1);
-      vertices_.push_back(p2);
-      vertices_.push_back(p3);
-      vertices_.push_back(p4); }
-
-  inline TopologicalFaceI(int p1, int p2, int p3)
-    { vertices_.push_back(p1);
-      vertices_.push_back(p2);
-      vertices_.push_back(p3); }
-
-  //accessor
-  int vertex(int i) const { return vertices_[i];} 
-  int faceDegree() const { return (int)vertices_.size(); } 
-
-  inline void sortVertices() { sort(vertices_.begin(),vertices_.end()); }
-
-protected:
-  std::vector<int> vertices_;
-};
-
-class FaceOrder
-{
-public:
-  bool operator()(const TopologicalFaceI & x, const TopologicalFaceI & y) const;
-};
-
-bool FaceOrder::operator()(const TopologicalFaceI & x, const TopologicalFaceI & y) const
-{
-  // first, sort the vertices on each face (order of vertices is irrelevant when comparing if two faces are equal)
-  TopologicalFaceI xSorted = x; 
-  xSorted.sortVertices();
-  TopologicalFaceI ySorted = y; 
-  ySorted.sortVertices();
-
-  int degx = x.faceDegree();
-  int degy = y.faceDegree();
-  int mindeg = (degx < degy) ? degx : degy;
-
-  for (int i=0; i<mindeg; i++)
-  {
-    int x1 = xSorted.vertex(i);
-    int y1 = ySorted.vertex(i);
-
-    if (x1 < y1)
-      return true;
-    if (y1 < x1)
-      return false;
-  }
-
-  return false;
-}
-
-// the main routine
-ObjMesh * GenerateSurfaceMesh::ComputeMesh(VolumetricMesh * mesh, bool triangulateOutputMesh)
-{
-  // create an empty surface mesh
-  ObjMesh * objMesh = new ObjMesh();
-
-  // create default group
-  objMesh->addGroup("Default");
-
-  // add all vertices
-  for(int i=0; i<mesh->getNumVertices(); i++)
-  {
-    Vec3d posm = *(mesh->getVertex(i));
-    Vec3d pos;
-    pos[0] = posm[0]; 
-    pos[1] = posm[1]; 
-    pos[2] = posm[2];
-    objMesh->addVertexPosition(pos);
-  }
-
-  set<TopologicalFaceI,FaceOrder> surfaceFaces;
-  //set<TopologicalFaceI,FaceOrder> interiorFaces; // not needed
-
-  // create a unique list of faces
-  surfaceFaces.clear();
-  //interiorFaces.clear();
-
-  int numElementVertices = mesh->getNumElementVertices();
-  int faceDegree = 0;
-
-  if (numElementVertices == 4)
-  {
-    faceDegree = 3;
-    triangulateOutputMesh = false;
-  }
-
-  if (numElementVertices == 8)
-    faceDegree = 4;
-
-  if (faceDegree == 0)
-  {
-    printf("Error: unsupported mesh type encountered.\n");
-    return NULL;
-  }  
-
-  // build unique list of all surface faces
-
-  if (numElementVertices == 4) // tet mesh
-  {
-    for (int i=0; i<mesh->getNumElements(); i++)
-    {
-      // compute determinant to establish orientation
-      double det = dot(*(mesh->getVertex(i, 1)) - *(mesh->getVertex(i, 0)), cross(*(mesh->getVertex(i, 2)) - *(mesh->getVertex(i, 0)), *(mesh->getVertex(i, 3)) - *(mesh->getVertex(i, 0))));
-
-      TopologicalFaceI * face;
-
-        //surfaceFaces.erase(*face);
-        //interiorFaces.insert(*face);
-  
-      #define PROCESS_FACE3(q0,q1,q2)\
-      face = new TopologicalFaceI(mesh->getVertexIndex(i,q0),mesh->getVertexIndex(i,q1),mesh->getVertexIndex(i,q2));\
-      if (surfaceFaces.find(*face) != surfaceFaces.end())\
-      {\
-        surfaceFaces.erase(*face);\
-      }\
-      else\
-      {\
-        surfaceFaces.insert(*face);\
-      }\
-      delete(face);
-  
-      if (det >= 0)
-      {
-        PROCESS_FACE3(1,2,3)
-        PROCESS_FACE3(2,0,3)
-        PROCESS_FACE3(3,0,1)
-        PROCESS_FACE3(1,0,2)
-      }
-      else
-      {
-        PROCESS_FACE3(3,2,1)
-        PROCESS_FACE3(3,0,2)
-        PROCESS_FACE3(1,0,3)
-        PROCESS_FACE3(2,0,1)
-      }
-
-      #undef PROCESS_FACE3
-    }
-  }
-
-  if (numElementVertices == 8) // cubic mesh
-  {
-    for (int i=0; i<mesh->getNumElements(); i++)
-    {
-      TopologicalFaceI * face;
-
-        //surfaceFaces.erase(*face);
-        //interiorFaces.insert(*face);
-  
-      #define PROCESS_FACE4(q0,q1,q2,q3)\
-      face = new TopologicalFaceI(mesh->getVertexIndex(i,q0),mesh->getVertexIndex(i,q1),mesh->getVertexIndex(i,q2),mesh->getVertexIndex(i,q3));\
-      if (surfaceFaces.find(*face) != surfaceFaces.end())\
-      {\
-        surfaceFaces.erase(*face);\
-      }\
-      else\
-      {\
-        surfaceFaces.insert(*face);\
-      }\
-      delete(face);
-  
-      PROCESS_FACE4(0,3,2,1)
-      PROCESS_FACE4(4,5,6,7)
-      PROCESS_FACE4(0,1,5,4)
-      PROCESS_FACE4(3,7,6,2)
-      PROCESS_FACE4(1,2,6,5)
-      PROCESS_FACE4(0,4,7,3)
-
-      #undef PROCESS_FACE4
-    }
-  }
-
-  // now, surfaceFaces contains a unique list of all surface faces
-  // add all faces to the surface mesh 
-  int * index = (int*) malloc (sizeof(int) * faceDegree);
-  set<TopologicalFaceI,FaceOrder>::iterator face;
-  for (face = surfaceFaces.begin(); face != surfaceFaces.end(); ++face) // all surface faces
-  {
-    for (int i=0; i<faceDegree; i++)
-      index[i] = face->vertex(i);
-
-    std::pair< bool, unsigned int > texPos(false,0); // no textures
-    std::pair< bool, unsigned int > normal(false,0); // no normals
-
-    if (triangulateOutputMesh)
-    {
-      // triangulate the face into two triangles
-      ObjMesh::Face newFace1;
-      newFace1.addVertex( ObjMesh::Vertex( index[0], texPos, normal ) );
-      newFace1.addVertex( ObjMesh::Vertex( index[1], texPos, normal ) );
-      newFace1.addVertex( ObjMesh::Vertex( index[2], texPos, normal ) );
-
-      ObjMesh::Face newFace2;
-      newFace2.addVertex( ObjMesh::Vertex( index[2], texPos, normal ) );
-      newFace2.addVertex( ObjMesh::Vertex( index[3], texPos, normal ) );
-      newFace2.addVertex( ObjMesh::Vertex( index[0], texPos, normal ) );
- 
-      objMesh->addFaceToGroup(newFace1,0);
-      objMesh->addFaceToGroup(newFace2,0);
-    }
-    else
-    {
-      ObjMesh::Face newFace1;
-      for(int i=0; i<faceDegree; i++)
-        newFace1.addVertex( ObjMesh::Vertex( index[i], texPos, normal ) );
-      objMesh->addFaceToGroup(newFace1, 0);
-    }
-  }
-
-  free(index);
-
-  if (mesh->getElementType() == CubicMesh::elementType())
-  {
-    // cubic mesh
-    objMesh->setNormalsToFaceNormals();
-  }
-  else
-  {
-    // other types of meshes (e.g., tet)
-    objMesh->computePseudoNormals();
-    objMesh->setNormalsToPseudoNormals();
-  }
-
-  objMesh->setSingleMaterial(ObjMesh::Material());
-
-  return objMesh;
-}
-
-// advanced routine, not used very often
-ObjMesh * GenerateSurfaceMesh::ComputeMesh(VolumetricMesh * mesh, ObjMesh * superMesh, bool triangulateOutputMesh)
-{
-  // for each volumetric mesh vertex, find the nearest obj file vertex
-  int * closestObjVertex = (int*) malloc (sizeof(int) * mesh->getNumVertices());
-  for(int i=0; i<mesh->getNumVertices(); i++)
-  {
-    Vec3d pos = *(mesh->getVertex(i));
-    double dist;
-    closestObjVertex[i] = superMesh->getClosestVertex(pos, &dist);
-  }
-
-  // build the list of triangles
-  set<TopologicalFaceI,FaceOrder> superMeshFaces;
-  for(unsigned int i=0; i < superMesh->getNumGroups(); i++)
-  {
-    const ObjMesh::Group * groupHandle = superMesh->getGroupHandle(i);
-    for(unsigned int iFace = 0; iFace < groupHandle->getNumFaces(); iFace++)
-    {
-      const ObjMesh::Face * faceHandle = groupHandle->getFaceHandle(iFace);
-      if (faceHandle->getNumVertices() != 3)
-      {
-        printf("Error: input superMesh is not triangulated.\n");
-        free(closestObjVertex);
-        return NULL;
-      }
-
-      superMeshFaces.insert(TopologicalFaceI(faceHandle->getVertexHandle(0)->getPositionIndex(), faceHandle->getVertexHandle(1)->getPositionIndex(), faceHandle->getVertexHandle(2)->getPositionIndex()));
-    }
-  }
-
-  // create empty surface mesh
-  ObjMesh * objMesh = new ObjMesh();
-
-  // create default group
-  objMesh->addGroup("Default");
-
-  // add all vertices
-  for(int i=0; i<mesh->getNumVertices(); i++)
-  {
-    Vec3d posm = *(mesh->getVertex(i));
-    Vec3d pos;
-    pos[0] = posm[0]; 
-    pos[1] = posm[1]; 
-    pos[2] = posm[2];
-    objMesh->addVertexPosition(pos);
-  }
-
-  set<TopologicalFaceI,FaceOrder> surfaceFaces;
-  //set<TopologicalFaceI,FaceOrder> interiorFaces;
-
-  // create a unique list of faces
-  surfaceFaces.clear();
-  //interiorFaces.clear();
-
-  int numElementVertices = mesh->getNumElementVertices();
-  int faceDegree = 0;
-
-  if (numElementVertices == 4)
-  {
-    faceDegree = 3;
-    triangulateOutputMesh = false;
-  }
-
-  if (numElementVertices == 8)
-    faceDegree = 4;
-
-  if (faceDegree == 0)
-  {
-    printf("Error: unsupported mesh type encountered.\n");
-    free(closestObjVertex);
-    return NULL;
-  }  
-
-  // build unique list of all surface faces
-
-  if (numElementVertices == 4)
-  {
-    for (int i=0; i<mesh->getNumElements(); i++)
-    {
-      // compute determinant to establish orientation
-      double det = dot(*(mesh->getVertex(i, 1)) - *(mesh->getVertex(i, 0)), cross(*(mesh->getVertex(i, 2)) - *(mesh->getVertex(i, 0)), *(mesh->getVertex(i, 3)) - *(mesh->getVertex(i, 0))));
-
-      TopologicalFaceI * face;
-
-        //surfaceFaces.erase(*face);
-        //interiorFaces.insert(*face);
-  
-      #define PROCESS_FACE3(q0,q1,q2)\
-      face = new TopologicalFaceI(mesh->getVertexIndex(i,q0),mesh->getVertexIndex(i,q1),mesh->getVertexIndex(i,q2));\
-      if (surfaceFaces.find(*face) != surfaceFaces.end())\
-      {\
-        surfaceFaces.erase(*face);\
-      }\
-      else\
-      {\
-        surfaceFaces.insert(*face);\
-      }\
-      delete(face);
-  
-      if (det >= 0)
-      {
-        PROCESS_FACE3(1,2,3)
-        PROCESS_FACE3(2,0,3)
-        PROCESS_FACE3(3,0,1)
-        PROCESS_FACE3(1,0,2)
-      }
-      else
-      {
-        PROCESS_FACE3(3,2,1)
-        PROCESS_FACE3(3,0,2)
-        PROCESS_FACE3(1,0,3)
-        PROCESS_FACE3(2,0,1)
-      }
-
-      #undef PROCESS_FACE3
-    }
-  }
-
-  if (numElementVertices == 8)
-  {
-    for (int i=0; i<mesh->getNumElements(); i++)
-    {
-      TopologicalFaceI * face;
-
-        //surfaceFaces.erase(*face);
-        //interiorFaces.insert(*face);
-  
-      #define PROCESS_FACE4(q0,q1,q2,q3)\
-      face = new TopologicalFaceI(mesh->getVertexIndex(i,q0),mesh->getVertexIndex(i,q1),mesh->getVertexIndex(i,q2),mesh->getVertexIndex(i,q3));\
-      if (surfaceFaces.find(*face) != surfaceFaces.end())\
-      {\
-        surfaceFaces.erase(*face);\
-      }\
-      else\
-      {\
-        surfaceFaces.insert(*face);\
-      }\
-      delete(face);
-  
-      PROCESS_FACE4(0,3,2,1)
-      PROCESS_FACE4(4,5,6,7)
-      PROCESS_FACE4(0,1,5,4)
-      PROCESS_FACE4(3,7,6,2)
-      PROCESS_FACE4(1,2,6,5)
-      PROCESS_FACE4(0,4,7,3)
-
-      #undef PROCESS_FACE4
-    }
-  }
-
-  // now, surfaceFaces contains a unique list of all surface faces
-  // erase any faces that are not also superMesh faces
-  set<TopologicalFaceI,FaceOrder> trueSurfaceFaces;
-  for(set<TopologicalFaceI,FaceOrder> :: iterator iter = surfaceFaces.begin(); iter != surfaceFaces.end(); iter++)
-  {
-    int vtx0 = closestObjVertex[iter->vertex(0)];
-    int vtx1 = closestObjVertex[iter->vertex(1)];
-    int vtx2 = closestObjVertex[iter->vertex(2)];
-    if (superMeshFaces.find(TopologicalFaceI(vtx0, vtx1, vtx2)) != superMeshFaces.end())
-      trueSurfaceFaces.insert(*iter);
-  }
-
-  surfaceFaces = trueSurfaceFaces;
-
-  // add all faces to the surface obj mesh of the voxel mesh
-  int * index = (int*) malloc (sizeof(int) * faceDegree);
-  set<TopologicalFaceI,FaceOrder>::iterator face;
-  for (face = surfaceFaces.begin(); face != surfaceFaces.end(); ++face) // all surface faces
-  {
-    for (int i=0; i<faceDegree; i++)
-      index[i] = face->vertex(i);
-
-    std::pair< bool, unsigned int > texPos(false,0); // no textures
-    std::pair< bool, unsigned int > normal(false,0); // no normals
-
-    if (triangulateOutputMesh)
-    {
-      // triangulate the face into two triangles
-      ObjMesh::Face newFace1;
-      newFace1.addVertex( ObjMesh::Vertex( index[0], texPos, normal ) );
-      newFace1.addVertex( ObjMesh::Vertex( index[1], texPos, normal ) );
-      newFace1.addVertex( ObjMesh::Vertex( index[2], texPos, normal ) );
-
-      ObjMesh::Face newFace2;
-      newFace2.addVertex( ObjMesh::Vertex( index[2], texPos, normal ) );
-      newFace2.addVertex( ObjMesh::Vertex( index[3], texPos, normal ) );
-      newFace2.addVertex( ObjMesh::Vertex( index[0], texPos, normal ) );
- 
-      objMesh->addFaceToGroup(newFace1,0);
-      objMesh->addFaceToGroup(newFace2,0);
-    }
-    else
-    {
-      ObjMesh::Face newFace1;
-      for(int i=0; i<faceDegree; i++)
-        newFace1.addVertex( ObjMesh::Vertex( index[i], texPos, normal ) );
-      objMesh->addFaceToGroup(newFace1, 0);
-    }
-  }
-
-  free(index);
-
-  if (mesh->getElementType() == CubicMesh::elementType())
-  {
-    // cubic mesh
-    objMesh->setNormalsToFaceNormals();
-  }
-  else
-  {
-    // other types of meshes (e.g., tet)
-    objMesh->computePseudoNormals();
-    objMesh->setNormalsToPseudoNormals();
-  }
-
-  free(closestObjVertex);
-
-  objMesh->setSingleMaterial(ObjMesh::Material());
-
-  return objMesh;
-}
-
-}
diff --git a/src/libvolumetricMesh/volumetricMesh.cpp b/src/libvolumetricMesh/volumetricMesh.cpp
deleted file mode 100644
index 07fc915f38258c406d08c371f7517e7678ec7ebd..0000000000000000000000000000000000000000
--- a/src/libvolumetricMesh/volumetricMesh.cpp
+++ /dev/null
@@ -1,1829 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "volumetricMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2013 USC *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This library is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This library is distributed in the hope that it will be useful,       *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-#include <float.h>
-#include <string.h>
-#include <assert.h>
-#include "volumetricMeshParser.h"
-#include "volumetricMesh.h"
-#include "volumetricMeshENuMaterial.h"
-#include "volumetricMeshMooneyRivlinMaterial.h"
-using namespace std;
-
-namespace vega
-{
-double VolumetricMesh::E_default = 1E9;
-double VolumetricMesh::nu_default = 0.45;
-double VolumetricMesh::density_default = 1000;
-
-// parses the mesh, and returns the string corresponding to the element type
-VolumetricMesh::VolumetricMesh(char * filename, int numElementVertices_, int verbose, elementType * elementType_): numElementVertices(numElementVertices_)
-{
-  if (verbose)
-    printf("Opening file %s.\n", filename); fflush(NULL);
-
-  // create buffer for element vertices
-  int * v = (int*) malloc (sizeof(int) * numElementVertices);
-
-  // parse the .veg file
-  VolumetricMeshParser volumetricMeshParser;
-
-  if (volumetricMeshParser.open(filename) != 0)
-  {
-    printf("Error: could not open file %s.\n",filename);
-    free(v);
-    throw 1;
-  }
-
-  // === First pass: parse vertices and elements, and count the number of materials, sets and regions  ===
-
-  int countNumVertices = 0;
-  int countNumElements = 0;
-
-  numElements = -1;
-  numMaterials = 0;
-  numSets = 1; // set 0 is "allElements"
-  numRegions = 0;
-  *elementType_ = INVALID;
-  int parseState = 0;
-  char lineBuffer[1024];
-
-  int oneIndexedVertices = 1;
-  int oneIndexedElements = 1;
-  while (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
-  {
-    //lineBuffer now contains the next line
-    //printf("%s\n", lineBuffer);
-
-    // find *VERTICES
-    if ((parseState == 0) && (strncmp(lineBuffer, "*VERTICES", 9) == 0))
-    {
-      parseState = 1;
-
-      if (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
-      {
-        // format is numVertices, 3, 0, 0
-        sscanf(lineBuffer, "%d", &numVertices);  // ignore 3, 0, 0
-        vertices = (Vec3d**) malloc (sizeof(Vec3d*) * numVertices);
-      }
-      else
-      {
-        printf("Error: file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
-        free(v);
-        throw 2;
-      }
-  
-      continue;
-    }
-
-    // find *ELEMENTS
-    if ((parseState == 1) && (strncmp(lineBuffer, "*ELEMENTS", 9) == 0))
-    {
-      parseState = 2;
-
-      // parse element type
-      if (volumetricMeshParser.getNextLine(lineBuffer) != NULL)
-      {
-        volumetricMeshParser.removeWhitespace(lineBuffer);
-
-        if (strncmp(lineBuffer, "TET", 3) == 0)
-          *elementType_ = TET;
-        else if (strncmp(lineBuffer, "CUBIC", 5) == 0)
-          *elementType_ = CUBIC;
-        else
-        {
-          printf("Error: unknown mesh type %s in file %s\n", lineBuffer, filename);
-          free(v);
-          throw 3;
-        }
-      }
-      else
-      {
-        printf("Error: file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
-        free(v);
-        throw 4;
-      }
-
-      // parse the number of elements
-      if (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
-      {
-        // format is: numElements, numElementVertices, 0
-        sscanf(lineBuffer, "%d", &numElements);  // only use numElements; ignore numElementVertices, 0
-        elements = (int**) malloc (sizeof(int*) * numElements);
-      }
-      else
-      {
-        printf("Error: file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
-        free(v);
-        throw 5;
-      }
-
-      continue;
-    }
-
-    if ((parseState == 2) && (lineBuffer[0] == '*'))
-    {
-      parseState = 3; // end of elements
-    }
-
-    if (parseState == 1)
-    {
-      // read the vertex position
-      if (countNumVertices >= numVertices)
-      {
-        printf("Error: mismatch in the number of vertices in %s.\n", filename);
-        free(v);
-        throw 6;
-      }
-
-      // ignore space, comma or tab
-      char * ch = lineBuffer;
-      while((*ch == ' ') || (*ch == ',') || (*ch == '\t'))
-        ch++;
-
-      int index;
-      sscanf(ch, "%d", &index);
-      // seek next separator
-      while((*ch != ' ') && (*ch != ',') && (*ch != '\t') && (*ch != 0))
-        ch++;
-
-      if (index == 0)
-        oneIndexedVertices = 0; // input mesh has 0-indexed vertices
-
-      double pos[3];
-      for(int i=0; i<3; i++)
-      {
-        // ignore space, comma or tab
-        while((*ch == ' ') || (*ch == ',') || (*ch == '\t'))
-          ch++;
-
-        if (*ch == 0)
-        {
-          printf("Error parsing line %s in file %s.\n", lineBuffer, filename);
-          free(v);
-          throw 7;
-        }
-
-        sscanf(ch, "%lf", &pos[i]);
- 
-        // seek next separator
-        while((*ch != ' ') && (*ch != ',') && (*ch != '\t') && (*ch != 0))
-          ch++;
-      }
-
-      vertices[countNumVertices] = new Vec3d(pos);
-      countNumVertices++;
-    }
-
-    if (parseState == 2)
-    {
-      // read the element vertices
-      if (countNumElements >= numElements)
-      {
-        printf("Error: mismatch in the number of elements in %s.\n", filename);
-        throw 8;
-      }
-
-      // ignore space, comma or tab
-      char * ch = lineBuffer;
-      while((*ch == ' ') || (*ch == ',') || (*ch == '\t'))
-        ch++;
-
-      int index;
-      sscanf(ch, "%d", &index);
-
-      if (index == 0)
-        oneIndexedElements = 0; // input mesh has 0-indexed elements
-
-      // seek next separator
-      while((*ch != ' ') && (*ch != ',') && (*ch != '\t') && (*ch != 0))
-        ch++;
-
-      for(int i=0; i<numElementVertices; i++)
-      {
-        // ignore space, comma or tab
-        while((*ch == ' ') || (*ch == ',') || (*ch == '\t'))
-          ch++;
-
-        if (*ch == 0)
-        {
-          printf("Error parsing line %s in file %s.\n", lineBuffer, filename);
-          free(v);
-          throw 9;
-        }
-
-        sscanf(ch, "%d", &v[i]);
-
-        // seek next separator
-        while((*ch != ' ') && (*ch != ',') && (*ch != '\t') && (*ch != 0))
-          ch++;
-      }
-      
-      // if vertices were 1-numbered in the .veg file, convert to 0-numbered
-      for (int k=0; k<numElementVertices; k++)
-        v[k] -= oneIndexedVertices;
-
-      elements[countNumElements] = (int*) malloc (sizeof(int) * numElementVertices);
-      for(int j=0; j<numElementVertices; j++)
-        elements[countNumElements][j] = v[j];
-
-      countNumElements++;
-    }
-
-    if (strncmp(lineBuffer, "*MATERIAL", 9) == 0)
-    {
-      numMaterials++;
-      continue;
-    }
-
-    if (strncmp(lineBuffer, "*SET", 4) == 0)
-    {
-      numSets++;
-      continue;
-    }
-
-    if (strncmp(lineBuffer, "*REGION", 7) == 0)
-    {
-      numRegions++;
-      continue;
-    }
-  }
-
-  if (numElements < 0)
-  {
-    printf("Error: incorrect number of elements.  File %s may not be in the .veg format.\n", filename);
-    throw 10;
-  }
-
-  // === Second pass: parse materials, sets and regions ===
-
-  volumetricMeshParser.rewindToStart();
-
-  if (verbose)
-  {
-    if (numMaterials == 0)
-      printf("Warning: no materials encountered in %s.\n", filename);
-
-    if (numRegions == 0)
-      printf("Warning: no regions encountered in %s.\n", filename);
-  }
-
-  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
-  sets = (Set**) malloc (sizeof(Set*) * numSets);
-  regions = (Region**) malloc (sizeof(Region*) * numRegions);
-
-  // create the "allElements" set, containing all the elements
-  sets[0] = new Set("allElements");
-  for(int el=0; el<numElements; el++)
-    sets[0]->insert(el);
-
-  int countNumMaterials = 0;
-  int countNumSets = 1; // set 0 is "allElements"
-  int countNumRegions = 0;
-  parseState = 0;
-
-  while (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
-  {
-    //printf("%s\n", lineBuffer);
-
-     // exit comma separated set parseState upon the new command
-    if ((parseState == 11) && (lineBuffer[0] == '*'))
-      parseState = 0;
-
-    // parse material
-
-    if ((parseState == 0) && (strncmp(lineBuffer, "*MATERIAL", 9) == 0))
-    {
-      volumetricMeshParser.removeWhitespace(lineBuffer);
-
-      // read material name
-      char materialNameC[4096];
-      strcpy(materialNameC, &lineBuffer[9]);
-
-      // read the material type
-      char materialType[4096];
-      if (volumetricMeshParser.getNextLine(lineBuffer) != NULL)
-      {
-        volumetricMeshParser.removeWhitespace(lineBuffer);
-        sscanf(lineBuffer, "%s", materialType);
-      }
-      else
-      {
-        printf("Error: incorrect material in file %s. Offending line:\n%s\n", filename, lineBuffer);
-        free(v);
-        throw 11;
-      }
-
-      if (strncmp(materialType, "ENU", 3) == 0)
-      {
-        // seek for first comma
-        char * ch = lineBuffer;
-        while((*ch != ',') && (*ch != 0))
-          ch++;
-
-        if (*ch == 0)
-        {
-          printf("Error parsing file %s. Offending line: %s.\n", filename, lineBuffer);
-          free(v);
-          throw 12;
-        }
-
-        ch++;
-
-        // material specified by E, nu, density
-        double density, E, nu;
-        sscanf(ch, "%lf,%lf,%lf", &density, &E, &nu);
-
-        if ((E > 0) && (nu > -1.0) && (nu < 0.5) && (density > 0))
-        {
-          // create new material
-          materials[countNumMaterials] = new ENuMaterial(string(materialNameC), density, E, nu);
-        }
-        else
-        {
-          printf("Error: incorrect material specification in file %s. Offending line: %s\n", filename, lineBuffer);
-          free(v);
-          throw 13;
-        }
-      }
-      else if (strncmp(materialType, "MOONEYRIVLIN", 12) == 0)
-      {
-        // seek for first comma
-        char * ch = lineBuffer;
-        while((*ch != ',') && (*ch != 0))
-          ch++;
-
-        if (*ch == 0)
-        {
-          printf("Error parsing file %s. Offending line: %s.\n", filename, lineBuffer);
-          free(v);
-          throw 14;
-        }
-
-        ch++;
-
-        // mu01, m10, v1, density
-        double density, mu01, mu10, v1;
-        sscanf(ch, "%lf,%lf,%lf,%lf", &density, &mu01, &mu10, &v1);
-
-        if (density > 0)
-        {
-          // create new material
-          materials[countNumMaterials] = new MooneyRivlinMaterial(string(materialNameC), density, mu01, mu10, v1);
-        }
-        else
-        {
-          printf("Error: incorrect material specification in file %s. Offending line:\n%s\n", filename, lineBuffer);
-          free(v);
-          throw 15;
-        }
-      }
-      else
-      {
-        printf("Error: incorrect material specification in file %s. Offending line:\n%s\n", filename, lineBuffer);
-        free(v);
-        throw 16;
-      }
-
-      countNumMaterials++;
-    }
-
-    // parse region
-
-    if ((parseState == 0) && (strncmp(lineBuffer, "*REGION,", 7) == 0))
-    {
-      volumetricMeshParser.removeWhitespace(lineBuffer);
-
-      char setNameC[4096];
-      char materialNameC[4096];
-
-      if (volumetricMeshParser.getNextLine(lineBuffer) != NULL)
-      {
-        volumetricMeshParser.removeWhitespace(lineBuffer);
-
-        // format is set, material
-        sscanf(lineBuffer, "%s,%s", setNameC, materialNameC);
-
-        // seek for first comma
-        char * ch = lineBuffer;
-        while((*ch != ',') && (*ch != 0))
-          ch++;
-
-        if (*ch == 0)
-        {
-          printf("Error parsing file %s. Offending line: %s.\n", filename, lineBuffer);
-          free(v);
-          throw 17;
-        }
-
-        *ch = 0;
-        strcpy(setNameC, lineBuffer);
-        ch++;
-        strcpy(materialNameC, ch);
-      }
-      else
-      {
-        printf("Error: file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
-        free(v);
-        throw 18;
-      }
-
-      // seek for setName
-      int setNum = -1;
-      for(int set=0; set < numSets; set++)
-      {
-        string name = sets[set]->getName();
-        if (name == string(setNameC))
-        {
-          setNum = set;
-          break;
-        }
-      }
-      if (setNum == -1)
-      {
-        printf("Error: set %s not found among the sets.\n", setNameC);
-        free(v);
-        throw 19;
-      }
-
-      // seek for materialName
-      int materialNum = -1;
-      for(int material=0; material < numMaterials; material++)
-      {
-        string name = materials[material]->getName();
-        if (name == string(materialNameC))
-        {
-          materialNum = material;
-          break;
-        }
-      }
-      if (materialNum == -1)
-      {
-        printf("Error: material %s not found among the materials.\n", materialNameC);
-        free(v);
-        throw 20;
-      }
-
-      regions[countNumRegions] = new Region(materialNum, setNum);
-      countNumRegions++;
-    }
-   
-    // parse set
-
-    if ((parseState == 0) && (strncmp(lineBuffer, "*SET", 4) == 0))
-    {
-      volumetricMeshParser.removeWhitespace(lineBuffer);
-
-      char setNameC[4096];
-      strcpy(setNameC, &lineBuffer[4]);
-      sets[countNumSets] = new Set(string(setNameC));
-      countNumSets++;
-      parseState = 11;
-    }
-
-    if (parseState == 11)
-    {
-      // we know that lineBuffer[0] != '*' (i.e., not end of the list) as that case was already previously handled
-
-      volumetricMeshParser.removeWhitespace(lineBuffer);
-      //printf("%s\n", lineBuffer);
-
-      // parse the comma separated line
-      char * pch;
-      pch = strtok(lineBuffer, ",");
-      while ((pch != NULL) && (isdigit(*pch)))
-      {
-        int newElement = atoi(pch);
-        sets[countNumSets-1]->insert(newElement-oneIndexedElements); // sets are 0-indexed, but .veg files may be 1-indexed (oneIndexedElements == 1)
-        pch = strtok (NULL, ",");
-      }
-    }
-  }
-
-  // === assign materials to elements ===
-
-  elementMaterial = (int*) malloc (sizeof(int) * numElements);
-  for(int el=0; el<numElements; el++)
-    elementMaterial[el] = numMaterials;
-
-  PropagateRegionsToElements();
-
-  // seek for unassigned elements
-  set<int> unassignedElements;
-  for(int el=0; el<numElements; el++)
-  {
-    if (elementMaterial[el] == numMaterials)
-      unassignedElements.insert(el);
-  }
-
-  if (unassignedElements.size() > 0)
-  {
-    // assign set and region for the unnassigned elements
-
-    // create a material if none exists
-    if (numMaterials == 0)
-    {
-      numMaterials++;
-      materials = (Material**) realloc (materials, sizeof(Material*) * numMaterials);
-      materials[numMaterials - 1] = new ENuMaterial("defaultMaterial", E_default, nu_default, density_default);
-    }
-
-    numSets++;
-    sets = (Set**) realloc (sets, sizeof(Set*) * numSets);
-    sets[numSets-1] = new Set("defaultSet"); 
-    for(set<int>::iterator iter = unassignedElements.begin(); iter != unassignedElements.end(); iter++)
-      sets[numSets-1]->insert(*iter); 
-
-    numRegions++;
-    regions = (Region**) realloc (regions, sizeof(Region*) * numRegions);
-    regions[numRegions - 1] = new Region(numMaterials - 1, numSets - 1);
-
-    if (verbose)
-      printf("Warning: %d elements were not found in any of the regions. Using default material parameters for these elements.\n", (int)unassignedElements.size());
-  }
-
-  volumetricMeshParser.close();
-
-  free(v);
-}
-
-VolumetricMesh::VolumetricMesh(int numVertices_, double * vertices_,
-               int numElements_, int numElementVertices_, int * elements_,
-               double E, double nu, double density): numElementVertices(numElementVertices_)
-{
-  numElements = numElements_;
-  numVertices = numVertices_;
-
-  numMaterials = 1;
-  numSets = 1;
-  numRegions = 1;
-
-  vertices = (Vec3d**) malloc (sizeof(Vec3d*) * numVertices);
-  elements = (int**) malloc (sizeof(int*) * numElements);
-  elementMaterial = (int*) malloc (sizeof(int) * numElements);
-  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
-  sets = (Set**) malloc (sizeof(Set*) * numSets);
-  regions = (Region**) malloc (sizeof(Region*) * numRegions);
-
-  for(int i=0; i<numVertices; i++)
-    vertices[i] = new Vec3d(vertices_[3*i+0], vertices_[3*i+1], vertices_[3*i+2]);
-
-  Material * material = new ENuMaterial("defaultMaterial", density, E, nu);
-  materials[0] = material;
-
-  Set * set = new Set("allElements");
-
-  int * v = (int*) malloc (sizeof(int) * numElementVertices);
-  for(int i=0; i<numElements; i++)
-  {
-    set->insert(i);
-    elements[i] = (int*) malloc (sizeof(int) * numElementVertices);
-    elementMaterial[i] = 0;
-    for(int j=0; j<numElementVertices; j++)
-    {
-      v[j] = elements_[numElementVertices * i + j];
-      elements[i][j] = v[j];
-    }
-  }
-  free(v);
-
-  sets[0] = set;
-  Region * region = new Region(0, 0);
-  regions[0] = region;
-}
-
-VolumetricMesh::VolumetricMesh(int numVertices_, double * vertices_,
-         int numElements_, int numElementVertices_, int * elements_,
-         int numMaterials_, Material ** materials_,
-         int numSets_, Set ** sets_,
-         int numRegions_, Region ** regions_): numElementVertices(numElementVertices_)
-{
-  numElements = numElements_;
-  numVertices = numVertices_;
-
-  numMaterials = numMaterials_;
-  numSets = numSets_;
-  numRegions = numRegions_;
-
-  vertices = (Vec3d**) malloc (sizeof(Vec3d*) * numVertices);
-  elements = (int**) malloc (sizeof(int*) * numElements);
-  elementMaterial = (int*) malloc (sizeof(int) * numElements);
-  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
-  sets = (Set**) malloc (sizeof(Set*) * numSets);
-  regions = (Region**) malloc (sizeof(Region*) * numRegions);
-
-  for(int i=0; i<numVertices; i++)
-    vertices[i] = new Vec3d(vertices_[3*i+0], vertices_[3*i+1], vertices_[3*i+2]);
-
-  int * v = (int*) malloc (sizeof(int) * numElementVertices);
-  for(int i=0; i<numElements; i++)
-  {
-    elements[i] = (int*) malloc (sizeof(int) * numElementVertices);
-    for(int j=0; j<numElementVertices; j++)
-    {
-      v[j] = elements_[numElementVertices * i + j];
-      elements[i][j] = v[j];
-    }
-  }
-  free(v);
-
-  for(int i=0; i<numMaterials; i++)
-    materials[i] = materials_[i]->clone();
-
-  for(int i=0; i<numSets; i++)
-    sets[i] = new Set(*(sets_[i]));
-
-  for(int i=0; i<numRegions; i++)
-    regions[i] = new Region(*(regions_[i]));
-
-  // set elementMaterial:
-  PropagateRegionsToElements();
-}
-
-VolumetricMesh::VolumetricMesh(const VolumetricMesh & volumetricMesh)
-{
-  numVertices = volumetricMesh.numVertices;
-  vertices = (Vec3d**) malloc (sizeof(Vec3d*) * numVertices);
-  for(int i=0; i<numVertices; i++)
-    vertices[i] = new Vec3d(*(volumetricMesh.vertices[i]));
-
-  numElementVertices = volumetricMesh.numElementVertices;
-  numElements = volumetricMesh.numElements;
-  elements = (int**) malloc (sizeof(int*) * numElements);
-  for(int i=0; i<numElements; i++)
-  {
-    elements[i] = (int*) malloc (sizeof(int) * numElementVertices);
-    for(int j=0; j<numElementVertices; j++)
-      elements[i][j] = (volumetricMesh.elements)[i][j];
-  }
-
-  numMaterials = volumetricMesh.numMaterials;
-  numSets = volumetricMesh.numSets;
-  numRegions = volumetricMesh.numRegions;
-
-  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
-  for(int i=0; i<numMaterials; i++)
-    materials[i] = (volumetricMesh.materials)[i]->clone();
-
-  sets = (Set**) malloc (sizeof(Set*) * numSets);
-  for(int i=0; i<numSets; i++)
-    sets[i] = new Set(*((volumetricMesh.sets)[i]));
-
-  regions = (Region**) malloc (sizeof(Region*) * numRegions);
-  for(int i=0; i<numRegions; i++)
-    regions[i] = new Region((*(volumetricMesh.regions)[i]));
-
-  elementMaterial = (int*) malloc (sizeof(int) * numElements);
-  for(int i=0; i<numElements; i++)
-    elementMaterial[i] = (volumetricMesh.elementMaterial)[i];
-}
-
-VolumetricMesh::~VolumetricMesh()
-{
-  for(int i=0; i< numVertices; i++)
-    delete(vertices[i]);
-  free(vertices);
-
-  for(int i=0; i< numElements; i++)
-    free(elements[i]);
-  free(elements);
-
-  for(int i=0; i< numMaterials; i++)
-    delete(materials[i]);
-  free(materials);
-  
-  for(int i=0; i< numSets; i++)
-    delete(sets[i]);
-  free(sets);
-
-  for(int i=0; i< numRegions; i++)
-    delete(regions[i]);
-  free(regions);
-}
-
-int VolumetricMesh::save(char * filename, elementType elementType_) const // saves the mesh to a .veg file
-{       
-  FILE * fout = fopen(filename, "w");
-  if (!fout)
-  {       
-    printf("Error: could not write to %s.\n",filename);
-    return 1;
-  }         
-
-  fprintf(fout, "# Vega mesh file.\n");
-  fprintf(fout, "# %d vertices, %d elements\n", numVertices, numElements);
-  fprintf(fout, "\n");
-          
-  // write vertices
-  fprintf(fout,"*VERTICES\n");
-  fprintf(fout,"%d 3 0 0\n", numVertices);
-          
-  for(int i=0; i < numVertices; i++)  
-  {
-    Vec3d v = *getVertex(i);
-    fprintf(fout,"%d %.15G %.15G %.15G\n", i+1, v[0], v[1], v[2]);
-  }   
-  fprintf(fout, "\n");
-
-  // write elements
-  fprintf(fout,"*ELEMENTS\n");
-
-  char elementName[4096] = "INVALID";
-  if (elementType_ == TET)
-    strcpy(elementName, "TET");
-  if (elementType_ == CUBIC)
-    strcpy(elementName, "CUBIC");
-  fprintf(fout,"%s\n", elementName);
-
-  fprintf(fout,"%d %d 0\n", numElements, numElementVertices);
-
-  for(int el=0; el < numElements; el++)
-  {   
-    fprintf(fout,"%d ", el+1);
-    for(int j=0; j < numElementVertices; j++)
-    {   
-      fprintf(fout, "%d", getVertexIndex(el, j) + 1);
-      if (j != numElementVertices - 1)
-        fprintf(fout," ");
-    } 
-    fprintf(fout,"\n");
-  }     
-  fprintf(fout, "\n");
-
-  // write materials
-  for(int materialIndex=0; materialIndex < numMaterials; materialIndex++)
-  {
-    string name = materials[materialIndex]->getName();
-    fprintf(fout, "*MATERIAL %s\n", name.c_str());
-
-    if (materials[materialIndex]->getType() == Material::ENU)
-    {
-      ENuMaterial * material = downcastENuMaterial(materials[materialIndex]);
-      double density = material->getDensity();
-      double E = material->getE();
-      double nu = material->getNu();
-      fprintf(fout, "ENU, %.15G, %.15G, %.15G\n", density, E, nu);
-    }
-
-    if (materials[materialIndex]->getType() == Material::MOONEYRIVLIN)
-    {
-      MooneyRivlinMaterial * material = downcastMooneyRivlinMaterial(materials[materialIndex]);
-      double density = material->getDensity();
-      double mu01 = material->getmu01();
-      double mu10 = material->getmu10();
-      double v1 = material->getv1();
-      fprintf(fout, "MOONEYRIVLIN, %.15G, %.15G, %.20G %.15G\n", density, mu01, mu10, v1);
-    }
-    fprintf(fout, "\n");
-  }
-
-  // write sets (skip the allElements set)
-  for(int setIndex=1; setIndex < numSets; setIndex++)
-  {
-    string name = sets[setIndex]->getName();
-    fprintf(fout, "*SET %s\n", name.c_str());
-    set<int> setElements;
-    sets[setIndex]->getElements(setElements);
-    int count = 0;
-    for(set<int>::iterator iter = setElements.begin(); iter != setElements.end(); iter++)
-    {
-      fprintf(fout, "%d, ", *iter + 1); // .veg files are 1-indexed
-      count++;
-      if (count == 8)
-      {
-        fprintf(fout, "\n");
-        count = 0;
-      }
-    }
-    if (count != 0)
-      fprintf(fout, "\n");
-    fprintf(fout, "\n");
-  }
-
-  // write regions
-  for(int regionIndex=0; regionIndex < numRegions; regionIndex++)
-  {
-    int materialIndex = regions[regionIndex]->getMaterialIndex();
-    int setIndex = regions[regionIndex]->getSetIndex();
-
-    fprintf(fout, "*REGION\n");
-    fprintf(fout, "%s, %s\n", sets[setIndex]->getName().c_str(), materials[materialIndex]->getName().c_str());
-    fprintf(fout, "\n");
-  }
-        
-  fclose(fout);
-  return 0;
-}   
-
-VolumetricMesh::elementType VolumetricMesh::getElementType(char * filename) 
-{
-  //printf("Parsing %s... (for element type determination)\n",filename);fflush(NULL);
-  elementType elementType_;
-
-  // parse the .veg file
-  VolumetricMeshParser volumetricMeshParser;
-  elementType_ = INVALID;
-
-  if (volumetricMeshParser.open(filename) != 0)
-  {
-    printf("Error: could not open file %s.\n",filename);
-    return elementType_;
-  }
-
-  char lineBuffer[1024];
-  while (volumetricMeshParser.getNextLine(lineBuffer, 0, 0) != NULL)
-  {
-    //printf("%s\n", lineBuffer);
-
-    // seek for *ELEMENTS
-    if (strncmp(lineBuffer, "*ELEMENTS", 9) == 0)
-    {
-      // parse element type
-      if (volumetricMeshParser.getNextLine(lineBuffer) != NULL)
-      {
-        volumetricMeshParser.removeWhitespace(lineBuffer);
-
-        if (strncmp(lineBuffer, "TET", 3) == 0)
-          elementType_ = TET;
-        else if (strncmp(lineBuffer, "CUBIC", 5) == 0)
-          elementType_ = CUBIC;
-        else
-        {
-          printf("Error: unknown mesh type %s in file %s\n", lineBuffer, filename);
-          return elementType_;
-        }
-      }
-      else
-      {
-        printf("Error (getElementType): file %s is not in the .veg format. Offending line:\n%s\n", filename, lineBuffer);
-        return elementType_;
-      }
-    }
-  }
-
-  volumetricMeshParser.close();
-
-  if (elementType_ == INVALID)
-    printf("Error: could not determine the mesh type in file %s. File may not be in .veg format.\n", filename);
-
-  return elementType_;
-}
-
-double VolumetricMesh::getVolume() const
-{
-  double vol = 0.0;
-  for(int el=0; el<numElements; el++)
-    vol += getElementVolume(el);
-  return vol;
-}
-
-void VolumetricMesh::getVertexVolumes(double * vertexVolumes) const
-{
-  memset(vertexVolumes, 0, sizeof(double) * numVertices);
-  double factor = 1.0 / numElementVertices;
-  for(int el=0; el<numElements; el++)
-  {
-    double volume = getElementVolume(el);
-    for(int j=0; j<numElementVertices; j++)
-      vertexVolumes[getVertexIndex(el, j)] += factor * volume;
-  }
-}
-
-Vec3d VolumetricMesh::getElementCenter(int el) const
-{
-  Vec3d pos(0,0,0);
-  for(int i=0; i<numElementVertices; i++)
-    pos += *getVertex(el,i);
-
-  pos *= 1.0 / numElementVertices;
-
-  return pos;
-}
-
-void VolumetricMesh::getVerticesInElements(vector<int> & elements_, vector<int> & vertices_) const
-{
-  set<int> ver;
-  for(unsigned int i=0; i< elements_.size(); i++)
-    for(int j=0; j< numElementVertices; j++)
-      ver.insert(getVertexIndex(elements_[i],j));
-
-  vertices_.clear();
-  set<int>::iterator iter;
-  for(iter = ver.begin(); iter != ver.end(); iter++)
-    vertices_.push_back(*iter);
-}
-
-void VolumetricMesh::getElementsTouchingVertices(vector<int> & vertices_, vector<int> & elements_) const
-{
-  set<int> ver;
-  for(unsigned int i=0; i<vertices_.size(); i++)
-    ver.insert(vertices_[i]);
-
-  elements_.clear();
-  for(int i=0; i< numElements; i++)
-  {
-    set<int> :: iterator iter;
-    for(int j=0; j<numElementVertices; j++)
-    {
-      iter = ver.find(getVertexIndex(i,j));
-      if (iter != ver.end())
-      {
-        elements_.push_back(i);
-        break;
-      }
-    }
-  }
-}
-
-void VolumetricMesh::getVertexNeighborhood(vector<int> & vertices_, vector<int> & neighborhood) const
-{
-  vector<int> elements_;
-  getElementsTouchingVertices(vertices_, elements_);
-  getVerticesInElements(elements_, neighborhood);
-}
-
-void VolumetricMesh::getInertiaParameters(double & mass, Vec3d & centerOfMass, Mat3d & inertiaTensor) const
-{
-  mass = 0.0;
-  centerOfMass[0] = centerOfMass[1] = centerOfMass[2] = 0;
-  inertiaTensor[0][0] = inertiaTensor[0][1] = inertiaTensor[0][2] = 0;
-  inertiaTensor[1][0] = inertiaTensor[1][1] = inertiaTensor[1][2] = 0;
-  inertiaTensor[2][0] = inertiaTensor[2][1] = inertiaTensor[2][2] = 0;
-
-  // compute mass, center of mass, inertia tensor
-  for(int i=0; i< getNumRegions(); i++)
-  {
-    Region * region = getRegion(i);
-    double density = getMaterial(region->getMaterialIndex())->getDensity();
-    set<int> setElements; // elements in the region
-    getSet(region->getSetIndex())->getElements(setElements);
-
-    // over all elements in the region
-    for(set<int> :: iterator iter = setElements.begin(); iter != setElements.end(); iter++)
-    {
-      int element = *iter;
-      double elementVolume = getElementVolume(element);
-      double elementMass = elementVolume * density;
-
-      mass += elementMass;
-      Vec3d elementCenter = getElementCenter(element);
-      centerOfMass += elementMass * elementCenter;
-
-      Mat3d elementITUnitDensity;
-      getElementInertiaTensor(element, elementITUnitDensity);
-
-      double a = elementCenter[0];
-      double b = elementCenter[1];
-      double c = elementCenter[2];
-
-      Mat3d elementITCorrection
-       (  b*b + c*c, -a*b, -a*c,
-         -a*b, a*a + c*c, -b*c,
-         -a*c, -b*c, a*a + b*b );
-
-      Mat3d elementIT = density * elementITUnitDensity + elementMass * elementITCorrection;
-
-      inertiaTensor += elementIT;
-    }
-  }
-
-  //printf("final mass: %G\n",mass);
-  centerOfMass /= mass;
-
-  // correct inertia tensor so it's around the center of mass
-  double a = centerOfMass[0];
-  double b = centerOfMass[1];
-  double c = centerOfMass[2];
-
-  Mat3d correction
-       ( b*b + c*c, -a*b, -a*c,
-         -a*b, a*a + c*c, -b*c,
-         -a*c, -b*c, a*a + b*b );
-
-  inertiaTensor -= mass * correction;
-}
-
-void VolumetricMesh::getMeshGeometricParameters(Vec3d & centroid, double * radius) const
-{
-  // compute centroid
-  centroid = Vec3d(0, 0, 0);
-  for(int i=0; i < numVertices ; i++)
-  {
-    Vec3d * vertex = getVertex(i);
-    centroid += *vertex;
-  }
-
-  centroid /= numVertices;
-
-  // compute radius
-  *radius = 0;
-  for(int i=0; i < numVertices; i++)
-  {
-    Vec3d * vertex = getVertex(i);
-    double dist = len(*vertex - centroid);
-    if (dist > *radius)
-      *radius = dist;
-  }
-}
-
-int VolumetricMesh::getClosestVertex(Vec3d pos) const
-{
-  // linear scan
-  double closestDist = DBL_MAX;
-  int closestVertex = -1;
-
-  for(int i=0; i<numVertices; i++)
-  {
-    Vec3d * vertexPosition = vertices[i];
-    double dist = len(pos - *vertexPosition);
-    if (dist < closestDist)
-    {
-      closestDist = dist;
-      closestVertex = i;
-    }
-  }
-
-  return closestVertex;
-}
-
-int VolumetricMesh::getClosestElement(Vec3d pos) const
-{
-  // linear scan
-  double closestDist = DBL_MAX;
-  int closestElement = 0;
-  for(int element=0; element < numElements; element++)
-  {
-    Vec3d center = getElementCenter(element);
-    double dist = len(pos - center);
-    if (dist < closestDist)
-    {
-      closestDist = dist;
-      closestElement = element;
-    }
-  }
-
-  return closestElement;
-}
-
-int VolumetricMesh::getContainingElement(Vec3d pos) const
-{
-  // linear scan
-  for(int element=0; element < numElements; element++)
-  {
-    if (containsVertex(element, pos))
-      return element;
-  }
-
-  return -1;
-}
-
-void VolumetricMesh::setSingleMaterial(double E, double nu, double density)
-{
-  // erase previous materials
-  for(int i=0; i<numMaterials; i++)
-    delete(materials[i]);
-  free(materials);
-
-  for(int i=0; i<numSets; i++)
-    delete(sets[i]);
-  free(sets);
-
-  for(int i=0; i<numRegions; i++)
-    delete(regions[i]);
-  free(regions);
-
-  // add a single material
-  numMaterials = 1;
-  numSets = 1;
-  numRegions = 1;
-
-  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
-  sets = (Set**) malloc (sizeof(Set*) * numSets);
-  regions = (Region**) malloc (sizeof(Region*) * numRegions);
-
-  Material * material = new ENuMaterial("defaultMaterial", density, E, nu);
-  materials[0] = material;
-
-  Set * set = new Set("allElements");
-  for(int i=0; i<numElements; i++)
-  {
-    set->insert(i);
-    elementMaterial[i] = 0;
-  }
-  sets[0] = set;
-
-  Region * region = new Region(0, 0);
-  regions[0] = region;
-}
-
-void VolumetricMesh::getDefaultMaterial(double * E, double * nu, double * density)
-{
-  *E = E_default;
-  *nu = nu_default;
-  *density = density_default;
-}
-
-void VolumetricMesh::PropagateRegionsToElements()
-{
-  for(int regionIndex=0; regionIndex < numRegions; regionIndex++)
-  {
-    Region * region = regions[regionIndex];
-    int materialIndex = region->getMaterialIndex();
-
-    set<int> setElements;
-    sets[region->getSetIndex()]->getElements(setElements);
-
-    for(set<int> :: iterator iter = setElements.begin(); iter != setElements.end(); iter++)
-    {
-      int elt = *iter;
-      elementMaterial[elt] = materialIndex;
-    }
-  }
-}
-
-int VolumetricMesh::generateInterpolationWeights(int numTargetLocations, 
-	double * targetLocations, int ** vertices_, double ** weights, 
-	double zeroThreshold, vector<int> * closestElementList, int verbose) const
-{  
-  // allocate interpolation arrays  
-  *vertices_ = (int*) malloc (sizeof(int) * numElementVertices * numTargetLocations);
-  *weights = (double*) malloc (sizeof(double) * numElementVertices * numTargetLocations);
-
-  double * baryWeights = (double*) malloc (sizeof(double) * numElementVertices);
-
-  int numExternalVertices = 0;
-
-  if (closestElementList != NULL)
-    closestElementList->clear();
-
-  for (int i=0; i < numTargetLocations; i++) // over all interpolation locations
-  {
-    if ((verbose) && (i % 100 == 0))
-    {
-      printf("%d ", i); fflush(NULL);
-    }
-
-    Vec3d pos = Vec3d(targetLocations[3*i+0],
-                      targetLocations[3*i+1],
-                      targetLocations[3*i+2]);
-
-    // find element containing pos
-    int element = getContainingElement(pos);
-
-    if (element < 0)
-    {
-      element = getClosestElement(pos);
-      numExternalVertices++;
-    }
-
-    if (closestElementList != NULL)
-      closestElementList->push_back(element);
-
-    computeBarycentricWeights(element, pos, baryWeights);
-
-    if (zeroThreshold > 0)
-    {
-      // check whether vertex is close enough to the mesh
-      double minDistance = DBL_MAX;
-      int numElementVertices = getNumElementVertices();
-      int assignedZero = 0;
-      for(int ii=0; ii< numElementVertices; ii++)
-      {
-        Vec3d * vpos = getVertex(element, ii);
-        if (len(*vpos-pos) < minDistance)
-        {
-          minDistance = len(*vpos-pos);
-        }
-      }
-
-      if (minDistance > zeroThreshold)
-      {
-        // assign zero weights
-        for(int ii=0; ii < numElementVertices; ii++)
-          baryWeights[ii] = 0.0;
-        assignedZero++;
-        continue;
-      }
-    }
-
-    for(int ii=0; ii<numElementVertices; ii++)
-    {
-      (*vertices_)[numElementVertices * i + ii] = getVertexIndex(element, ii);
-      (*weights)[numElementVertices * i + ii] = baryWeights[ii];
-    }
-  }
-
-  free(baryWeights);
-
-  return numExternalVertices;
-}
-
-int VolumetricMesh::getNumInterpolationElementVertices(char * filename)
-{
-  FILE * fin = fopen(filename, "r");
-  if (!fin)
-  {
-    printf("Error: unable to open file %s.\n", filename);
-    return -1;
-  }
-
-  char s[1024];
-  if (fgets(s, 1024, fin) == NULL)
-  {
-    printf("Error: incorrect first line of file %s.\n", filename);
-    return -2;
-  }
-  fclose(fin);
-
-  VolumetricMeshParser::beautifyLine(s, 1);
-
-  int slen = strlen(s);
-  int count = 0;
-  for(int i=0; i<slen; i++)
-    if (s[i] == ' ')
-      count++;
-
-  if (count % 2 == 1)
-  {
-    printf("Error: odd number of whitespaces in the first line of file %s.\n", filename);
-    return -3;
-  }
-
-  return count / 2;
-}
-
-int VolumetricMesh::loadInterpolationWeights(char * filename, int numTargetLocations, int numElementVertices_, int ** vertices_, double ** weights)
-{
-  FILE * fin = fopen(filename, "r");
-  if (!fin)
-  {
-    printf("Error: unable to open file %s.\n", filename);
-    return 2;
-  }
-
-  // allocate interpolation arrays
-  *vertices_ = (int*) malloc (sizeof(int) * numElementVertices_ * numTargetLocations);
-  *weights = (double*) malloc (sizeof(double) * numElementVertices_ * numTargetLocations);
-
-  int numReadTargetLocations = -1;
-  int currentVertex;
-
-  // read the elements one by one and accumulate entries
-  while (numReadTargetLocations < numTargetLocations-1)
-  {
-    numReadTargetLocations++;
-
-    if (feof(fin))
-    {
-      printf("Error: interpolation file is too short. Num vertices in interp file: %d . Should be: %d .\n", numReadTargetLocations, numTargetLocations);
-      return 1;
-    }
-
-    if (fscanf(fin, "%d", &currentVertex) < 1)
-      printf("Warning: bad file syntax. Unable to read interpolation info.\n");
-
-    if (currentVertex != numReadTargetLocations)
-    {
-      printf("Error: consecutive vertex index at position %d mismatch.\n", currentVertex);
-      return 1;
-    }
-
-    for(int j=0; j<numElementVertices_; j++)
-    {
-      if (fscanf(fin,"%d %lf", &((*vertices_)[currentVertex * numElementVertices_ + j]), &((*weights)[currentVertex * numElementVertices_ + j]) ) < 2)
-        printf("Warning: bad file syntax. Unable to read interpolation info.\n");
-    }
-
-    if (fscanf(fin,"\n") < 0)
-    {
-      //printf("Warning: bad file syntax. Missing end of line in the interpolation file.\n");
-      //do nothing
-      nop();
-    }
-  }
-
-  fclose(fin);
-
-  return 0;
-}
-
-int VolumetricMesh::saveInterpolationWeights(char * filename, int numTargetLocations, int numElementVertices_, int * vertices_, double * weights)
-{
-  FILE * fin = fopen(filename, "w");
-  if (!fin)
-  {
-    printf("Error: unable to open file %s.\n", filename);
-    return 1;
-  }
-
-  // read the elements one by one and accumulate entries
-  for(int currentVertex=0; currentVertex < numTargetLocations; currentVertex++)
-  {
-    fprintf(fin, "%d", currentVertex);
-
-    for(int j=0; j<numElementVertices_; j++)
-      fprintf(fin," %d %lf", vertices_[currentVertex * numElementVertices_ + j], 
-        weights[currentVertex * numElementVertices_ + j]);
-
-    fprintf(fin,"\n");
-  }
-
-  fclose(fin);
-
-  return 0;
-}
-
-void VolumetricMesh::interpolate(double * u, double * uTarget, 
-  int numTargetLocations, int numElementVertices_, int * vertices_, double * weights)
-{
-  for(int i=0; i< numTargetLocations; i++)
-  {
-    Vec3d defo(0,0,0);
-    for(int j=0; j<numElementVertices_; j++)
-    {
-      int volumetricMeshVertexIndex = vertices_[numElementVertices_ * i + j];
-      Vec3d volumetricMeshVertexDefo = Vec3d(u[3*volumetricMeshVertexIndex+0], u[3*volumetricMeshVertexIndex+1], u[3*volumetricMeshVertexIndex+2]);
-      defo += weights[numElementVertices_ * i + j] * volumetricMeshVertexDefo;
-    }
-    uTarget[3*i+0] = defo[0];
-    uTarget[3*i+1] = defo[1];
-    uTarget[3*i+2] = defo[2];
-  }
-}
-
-int VolumetricMesh::interpolateGradient(const double * U, int numFields, Vec3d pos, double * grad) const
-{
-  // find the element containing "pos"
-  int externalVertex = 0;
-  int element = getContainingElement(pos);
-  if (element < 0)
-  {
-    element = getClosestElement(pos);
-    externalVertex = 1;
-  }
-
-  interpolateGradient(element, U, numFields, pos, grad);
-
-  return externalVertex;
-}
-
-void VolumetricMesh::exportMeshGeometry(int * numVertices_, double ** vertices_, int * numElements_, int * numElementVertices_, int ** elements_) const
-{
-  *numVertices_ = numVertices;
-  *numElements_ = numElements;
-  *numElementVertices_ = numElementVertices;
-
-  *vertices_ = (double*) malloc (sizeof(double) * 3 * numVertices);
-  *elements_ = (int*) malloc (sizeof(int) * numElementVertices * numElements);
-
-  for(int i=0; i<numVertices; i++)
-  {
-    Vec3d v = *getVertex(i);
-    (*vertices_)[3*i+0] = v[0];
-    (*vertices_)[3*i+1] = v[1];
-    (*vertices_)[3*i+2] = v[2];
-  }
-
-  for(int i=0; i<numElements; i++)
-  {
-    for(int j=0; j<numElementVertices; j++)
-      (*elements_)[numElementVertices * i + j] = elements[i][j];
-  }
-}
-
-void VolumetricMesh::computeGravity(double * gravityForce, double g, bool addForce) const
-{
-  if (!addForce)
-    memset(gravityForce, 0, sizeof(double) * 3 * numVertices);
-
-  double invNumElementVertices = 1.0 / getNumElementVertices();
-
-  for(int el=0; el < numElements; el++)
-  {
-    double volume = getElementVolume(el);
-    double density = getElementDensity(el);
-    double mass = density * volume;
-    for(int j=0; j<getNumElementVertices(); j++)
-      gravityForce[3 * getVertexIndex(el,j) + 1] -= invNumElementVertices * mass * g; // gravity assumed to act in negative y-direction
-  }  
-}
-
-void VolumetricMesh::applyDeformation(double * u)
-{
-  for(int i=0; i<numVertices; i++)
-  {
-    Vec3d * v = getVertex(i);
-    (*v)[0] += u[3*i+0];
-    (*v)[1] += u[3*i+1];
-    (*v)[2] += u[3*i+2];
-  }
-}
-
-// transforms every vertex as X |--> pos + R * X
-void VolumetricMesh::applyLinearTransformation(double * pos, double * R)
-{
-  for(int i=0; i<numVertices; i++)
-  {
-    Vec3d * v = getVertex(i);
-    
-    double newPos[3];
-    for(int j=0; j<3; j++)
-    {
-      newPos[j] = pos[j];
-      for(int k=0; k<3; k++)
-        newPos[j] += R[3*j+k] * (*v)[k];
-    }
-
-    (*v)[0] = newPos[0];
-    (*v)[1] = newPos[1];
-    (*v)[2] = newPos[2];
-  }
-}
-
-void VolumetricMesh::setMaterial(int i, const Material * material)
-{
-  delete(materials[i]);
-  materials[i] = material->clone();
-}
-
-VolumetricMesh::VolumetricMesh(const VolumetricMesh & volumetricMesh, int numElements_, int * elements_, map<int,int> * vertexMap_)
-{
-  // determine vertices in the submesh
-  numElementVertices = volumetricMesh.getNumElementVertices();
-  set<int> vertexSet;
-  for(int i=0; i<numElements_; i++)
-    for(int j=0; j < numElementVertices; j++)
-      vertexSet.insert(volumetricMesh.getVertexIndex(elements_[i],j));
-
-  // copy vertices into place and also into vertexMap
-  numVertices = vertexSet.size();
-  vertices = (Vec3d**) malloc (sizeof(Vec3d*) * numVertices);
-  set<int> :: iterator iter;
-  int vertexNo = 0;
-  map<int, int> vertexMap;
-  for(iter = vertexSet.begin(); iter != vertexSet.end(); iter++)
-  {
-    vertices[vertexNo] = new Vec3d(*(volumetricMesh.getVertex(*iter)));
-    vertexMap.insert(make_pair(*iter,vertexNo));
-    vertexNo++;
-  }
-
-  if (vertexMap_ != NULL)
-    *vertexMap_ = vertexMap;
-
-  // copy elements
-  numElements = numElements_;
-  elements = (int**) malloc (sizeof(int*) * numElements);
-  elementMaterial = (int*) malloc (sizeof(int) * numElements);
-  map<int,int> elementMap;
-  for(int i=0; i<numElements; i++)
-  {
-    elements[i] = (int*) malloc (sizeof(int) * numElementVertices);
-    for(int j=0; j< numElementVertices; j++)
-    {
-      map<int,int> :: iterator iter2 = vertexMap.find((volumetricMesh.elements)[elements_[i]][j]);
-      if (iter2 == vertexMap.end())
-      {
-        printf("Internal error 1.\n");
-        exit(1);
-      }
-      elements[i][j] = iter2->second;
-    }
-
-    elementMaterial[i] = (volumetricMesh.elementMaterial)[elements_[i]];
-    elementMap.insert(make_pair(elements_[i], i)); 
-  }
-
-  // copy materials
-  numMaterials = volumetricMesh.getNumMaterials();
-  numSets = volumetricMesh.getNumSets();
-  numRegions = volumetricMesh.getNumRegions();
-
-  materials = (Material**) malloc (sizeof(Material*) * numMaterials);
-  for(int i=0; i < numMaterials; i++)
-    materials[i] = volumetricMesh.getMaterial(i)->clone();
-
-  // copy element sets; restrict element sets to the new mesh, also rename vertices to reflect new vertex indices
-  vector<Set*> newSets;
-  map<int,int> oldToNewSetIndex;
-  for(int oldSetIndex=0; oldSetIndex < volumetricMesh.getNumSets(); oldSetIndex++)
-  {
-    Set * oldSet = volumetricMesh.getSet(oldSetIndex);
-    set<int> oldElements;
-    oldSet->getElements(oldElements);
-
-    for(set<int> :: iterator iter = oldElements.begin(); iter != oldElements.end(); iter++)
-    {
-      if(*iter < 0)
-      {
-        printf("Internal error 2.\n");
-        exit(1);
-      }
-    }
-
-    // construct the element list
-    vector<int> newElements;
-    for(set<int> :: iterator iter = oldElements.begin(); iter != oldElements.end(); iter++)
-    {
-      map<int,int> :: iterator iter2 = elementMap.find(*iter);
-      if (iter2 != elementMap.end())
-        newElements.push_back(iter2->second);
-    }
-
-    // if there is at least one element in the new set, create a set for it
-    if (newElements.size() > 0)
-    {
-      Set * newSet = new Set(oldSet->getName());
-      for(unsigned int j=0; j<newElements.size(); j++)
-      {
-        if(newElements[j] < 0)
-        {
-          printf("Internal error 3.\n");
-          exit(1);
-        }
-        newSet->insert(newElements[j]);
-      }
-      newSets.push_back(newSet);
-      oldToNewSetIndex.insert(make_pair(oldSetIndex, newSets.size() - 1));
-    }
-  }
-
-  numSets = newSets.size();
-  sets = (Set**) malloc (sizeof(Set*) * numSets);
-  for(int i=0; i<numSets; i++)
-    sets[i] = newSets[i];
-
-  //printf("numSets: %d\n", numSets);
-
-  // copy regions; remove empty ones
-  vector<Region*> vregions;
-  for(int i=0; i < numRegions; i++)
-  {
-    Region * sregion = volumetricMesh.getRegion(i);
-    map<int,int> :: iterator iter = oldToNewSetIndex.find(sregion->getSetIndex());
-    if (iter != oldToNewSetIndex.end())
-    {
-      Region * newRegion = new Region(sregion->getMaterialIndex(),iter->second);
-      vregions.push_back(newRegion);
-    }
-  }
-
-  numRegions = vregions.size();
-  regions = (Region**) malloc (sizeof(Region*) * numRegions);
-  for(int j=0; j<numRegions; j++)
-    regions[j] = vregions[j];
-
-  // sanity check
-  // seek each element in all the regions
-  for(int el=0; el<numElements; el++)
-  {
-    int found = 0;
-    for(int region=0; region < numRegions; region++)
-    {
-      int elementSet = (regions[region])->getSetIndex();
-
-      // seek for element in elementSet
-      if (sets[elementSet]->isMember(el))
-      {
-        if (found != 0)
-          printf("Warning: element %d (1-indexed) is in more than one region.\n",el+1);
-        else
-          found = 1;
-      }
-    }
-    if (found == 0)
-      printf("Warning: element %d (1-indexed) is not in any of the regions.\n",el+1);
-  }
-
-  // sanity check: make sure all elements are between bounds
-  for(int i=0; i < numSets; i++)
-  {
-    set<int> elts;
-    sets[i]->getElements(elts);
-    for(set<int> :: iterator iter = elts.begin(); iter != elts.end(); iter++)
-    {
-      if (*iter < 0)
-        printf("Warning: encountered negative element index in element set %d.\n",i);
-      if (*iter >= numElements)
-        printf("Warning: encountered too large element index in element set %d.\n",i);
-    }
-  }
-}
-
-// if vertexMap is non-null, it also returns a renaming datastructure: vertexMap[big mesh vertex] is the vertex index in the subset mesh
-void VolumetricMesh::setToSubsetMesh(std::set<int> & subsetElements, int removeIsolatedVertices, std::map<int,int> * vertexMap)
-{
-  int numRemovedElements = 0;
-  for(int el=0; el<numElements; el++)
-  {
-    if (subsetElements.find(el) == subsetElements.end())
-    {
-      delete(elements[el]);
-      elements[el] = NULL;
-      numRemovedElements++;
-    }
-  }
-
-  int head = 0;
-  int tail = 0;
-
-  int * lookupTable = (int *) malloc (sizeof(int) * numElements); 
-  for(int i=0; i<numElements; i++)
-    lookupTable[i] = i;
-
-  while (tail < numElements)
-  {
-    if (elements[tail] != NULL)
-    {
-      elements[head] = elements[tail];
-      lookupTable[tail] = head;  // update to new index 
-      head++;
-    }
-    tail++;
-  }
-  numElements -= numRemovedElements;
-  elements = (int**) realloc (elements, sizeof(int*) * numElements);
-
-  for(int setIndex=0; setIndex < numSets; setIndex++)
-  {
-    set<int> setElements;
-    sets[setIndex]->getElements(setElements);
-    sets[setIndex]->clear();
-    for(set<int>::iterator iter = setElements.begin(); iter != setElements.end(); iter++)
-    {
-      if (subsetElements.find(*iter) == subsetElements.end()) // not found!!
-        continue;
-      int newIndex = lookupTable[(*iter)];
-      sets[setIndex]->insert(newIndex);
-    }
-  }
-  free(lookupTable);
-
-  if (removeIsolatedVertices)
-  {
-    set<int> retainedVertices;
-    for(int el=0; el<numElements; el++)
-      for(int j=0; j < numElementVertices; j++)
-        retainedVertices.insert(getVertexIndex(el,j));
-
-    int numRemovedVertices = 0;
-    for(int v=0; v<numVertices; v++)
-    {
-      if (retainedVertices.find(v) == retainedVertices.end())
-      {
-        delete(vertices[v]);
-        vertices[v] = NULL;
-        numRemovedVertices++;
-      }
-    }
-  
-    int head = 0;
-    int tail = 0;
-  
-    int * renamingFunction = (int*) malloc (sizeof(int) * numVertices);
-    if (vertexMap != NULL)
-      vertexMap->clear();
-    while (tail < numVertices)
-    {
-      if (vertices[tail] != NULL)
-      {
-        renamingFunction[tail] = head;
-        if (vertexMap != NULL)
-          vertexMap->insert(make_pair(tail, head));
-        vertices[head] = vertices[tail];
-        head++;
-      }
-      tail++;
-    }
-
-    // rename vertices inside the elements
-    for(int el=0; el<numElements; el++)
-      for(int j=0; j < numElementVertices; j++)
-        elements[el][j] = renamingFunction[getVertexIndex(el,j)];
-   
-    free(renamingFunction);
-    numVertices -= numRemovedVertices;
-    vertices = (Vec3d**) realloc (vertices, sizeof(Vec3d*) * numVertices );
-  }
-}
-
-int VolumetricMesh::exportToEle(char * baseFilename, int includeRegions) const
-{
-  char s[1024];
-  sprintf(s, "%s.ele", baseFilename);
-
-  FILE * fout = fopen(s, "w");
-  if (!fout)
-  {       
-    printf("Error: could not write to %s.\n",s);
-    return 1;
-  }         
-
-  int * elementRegion = NULL;
-  if (includeRegions)
-  {
-    elementRegion = (int*) malloc (sizeof(int) * getNumElements());
-    for(int el=0; el<getNumElements(); el++)
-    {
-      int found = 0;
-      for(int region=0; region < numRegions; region++)
-      {
-        int elementSet = (regions[region])->getSetIndex();
-
-        // seek for element in elementSet
-        if (sets[elementSet]->isMember(el))
-        {
-          if (found != 0)
-            printf("Warning: element %d (1-indexed) is in more than one region.\n",el+1);
-          else
-            found = region+1;
-        }
-      }
-      if (found == 0)
-        printf("Warning: element %d (1-indexed) is not in any of the regions.\n",el+1);
-      elementRegion[el] = found;
-    }
-  }
-
-  if (includeRegions)
-    fprintf(fout,"%d %d %d\n", numElements, numElementVertices, 1);
-  else
-    fprintf(fout,"%d %d %d\n", numElements, numElementVertices, 0);
-
-  for(int el=0; el < numElements; el++)
-  {   
-    fprintf(fout,"%d ",el+1);
-    for(int j=0; j < numElementVertices; j++)
-    {   
-      fprintf(fout,"%d", getVertexIndex(el,j)+1);
-      if (j != numElementVertices - 1)
-        fprintf(fout," ");
-    } 
-    if (includeRegions)
-    {
-      fprintf(fout," %d", elementRegion[el]);
-    }
-    fprintf(fout,"\n");
-  }     
-        
-  fprintf(fout,"# generated by the volumetricMesh class\n");
-
-  fclose(fout);
-
-  if (includeRegions)
-    free(elementRegion);
-
-  sprintf(s, "%s.node", baseFilename);
-
-  fout = fopen(s, "w");
-  if (!fout)
-  {       
-    printf("Error: could not write to %s.\n",s);
-    return 1;
-  }         
-          
-  fprintf(fout,"%d %d %d %d\n", numVertices, 3, 0, 0);
-  for(int v=0; v < numVertices; v++)
-  {   
-    fprintf(fout,"%d ",v+1);
-    Vec3d vtx = *getVertex(v);
-    fprintf(fout,"%.15f %.15f %.15f\n", vtx[0], vtx[1], vtx[2]);
-  }     
-        
-  fprintf(fout,"# generated by the volumetricMesh class\n");
-
-  fclose(fout);
-
-  return 0;
-}
-
-void VolumetricMesh::nop()
-{
-}
-
-}
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
deleted file mode 100644
index 08d85b8362c11b10d3040c85aa253ff883b601d0..0000000000000000000000000000000000000000
--- a/src/util/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-if (VegaFEM_BUILD_MODEL_REDUCTION)
-  add_subdirectory(largeModalDeformationFactory)
-  if (VegaFEM_ENABLE_Cg_SUPPORT)
-    add_subdirectory(reducedDynamicSolver-rt)
-  endif()
-endif()
-if (VegaFEM_ENABLE_OpenGL_SUPPORT)
-  add_subdirectory(displayObj)
-  add_subdirectory(interactiveDeformableSimulator)
-endif()
-add_subdirectory(volumetricMeshUtilities)
diff --git a/src/util/displayObj/CMakeLists.txt b/src/util/displayObj/CMakeLists.txt
deleted file mode 100644
index 24d6e72df14315d4cf839f1fc9681983acc43feb..0000000000000000000000000000000000000000
--- a/src/util/displayObj/CMakeLists.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-
-include_directories(
-#     ${CMAKE_CURRENT_SOURCE_DIR}
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libopenGLHelper
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libperformanceCounter
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libglslPhong
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libobjMesh
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libvega-getopts
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libmatrixIO
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libloadList
-)
-
-add_executable(displayObj displayObj.cpp)
-target_include_directories(displayObj
-  PUBLIC
-    ${GLUI_INCLUDE_DIRS}
-)
-target_link_libraries(displayObj 
-  vega-getopts
-  objMesh 
-  imageIO 
-  glslPhong 
-  camera 
-  matrixIO 
-  lighting 
-  configFile 
-  loadList
-  ${OPENGL_gl_LIBRARY} 
-  ${GLUT_glut_LIBRARY} 
-  ${OPENGL_glu_LIBRARY} 
-  ${GLUI_LIBRARY} 
-  ${GLEW_LIBRARIES})
-
-add_executable(objMergeFiles objMergeFiles.cpp)
-target_include_directories(objMergeFiles
-  PUBLIC
-    ${GLUI_INCLUDE_DIRS}
-)
-target_link_libraries(objMergeFiles 
-  vega-getopts
-  objMesh 
-  imageIO 
-  glslPhong 
-  camera 
-  matrixIO 
-  lighting 
-  configFile 
-  loadList
-  ${OPENGL_gl_LIBRARY} 
-  ${GLUT_glut_LIBRARY} 
-  ${OPENGL_glu_LIBRARY} 
-  ${GLUI_LIBRARY} 
-  ${GLEW_LIBRARIES})
diff --git a/src/util/displayObj/Makefile b/src/util/displayObj/Makefile
deleted file mode 100644
index fb2adf16636ac8f359d7013c6e4804c22367f5a4..0000000000000000000000000000000000000000
--- a/src/util/displayObj/Makefile
+++ /dev/null
@@ -1,50 +0,0 @@
-ifndef DISPLAYOBJ
-DISPLAYOBJ=DISPLAYOBJ
-
-ifndef CLEANFOLDER
-CLEANFOLDER=DISPLAYOBJ
-endif
-
-include ../../Makefile-headers/Makefile-header
-R ?= ../..
-
-# the object files to be compiled for these utilities
-DISPLAYOBJ_OBJECTS=
-
-# the libraries these utilities depend on
-DISPLAYOBJ_LIBS=getopts objMesh imageIO glslPhong camera matrixIO lighting configFile loadList
-
-# the headers in this library
-DISPLAYOBJ_HEADERS=
-
-DISPLAYOBJ_LINK=$(addprefix -l, $(DISPLAYOBJ_LIBS)) $(SPOOLES_LIB) $(PARDISO_LIB) $(GLEW_LIB) $(STANDARD_LIBS) $(GLUI_LIB)
-
-DISPLAYOBJ_OBJECTS_FILENAMES=$(addprefix $(R)/utilities/displayObj/, $(DISPLAYOBJ_OBJECTS))
-DISPLAYOBJ_HEADER_FILENAMES=$(addprefix $(R)/utilities/displayObj/, $(DISPLAYOBJ_HEADERS))
-DISPLAYOBJ_LIB_MAKEFILES=$(call GET_LIB_MAKEFILES, $(DISPLAYOBJ_LIBS))
-DISPLAYOBJ_LIB_FILENAMES=$(call GET_LIB_FILENAMES, $(DISPLAYOBJ_LIBS))
-
-include $(DISPLAYOBJ_LIB_MAKEFILES)
-
-all: $(R)/utilities/displayObj/displayObj $(R)/utilities/displayObj/objMergeFiles
-
-$(R)/utilities/displayObj/displayObj: $(R)/utilities/displayObj/displayObj.cpp
-	$(CXXLD) $(LDFLAGS) $(INCLUDE) $(GLUI_INCLUDE) $(DISPLAYOBJ_OBJECTS) $^ $(DISPLAYOBJ_LINK) -Wl,-rpath,$(GLUI_DIR)/lib $(IMAGE_LIBS) -o $@; cp $@ $(R)/utilities/bin/
-
-$(R)/utilities/displayObj/objMergeFiles: $(R)/utilities/displayObj/objMergeFiles.cpp
-	$(CXXLD) $(LDFLAGS) $(INCLUDE) $(GLUI_INCLUDE) $(DISPLAYOBJ_OBJECTS) $^ $(DISPLAYOBJ_LINK) -Wl,-rpath,$(GLUI_DIR)/lib $(IMAGE_LIBS) -o $@; cp $@ $(R)/utilities/bin/
-
-$(DISPLAYOBJ_OBJECTS_FILENAMES): %.o: %.cpp $(DISPLAYOBJ_LIB_FILENAMES) $(DISPLAYOBJ_HEADER_FILENAMES)
-	$(CXX) $(CXXFLAGS) -c $(INCLUDE) $(IMAGE_INCLUDE) $(GLUI_INCLUDE) $< -o $@
-
-ifeq ($(CLEANFOLDER), DISPLAYOBJ)
-clean: cleandisplayObj
-endif
-
-deepclean: cleandisplayObj
-
-cleandisplayObj:
-	$(RM) $(R)/utilities/displayObj/displayObj 
-
-endif
-
diff --git a/src/util/interactiveDeformableSimulator/CMakeLists.txt b/src/util/interactiveDeformableSimulator/CMakeLists.txt
deleted file mode 100644
index 06d136755894362e3e59ec386288da7ae71f0888..0000000000000000000000000000000000000000
--- a/src/util/interactiveDeformableSimulator/CMakeLists.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-include_directories(
-    ${CMAKE_CURRENT_SOURCE_DIR}
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libcamera
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libconfigFile
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libcorotationalLinearFEM
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libelasticForceModel
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libforceModel
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libvega-getopts
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libgraph
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libinsertRows
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libintegrator
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libintegratorSparse
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libisotropicHyperelasticFEM
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../liblighting
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libloadList
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libmassSpringSystem
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libminivector
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libobjMesh
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libperformanceCounter
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libpolarDecomposition
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libsceneObject
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libsparseMatrix
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libsparseSolver
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libstvk
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libvolumetricMesh
-    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
-    $<INSTALL_INTERFACE:include>
-)
-
-add_executable(deformsim interactiveDeformableSimulator.cpp initGraphics.cpp)
-target_include_directories(deformsim
-  PUBLIC
-    ${GLUI_INCLUDE_DIRS}
-)
-target_link_libraries(deformsim
-  sceneObject
-  integrator
-  integratorSparse
-  elasticForceModel
-  forceModel
-  sparseMatrix
-  loadList
-  insertRows
-  lighting
-  configFile
-  volumetricMesh
-  vega-getopts
-  camera
-  graph
-  isotropicHyperelasticFEM
-  minivector
-  stvk
-  corotationalLinearFEM
-  polarDecomposition
-  massSpringSystem
-  objMesh
-  sparseSolver
-  ${OPENGL_gl_LIBRARY}
-  ${GLUT_glut_LIBRARY}
-  ${OPENGL_glu_LIBRARY}
-  ${GLUI_LIBRARY}
-)
diff --git a/src/util/largeModalDeformationFactory/CMakeLists.txt b/src/util/largeModalDeformationFactory/CMakeLists.txt
deleted file mode 100644
index d48d386e45ae4825ed222542b8eac6820f9ea1eb..0000000000000000000000000000000000000000
--- a/src/util/largeModalDeformationFactory/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-
-# FindwxWidgets
-find_package(wxWidgets)
-
-if(wxWidgets_FOUND)
-  include_directories(${wxWidgets_INCLUDE_DIRS})
-  set(LARGE_MODAL_DEF_SRCS cubicPolynomials.cpp fixedVertices.cpp frequencies.cpp largeModalDeformationFactory.cpp linearModes.cpp main.cpp nonlinearModes.cpp view.cpp renderingMesh.cpp simulationMesh.cpp canvas.cpp modalDerivatives.cpp sketch.cpp interpolate.cpp convert.cpp runtime.cpp StVKReducedInternalForcesWX.cpp)
-  add_executable(largeModalDeformationFactory ${LARGE_MODAL_DEF_SRCS})
-  target_link_libraries(largeModalDeformationFactory reducedStvk stvk reducedElasticForceModel reducedForceModel forceModel renderVolumetricMesh sparseSolver sparseMatrix volumetricMesh objMesh imageIO modalMatrix matrix matrixIO getopts insertRows loadList camera minivector openGLHelper
-  ${CMAKE_THREAD_LIBS_INIT}
-  ${OPENGL_gl_LIBRARY} ${GLUT_glut_LIBRARY} ${OPENGL_glu_LIBRARY} ${GLUI_LIBRARY})
-endif(wxWidgets_FOUND)
diff --git a/src/util/largeModalDeformationFactory/Makefile b/src/util/largeModalDeformationFactory/Makefile
deleted file mode 100644
index 2458ba95a3315e18dfe5e5f90be597dd26578ae6..0000000000000000000000000000000000000000
--- a/src/util/largeModalDeformationFactory/Makefile
+++ /dev/null
@@ -1,65 +0,0 @@
-ifndef LARGEMODALDEFORMATIONFACTORY
-LARGEMODALDEFORMATIONFACTORY=LARGEMODALDEFORMATIONFACTORY
-
-ifndef CLEANFOLDER
-CLEANFOLDER=LARGEMODALDEFORMATIONFACTORY
-endif
-
-include ../../Makefile-headers/Makefile-header
-R ?= ../..
-
-# the object files to be compiled for this utility
-LARGEMODALDEFORMATIONFACTORY_OBJECTS=cubicPolynomials.o fixedVertices.o frequencies.o largeModalDeformationFactory.o linearModes.o main.o nonlinearModes.o view.o renderingMesh.o simulationMesh.o canvas.o modalDerivatives.o sketch.o interpolate.o convert.o runtime.o StVKReducedInternalForcesWX.o
-
-# the libraries this utility depends on
-LARGEMODALDEFORMATIONFACTORY_LIBS=reducedStvk stvk reducedElasticForceModel reducedForceModel forceModel renderVolumetricMesh sparseSolver sparseMatrix volumetricMesh objMesh imageIO modalMatrix matrix matrixIO getopts insertRows loadList camera minivector openGLHelper
-
-# the headers in this library
-LARGEMODALDEFORMATIONFACTORY_HEADERS=StVKReducedInternalForcesWX.h canvas.h largeModalDeformationFactory.h states.h
-
-CG_LIBS=-framework Cg
-LARGEMODALDEFORMATIONFACTORY_LINK=$(addprefix -l, $(LARGEMODALDEFORMATIONFACTORY_LIBS)) $(ARPACK_LIB) $(SPOOLES_LIB) $(BLASLAPACK_LIB) $(PARDISO_LIB) $(FORTRAN_LIB) $(CG_LIBS) $(IMAGE_LIBS) $(STANDARD_LIBS)
-
-LARGEMODALDEFORMATIONFACTORY_OBJECTS_FILENAMES=$(addprefix $(R)/utilities/largeModalDeformationFactory/, $(LARGEMODALDEFORMATIONFACTORY_OBJECTS))
-LARGEMODALDEFORMATIONFACTORY_HEADER_FILENAMES=$(addprefix $(R)/utilities/largeModalDeformationFactory/, $(LARGEMODALDEFORMATIONFACTORY_HEADERS))
-LARGEMODALDEFORMATIONFACTORY_LIB_MAKEFILES=$(call GET_LIB_MAKEFILES, $(LARGEMODALDEFORMATIONFACTORY_LIBS))
-LARGEMODALDEFORMATIONFACTORY_LIB_FILENAMES=$(call GET_LIB_FILENAMES, $(LARGEMODALDEFORMATIONFACTORY_LIBS))
-
-include $(LARGEMODALDEFORMATIONFACTORY_LIB_MAKEFILES)
-
-TARGET=largeModalDeformationFactory
-all: $(TARGET) bundle
-
-$(TARGET): $(LARGEMODALDEFORMATIONFACTORY_OBJECTS_FILENAMES)
-	$(CXXLD) $(LDFLAGS) $(LARGEMODALDEFORMATIONFACTORY_OBJECTS) $(BIND_AT_LOAD) $(LARGEMODALDEFORMATIONFACTORY_LINK) `$(WX_CONFIG) --cxxflags --libs core,base,gl` -o $@; cp $@ $(R)/utilities/bin/
-
-$(LARGEMODALDEFORMATIONFACTORY_OBJECTS_FILENAMES): %.o: %.cpp $(LARGEMODALDEFORMATIONFACTORY_LIB_FILENAMES) $(LARGEMODALDEFORMATIONFACTORY_HEADER_FILENAMES)
-	$(CXX) $(CXXFLAGS) `$(WX_CONFIG) --cxxflags` -c $(BLASLAPACK_INCLUDE) $(PARDISO_INCLUDE) $(INCLUDE) $< -o $@
-
-bundle: $(TARGET) $(WX_DIR)/src/osx/carbon/Info.plist.in
-	mkdir -p $(TARGET).app/Contents
-	mkdir -p $(TARGET).app/Contents/MacOS
-	mkdir -p $(TARGET).app/Contents/Resources
-	
-	sed -e "s/IDENTIFIER/`echo $(WX_DIR) | sed -e 's,\.\./,,g' | sed -e 's,/,.,g'`/" \
-	-e "s/EXECUTABLE/$(TARGET)/" \
-	-e "s/VERSION/$(WX_VERSION)/" \
-	$(WX_DIR)/src/osx/carbon/Info.plist.in >$(TARGET).app/Contents/Info.plist
-	
-	echo -n "APPL????" >$(TARGET).app/Contents/PkgInfo
-	
-	ln -f $(TARGET)$(EXEEXT) $(TARGET).app/Contents/MacOS/$(TARGET)
-	
-	cp -f largeModalDeformationFactory.icns $(TARGET).app/Contents/Resources
-
-ifeq ($(CLEANFOLDER), LARGEMODALDEFORMATIONFACTORY)
-clean: cleanlargeModalDeformationFactory
-endif
-
-deepclean: cleanlargeModalDeformationFactory
-
-cleanlargeModalDeformationFactory:
-	$(RM) $(LARGEMODALDEFORMATIONFACTORY_OBJECTS_FILENAMES) $(R)/utilities/largeModalDeformationFactory/largeModalDeformationFactory
-
-endif
-
diff --git a/src/util/largeModalDeformationFactory/StVKReducedInternalForcesWX.cpp b/src/util/largeModalDeformationFactory/StVKReducedInternalForcesWX.cpp
deleted file mode 100644
index 2c0f7af2d623d5df399c938e76bfba009d72f65d..0000000000000000000000000000000000000000
--- a/src/util/largeModalDeformationFactory/StVKReducedInternalForcesWX.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*************************************************************************
- *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
- *                                                                       *
- * "Large Modal Deformation Factory",                                    *
- * a pre-processing utility for model reduction of                       *
- * deformable objects undergoing large deformations.                     *
- *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
- *                                                                       *
- * All rights reserved.                                                  *
- *                                                                       *
- * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
- *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
- *           Doug L. James, Jovan Popovic                                *
- *                                                                       *
- * Funding: National Science Foundation, Link Foundation,                *
- *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
- *                                                                       *
- * This utility is free software; you can redistribute it and/or         *
- * modify it under the terms of the BSD-style license that is            *
- * included with this library in the file LICENSE.txt                    *
- *                                                                       *
- * This utility 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 file     *
- * LICENSE.TXT for more details.                                         *
- *                                                                       *
- *************************************************************************/
-
-/*
-  A multi-threaded version of the StVKReducedInternalForces class, using 
-  the wxWidgets threading API. This class requires the wxWidgets header 
-  files. 
-*/
-
-#include <vector>
-#include "wx/wx.h"
-#include "lapack-headers.h"
-#include "matrixIO.h"
-#include "matrixMacros.h"
-#include "matrixProjection.h"
-#include "StVKReducedInternalForcesWX.h"
-
-struct StVKReducedInternalForcesWX_threadArg
-{
-  StVKReducedInternalForcesWX * stVKReducedInternalForcesWX;
-  double * targetBuffer;
-  int rank;
-};
-
-void * StVKReducedInternalForcesWX_WorkerThread(void * arg)
-{
-  struct StVKReducedInternalForcesWX_threadArg * threadArgp = (struct StVKReducedInternalForcesWX_threadArg*) arg;
-  StVKReducedInternalForcesWX * stVKReducedInternalForcesWX = threadArgp->stVKReducedInternalForcesWX;
-  double * targetBuffer = threadArgp->targetBuffer;
-  int rank = threadArgp->rank;
-  int r = stVKReducedInternalForcesWX->Getr();
-  int startElement = stVKReducedInternalForcesWX->GetStartElement(rank);
-  int endElement = stVKReducedInternalForcesWX->GetEndElement(rank);
-
-  double * target[3] = { &targetBuffer[0], &targetBuffer[r*StVKReducedInternalForces::GetLinearSize(r)], &targetBuffer[r*(StVKReducedInternalForces::GetLinearSize(r) + StVKReducedInternalForces::GetQuadraticSize(r))] };
-  stVKReducedInternalForcesWX->ProcessElements(startElement, endElement, target);
-
-  return NULL;
-}
-
-class MyThread : public wxThread
-{
-public:
-  MyThread(void * arg_) : wxThread(wxTHREAD_JOINABLE), arg(arg_) {}
-
-protected:
-  void * arg;
-  virtual ExitCode Entry();
-};
-
-MyThread::ExitCode MyThread::Entry()
-{
-  StVKReducedInternalForcesWX_WorkerThread(arg);
-  return (MyThread::ExitCode)0;
-}
-
-StVKReducedInternalForcesWX::StVKReducedInternalForcesWX(int r, double * U, VolumetricMesh * volumetricMesh, StVKElementABCD * precomputedABCDIntegrals, bool addGravity_, int numThreads): StVKReducedInternalForces(r, U, volumetricMesh, precomputedABCDIntegrals, 1, addGravity_)
-{
-  internalForceBuffer = (double*) malloc (sizeof(double) * numThreads * r * (linearSize + quadraticSize + cubicSize));
-
-  // split the workload
-  int numElements = volumetricMesh->getNumElements();
-  startElement = (int*) malloc (sizeof(int) * numThreads);
-  endElement = (int*) malloc (sizeof(int) * numThreads);
-
-  int remainder = numElements % numThreads;
-  // the first 'remainder' nodes will process one element more
-  int jobSize = numElements / numThreads;
-
-  for(int rank=0; rank < numThreads; rank++)
-  {
-    if (rank < remainder)
-    {
-      startElement[rank] = rank * (jobSize+1);
-      endElement[rank] = (rank+1) * (jobSize+1);
-    }
-    else
-    {
-      startElement[rank] = remainder * (jobSize+1) + (rank-remainder) * jobSize;
-      endElement[rank] = remainder * (jobSize+1) + ((rank-remainder)+1) * jobSize;
-    }
-  }
-
-  printf("Reduced StVK coefficients computation info:\n");
-  printf("Total elements: %d \n",numElements);
-  printf("Num threads: %d \n",numThreads);
-  printf("Canonical job size: %d \n",jobSize);
-  printf("Num threads with job size augmented by one element: %d \n",remainder);
-
-  // launch threads 
-  struct StVKReducedInternalForcesWX_threadArg * threadArgv = (struct StVKReducedInternalForcesWX_threadArg*) malloc (sizeof(struct StVKReducedInternalForcesWX_threadArg) * numThreads);
-
-  for(int i=0; i<numThreads; i++)
-  {
-    threadArgv[i].stVKReducedInternalForcesWX = this;
-    threadArgv[i].targetBuffer = &internalForceBuffer[i * r * (linearSize + quadraticSize + cubicSize)];
-    threadArgv[i].rank = i;
-  }
-    
-  memset(internalForceBuffer, 0, sizeof(double) * numThreads * r * (linearSize + quadraticSize + cubicSize));
-
-  MyThread ** threads = (MyThread**) malloc (sizeof(MyThread*) * numThreads);
-
-  for(int i=0; i<numThreads; i++)
-  {
-    threads[i] = new MyThread(&threadArgv[i]);
-
-    if (threads[i]->Create() != wxTHREAD_NO_ERROR)
-    {
-      printf("Error: unable to create thread %d.\n", i);
-      throw 1;
-    }
-
-    threads[i]->SetPriority(WXTHREAD_MAX_PRIORITY);
-
-    if (threads[i]->Run() != wxTHREAD_NO_ERROR)
-    {
-      printf("Error: unable to run thread %d.\n", i);
-      throw 2;
-    }
-  }
-
-  for(int i=0; i<numThreads; i++)
-  {
-    if (threads[i]->Wait() != 0)
-    {
-      printf("Error: unable to join thread %d.\n", i);
-      throw 3;
-    } 
-
-    delete(threads[i]);
-  }
-
-  free(threads);
-  free(threadArgv);
-
-  // assemble
-  memset(linearCoef_, 0, sizeof(double) * r * linearSize);
-  memset(quadraticCoef_, 0, sizeof(double) * r * quadraticSize);
-  memset(cubicCoef_, 0, sizeof(double) * r * cubicSize);
-  for(int i=0; i<numThreads; i++)
-  {
-    double * sourceLinear = &internalForceBuffer[i * r * (linearSize + quadraticSize + cubicSize)];
-    for(int j=0; j<r*linearSize; j++)
-      linearCoef_[j] += sourceLinear[j];
-
-    double * sourceQuadratic = &internalForceBuffer[i * r * (linearSize + quadraticSize + cubicSize) + r * linearSize];
-    for(int j=0; j<r*quadraticSize; j++)
-      quadraticCoef_[j] += sourceQuadratic[j];
-
-    double * sourceCubic = &internalForceBuffer[i * r * (linearSize + quadraticSize + cubicSize) + r * (linearSize + quadraticSize)];
-    for(int j=0; j<r*cubicSize; j++)
-      cubicCoef_[j] += sourceCubic[j];
-  }
-}
-
-StVKReducedInternalForcesWX::~StVKReducedInternalForcesWX()
-{
-  free(startElement);
-  free(endElement);
-  free(internalForceBuffer);
-}
-
-int StVKReducedInternalForcesWX::GetStartElement(int rank)
-{
-  return startElement[rank];
-}
-
-int StVKReducedInternalForcesWX::GetEndElement(int rank)
-{
-  return endElement[rank];
-}
-
diff --git a/src/util/reducedDynamicSolver-rt/CMakeLists.txt b/src/util/reducedDynamicSolver-rt/CMakeLists.txt
deleted file mode 100644
index b8afd26245300e6e8e36c21fc7bfbc91dfba4c57..0000000000000000000000000000000000000000
--- a/src/util/reducedDynamicSolver-rt/CMakeLists.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-include_directories(
-    ${CMAKE_CURRENT_SOURCE_DIR}
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libcamera
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libconfigFile
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libcorotationalLinearFEM
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libelasticForceModel
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libforceModel
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libvega-getopts
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libgraph
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libinsertRows
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libintegrator
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libintegratorSparse
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libintegratorDense
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libisotropicHyperelasticFEM
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../liblighting
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libloadList
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libmassSpringSystem
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libminivector
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libobjMesh
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libperformanceCounter
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libpolarDecomposition
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libsceneObject
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libsparseMatrix
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libsparseSolver
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libstvk
-    ${CMAKE_CURRENT_SOURCE_DIR}/../../libvolumetricMesh
-    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
-    $<INSTALL_INTERFACE:include>
-)
-
-add_executable(reducedDynamicSolver-rt reducedDynamicSolver-rt.cpp initGraphics.cpp)
-target_include_directories(reducedDynamicSolver-rt
-  PUBLIC
-    ${GLUI_INCLUDE_DIRS}
-)
-target_link_libraries(reducedDynamicSolver-rt 
-  objMeshGPUDeformer 
-  objMesh 
-  imageIO 
-  modalMatrix 
-  sceneObjectReduced 
-  sceneObject 
-  objMeshGPUDeformer 
-  objMesh 
-  imageIO 
-  integratorSparse 
-  integratorDense 
-  integrator 
-  reducedStvk 
-  stvk 
-  reducedElasticForceModel 
-  reducedForceModel 
-  forceModel 
-  matrix 
-  matrixIO 
-  lighting 
-  configFile 
-  volumetricMesh 
-  loadList 
-  vega-getopts 
-  camera 
-  minivector 
-  openGLHelper
-  ${CMAKE_THREAD_LIBS_INIT}
-  ${OPENGL_gl_LIBRARY} 
-  ${GLUT_glut_LIBRARY} 
-  ${OPENGL_glu_LIBRARY} 
-  ${GLUI_LIBRARY})
-
diff --git a/src/util/reducedDynamicSolver-rt/Makefile b/src/util/reducedDynamicSolver-rt/Makefile
deleted file mode 100644
index 49bb702087037e27f8fb6f3d8af2f69269190e11..0000000000000000000000000000000000000000
--- a/src/util/reducedDynamicSolver-rt/Makefile
+++ /dev/null
@@ -1,48 +0,0 @@
-ifndef REDUCEDDYNAMICSOLVERRT
-REDUCEDDYNAMICSOLVERRT=REDUCEDDYNAMICSOLVERRT
-
-ifndef CLEANFOLDER
-CLEANFOLDER=REDUCEDDYNAMICSOLVERRT
-endif
-
-include ../../Makefile-headers/Makefile-header
-R ?= ../..
-
-# the object files to be compiled for this utility
-REDUCEDDYNAMICSOLVERRT_OBJECTS=reducedDynamicSolver-rt.o initGraphics.o
-
-# the libraries this utility depends on
-
-REDUCEDDYNAMICSOLVERRT_LIBS=objMeshGPUDeformer objMesh imageIO modalMatrix sceneObjectReduced sceneObject objMeshGPUDeformer objMesh imageIO integratorSparse integratorDense integrator reducedStvk stvk reducedElasticForceModel reducedForceModel forceModel matrix matrixIO lighting configFile volumetricMesh loadList getopts camera minivector openGLHelper
-
-# the headers in this library
-REDUCEDDYNAMICSOLVERRT_HEADERS=initGraphics.h
-
-REDUCEDDYNAMICSOLVERRT_LINK=$(addprefix -l, $(REDUCEDDYNAMICSOLVERRT_LIBS)) $(SPOOLES_LIB) $(BLASLAPACK_LIB) $(GLUI_LIB) $(CG_LIBS) $(IMAGE_LIBS) $(STANDARD_LIBS)
-
-REDUCEDDYNAMICSOLVERRT_OBJECTS_FILENAMES=$(addprefix $(R)/utilities/reducedDynamicSolver-rt/, $(REDUCEDDYNAMICSOLVERRT_OBJECTS))
-REDUCEDDYNAMICSOLVERRT_HEADER_FILENAMES=$(addprefix $(R)/utilities/reducedDynamicSolver-rt/, $(REDUCEDDYNAMICSOLVERRT_HEADERS))
-REDUCEDDYNAMICSOLVERRT_LIB_MAKEFILES=$(call GET_LIB_MAKEFILES, $(REDUCEDDYNAMICSOLVERRT_LIBS))
-REDUCEDDYNAMICSOLVERRT_LIB_FILENAMES=$(call GET_LIB_FILENAMES, $(REDUCEDDYNAMICSOLVERRT_LIBS))
-
-include $(REDUCEDDYNAMICSOLVERRT_LIB_MAKEFILES) ../../libraries/glui/Makefile
-
-all: $(R)/utilities/reducedDynamicSolver-rt/reducedDynamicSolver-rt
-
-$(R)/utilities/reducedDynamicSolver-rt/reducedDynamicSolver-rt: $(REDUCEDDYNAMICSOLVERRT_OBJECTS_FILENAMES)
-	$(CXXLD) $(LDFLAGS) $(REDUCEDDYNAMICSOLVERRT_OBJECTS) $(REDUCEDDYNAMICSOLVERRT_LINK) -Wl,-rpath,$(GLUI_DIR)/lib -o $@; cp $@ $(R)/utilities/bin/
-
-$(REDUCEDDYNAMICSOLVERRT_OBJECTS_FILENAMES): %.o: %.cpp $(REDUCEDDYNAMICSOLVERRT_LIB_FILENAMES) $(REDUCEDDYNAMICSOLVERRT_HEADER_FILENAMES)
-	$(CXX) $(CXXFLAGS) -c $(INCLUDE) $(CG_INCLUDE) $(GLUI_INCLUDE) $< -o $@
-
-ifeq ($(CLEANFOLDER), REDUCEDDYNAMICSOLVERRT)
-clean: cleanreducedDynamicSolver-rt
-endif
-
-deepclean: cleanreducedDynamicSolver-rt
-
-cleanreducedDynamicSolver-rt:
-	$(RM) $(REDUCEDDYNAMICSOLVERRT_OBJECTS_FILENAMES) $(R)/utilities/reducedDynamicSolver-rt/reducedDynamicSolver-rt
-
-endif
-
diff --git a/src/util/volumetricMeshUtilities/CMakeLists.txt b/src/util/volumetricMeshUtilities/CMakeLists.txt
deleted file mode 100644
index ad537e219d1c6d1ab6b24452b47bbc84e23c104c..0000000000000000000000000000000000000000
--- a/src/util/volumetricMeshUtilities/CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-include_directories(
-   ${CMAKE_CURRENT_SOURCE_DIR}
-   ${CMAKE_CURRENT_SOURCE_DIR}/../../libminivector
-   ${CMAKE_CURRENT_SOURCE_DIR}/../../libobjMesh
-   ${CMAKE_CURRENT_SOURCE_DIR}/../../libmatrixIO
-   ${CMAKE_CURRENT_SOURCE_DIR}/../../libvega-getopts
-   ${CMAKE_CURRENT_SOURCE_DIR}/../../libloadList
-   ${CMAKE_CURRENT_SOURCE_DIR}/../../libsparseMatrix
-   ${CMAKE_CURRENT_SOURCE_DIR}/../../libvolumetricMesh
-   ${CMAKE_CURRENT_SOURCE_DIR}/include
-)
-
-set(VOLUTILS_LIBS volumetricMesh objMesh matrixIO vega-getopts loadList sparseMatrix)
-set(VOLUTILS_LIBS ${VOLUTILS_LIBS} ${SPOOLES_LIBRARY} ${PARDISO_LIBRARY} ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY})
-
-add_executable(generateMassMatrix generateMassMatrix.cpp)
-target_link_libraries(generateMassMatrix ${VOLUTILS_LIBS})
-add_executable(generateInterpolant generateInterpolant.cpp)
-target_link_libraries(generateInterpolant ${VOLUTILS_LIBS})
-add_executable(generateInterpolationMatrix generateInterpolationMatrix.cpp)
-target_link_libraries(generateInterpolationMatrix ${VOLUTILS_LIBS})
-add_executable(generateSurfaceMesh generateSurfaceMesh.cpp)
-target_link_libraries(generateSurfaceMesh ${VOLUTILS_LIBS})
diff --git a/thirdparty/glui/2.36/src/CMakeLists.txt b/thirdparty/glui/2.36/src/CMakeLists.txt
deleted file mode 100644
index e637a6fcd5f7ddc88614d30ce59bb3f47cfd3a5f..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/CMakeLists.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-if (NOT APPLE)
-  find_package(X11)
-endif()
-
-#LIBGLUI = -L./lib -lglui
-#LIBGL   = -lGLU -lGL
-#LIBS    = -lXmu -lXext -lX11 -lXi -lm
-
-# One of the following options only...
-
-# (1) OpenGLUT
-# LIBGLUT   = -L/usr/X11R6/lib -lopenglut
-# CPPFLAGS += -I/usr/X11R6/include -DGLUI_OPENGLUT
-
-# (2) FreeGLUT
-# LIBGLUT   = -L/usr/X11R6/lib -lfreeglut
-# CPPFLAGS += -I/usr/X11R6/include -DGLUI_FREEGLUT
-
-#CPPFLAGS += -I/usr/X11R6/include
-
-#######################################
-
-set(GLUI_SRCS
-	algebra3.cpp
-	arcball.cpp
-	glui.cpp
-	glui_add_controls.cpp
-	glui_bitmap_img_data.cpp
-	glui_bitmaps.cpp
-	glui_button.cpp
-	glui_checkbox.cpp
-	glui_column.cpp
-	glui_commandline.cpp
-	glui_control.cpp
-	glui_edittext.cpp
-	glui_filebrowser.cpp
-	glui_list.cpp
-	glui_listbox.cpp
-	glui_mouse_iaction.cpp
-	glui_node.cpp
-	glui_panel.cpp
-	glui_radio.cpp
-	glui_rollout.cpp
-	glui_rotation.cpp
-	glui_scrollbar.cpp
-	glui_separator.cpp
-	glui_spinner.cpp
-	glui_statictext.cpp
-	glui_string.cpp
-	glui_textbox.cpp
-	glui_translation.cpp
-	glui_tree.cpp
-	glui_treepanel.cpp
-	glui_window.cpp
-	quaternion.cpp
-	viewmodel.cpp
-	)
-	
-vega_add_library(glui
-  SOURCES ${GLUI_SRCS}
-)
-target_include_directories(glui
-  PUBLIC
-    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
-    $<INSTALL_INTERFACE:include>
-)
-target_link_libraries(glui
-  PUBLIC
-    ${GLUT_LIBRARY}
-    ${OPENGL_LIBRARIES}
-)
-
diff --git a/thirdparty/glui/2.36/src/LICENSE.txt b/thirdparty/glui/2.36/src/LICENSE.txt
deleted file mode 100644
index 7c7481e80533874b80b916452739cb7a7f2c8047..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/LICENSE.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-  GLUI User Interface Toolkit 
-  ---------------------------
-
-  (The ZLIB License)
-
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
-
-
-  History of GLUI's License:
-  --------------------------
-  Originally, GLUI was licensed under LGPL because Paul Rademacher
-  just wanted something that was very permissive, and LGPL was the
-  thing he had heard of.  I discussed the license with Paul at
-  SIGGRAPH 2006, where he told me the above, and also told me he was
-  supportive of changing the license to one of the less restrictive
-  open source licenses.  So after contacting all the authors to get
-  their OK, GLUI is now under the ZLIB license.
-
-  Bill Baxter -- November 2007
diff --git a/thirdparty/glui/2.36/src/doc/doxygen.cfg b/thirdparty/glui/2.36/src/doc/doxygen.cfg
deleted file mode 100644
index d31a2cc10297321c5cd1c8811042e353b2a95a56..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/doc/doxygen.cfg
+++ /dev/null
@@ -1,231 +0,0 @@
-# Doxyfile 1.5.3
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-DOXYFILE_ENCODING      = UTF-8
-PROJECT_NAME           = Glui
-PROJECT_NUMBER         = 2.35
-OUTPUT_DIRECTORY       = doc/doxygen
-CREATE_SUBDIRS         = NO
-OUTPUT_LANGUAGE        = English
-BRIEF_MEMBER_DESC      = YES
-REPEAT_BRIEF           = YES
-ABBREVIATE_BRIEF       = 
-ALWAYS_DETAILED_SEC    = NO
-INLINE_INHERITED_MEMB  = NO
-FULL_PATH_NAMES        = NO
-STRIP_FROM_PATH        = lib/
-STRIP_FROM_INC_PATH    = 
-SHORT_NAMES            = NO
-JAVADOC_AUTOBRIEF      = NO
-QT_AUTOBRIEF           = NO
-MULTILINE_CPP_IS_BRIEF = NO
-DETAILS_AT_TOP         = NO
-INHERIT_DOCS           = YES
-SEPARATE_MEMBER_PAGES  = NO
-TAB_SIZE               = 8
-ALIASES                = 
-OPTIMIZE_OUTPUT_FOR_C  = YES
-OPTIMIZE_OUTPUT_JAVA   = NO
-BUILTIN_STL_SUPPORT    = NO
-CPP_CLI_SUPPORT        = NO
-DISTRIBUTE_GROUP_DOC   = NO
-SUBGROUPING            = YES
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-EXTRACT_ALL            = YES
-EXTRACT_PRIVATE        = YES
-EXTRACT_STATIC         = YES
-EXTRACT_LOCAL_CLASSES  = YES
-EXTRACT_LOCAL_METHODS  = YES
-EXTRACT_ANON_NSPACES   = YES
-HIDE_UNDOC_MEMBERS     = NO
-HIDE_UNDOC_CLASSES     = NO
-HIDE_FRIEND_COMPOUNDS  = NO
-HIDE_IN_BODY_DOCS      = NO
-INTERNAL_DOCS          = NO
-CASE_SENSE_NAMES       = NO
-HIDE_SCOPE_NAMES       = NO
-SHOW_INCLUDE_FILES     = YES
-INLINE_INFO            = YES
-SORT_MEMBER_DOCS       = YES
-SORT_BRIEF_DOCS        = NO
-SORT_BY_SCOPE_NAME     = NO
-GENERATE_TODOLIST      = YES
-GENERATE_TESTLIST      = YES
-GENERATE_BUGLIST       = YES
-GENERATE_DEPRECATEDLIST= YES
-ENABLED_SECTIONS       = 
-MAX_INITIALIZER_LINES  = 30
-SHOW_USED_FILES        = YES
-SHOW_DIRECTORIES       = NO
-FILE_VERSION_FILTER    = 
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-QUIET                  = NO
-WARNINGS               = YES
-WARN_IF_UNDOCUMENTED   = YES
-WARN_IF_DOC_ERROR      = YES
-WARN_NO_PARAMDOC       = NO
-WARN_FORMAT            = "$file:$line: $text   "
-WARN_LOGFILE           = 
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-INPUT                  = . include/GL
-INPUT_ENCODING         = UTF-8
-FILE_PATTERNS          = *.cpp *.c *.h
-RECURSIVE              = NO
-EXCLUDE                = 
-EXCLUDE_SYMLINKS       = NO
-EXCLUDE_PATTERNS       = 
-EXCLUDE_SYMBOLS        = 
-EXAMPLE_PATH           = 
-EXAMPLE_PATTERNS       = 
-EXAMPLE_RECURSIVE      = NO
-IMAGE_PATH             = 
-INPUT_FILTER           = 
-FILTER_PATTERNS        = 
-FILTER_SOURCE_FILES    = NO
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-SOURCE_BROWSER         = NO
-INLINE_SOURCES         = NO
-STRIP_CODE_COMMENTS    = YES
-REFERENCED_BY_RELATION = YES
-REFERENCES_RELATION    = YES
-REFERENCES_LINK_SOURCE = YES
-USE_HTAGS              = NO
-VERBATIM_HEADERS       = YES
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-ALPHABETICAL_INDEX     = YES
-COLS_IN_ALPHA_INDEX    = 5
-IGNORE_PREFIX          = 
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-GENERATE_HTML          = YES
-HTML_OUTPUT            = html
-HTML_FILE_EXTENSION    = .html
-HTML_HEADER            = 
-HTML_FOOTER            = 
-HTML_STYLESHEET        = 
-HTML_ALIGN_MEMBERS     = YES
-GENERATE_HTMLHELP      = NO
-HTML_DYNAMIC_SECTIONS  = NO
-CHM_FILE               = 
-HHC_LOCATION           = 
-GENERATE_CHI           = NO
-BINARY_TOC             = NO
-TOC_EXPAND             = NO
-DISABLE_INDEX          = NO
-ENUM_VALUES_PER_LINE   = 4
-GENERATE_TREEVIEW      = NO
-TREEVIEW_WIDTH         = 250
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-GENERATE_LATEX         = YES
-LATEX_OUTPUT           = latex
-LATEX_CMD_NAME         = latex
-MAKEINDEX_CMD_NAME     = makeindex
-COMPACT_LATEX          = NO
-PAPER_TYPE             = a4wide
-EXTRA_PACKAGES         = 
-LATEX_HEADER           = 
-PDF_HYPERLINKS         = NO
-USE_PDFLATEX           = YES
-LATEX_BATCHMODE        = NO
-LATEX_HIDE_INDICES     = NO
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-GENERATE_RTF           = NO
-RTF_OUTPUT             = rtf
-COMPACT_RTF            = NO
-RTF_HYPERLINKS         = NO
-RTF_STYLESHEET_FILE    = 
-RTF_EXTENSIONS_FILE    = 
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-GENERATE_MAN           = NO
-MAN_OUTPUT             = man
-MAN_EXTENSION          = .3
-MAN_LINKS              = NO
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-GENERATE_XML           = NO
-XML_OUTPUT             = xml
-XML_SCHEMA             = 
-XML_DTD                = 
-XML_PROGRAMLISTING     = YES
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-GENERATE_AUTOGEN_DEF   = NO
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-GENERATE_PERLMOD       = NO
-PERLMOD_LATEX          = NO
-PERLMOD_PRETTY         = YES
-PERLMOD_MAKEVAR_PREFIX = 
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
-#---------------------------------------------------------------------------
-ENABLE_PREPROCESSING   = YES
-MACRO_EXPANSION        = NO
-EXPAND_ONLY_PREDEF     = NO
-SEARCH_INCLUDES        = YES
-INCLUDE_PATH           = 
-INCLUDE_FILE_PATTERNS  = 
-PREDEFINED             = 
-EXPAND_AS_DEFINED      = 
-SKIP_FUNCTION_MACROS   = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references   
-#---------------------------------------------------------------------------
-TAGFILES               = 
-GENERATE_TAGFILE       = 
-ALLEXTERNALS           = NO
-EXTERNAL_GROUPS        = YES
-PERL_PATH              = /usr/bin/perl
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
-#---------------------------------------------------------------------------
-CLASS_DIAGRAMS         = YES
-MSCGEN_PATH            = 
-HIDE_UNDOC_RELATIONS   = YES
-HAVE_DOT               = YES
-CLASS_GRAPH            = YES
-COLLABORATION_GRAPH    = NO
-GROUP_GRAPHS           = YES
-UML_LOOK               = NO
-TEMPLATE_RELATIONS     = YES
-INCLUDE_GRAPH          = YES
-INCLUDED_BY_GRAPH      = YES
-CALL_GRAPH             = YES
-CALLER_GRAPH           = YES
-GRAPHICAL_HIERARCHY    = YES
-DIRECTORY_GRAPH        = YES
-DOT_IMAGE_FORMAT       = png
-DOT_PATH               = 
-DOTFILE_DIRS           = 
-DOT_GRAPH_MAX_NODES    = 50
-MAX_DOT_GRAPH_DEPTH    = 0
-DOT_TRANSPARENT        = NO
-DOT_MULTI_TARGETS      = NO
-GENERATE_LEGEND        = YES
-DOT_CLEANUP            = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine   
-#---------------------------------------------------------------------------
-SEARCHENGINE           = NO
diff --git a/thirdparty/glui/2.36/src/doc/glui_manual.doc b/thirdparty/glui/2.36/src/doc/glui_manual.doc
deleted file mode 100644
index 714c5bf6c62b4fd679b0e17f0f1731c073487f87..0000000000000000000000000000000000000000
Binary files a/thirdparty/glui/2.36/src/doc/glui_manual.doc and /dev/null differ
diff --git a/thirdparty/glui/2.36/src/glui_window.cpp b/thirdparty/glui/2.36/src/glui_window.cpp
deleted file mode 100644
index 7e5d7f08864cc5cd93dd949afd54b7adb30308f0..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/glui_window.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-
-  glui_window.cpp - GLUI_Button control class
-
-  GLUI User Interface Toolkit 
-  Copyright (c) 1998 Paul Rademacher
-
-  WWW:    http://sourceforge.net/projects/glui/
-  Forums: http://sourceforge.net/forum/?group_id=92496
-
-  This software is provided 'as-is', without any express or implied 
-  warranty. In no event will the authors be held liable for any damages 
-  arising from the use of this software. 
-
-  Permission is granted to anyone to use this software for any purpose, 
-  including commercial applications, and to alter it and redistribute it 
-  freely, subject to the following restrictions: 
-
-  1. The origin of this software must not be misrepresented; you must not 
-  claim that you wrote the original software. If you use this software 
-  in a product, an acknowledgment in the product documentation would be 
-  appreciated but is not required. 
-  2. Altered source versions must be plainly marked as such, and must not be 
-  misrepresented as being the original software. 
-  3. This notice may not be removed or altered from any source distribution. 
-
-*/
-
-#include "GL/glui.h"
-#include "glui_internal.h"
-
-GLUI_Glut_Window::GLUI_Glut_Window()
-:   GLUI_Node(),
-
-	glut_window_id(0),
-	glut_keyboard_CB(NULL),
-	glut_special_CB(NULL),
-	glut_reshape_CB(NULL),
-	glut_passive_motion_CB(NULL),
-	glut_mouse_CB(NULL),
-	glut_visibility_CB(NULL),
-	glut_motion_CB(NULL),
-	glut_display_CB(NULL),
-	glut_entry_CB(NULL)
-{
-}
diff --git a/thirdparty/glui/2.36/src/makefile b/thirdparty/glui/2.36/src/makefile
deleted file mode 100644
index eb2225156e3a80ac3e3cf66b986019fa558a3dcc..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/makefile
+++ /dev/null
@@ -1,161 +0,0 @@
-.SUFFIXES: .cpp
-
-#for sgi   -- comment out the lines below to use on HP
-#CC=CC -g0 -o32
-#CC=gcc
-
-# Compiler options
-OPTS=-g
-OPTS=-O0
-#OPTS=-O2
-
-UNAME = $(shell uname)
-
-ifeq ($(UNAME), Linux)
-CXX       = g++
-CPPFLAGS += $(OPTS) -Wall -pedantic
-endif
-
-#######################################
-
-CPPFLAGS += -I./ -I./include
-
-LIBGLUI = -L./lib -lglui
-LIBGL   = -lGLU -lGL
-LIBS    = -lXmu -lXext -lX11 -lXi -lm
-
-# One of the following options only...
-
-# (1) OpenGLUT
-# LIBGLUT   = -L/usr/X11R6/lib -lopenglut
-# CPPFLAGS += -I/usr/X11R6/include -DGLUI_OPENGLUT
-
-# (2) FreeGLUT
-# LIBGLUT   = -L/usr/X11R6/lib -lfreeglut
-# CPPFLAGS += -I/usr/X11R6/include -DGLUI_FREEGLUT
-
-# (3) GLUT
-LIBGLUT   = -L/usr/X11R6/lib -lglut
-CPPFLAGS += -I/usr/X11R6/include
-
-#######################################
-
-GLUI_OBJS = glui_add_controls.o glui_string.o glui.o glui_bitmap_img_data.o glui_bitmaps.o glui_button.o glui_edittext.o glui_commandline.o glui_checkbox.o glui_node.o glui_radio.o glui_statictext.o glui_panel.o glui_separator.o glui_spinner.o glui_control.o glui_column.o glui_translation.o glui_rotation.o glui_mouse_iaction.o glui_listbox.o glui_rollout.o glui_window.o arcball.o algebra3.o quaternion.o viewmodel.o glui_treepanel.o glui_tree.o glui_textbox.o glui_scrollbar.o glui_list.o glui_filebrowser.o
-
-GLUI_LIB = lib/libglui.a
-
-GLUI_EXAMPLES = bin/example1 bin/example2 bin/example3 bin/example4 bin/example5 bin/example6
-
-GLUI_TOOLS = bin/ppm2array
-
-.PHONY: all setup examples tools clean depend doc doc-pdf doc-dist dist
-
-all: setup $(GLUI_LIB) examples tools
-
-setup:
-	mkdir -p bin
-	mkdir -p lib
-
-examples: $(GLUI_EXAMPLES)
-
-tools: $(GLUI_TOOLS)
-
-bin/ppm2array: tools/ppm2array.cpp tools/ppm.cpp
-	$(CXX) $(CPPFLAGS) -o $@ $^
-
-bin/%: example/%.cpp $(GLUI_LIB)
-	$(CXX) $(CPPFLAGS) -o $@ $<  $(LIBGLUI) $(LIBGLUT) $(LIBGL) $(LIBS)
-
-$(GLUI_LIB): $(GLUI_OBJS)
-	ar -r $(GLUI_LIB) $(GLUI_OBJS)
-
-.cpp.o:
-	$(CXX) $(CPPFLAGS) -c $<
-
-.c.o:
-	$(CXX) $(CPPFLAGS) -c $<
-
-docs:
-	doxygen doc/doxygen.cfg
-
-clean:
-	rm -f *.o $(GLUI_LIB) $(GLUI_EXAMPLES) $(GLUI_TOOLS) 
-	rm -fr doc/doxygen
-
-depend:
-	makedepend -Y./include `find -name "*.cpp"` `find -name "*.c"`
-
-DIST = glui-2.35
-
-doc:
-	doxygen doc/doxygen.cfg
-
-doc-pdf:
-	cd doc/doxygen/latex &&	pdflatex refman.tex && pdflatex refman.tex && pdflatex refman.tex
-
-doc-dist:
-	mkdir -p $(DIST)/doc
-	cp `find doc/doxygen/html -type f` $(DIST)/doc
-	tar cv $(DIST) | gzip -9 - > $(DIST)-doc.tgz
-	zip -vr9 $(DIST)-doc.zip $(DIST)
-	rm -Rf $(DIST)
-	
-dist: clean
-	mkdir -p $(DIST) 
-	cp --parents \
-		`find -type f -name "*.cpp"` \
-		`find -type f -name "*.c"` \
-		`find -type f -name "*.h"` \
-		`find -type f -name "*.dev"` \
-		`find -type f -name "*.dsp"` \
-		`find -type f -name "*.dsw"` \
-		`find -type f -name "*.vcproj"` \
-		`find -type f -name "*.sln"` \
-		`find -type f -name "*.txt"` \
-		makefile \
-		$(DIST)
-	tar cv $(DIST) | gzip -9 - > $(DIST).tgz
-	rm -Rf $(DIST)
-
-# DO NOT DELETE THIS LINE -- make depend depends on it.
-
-./algebra3.o: algebra3.h glui_internal.h
-./arcball.o: arcball.h glui_internal.h algebra3.h quaternion.h
-./glui_button.o: ./include/GL/glui.h glui_internal.h
-./glui_checkbox.o: ./include/GL/glui.h glui_internal.h
-./glui_column.o: ./include/GL/glui.h glui_internal.h
-./glui_control.o: ./include/GL/glui.h glui_internal.h
-./glui_edittext.o: ./include/GL/glui.h glui_internal.h
-./glui_listbox.o: ./include/GL/glui.h glui_internal.h
-./glui_mouse_iaction.o: ./include/GL/glui.h glui_internal.h
-./glui_node.o: ./include/GL/glui.h glui_internal.h
-./glui_panel.o: ./include/GL/glui.h glui_internal.h
-./glui_radio.o: ./include/GL/glui.h glui_internal.h
-./glui_rollout.o: ./include/GL/glui.h glui_internal.h
-./glui_rotation.o: ./include/GL/glui.h arcball.h glui_internal.h algebra3.h
-./glui_rotation.o: quaternion.h
-./glui_separator.o: ./include/GL/glui.h glui_internal.h
-./glui_spinner.o: ./include/GL/glui.h glui_internal.h
-./glui_translation.o: ./include/GL/glui.h glui_internal.h algebra3.h
-./glui_window.o: ./include/GL/glui.h glui_internal.h
-./quaternion.o: quaternion.h algebra3.h glui_internal.h
-./viewmodel.o: viewmodel.h algebra3.h ./include/GL/glui.h
-./glui_bitmaps.o: ./include/GL/glui.h glui_internal.h
-./glui_statictext.o: ./include/GL/glui.h glui_internal.h
-./glui.o: ./include/GL/glui.h glui_internal.h
-./glui_add_controls.o: ./include/GL/glui.h glui_internal.h
-./glui_commandline.o: ./include/GL/glui.h glui_internal.h
-./glui_list.o: ./include/GL/glui.h glui_internal.h
-./glui_scrollbar.o: ./include/GL/glui.h glui_internal.h
-./glui_string.o: ./include/GL/glui.h
-./glui_textbox.o: ./include/GL/glui.h glui_internal.h
-./glui_tree.o: ./include/GL/glui.h glui_internal.h
-./glui_treepanel.o: ./include/GL/glui.h
-./example/example1.o: ./include/GL/glui.h
-./example/example2.o: ./include/GL/glui.h
-./example/example3.o: ./include/GL/glui.h
-./example/example4.o: ./include/GL/glui.h
-./example/example5.o: ./include/GL/glui.h
-./example/example6.o: ./include/GL/glui.h
-./tools/ppm2array.o: ./tools/ppm.hpp
-./glui_filebrowser.o: ./include/GL/glui.h glui_internal.h
diff --git a/thirdparty/glui/2.36/src/msvc/README.txt b/thirdparty/glui/2.36/src/msvc/README.txt
deleted file mode 100644
index b96498c76a64a1c3e299f7f97eab0d22ed23df84..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/README.txt
+++ /dev/null
@@ -1,94 +0,0 @@
-About the GLUI Project Files for Microsoft Visual Studio.
-
-
-----------------------------------------
-Microsoft Visual Studio 7.1
-----------------------------------------
-
-Using these project files you can build four different versions of
-glui:
-
-=== Release ===
-
-  - static GLUI library, debug symbols off, optimizations on
-  - outputs lib\glui32.lib
-
-  - To use in your program:
-
-    The lib should link in automatically if you include glui.h
-      thanks to a #pragma comment(lib...) statement.
-
-=== Debug ===
-
-  - static GLUI library, debug symbols on, optimizations off
-  - outputs lib\glui32d.lib
-
-  - To use in your program:
-
-    Define preprocessor symbols:  NO_GLUI_LIB_PRAGMA
-    Add to the list of input libs:  glui32d.lib
-
-=== Release GLUIDLL ===
-
-  - dynamic GLUI library, debug symbols off, optimizations on
-  - outputs lib\glui32dll.lib and bin\glui32dll.dll
-
-  - To use in your program:
-
-    Define the preprocessor symbol:  GLUIDLL
-    Then the dll should link in automatically if you include glui.h
-    thanks to a #pragma comment(lib...) statement.
-
-=== Debug GLUIDLL ===
-
-  - dynamic GLUI library, debug symbols on, optimizations off
-  - outputs lib\glui32dlld.lib and bin\glui32dlld.dll
-
-  - To use in your program:
-
-    Define preprocessor symbols:  NO_GLUI_LIB_PRAGMA;GLUIDLL
-    Add to the list of input libs:  glui32dlld.lib
-
-
-To build the examples, you need to build the libraries first, then the
-examples (no automatic dependencies are set up in the solution file).
-
-If you're lazy, you can just batch build everything twice.  (Some
-examples will fail to link the first time through because not all the
-libraries have been built yet, but the second time through everything
-should link.)
-
-The examples are set up in pretty much the way you would need to set
-them up in your own programs in terms of preprocessor defines and extra 
-libraries in the list of link libs.  However, you will probably only want 
-to link with either glui32.lib or glui32dll.lib in your programs.  The debug
-versions are only useful if you want to debug GLUI itself.
-
-
-RUNNING GLUIDLL EXAMPLES:
-
-To make the GLUIDLL examples run from within Visual Studio you may need to
-change the working directory on the example projects to "bin":
-
-  Properties -> Debugging -> Working Directory
-
-
-Unfortunately Microsoft considers this setting "user-specific" rather
-than "project-specific" and so doesn't save it in the main project or
-solution files.
-
-
-----------------------------------------
-Microsoft Visual Studio 8.0
-----------------------------------------
-
-Visual Studio 8 should be able to convert the 7.1 project files automatically.
-
-----------------------------------------
-Microsoft Visual Studio 6.0
-----------------------------------------
-
-These project files are out of date.
-
-We are looking for any volunteers who still use MSVC 6.0 to maintain
-these files.
\ No newline at end of file
diff --git a/thirdparty/glui/2.36/src/msvc/example1.vcproj b/thirdparty/glui/2.36/src/msvc/example1.vcproj
deleted file mode 100644
index 2f66cc9e8e097c834772106832621308feaf2e10..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/example1.vcproj
+++ /dev/null
@@ -1,296 +0,0 @@
-<?xml version="1.0" encoding="shift_jis"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="7.10"
-	Name="example1"
-	ProjectGUID="{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}"
-	RootNamespace="example1"
-	SccProjectName=""
-	SccLocalPath="">
-	<Platforms>
-		<Platform
-			Name="Win32"/>
-	</Platforms>
-	<Configurations>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory=".\Release"
-			IntermediateDirectory=".\Release"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/example1.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example1.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example1.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="Debug"
-			IntermediateDirectory="Debug"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/example1.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32d.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example1d.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Debug/example1.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug GLUIDLL|Win32"
-			OutputDirectory="DebugDLL"
-			IntermediateDirectory="DebugDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA;GLUIDLL"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/example1.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32dlld.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example1dlld.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Debug/example1.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release GLUIDLL|Win32"
-			OutputDirectory="ReleaseDLL"
-			IntermediateDirectory="ReleaseDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUIDLL"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/example1.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example1dll.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example1.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\example\example1.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Release GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/thirdparty/glui/2.36/src/msvc/example2.vcproj b/thirdparty/glui/2.36/src/msvc/example2.vcproj
deleted file mode 100644
index 5b7494e69628f7ebf116dceee590eb3782a66520..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/example2.vcproj
+++ /dev/null
@@ -1,295 +0,0 @@
-<?xml version="1.0" encoding="shift_jis"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="7.10"
-	Name="example2"
-	ProjectGUID="{10FB199D-975A-4320-A09A-570C0E8A7890}"
-	SccProjectName=""
-	SccLocalPath="">
-	<Platforms>
-		<Platform
-			Name="Win32"/>
-	</Platforms>
-	<Configurations>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory=".\Release"
-			IntermediateDirectory=".\Release"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/example2.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example2.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example2.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="Debug"
-			IntermediateDirectory="Debug"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/example2.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32d.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example2d.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Debug/example2.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug GLUIDLL|Win32"
-			OutputDirectory="DebugDLL"
-			IntermediateDirectory="DebugDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA;GLUIDLL"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/example2.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32dlld.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example2dlld.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Debug/example2.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release GLUIDLL|Win32"
-			OutputDirectory="ReleaseDLL"
-			IntermediateDirectory="ReleaseDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUIDLL"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/example2.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example2dll.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example2.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\example\example2.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Release GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/thirdparty/glui/2.36/src/msvc/example3.vcproj b/thirdparty/glui/2.36/src/msvc/example3.vcproj
deleted file mode 100644
index 8adcc6573bbab06fe1ed63d64453e3851338bcba..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/example3.vcproj
+++ /dev/null
@@ -1,293 +0,0 @@
-<?xml version="1.0" encoding="shift_jis"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="7.10"
-	Name="example3"
-	ProjectGUID="{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}"
-	SccProjectName=""
-	SccLocalPath="">
-	<Platforms>
-		<Platform
-			Name="Win32"/>
-	</Platforms>
-	<Configurations>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="Debug"
-			IntermediateDirectory="Debug"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/example3.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32d.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example3d.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Debug/example3.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory=".\Release"
-			IntermediateDirectory=".\Release"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				AdditionalIncludeDirectories="../include"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/example3.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example3.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example3.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug GLUIDLL|Win32"
-			OutputDirectory="DebugDLL"
-			IntermediateDirectory="DebugDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA;GLUIDLL"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/example3.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32dlld.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example3dlld.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Debug/example3.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release GLUIDLL|Win32"
-			OutputDirectory="ReleaseDLL"
-			IntermediateDirectory="ReleaseDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUIDLL"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/example3.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example3dll.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example3.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\example\example3.cpp">
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Release GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/thirdparty/glui/2.36/src/msvc/example4.vcproj b/thirdparty/glui/2.36/src/msvc/example4.vcproj
deleted file mode 100644
index a996058d1603db379862162c9341785f36362c78..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/example4.vcproj
+++ /dev/null
@@ -1,295 +0,0 @@
-<?xml version="1.0" encoding="shift_jis"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="7.10"
-	Name="example4"
-	ProjectGUID="{DE98CD9A-9830-46F8-835D-FB4D82182A7C}"
-	SccProjectName=""
-	SccLocalPath="">
-	<Platforms>
-		<Platform
-			Name="Win32"/>
-	</Platforms>
-	<Configurations>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="Debug"
-			IntermediateDirectory="Debug"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/example4.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32d.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example4d.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Debug/example4.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory=".\Release"
-			IntermediateDirectory=".\Release"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/example4.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example4.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example4.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug GLUIDLL|Win32"
-			OutputDirectory="DebugDLL"
-			IntermediateDirectory="DebugDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA;GLUIDLL"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/example4.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32dlld.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example4dlld.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Debug/example4.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release GLUIDLL|Win32"
-			OutputDirectory="ReleaseDLL"
-			IntermediateDirectory="ReleaseDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUIDLL"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/example4.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example4dll.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example4.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\example\example4.cpp">
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Release GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/thirdparty/glui/2.36/src/msvc/example5.vcproj b/thirdparty/glui/2.36/src/msvc/example5.vcproj
deleted file mode 100644
index 99d3fd43560a6f752debe8eec56099b86bf3d0a5..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/example5.vcproj
+++ /dev/null
@@ -1,296 +0,0 @@
-<?xml version="1.0" encoding="shift_jis"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="7.10"
-	Name="example5"
-	ProjectGUID="{7574E15F-58D0-41DD-8EC2-D8EED683230F}"
-	RootNamespace="example5"
-	SccProjectName=""
-	SccLocalPath="">
-	<Platforms>
-		<Platform
-			Name="Win32"/>
-	</Platforms>
-	<Configurations>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory=".\Release"
-			IntermediateDirectory=".\Release"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=""
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				OutputFile="bin/example5.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example5.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="Debug"
-			IntermediateDirectory="Debug"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=""
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32d.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example5d.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\example5/example5.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug GLUIDLL|Win32"
-			OutputDirectory="DebugDLL"
-			IntermediateDirectory="DebugDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA;GLUIDLL"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=""
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32dlld.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example5dlld.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\example5/example5.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release GLUIDLL|Win32"
-			OutputDirectory="ReleaseDLL"
-			IntermediateDirectory="ReleaseDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUIDLL"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=""
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				OutputFile="bin/example5dll.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example5.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\example\example5.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Release GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/thirdparty/glui/2.36/src/msvc/example6.vcproj b/thirdparty/glui/2.36/src/msvc/example6.vcproj
deleted file mode 100644
index d143f9cd409459beaeb1efae05920e6511d23c3e..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/example6.vcproj
+++ /dev/null
@@ -1,295 +0,0 @@
-<?xml version="1.0" encoding="shift_jis"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="7.10"
-	Name="example6"
-	ProjectGUID="{7574E15F-58D0-41DD-8EC2-D8EED683230F}"
-	SccProjectName=""
-	SccLocalPath="">
-	<Platforms>
-		<Platform
-			Name="Win32"/>
-	</Platforms>
-	<Configurations>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory=".\Release"
-			IntermediateDirectory=".\Release"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=""
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				OutputFile="bin/example6.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example6.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory="Debug"
-			IntermediateDirectory="Debug"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=""
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32d.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example6d.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\example6/example6.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug GLUIDLL|Win32"
-			OutputDirectory="DebugDLL"
-			IntermediateDirectory="DebugDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUI_NO_LIB_PRAGMA;GLUIDLL"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=""
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				AdditionalDependencies="glui32dlld.lib glut32.lib glu32.lib opengl32.lib odbc32.lib odbccp32.lib"
-				OutputFile="bin/example6dlld.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\example6/example6.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="_DEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release GLUIDLL|Win32"
-			OutputDirectory="ReleaseDLL"
-			IntermediateDirectory="ReleaseDLL"
-			ConfigurationType="1"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="GLUIDLL"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=""
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				OutputFile="bin/example6dll.exe"
-				LinkIncremental="0"
-				SuppressStartupBanner="TRUE"
-				AdditionalLibraryDirectories="lib"
-				ProgramDatabaseFile="$(OutDir)/$(InputName).pdb"
-				SubSystem="1"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"
-				TypeLibraryName=".\Release/example6.tlb"
-				HeaderFileName=""/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				PreprocessorDefinitions="NDEBUG"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\example\example6.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Release GLUIDLL|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/thirdparty/glui/2.36/src/msvc/glui.sln b/thirdparty/glui/2.36/src/msvc/glui.sln
deleted file mode 100644
index 810f65b7ddd51f2bd455e5ef9198b01fc21e23c3..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/glui.sln
+++ /dev/null
@@ -1,114 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 8.00
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_glui library", "glui.vcproj", "{304505FF-4A2A-471E-B163-17446985C98E}"
-	ProjectSection(ProjectDependencies) = postProject
-	EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example1", "example1.vcproj", "{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}"
-	ProjectSection(ProjectDependencies) = postProject
-	EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example2", "example2.vcproj", "{10FB199D-975A-4320-A09A-570C0E8A7890}"
-	ProjectSection(ProjectDependencies) = postProject
-	EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example3", "example3.vcproj", "{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}"
-	ProjectSection(ProjectDependencies) = postProject
-	EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example4", "example4.vcproj", "{DE98CD9A-9830-46F8-835D-FB4D82182A7C}"
-	ProjectSection(ProjectDependencies) = postProject
-	EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example5", "example5.vcproj", "{7574E15F-58D0-41DD-8EC2-D8EED683230F}"
-	ProjectSection(ProjectDependencies) = postProject
-	EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example6", "example6.vcproj", "{7574E15F-58D0-41DD-8EC2-D8EED683230F}"
-	ProjectSection(ProjectDependencies) = postProject
-	EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_gluidll library", "gluidll.vcproj", "{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}"
-	ProjectSection(ProjectDependencies) = postProject
-	EndProjectSection
-EndProject
-Global
-	GlobalSection(SolutionConfiguration) = preSolution
-		Debug = Debug
-		Debug GLUIDLL = Debug GLUIDLL
-		Release = Release
-		Release GLUIDLL = Release GLUIDLL
-	EndGlobalSection
-	GlobalSection(ProjectConfiguration) = postSolution
-		{304505FF-4A2A-471E-B163-17446985C98E}.Debug.ActiveCfg = Debug|Win32
-		{304505FF-4A2A-471E-B163-17446985C98E}.Debug.Build.0 = Debug|Win32
-		{304505FF-4A2A-471E-B163-17446985C98E}.Debug GLUIDLL.ActiveCfg = Debug|Win32
-		{304505FF-4A2A-471E-B163-17446985C98E}.Debug GLUIDLL.Build.0 = Debug|Win32
-		{304505FF-4A2A-471E-B163-17446985C98E}.Release.ActiveCfg = Release|Win32
-		{304505FF-4A2A-471E-B163-17446985C98E}.Release.Build.0 = Release|Win32
-		{304505FF-4A2A-471E-B163-17446985C98E}.Release GLUIDLL.ActiveCfg = Release|Win32
-		{304505FF-4A2A-471E-B163-17446985C98E}.Release GLUIDLL.Build.0 = Release|Win32
-		{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}.Debug.ActiveCfg = Debug|Win32
-		{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}.Debug.Build.0 = Debug|Win32
-		{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}.Debug GLUIDLL.ActiveCfg = Debug GLUIDLL|Win32
-		{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}.Debug GLUIDLL.Build.0 = Debug GLUIDLL|Win32
-		{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}.Release.ActiveCfg = Release|Win32
-		{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}.Release.Build.0 = Release|Win32
-		{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}.Release GLUIDLL.ActiveCfg = Release GLUIDLL|Win32
-		{7DE9C1DC-6293-49CE-AC29-AADE92D03ADC}.Release GLUIDLL.Build.0 = Release GLUIDLL|Win32
-		{10FB199D-975A-4320-A09A-570C0E8A7890}.Debug.ActiveCfg = Debug|Win32
-		{10FB199D-975A-4320-A09A-570C0E8A7890}.Debug.Build.0 = Debug|Win32
-		{10FB199D-975A-4320-A09A-570C0E8A7890}.Debug GLUIDLL.ActiveCfg = Debug GLUIDLL|Win32
-		{10FB199D-975A-4320-A09A-570C0E8A7890}.Debug GLUIDLL.Build.0 = Debug GLUIDLL|Win32
-		{10FB199D-975A-4320-A09A-570C0E8A7890}.Release.ActiveCfg = Release|Win32
-		{10FB199D-975A-4320-A09A-570C0E8A7890}.Release.Build.0 = Release|Win32
-		{10FB199D-975A-4320-A09A-570C0E8A7890}.Release GLUIDLL.ActiveCfg = Release GLUIDLL|Win32
-		{10FB199D-975A-4320-A09A-570C0E8A7890}.Release GLUIDLL.Build.0 = Release GLUIDLL|Win32
-		{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}.Debug.ActiveCfg = Debug|Win32
-		{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}.Debug.Build.0 = Debug|Win32
-		{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}.Debug GLUIDLL.ActiveCfg = Debug GLUIDLL|Win32
-		{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}.Debug GLUIDLL.Build.0 = Debug GLUIDLL|Win32
-		{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}.Release.ActiveCfg = Release|Win32
-		{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}.Release.Build.0 = Release|Win32
-		{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}.Release GLUIDLL.ActiveCfg = Release GLUIDLL|Win32
-		{A0DFF582-F13B-4CFB-95CE-A026A503BDD8}.Release GLUIDLL.Build.0 = Release GLUIDLL|Win32
-		{DE98CD9A-9830-46F8-835D-FB4D82182A7C}.Debug.ActiveCfg = Debug|Win32
-		{DE98CD9A-9830-46F8-835D-FB4D82182A7C}.Debug.Build.0 = Debug|Win32
-		{DE98CD9A-9830-46F8-835D-FB4D82182A7C}.Debug GLUIDLL.ActiveCfg = Debug GLUIDLL|Win32
-		{DE98CD9A-9830-46F8-835D-FB4D82182A7C}.Debug GLUIDLL.Build.0 = Debug GLUIDLL|Win32
-		{DE98CD9A-9830-46F8-835D-FB4D82182A7C}.Release.ActiveCfg = Release|Win32
-		{DE98CD9A-9830-46F8-835D-FB4D82182A7C}.Release.Build.0 = Release|Win32
-		{DE98CD9A-9830-46F8-835D-FB4D82182A7C}.Release GLUIDLL.ActiveCfg = Release GLUIDLL|Win32
-		{DE98CD9A-9830-46F8-835D-FB4D82182A7C}.Release GLUIDLL.Build.0 = Release GLUIDLL|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Debug.ActiveCfg = Debug|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Debug.Build.0 = Debug|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Debug GLUIDLL.ActiveCfg = Debug GLUIDLL|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Debug GLUIDLL.Build.0 = Debug GLUIDLL|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Release.ActiveCfg = Release|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Release.Build.0 = Release|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Release GLUIDLL.ActiveCfg = Release GLUIDLL|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Release GLUIDLL.Build.0 = Release GLUIDLL|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Debug.ActiveCfg = Debug|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Debug.Build.0 = Debug|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Debug GLUIDLL.ActiveCfg = Debug GLUIDLL|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Debug GLUIDLL.Build.0 = Debug GLUIDLL|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Release.ActiveCfg = Release|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Release.Build.0 = Release|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Release GLUIDLL.ActiveCfg = Release GLUIDLL|Win32
-		{7574E15F-58D0-41DD-8EC2-D8EED683230F}.Release GLUIDLL.Build.0 = Release GLUIDLL|Win32
-		{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}.Debug.ActiveCfg = Debug GLUIDLL|Win32
-		{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}.Debug.Build.0 = Debug GLUIDLL|Win32
-		{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}.Debug GLUIDLL.ActiveCfg = Debug GLUIDLL|Win32
-		{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}.Debug GLUIDLL.Build.0 = Debug GLUIDLL|Win32
-		{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}.Release.ActiveCfg = Release GLUIDLL|Win32
-		{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}.Release.Build.0 = Release GLUIDLL|Win32
-		{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}.Release GLUIDLL.ActiveCfg = Release GLUIDLL|Win32
-		{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}.Release GLUIDLL.Build.0 = Release GLUIDLL|Win32
-	EndGlobalSection
-	GlobalSection(SolutionItems) = postSolution
-		README.txt = README.txt
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-	EndGlobalSection
-	GlobalSection(ExtensibilityAddIns) = postSolution
-	EndGlobalSection
-EndGlobal
diff --git a/thirdparty/glui/2.36/src/msvc/glui.vcproj b/thirdparty/glui/2.36/src/msvc/glui.vcproj
deleted file mode 100644
index 7b5e525ac4c142ce12594163eb899a0564f3b0eb..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/glui.vcproj
+++ /dev/null
@@ -1,507 +0,0 @@
-<?xml version="1.0" encoding="shift_jis"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="7.10"
-	Name="_glui library"
-	ProjectGUID="{304505FF-4A2A-471E-B163-17446985C98E}"
-	RootNamespace="_glui library"
-	SccProjectName=""
-	SccLocalPath="">
-	<Platforms>
-		<Platform
-			Name="Win32"/>
-	</Platforms>
-	<Configurations>
-		<Configuration
-			Name="Release|Win32"
-			OutputDirectory=".\Release"
-			IntermediateDirectory=".\Release"
-			ConfigurationType="4"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="2"
-				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;GLUI_BUILDING_LIB"
-				StringPooling="TRUE"
-				RuntimeLibrary="2"
-				EnableFunctionLevelLinking="TRUE"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Release/glui.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="lib\glui32.lib"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCMIDLTool"/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Debug|Win32"
-			OutputDirectory=".\Debug"
-			IntermediateDirectory=".\Debug"
-			ConfigurationType="4"
-			UseOfMFC="0"
-			ATLMinimizesCRunTimeLibraryUsage="FALSE">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;GLUI_BUILDING_DLL"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				PrecompiledHeaderFile=".\Debug/glui.pch"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				SuppressStartupBanner="TRUE"
-				DebugInformationFormat="4"
-				CompileAs="0"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLibrarianTool"
-				OutputFile="lib\glui32d.lib"
-				SuppressStartupBanner="TRUE"/>
-			<Tool
-				Name="VCMIDLTool"/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"
-				Culture="1033"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\algebra3.cpp">
-		</File>
-		<File
-			RelativePath="..\arcball.cpp">
-		</File>
-		<File
-			RelativePath="..\glui.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\include\Gl\glui.h">
-		</File>
-		<File
-			RelativePath="..\glui_add_controls.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_bitmap_img_data.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_bitmaps.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_button.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_checkbox.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_column.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_commandline.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_control.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_edittext.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_filebrowser.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_internal.h">
-		</File>
-		<File
-			RelativePath="..\glui_list.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_listbox.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_mouse_iaction.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_node.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_panel.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_radio.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_rollout.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_rotation.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_scrollbar.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_separator.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_spinner.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_statictext.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-		<File
-			RelativePath="..\glui_string.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_textbox.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_translation.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_tree.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_treepanel.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_window.cpp">
-		</File>
-		<File
-			RelativePath="..\quaternion.cpp">
-			<FileConfiguration
-				Name="Release|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="2"
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-			<FileConfiguration
-				Name="Debug|Win32">
-				<Tool
-					Name="VCCLCompilerTool"
-					Optimization="0"
-					AdditionalIncludeDirectories=""
-					PreprocessorDefinitions=""/>
-			</FileConfiguration>
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/thirdparty/glui/2.36/src/msvc/gluidll.vcproj b/thirdparty/glui/2.36/src/msvc/gluidll.vcproj
deleted file mode 100644
index 3bf22dad89e3e622724e2843c3d1a71ba77be896..0000000000000000000000000000000000000000
--- a/thirdparty/glui/2.36/src/msvc/gluidll.vcproj
+++ /dev/null
@@ -1,242 +0,0 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
-	ProjectType="Visual C++"
-	Version="7.10"
-	Name="_gluidll library"
-	ProjectGUID="{5803648B-B9F4-4BEB-A954-3C3EB5AE9FF6}"
-	RootNamespace="_gluidll library"
-	Keyword="Win32Proj">
-	<Platforms>
-		<Platform
-			Name="Win32"/>
-	</Platforms>
-	<Configurations>
-		<Configuration
-			Name="Debug GLUIDLL|Win32"
-			OutputDirectory="DebugDLL"
-			IntermediateDirectory="DebugDLL"
-			ConfigurationType="2"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				Optimization="0"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GLUI_BUILDING_LIB;GLUIDLL"
-				MinimalRebuild="TRUE"
-				BasicRuntimeChecks="3"
-				RuntimeLibrary="3"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				Detect64BitPortabilityProblems="TRUE"
-				DebugInformationFormat="4"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				OutputFile="bin/glui32dlld.dll"
-				LinkIncremental="2"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="2"
-				ImportLibrary="lib/glui32dlld.lib"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-		<Configuration
-			Name="Release GLUIDLL|Win32"
-			OutputDirectory="ReleaseDLL"
-			IntermediateDirectory="ReleaseDLL"
-			ConfigurationType="2"
-			CharacterSet="2">
-			<Tool
-				Name="VCCLCompilerTool"
-				AdditionalIncludeDirectories="../include"
-				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GLUI_BUILDING_LIB;GLUIDLL"
-				RuntimeLibrary="2"
-				RuntimeTypeInfo="TRUE"
-				UsePrecompiledHeader="0"
-				AssemblerListingLocation="$(OutDir)\"
-				ObjectFile="$(OutDir)\"
-				ProgramDataBaseFileName="$(OutDir)\$(TargetName)"
-				WarningLevel="3"
-				Detect64BitPortabilityProblems="TRUE"
-				DebugInformationFormat="0"/>
-			<Tool
-				Name="VCCustomBuildTool"/>
-			<Tool
-				Name="VCLinkerTool"
-				OutputFile="bin/glui32dll.dll"
-				LinkIncremental="1"
-				GenerateDebugInformation="TRUE"
-				ProgramDatabaseFile="$(OutDir)/$(TargetName).pdb"
-				SubSystem="2"
-				OptimizeReferences="2"
-				EnableCOMDATFolding="2"
-				ImportLibrary="lib/glui32dll.lib"
-				TargetMachine="1"/>
-			<Tool
-				Name="VCMIDLTool"/>
-			<Tool
-				Name="VCPostBuildEventTool"/>
-			<Tool
-				Name="VCPreBuildEventTool"/>
-			<Tool
-				Name="VCPreLinkEventTool"/>
-			<Tool
-				Name="VCResourceCompilerTool"/>
-			<Tool
-				Name="VCWebServiceProxyGeneratorTool"/>
-			<Tool
-				Name="VCXMLDataGeneratorTool"/>
-			<Tool
-				Name="VCWebDeploymentTool"/>
-			<Tool
-				Name="VCManagedWrapperGeneratorTool"/>
-			<Tool
-				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
-		</Configuration>
-	</Configurations>
-	<References>
-	</References>
-	<Files>
-		<File
-			RelativePath="..\algebra3.cpp">
-		</File>
-		<File
-			RelativePath="..\algebra3.h">
-		</File>
-		<File
-			RelativePath="..\arcball.cpp">
-		</File>
-		<File
-			RelativePath="..\arcball.h">
-		</File>
-		<File
-			RelativePath="..\glui.cpp">
-		</File>
-		<File
-			RelativePath="..\include\Gl\glui.h">
-		</File>
-		<File
-			RelativePath="..\glui_add_controls.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_bitmap_img_data.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_bitmaps.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_button.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_checkbox.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_column.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_commandline.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_control.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_edittext.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_filebrowser.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_internal.h">
-		</File>
-		<File
-			RelativePath="..\glui_internal_control.h">
-		</File>
-		<File
-			RelativePath="..\glui_list.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_listbox.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_mouse_iaction.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_node.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_panel.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_radio.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_rollout.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_rotation.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_scrollbar.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_separator.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_spinner.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_statictext.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_string.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_textbox.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_translation.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_tree.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_treepanel.cpp">
-		</File>
-		<File
-			RelativePath="..\glui_window.cpp">
-		</File>
-		<File
-			RelativePath="..\quaternion.cpp">
-		</File>
-		<File
-			RelativePath="..\quaternion.h">
-		</File>
-	</Files>
-	<Globals>
-	</Globals>
-</VisualStudioProject>
diff --git a/thirdparty/glui/2.36/www/screen4.png b/thirdparty/glui/2.36/www/screen4.png
deleted file mode 100644
index be9e9ccc1f099252819043acf84242de206914d4..0000000000000000000000000000000000000000
Binary files a/thirdparty/glui/2.36/www/screen4.png and /dev/null differ
diff --git a/thirdparty/glui/Makefile b/thirdparty/glui/Makefile
deleted file mode 100644
index c5d667be89e7cdfa027a1cb4dd617e7b2d06c4f8..0000000000000000000000000000000000000000
--- a/thirdparty/glui/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-ifndef GLUI
-GLUI=GLUI
-
-ifndef CLEANFOLDER
-CLEANFOLDER=GLUI
-endif
-
-include ../../Makefile-headers/Makefile-header
-
-all: ../../libraries/glui/glui-2.35/src/lib/libglui.$(DYLIB_EXT)
-
-../../libraries/glui/glui-2.35/src/lib/libglui.$(DYLIB_EXT):
-	cd ../../libraries/glui/glui-2.35/src; g++ -O2 -I./ -I./include -I/usr/X11R6/include glui_add_controls.cpp glui_string.cpp glui.cpp glui_bitmap_img_data.cpp glui_bitmaps.cpp glui_button.cpp glui_edittext.cpp glui_commandline.cpp glui_checkbox.cpp glui_node.cpp glui_radio.cpp glui_statictext.cpp glui_panel.cpp glui_separator.cpp glui_spinner.cpp glui_control.cpp glui_column.cpp glui_translation.cpp glui_rotation.cpp glui_mouse_iaction.cpp glui_listbox.cpp glui_rollout.cpp glui_window.cpp arcball.cpp algebra3.cpp quaternion.cpp viewmodel.cpp glui_treepanel.cpp glui_tree.cpp glui_textbox.cpp glui_scrollbar.cpp glui_list.cpp glui_filebrowser.cpp $(DYLIB_FLAG) -fPIC -o libglui.$(DYLIB_EXT) $(SET_GLUI_RPATH) $(OPENGL_LIBS); mv libglui.$(DYLIB_EXT) lib/
-
-ifeq ($(CLEANFOLDER), GLUI)
-clean: cleanglui
-endif
-
-deepclean: cleanglui
-
-cleanglui:
-	cd ../../libraries/glui/glui-2.35/src; make clean; rm lib/libglui.$(DYLIB_EXT)
-
-endif
-
diff --git a/utilities/clothBW-rt/clothBW-rt.cpp b/utilities/clothBW-rt/clothBW-rt.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb6756e36ea18fb672a21735876bd6895b03674d
--- /dev/null
+++ b/utilities/clothBW-rt/clothBW-rt.cpp
@@ -0,0 +1,1319 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Cloth simulator" application,                                        *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <math.h>
+#include <vector>
+#include "GL/glui.h"
+#include "initGraphics.h"
+#include "performanceCounter.h"
+#include "objMesh.h"
+#include "clothBWFromObjMesh.h"
+
+#include "clothBWStencilForceModel.h"
+#include "forceModelAssembler.h"
+
+#include "implicitNewmarkSparse.h"
+#include "implicitBackwardEulerSparse.h"
+#include "configFile.h"
+#include "listIO.h"
+#include "sceneObjectDeformable.h"
+#include "saveScreenShot.h"
+
+/*
+  Driver for cloth simulation. Cloth is implemented using:
+
+  Baraff and Witkin: Large Steps in Cloth Simulation, SIGGRAPH 1998. 
+  David Pritchard: Implementing Baraff & Witkin's Cloth Simulation, May 2003, with minor updates April 2012
+*/
+
+#define Mm_PI 3.1415926
+#define  MAX_FILE 4096
+
+// simulation parameters
+float timeStep;               //default: 0.01
+float dampingMass;            //default: 0.00
+float dampingStiffness;       //default: 0.001
+float surfaceDensity;         //default: 0.25;
+float tensileStiffness;       //default: 8500.0; 
+float shearStiffness;         //default: 100.0;
+float bendStiffnessU;         //default: 0.0002;
+float bendStiffnessV;         //default: 0.0002;
+float mouseForceCoeff;         //default: 0.05;
+float wind_x;                 //default: 0.0;
+float wind_y;                 //default: 0.0;
+float wind_z;                 //default: 0.0;
+float gravityForce;           //default: 9.81
+int addGravity;               //default: 1;
+int useRestAngles;            //default: 1
+
+// computation parameters (default to computing everything)
+int computeStretchShearForce, computeStretchShearStiffness, computeBendForce, computeBendStiffness;
+int numInternalForceThreads; // default = 0 (not multi-threaded)
+int numSolverThreads; // default = 0 (not multi-threaded)
+int renderFixedVertices = 1;
+
+void initScene();
+
+//glui
+GLUI * glui = NULL;
+GLUI_StaticText * systemSolveStaticText;
+GLUI_StaticText * forceAssemblyStaticText;
+
+int loadNewConfigFile = 0;
+
+// graphics
+char windowTitleBase[4096] = "Cloth Simulator";
+int windowID;
+int windowWidth = 800;
+int windowHeight = 600;
+
+//interactive control
+double zNear = 0.01;               //default: 0.01
+double zFar = 10.0;                //default:10.0;
+double cameraRadius;
+double focusPositionX, focusPositionY, focusPositionZ;
+double cameraLongitude, cameraLatitude;
+int g_iMenuId;			// mouse activity
+int g_vMousePos[2];
+int g_iLeftMouseButton,g_iMiddleMouseButton,g_iRightMouseButton;
+double forceAssemblyTime = 0.0;
+double systemSolveTime = 0.0;
+
+// start out paused, wire-frame view, scene unlocked (you can drag to add forces)
+int runSimulation=0, renderWireframe=1, saveScreenToFile=0, dragForce = 0, pulledVertex = -1, lockScene = 0, axis = 0, pin = 0, sprite=0, renderNormals = 0, displayMenu = 0, useTextures = 1;
+
+int shiftPressed=0;
+int altPressed=0;
+int ctrlPressed=0;
+
+int dragStartX, dragStartY;
+int loadOption = 0;
+int cloth_width = 30;
+int cloth_length =45;
+std::vector<int> pin_points;
+int graphicsFrame = 0;
+
+//simulation objects
+ClothBW * clothBW = NULL;
+ClothBWStencilForceModel * clothBWStencilForceModel = NULL;
+ForceModelAssembler * forceModelAssembler = NULL;
+
+// rendering
+SceneObjectDeformable * sceneObjDeform = NULL;
+SceneObject * extraSceneGeometry = NULL;
+Lighting * light = NULL;
+
+// solver
+IntegratorBase * integratorBase = NULL;
+ImplicitNewmarkSparse * implicitNewmarkSparse = NULL;
+ImplicitBackwardEulerSparse * implicitBackwardEulerSparse = NULL;
+ForceModel * forceModel = NULL;
+SparseMatrix * massMatrix = NULL;
+
+// camera
+SphericalCamera * camera = NULL;
+
+// load cloth from .obj
+ObjMesh * objMesh = NULL;
+ClothBWFromObjMesh * clothBWFromObjMesh = NULL;
+
+PerformanceCounter timer;
+double FPS = 0.0; 
+
+PerformanceCounter display_timer;
+double display_time = 0.0;
+double dotimestep_time = 0.0;
+
+int * fixedVertices;
+int numFixedVertices;
+
+int n;						// record number of particles
+double * u = NULL;			// record current deformation
+double * f_ext = NULL;		// record current external forces
+
+// files
+char configFilename[MAX_FILE];
+char fixedVerticesFilename[MAX_FILE];
+char objMeshname[MAX_FILE] = "skirt.obj";
+char lightingFilename[MAX_FILE];
+char extraSceneGeometryFilename[MAX_FILE];
+
+// This function specifies parameters (and default values, if applicable) for the
+// configuration file. It then loads the config file and parses the options contained
+// within. After parsing is complete, a list of parameters is printed to the terminal.
+void initConfigurations()
+{
+  printf("Parsing configuration file %s...\n", configFilename);
+  ConfigFile configFile;
+  
+  // specify the entries of the config file
+  
+  // get camera info (optional)
+  configFile.addOptionOptional("focusPositionX", &focusPositionX, 0.0);
+  configFile.addOptionOptional("focusPositionY", &focusPositionY, 10.0);
+  configFile.addOptionOptional("focusPositionZ", &focusPositionZ, 0.0);
+  configFile.addOptionOptional("cameraRadius", &cameraRadius, 6.0);
+  configFile.addOptionOptional("cameraLongitude", &cameraLongitude, -10.0);
+  configFile.addOptionOptional("cameraLatitude", &cameraLatitude, 45.0);
+  configFile.addOptionOptional("zBufferNear", &zNear, 0.01);
+  configFile.addOptionOptional("zBufferFar", &zFar, 10.0);
+  configFile.addOptionOptional("renderWireframe", &renderWireframe, renderWireframe);
+  
+  // stiffness and damping parameters (optional)
+  configFile.addOptionOptional("tensileStiffness", &tensileStiffness, 6500.0f);
+  configFile.addOptionOptional("shearStiffness", &shearStiffness, 100.0f);
+  configFile.addOptionOptional("bendStiffnessU", &bendStiffnessU, 0.0002f);
+  configFile.addOptionOptional("bendStiffnessV", &bendStiffnessV, 0.0002f);
+  configFile.addOptionOptional("dampingMass", &dampingMass, 0.00f);
+  configFile.addOptionOptional("dampingStiffness", &dampingStiffness, 0.001f);
+  configFile.addOptionOptional("surfaceDensity", &surfaceDensity, 0.25f);
+  
+  // force parameters and gravity (optional)
+  configFile.addOptionOptional("mouseForceCoeff", &mouseForceCoeff, 0.05f); // how powerful your mouse is
+  configFile.addOptionOptional("wind_x", &wind_x, 0.0f); // default is no wind
+  configFile.addOptionOptional("wind_y", &wind_y, 0.0f);
+  configFile.addOptionOptional("wind_z", &wind_z, 0.0f);
+  configFile.addOptionOptional("gravityForce", &gravityForce, 9.81f);
+  configFile.addOptionOptional("addGravity", &addGravity, 1); // 1 for gravity, 0 for no gravity
+  
+  // computation parameters
+  configFile.addOptionOptional("computeStretchShearForce", &computeStretchShearForce, 1);
+  configFile.addOptionOptional("computeStretchShearStiffness", &computeStretchShearStiffness, 1);
+  configFile.addOptionOptional("computeBendForce", &computeBendForce, 1);
+  configFile.addOptionOptional("computeBendStiffness", &computeBendStiffness, 1);
+  
+  configFile.addOptionOptional("useRestAngles", &useRestAngles, 1);
+  
+  //set to 0 to disable multi-threading (or 1 to only launch a single thread...)
+  configFile.addOptionOptional("numInternalForceThreads", &numInternalForceThreads, 0);
+  configFile.addOptionOptional("numSolverThreads", &numSolverThreads, 0);
+  
+  // timestep (optional)
+  configFile.addOptionOptional("timeStep", &timeStep, 0.02f);
+  
+  // ===== files =====
+  
+  // need .obj file
+  configFile.addOption("objMeshname", objMeshname);
+  
+  // need file for fixed vertices
+  configFile.addOption("fixedVerticesFilename", fixedVerticesFilename);
+  
+  // optional file for lighting
+  //configFile.addOptionOptional("lightingFilename", lightingFilename, "/Users/Andy/Downloads/lighting-v1.0/example.lighting");
+  configFile.addOption("lightingFilename", lightingFilename);
+
+  // statis scene geometry (optional)
+  configFile.addOptionOptional("extraSceneGeometryFilename", extraSceneGeometryFilename, "__none");
+  
+  // parse the configuration file
+  if (configFile.parseOptions(configFilename) != 0)
+  {
+    printf("Error parsing options.\n");
+    exit(1);
+  }
+  
+  // the config variables have now been loaded with their specified values
+  
+  // informatively print the variables (with assigned values) that were just parsed
+  configFile.printOptions();
+}
+  
+void Sync_GLUI()
+{
+  glui->sync_live();
+}
+
+void drawString(const char * str) 
+{
+  glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT); // lighting and color mask
+  glDisable(GL_LIGHTING);     // need to disable lighting for proper text color
+  
+  glColor3f(1.0, 1.0, 1.0); // set text color
+  
+  // loop all characters in the string
+  while(*str)
+  {
+    glutBitmapCharacter(GLUT_BITMAP_8_BY_13, *str);
+    ++str;
+  }
+  
+  glEnable(GL_LIGHTING);
+  glPopAttrib();
+}
+
+void display_menu(double fps)
+{
+  glDisable(GL_LIGHTING);
+  glColor3f(1.0,0.0,0.0);
+  glMatrixMode(GL_MODELVIEW);
+  glPushMatrix(); //push
+  glLoadIdentity();
+  glMatrixMode(GL_PROJECTION);
+  glPushMatrix(); //push
+  glLoadIdentity();
+  gluOrtho2D (0, windowWidth, 0, windowHeight);
+  
+  int dist_small = 15;
+  int dist_big = 25;
+  int base = 15;
+ 
+  glRasterPos2f(0,windowHeight-base); 
+  char fps_string[20];
+  sprintf(fps_string,"FPS:%G",fps);
+  drawString(fps_string);
+  
+  
+  base = base + dist_big;
+  glRasterPos2f(0,windowHeight-base); 
+  char simulation_stat[50];
+  if(runSimulation == 0)
+    sprintf(simulation_stat,"Simulation[p]: Paused");
+  else
+    sprintf(simulation_stat,"Simulation[p]: Running");
+  drawString(simulation_stat);
+  
+  base = base + dist_big;
+  glRasterPos2f(0,windowHeight-base); 
+  char scene_lock_status[100];
+  if(lockScene == 1)
+  {
+    sprintf(scene_lock_status,"Scene Status[l]:Locked! Cannot add interactive force.");
+    drawString(scene_lock_status);
+  }
+  else
+  {
+    sprintf(scene_lock_status,"Scene Status[l]:Unlocked!");
+    drawString(scene_lock_status);
+    if(runSimulation == 1)
+    {
+      base = base + dist_small;
+      glRasterPos2f(20,windowHeight-base); 
+      sprintf(scene_lock_status,"Press left mouse button to select a vertex");
+      drawString(scene_lock_status);
+      base = base + dist_small;
+      glRasterPos2f(20,windowHeight-base); 
+      sprintf(scene_lock_status,"Press right mouse button to finish adding the force");
+      drawString(scene_lock_status);
+    }
+    else
+    {
+      base = base + dist_small;
+      glRasterPos2f(20,windowHeight-base); 
+      sprintf(scene_lock_status,"Cannot add interactive force unless running the simulation!");
+      drawString(scene_lock_status);			
+    }
+  }
+  
+  base = base + dist_big;
+  glRasterPos2f(0,windowHeight-base); 
+  char axis_status[50];
+  if(axis == 1)
+  {
+    sprintf(axis_status,"Axis Display[a]:ON");
+    drawString(axis_status);
+  }
+  else
+  {
+    sprintf(axis_status,"Axis Display[a]:OFF");
+    drawString(axis_status);
+  }
+  
+  base = base + dist_big;
+  glRasterPos2f(0,windowHeight-base); 
+  char constraint_status[100];
+  if(pin == 0)
+  {
+    sprintf(constraint_status,"Constraint[c]:DEFAULT");
+    drawString(constraint_status);		
+  }
+  else
+  {
+    sprintf(constraint_status,"Constraint[c]:RANDOM");
+    drawString(constraint_status);
+    base = base + dist_small;
+    glRasterPos2f(20,windowHeight-base); 
+    sprintf(constraint_status,"Click On the Vertex you want to PIN, Reset and RUN!");
+    drawString(constraint_status);	
+  }
+  
+  // adding force/stiffness matrix toggling functionality
+  base = base + dist_big;
+  glRasterPos2f(0,windowHeight-base); 
+  
+  char stshStatus[100];
+  char bendStatus[100];
+  
+  // first forces
+  
+  // stretch & shear
+  if (computeStretchShearForce)
+    sprintf(stshStatus, "Stretch/Shear Force[s]: ENABLED");
+  else
+    sprintf(stshStatus, "Stretch/Shear Force[s]: DISABLED");
+  drawString(stshStatus);
+  
+  base = base + dist_small;
+  glRasterPos2f(0,windowHeight-base);
+  
+  // bend
+  if (computeBendForce)
+    sprintf(bendStatus, "Bend Force[b]: ENABLED");
+  else
+    sprintf(bendStatus, "Bend Force[b]: DISABLED");
+  drawString(bendStatus);
+  
+  base = base + dist_small;
+  glRasterPos2f(0,windowHeight-base);
+  
+  // now stiffness matrices
+  
+  // stretch & shear
+  if (computeStretchShearStiffness)
+    sprintf(stshStatus, "Stretch/Shear Stiffness[d]: ENABLED");
+  else
+    sprintf(stshStatus, "Stretch/Shear Stiffness[d]: DISABLED");
+  drawString(stshStatus);
+  
+  base = base + dist_small;
+  glRasterPos2f(0,windowHeight-base);
+  
+  // bend
+  if (computeBendStiffness)
+    sprintf(bendStatus, "Bend Stiffness[n]: ENABLED");
+  else
+    sprintf(bendStatus, "Bend Stiffness[n]: DISABLED");
+  drawString(bendStatus);
+  
+  glPopMatrix();
+  glMatrixMode(GL_MODELVIEW);
+  glPopMatrix();
+  glEnable(GL_LIGHTING);
+}
+
+// this function does the following:
+// (1) Clears display
+// (2) Points camera at scene
+// (3) Draws axes (if applicable) and sphere surrounding scene
+// (4) Displays GUI menu
+// (5) Sets cloth deformations
+// (6) Sets lighting conditions
+// (7) Builds surface normals for cloth (take this out to increase performance)
+// (8) Renders cloth
+// (9) Render pulled vertex in different color (if applicable)
+void displayFunction()
+{
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+  glMatrixMode(GL_MODELVIEW); 
+  glLoadIdentity();	
+  camera->Look();
+  glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+  glStencilFunc(GL_ALWAYS, 0, ~(0u));
+
+  // render any extra scene geometry
+  glStencilFunc(GL_ALWAYS, 0, ~(0u));
+  if (extraSceneGeometry != NULL)
+    extraSceneGeometry->Render();
+
+  if(axis)
+  {
+    glDisable(GL_LIGHTING);
+    glEnable(GL_COLOR_MATERIAL);
+
+    glBegin(GL_LINES);
+    for (int i = 0; i < 3; i++)
+    {
+      float color[3] = { 0, 0, 0 };
+      color[i] = 1.0;
+      glColor3fv(color);
+
+      float vertex[3] = {0, 0, 0};
+      vertex[i] = 1.0;
+      glVertex3fv(vertex);
+      glVertex3f(0, 0, 0);
+    }
+    glEnd();
+    glEnable(GL_LIGHTING);
+  }
+ 
+  if (displayMenu)
+    display_menu(FPS);
+  
+  sceneObjDeform->SetVertexDeformations(u);
+  
+  // render cloth
+  if (clothBW != NULL)
+  { 
+    glLineWidth(1.0);
+    glStencilFunc(GL_ALWAYS, 1, ~(0u));
+    //glEnable(GL_COLOR_MATERIAL);
+    
+    //sceneObjDeform->SetVertexDeformations(u);
+    sceneObjDeform->SetLighting(light);
+    
+    //sceneObjDeform->SetUpTextures(textureMode);
+    //sceneObjDeform->BuildNormals();
+    sceneObjDeform->BuildNormalsFancy();
+
+    if (renderNormals)
+    {
+      glDisable(GL_LIGHTING);
+      glColor3f(0,0,1);
+      sceneObjDeform->RenderNormals();
+      glEnable(GL_LIGHTING);
+    }
+
+    // render fixed vertices
+    glDisable(GL_LIGHTING);
+    if (renderFixedVertices)
+    {
+      for(int i=0; i<numFixedVertices; i++)
+      {
+        glColor3f(1,0,0);
+        double fixedVertexPos[3];
+        sceneObjDeform->GetSingleVertexRestPosition(fixedVertices[i],
+            &fixedVertexPos[0], &fixedVertexPos[1], &fixedVertexPos[2]);
+
+        glEnable(GL_POLYGON_OFFSET_POINT);
+        glPolygonOffset(-1.0,-1.0);
+        glPointSize(12.0);
+        glBegin(GL_POINTS);
+        glVertex3f(fixedVertexPos[0], fixedVertexPos[1], fixedVertexPos[2]);
+        glEnd();
+        glDisable(GL_POLYGON_OFFSET_FILL);
+      }
+    }
+    
+    glEnable(GL_LIGHTING);
+    sceneObjDeform->Render();
+
+    if (renderWireframe)
+    {
+      glDisable(GL_LIGHTING);
+      glColor3f(0,0,0);
+      sceneObjDeform->RenderEdges();
+      glEnable(GL_LIGHTING);
+    }
+  }
+  else
+  {
+    printf("Error: cloth object has not been created.\nExiting...\n");
+    exit(1);
+  }
+  
+  // if pulling a vertex, render that vertex in diff color
+  if (pulledVertex != -1)
+  {
+    glDisable(GL_LIGHTING);
+    glDisable(GL_TEXTURE_2D);
+    
+    //sceneObjDeform->HighlightVertex(pulledVertex);
+    
+    glColor3f(1.0, 0.0, 0.0);
+    glPointSize(8.5);
+    glBegin(GL_POINTS);
+
+    const Vec3d * restPos = clothBW->GetRestPositions();
+    double vertexCoords[3];
+    vertexCoords[0] = restPos[pulledVertex][0] + u[3*pulledVertex + 0];
+    vertexCoords[1] = restPos[pulledVertex][1] + u[3*pulledVertex + 1];
+    vertexCoords[2] = restPos[pulledVertex][2] + u[3*pulledVertex + 2];
+   
+    //printf("Vertex: (%G, %G, %G)\n", vertexCoords[0], vertexCoords[1], vertexCoords[2]);
+    
+    glVertex3f(vertexCoords[0], vertexCoords[1], vertexCoords[2]);
+    
+    glEnd();
+    glEnable(GL_TEXTURE_2D);
+    glEnable(GL_LIGHTING);
+  }
+  
+  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+  glutSwapBuffers();
+}
+
+// this function does the following:
+// (1) Takes a screenshot (if applicable)
+// (2) Updates & displays the fps
+// (3) Applies mouse forces
+// (4) Applies wind forces
+// (5) Sets computation mode for clothBW
+// (6) Performs a timestep
+void idleFunction(void)
+{
+  static int timeStepCount = 0;
+  float timestepsPerSecond = 1.0/timeStep;
+  
+  glutSetWindow(windowID);
+  
+  // recording
+  char s[70]="picxxxx.ppm\0";
+  
+  s[40] = 48 + (sprite / 1000);
+  s[41] = 48 + (sprite % 1000) / 100;
+  s[42] = 48 + (sprite % 100 ) / 10;
+  s[43] = 48 + sprite % 10;
+  
+  if (saveScreenToFile==1 && timeStepCount<timestepsPerSecond*10)
+  {
+    Screenshot::SaveScreenshot(s, ImageIO::FORMAT_PNG, windowWidth, windowHeight);
+    //saveScreenToFile=0; // save only once, change this if you want continuous image generation (i.e. animation)
+    sprite++;
+  }
+  if (timeStepCount>timestepsPerSecond*10 && saveScreenToFile)
+  {
+    printf("DONE!\n");
+    runSimulation = 0;
+    saveScreenToFile = 0;
+  }
+  if (sprite >= 300) // allow only 300 snapshots
+    exit(0);	
+  
+  // external force & time step
+  if (runSimulation == 1) // if unpaused
+  {
+    // clear old external force
+    integratorBase->SetExternalForcesToZero();
+    
+    // external forces
+    
+    // user mouse forces
+    if((g_iLeftMouseButton && pulledVertex != -1 && lockScene == 0))
+    {
+      double forceX = (g_vMousePos[0] - dragStartX);
+      double forceY = -(g_vMousePos[1] - dragStartY);
+      
+      //add external force here
+      double externalForce[3];
+      camera->CameraVector2WorldVector_OrientationOnly3D(forceX, forceY, 0, externalForce);	
+      for(int i = 0 ; i < 3; i++)
+        externalForce[i] *= mouseForceCoeff;
+      //printf("fx: %G fy: %G | %G %G %G\n", forceX, forceY, externalForce[0], externalForce[1], externalForce[2]);
+      
+      memset(f_ext, 0.0, 3*n);
+      for(int i = 0 ; i < 3; i++)
+        f_ext[3*pulledVertex+i] += externalForce[i];
+      
+      integratorBase->AddExternalForces(f_ext);
+    }
+
+    // wind forces
+    for(int i = 0 ; i < clothBW->GetNumVertices(); i++)
+    {
+      f_ext[3*i+0] = wind_x;
+      f_ext[3*i+1] = wind_y;
+      f_ext[3*i+2] = wind_z;
+    }
+    integratorBase->AddExternalForces(f_ext);
+    
+    // adding force/stiffness matrix toggling ability
+    bool parameters[4]; 
+    parameters[0] = computeStretchShearForce;
+    parameters[1] = computeBendForce;
+    parameters[2] = computeStretchShearStiffness;
+    parameters[3] = computeBendStiffness;
+    
+    clothBW->SetComputationMode(parameters);
+    clothBW->UseRestAnglesForBendingForces(useRestAngles);
+    
+    integratorBase->DoTimestep(); // the big timestep
+    timeStepCount++;
+
+    memcpy(u, integratorBase->Getq(), sizeof(double) * 3 * n);
+    
+    sceneObjDeform->BuildFaceNormals();
+  }
+
+  // fps
+  timer.StopCounter();
+  double elapsedTime = timer.GetElapsedTime();  
+  if (elapsedTime >= 1.0 / 4)  
+  {
+    timer.StartCounter(); 
+    FPS = graphicsFrame / elapsedTime;
+
+    forceAssemblyTime = implicitNewmarkSparse->GetForceAssemblyTime();
+    systemSolveTime = implicitNewmarkSparse->GetSystemSolveTime();
+
+    char ptext[96];
+    sprintf(ptext, "Force assembly: %G", forceAssemblyTime);
+    forceAssemblyStaticText->set_text(ptext);
+    sprintf(ptext, "System solve: %G", systemSolveTime);
+    systemSolveStaticText->set_text(ptext);
+    Sync_GLUI();
+
+    graphicsFrame = 0;
+  }
+ 
+  graphicsFrame++;
+  
+  glutPostRedisplay();
+}
+
+void reshape(int x,int y)
+{
+  glViewport(0,0,x,y);
+  
+  windowWidth = x;
+  windowHeight = y;
+  
+  glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
+  glLoadIdentity(); // Reset The Projection Matrix
+  
+  // gluPerspective(90.0,1.0,0.01,1000.0);
+  gluPerspective(60.0f, 1.0 * windowWidth / windowHeight, zNear, zFar);
+  
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+}
+
+void exit_buttonCallBack(int code)
+{
+  // free memory
+  
+  // cloth
+  if (clothBW != NULL)
+    delete clothBW;
+
+  if (clothBWFromObjMesh != NULL)
+    delete clothBWFromObjMesh;
+  
+  // solver
+  if (implicitBackwardEulerSparse != NULL)
+    delete implicitBackwardEulerSparse;
+  
+  // render objs
+  if (objMesh != NULL)
+    delete objMesh;
+
+  if (sceneObjDeform != NULL)
+    delete sceneObjDeform;
+
+  if (light != NULL)
+    delete light;
+  
+  // other
+  if (u)
+    free(u);
+  if (f_ext)
+    free(f_ext);
+  
+  exit(0);
+}
+
+void keyboardFunction(unsigned char key, int x, int y)
+{
+  switch (key)
+  {
+    case 27:
+      exit_buttonCallBack(0);
+      break;
+      
+    case 'a':
+      axis = 1 - axis;
+      break;
+      
+    case '\\':
+      camera->Reset();
+      break;
+      
+    case 'w':
+      renderWireframe = !renderWireframe;
+      break;
+
+    case 't':
+      useTextures = !useTextures;
+      if (useTextures)
+        sceneObjDeform->EnableTextures();
+      else
+        sceneObjDeform->DisableTextures();
+      break;
+      
+    case 'p':
+      runSimulation = 1 - runSimulation;
+      break;
+      
+    case 'l':
+      lockScene = 1 - lockScene;
+      pulledVertex = -1;
+      break;
+      
+    case 'i':
+    {
+      double focusPos[3];
+      camera->GetFocusPosition(focusPos);
+      double cameraX,cameraY,cameraZ;
+      camera->GetAbsWorldPosition(cameraX,cameraY,cameraZ);
+      printf("Camera is positioned at: %G %G %G\n", cameraX,cameraY,cameraZ);
+      printf("Camera radius is: %G\n", camera->GetRadius());
+      printf("Camera Phi is: %G\n", 180.0/M_PI*camera->GetPhi());
+      printf("Camera Theta is: %G\n", 180.0/M_PI*camera->GetTheta());
+      printf("Camera focus is: %G %G %G\n", focusPos[0], focusPos[1], focusPos[2]);
+    }
+
+    case 'c':
+      pin = 1 - pin;
+      break;
+      
+    case ' ':
+      saveScreenToFile = 1 - saveScreenToFile;
+      break;
+      
+    case 's':
+      computeStretchShearForce = !computeStretchShearForce;
+      printf("Computing stretch/shear force is now: %s.\n", computeStretchShearForce ? "ON" : "OFF");
+      break;
+
+    case 'S':
+      computeStretchShearStiffness = !computeStretchShearStiffness;
+      printf("Computing stretch/shear stiffness matrix is now: %s.\n", computeStretchShearStiffness ? "ON" : "OFF");
+      break;
+
+    case 'b':
+      computeBendForce = !computeBendForce;
+      printf("Computing bend force is now: %s.\n", computeBendForce ? "ON" : "OFF");
+      break;
+
+    case 'B':
+      computeBendStiffness = !computeBendStiffness;
+      printf("Computing bend stiffness matrix is now: %s.\n", computeBendStiffness ? "ON" : "OFF");
+      break;
+
+    case 'F':
+      renderFixedVertices = !renderFixedVertices;
+      break;
+
+    case 'm':
+      displayMenu = !displayMenu;
+      break;
+
+    case 'N':
+      renderNormals = !renderNormals;
+      break;
+  }	
+}
+
+// reacts to pressed "special" keys
+void specialFunction(int key, int x, int y)
+{
+  switch (key)
+  {
+    case GLUT_KEY_LEFT:
+      camera->MoveFocusRight(+0.1 * camera->GetRadius());
+    break;
+
+    case GLUT_KEY_RIGHT:
+      camera->MoveFocusRight(-0.1 * camera->GetRadius());
+    break;
+
+    case GLUT_KEY_DOWN:
+      camera->MoveFocusUp(+0.1 * camera->GetRadius());
+    break;
+
+    case GLUT_KEY_UP:
+      camera->MoveFocusUp(-0.1 * camera->GetRadius());
+    break;
+
+    case GLUT_KEY_PAGE_UP:
+      break;
+
+    case GLUT_KEY_PAGE_DOWN:
+      break;
+
+    case GLUT_KEY_HOME:
+      break;
+
+    case GLUT_KEY_END:
+      break;
+
+    case GLUT_KEY_INSERT:
+      break;
+
+    default:
+      break;
+  }
+}
+
+void mouseMotion (int x, int y)
+{
+  g_vMousePos[0] = x;
+  g_vMousePos[1] = y;
+}
+
+void mouseButtonActivityFunction(int button, int state, int x, int y)
+{
+  switch (button)
+  {
+    case GLUT_LEFT_BUTTON:
+      g_iLeftMouseButton = (state==GLUT_DOWN);
+      shiftPressed = (glutGetModifiers() == GLUT_ACTIVE_SHIFT);
+      altPressed = (glutGetModifiers() == GLUT_ACTIVE_ALT);
+      ctrlPressed = (glutGetModifiers() == GLUT_ACTIVE_CTRL);
+      if (g_iLeftMouseButton)
+      {
+        GLdouble model[16];
+        glGetDoublev (GL_MODELVIEW_MATRIX, model);
+        
+        GLdouble proj[16];
+        glGetDoublev (GL_PROJECTION_MATRIX, proj);
+        
+        GLint view[4];
+        glGetIntegerv (GL_VIEWPORT, view);
+        
+        int winX = x;
+        int winY = view[3]-1-y;
+        
+        float zValue;
+        glReadPixels(winX,winY,1,1, GL_DEPTH_COMPONENT, GL_FLOAT, &zValue); 
+        
+        GLubyte stencilValue;
+        glReadPixels(winX, winY, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, &stencilValue);
+        
+        GLdouble worldX, worldY, worldZ;
+        gluUnProject (winX, winY, zValue, model, proj, view, &worldX, &worldY, &worldZ);
+        
+        //printf("x:%d y:%d zValue:%f stencil:%d world: %f %f %f\n",
+        //       winX,winY,zValue,stencilValue,worldX,worldY,worldZ);
+        
+        if(lockScene == 0)
+        {
+          if (stencilValue == 1)
+          {
+            dragStartX = x;
+            dragStartY = y;
+            //pulledVertex = renderCloth->findClosestVertex(clothBW, u, worldX, worldY, worldZ);
+            Vec3d pos(worldX, worldY, worldZ);
+            pulledVertex = sceneObjDeform->GetClosestVertex(pos, u);
+            printf("Clicked on vertex: %d (0-indexed)\n", pulledVertex);
+          }
+          else
+          {
+            printf("Clicked on empty stencil: %d.\n", stencilValue);
+            pulledVertex = -1;
+          }
+        }
+        if(pin == 1)
+        {
+          if (stencilValue == 1)
+          {
+            //int vertex = renderCloth->findClosestVertex(clothBW, u, worldX, worldY, worldZ);
+            Vec3d pos(worldX, worldY, worldZ);
+            int vertex = sceneObjDeform->GetClosestVertex(pos, u);
+            printf("Pinned on vertex: %d (0-indexed)\n", vertex);
+            pin_points.push_back(vertex);
+          }
+        }
+        if (!g_iLeftMouseButton)
+        {
+          pulledVertex = -1;
+        }
+      }
+      if (!g_iLeftMouseButton)
+        pulledVertex = -1;
+      break;
+    case GLUT_MIDDLE_BUTTON:
+      g_iMiddleMouseButton = (state==GLUT_DOWN);
+      break;
+    case GLUT_RIGHT_BUTTON:
+      g_iRightMouseButton = (state==GLUT_DOWN);
+      break;
+  }
+  
+  g_vMousePos[0] = x;
+  g_vMousePos[1] = y;	
+}
+
+void mouseMotionFunction(int x, int y)
+{
+  int mouseDeltaX = x-g_vMousePos[0];
+  int mouseDeltaY = y-g_vMousePos[1];
+  
+  g_vMousePos[0] = x;
+  g_vMousePos[1] = y;
+  
+  if (g_iRightMouseButton) // handle camera rotations
+  {
+    const double factor = 0.1;
+    camera->MoveRight(factor * mouseDeltaX);
+    camera->MoveUp(factor * mouseDeltaY);
+  }	
+  
+  if ((g_iMiddleMouseButton) || (g_iLeftMouseButton && altPressed)) // handle zoom in/out
+  {
+    const double factor = 0.1;
+    camera->ZoomIn(cameraRadius * factor * mouseDeltaY);
+  }
+}
+
+// this function is called when the user
+// changes the timestep parameter from 
+// within the simulator
+void update_timestep(int code)
+{
+  integratorBase->SetTimestep(timeStep);
+  glui->sync_live();
+}
+
+// this function is called when a button in the
+// simulator is pressed
+void update_simulation_status(int code)
+{
+  switch (code)
+  {
+    case 0: // initialize new parameters (need to re-initialize scene)
+      initScene();
+      //runSimulation = 0;
+      break;
+      
+    case 1: // run
+      runSimulation = 1;
+      break;
+      
+    case 2: // pause
+      runSimulation = 0;
+      break;
+      
+    case 3: // reset cloth
+      integratorBase->ResetToRest();
+      memset(u, 0, sizeof(double) * 3 * n);
+      sceneObjDeform->ResetDeformationToRest();
+      //runSimulation = 0;
+      break;
+      
+    case 4: // load new config file
+      initConfigurations();
+      initScene();
+      initGraphics(windowWidth, windowHeight);
+      //runSimulation = 0;
+      break;
+      
+    default:
+      printf("Error. Invalid simulator update option.\n");
+      break;
+  }
+  
+  glui->sync_live();
+}
+
+// this function does the following:
+// (1) Creates the camera
+// (2) Loads a .obj file
+// (3) Creates a clothBW from the .obj file
+// (4) Loads file containing constrained vertices
+// (5) Creates an object to render the clothBW
+// (6) Creates a lighting object to light the scene
+// (7) Takes care of internal threading (if applicable)
+// (8) Creates numerical integrator
+// (9) Initializes textures on the cloth rendering object
+void initScene()
+{
+  if(camera != NULL)	
+    delete(camera);
+  
+  double virtualToPhysicalPositionFactor = 1.0;
+  int numConstrainedDOFs = 0; 
+  int * constrainedDOFs = NULL;
+  
+  initCamera(cameraRadius, cameraLongitude, cameraLatitude,
+             focusPositionX, focusPositionY, focusPositionZ,
+             1.0 / virtualToPhysicalPositionFactor,
+             &zNear, &zFar, &camera);
+  
+  printf("Loading the OBJ mesh.\n");    
+  
+  if (objMesh != NULL)
+    delete objMesh;
+  objMesh = new ObjMesh(objMeshname);
+  
+  if (clothBWFromObjMesh != NULL)
+    delete clothBWFromObjMesh;
+  clothBWFromObjMesh = new ClothBWFromObjMesh();
+
+  // the below region tests the assignment of different stiffness values 
+  // to different material groups within the same cloth model
+  /*
+  // TESTING PURPOSES ONLY
+  
+  int numMaterialGroups = objMesh->numMaterials();
+  
+  double * surfaceDensityArray = (double*) malloc (sizeof(double) * numMaterialGroups);
+  double * tensileStiffnessArray = (double*) malloc (sizeof(double) * numMaterialGroups);
+  double * shearStiffnessArray = (double*) malloc (sizeof(double) * numMaterialGroups);
+  double * bendStiffnessUArray = (double*) malloc (sizeof(double) * numMaterialGroups);
+  double * bendStiffnessVArray = (double*) malloc (sizeof(double) * numMaterialGroups);
+  double * dampingArray = (double*) malloc (sizeof(double) * numMaterialGroups);
+  int * buArray = (int*) malloc (sizeof(int) * numMaterialGroups);
+  int * bvArray = (int*) malloc (sizeof(int) * numMaterialGroups);
+  
+  for (int i=0; i<numMaterialGroups; i++)
+  {
+    surfaceDensityArray[i] = surfaceDensity;
+    tensileStiffnessArray[i] = tensileStiffness;
+    shearStiffnessArray[i] = shearStiffness;
+    bendStiffnessUArray[i] = bendStiffnessU;
+    bendStiffnessVArray[i] = bendStiffnessV;
+    dampingArray[i] = 0.0;
+    buArray[i] = 1;
+    bvArray[i] = 1;
+  }
+  
+  tensileStiffnessArray[2] = 99999.0f;
+  shearStiffnessArray[2] =   99999.0f;
+  
+  clothBWFromObjMesh->GenerateClothBW(objMesh, &clothBW, surfaceDensityArray, tensileStiffnessArray,
+                                  shearStiffnessArray, bendStiffnessUArray,
+                                  bendStiffnessVArray, dampingArray, buArray, bvArray, addGravity);
+  
+  // free memory we allocated
+  free(surfaceDensityArray);
+  free(tensileStiffnessArray);
+  free(shearStiffnessArray);
+  free(bendStiffnessUArray);
+  free(bendStiffnessVArray);
+  free(dampingArray);
+  free(buArray);
+  free(bvArray);
+  
+  // END TESTING PURPOSES ONLY
+  */
+
+  if (clothBW != NULL)
+    delete clothBW;
+  ClothBW::MaterialGroup material;
+  material.tensileStiffness = tensileStiffness;
+  material.shearStiffness = shearStiffness;
+  material.bendStiffnessU = bendStiffnessU;
+  material.bendStiffnessV = bendStiffnessV;
+  clothBW = clothBWFromObjMesh->GenerateClothBW(objMesh, surfaceDensity, material, addGravity);
+  clothBW->SetGravity(addGravity, gravityForce);
+  
+  // constrain vertices from file
+  ListIO loadlist;
+  int offset = 1;
+  if (loadlist.load(fixedVerticesFilename, &numFixedVertices, &fixedVertices, offset) != 0)
+  {
+    printf("Error reading fixed vertices.\n");
+    exit(1);
+  }
+  //loadlist.print(numFixedVertices,fixedVertices);
+  
+  numConstrainedDOFs = 3*numFixedVertices;
+  if (constrainedDOFs)
+    free(constrainedDOFs);
+  constrainedDOFs = (int*) malloc (sizeof(int) * numConstrainedDOFs);
+  for (int i=0; i<numFixedVertices; i++)
+  {
+    constrainedDOFs[3*i+0] = fixedVertices[i] * 3 + 0;
+    constrainedDOFs[3*i+1] = fixedVertices[i] * 3 + 1;
+    constrainedDOFs[3*i+2] = fixedVertices[i] * 3 + 2;
+  }
+  
+  // now create renderer
+  if (sceneObjDeform != NULL)
+    delete sceneObjDeform;
+  sceneObjDeform = new SceneObjectDeformable(objMeshname);
+  
+  if (light != NULL)
+    delete light;
+  light = new Lighting(lightingFilename);
+  
+  // handle multi-threading
+  // if (numInternalForceThreads > 0)
+  // {
+  //   printf("Launching threaded force/stiffness matrix evaluation: %d internal threads, %d solver threads.\n", numInternalForceThreads, numSolverThreads);
+    
+  // create multi-threaded version of clothBW
+  //   ClothBWMT * clothBWMT = new ClothBWMT(*clothBW, numInternalForceThreads);
+    
+  // delete clothBW
+  //  delete(clothBW);
+    
+  // and have old pointer now point to multi-threaded version
+  //  clothBW = clothBWMT;
+  // }
+  
+  // initialize the integrator (ImplicitBackwardEulerSparse)
+  //
+  // clothBW->SetBendingStiffness(1e7, 0.5, 1e-3);
+
+  clothBWStencilForceModel = new ClothBWStencilForceModel(clothBW);
+  forceModelAssembler = new ForceModelAssembler(clothBWStencilForceModel);
+
+  forceModel = forceModelAssembler; 
+  SparseMatrix * massMatrix;
+  clothBW->GenerateMassMatrix(&massMatrix);
+  double totalMass = massMatrix->SumEntries() / 3.0;
+  printf("Total cloth mass: %G\n", totalMass);
+  
+  int numVertices = clothBW->GetNumVertices();
+  
+  if (implicitBackwardEulerSparse != NULL)
+    delete implicitBackwardEulerSparse;
+  implicitBackwardEulerSparse = new ImplicitBackwardEulerSparse(3 * numVertices, timeStep, massMatrix, forceModel, numConstrainedDOFs, constrainedDOFs, dampingMass, dampingStiffness, 1, 1E-5, numSolverThreads);
+  implicitNewmarkSparse = implicitBackwardEulerSparse;
+  integratorBase = implicitNewmarkSparse;
+  
+  // initializing deformation
+  n = numVertices;
+  
+  if (u)
+    free(u);
+  if (f_ext)
+    free (f_ext);
+  u = (double*) malloc (sizeof(double) * 3 * numVertices);
+  f_ext = (double*) malloc (sizeof(double) * 3 * numVertices);
+  for(int i = 0 ; i < 3 * numVertices; i++)
+  {
+    u[i] = 0.0;
+    f_ext[i] = 0.0;
+  }
+  integratorBase->SetState(u);
+  integratorBase->SetExternalForces(f_ext);
+  
+  pin_points.clear();
+
+  sceneObjDeform->SetUpTextures(SceneObject::MODULATE);
+  sceneObjDeform->BuildNeighboringStructure();
+
+  // load any external geometry file (e.g. some static scene for decoration; usually there will be none)
+  if (strcmp(extraSceneGeometryFilename,"__none") != 0)
+  {
+    extraSceneGeometry = new SceneObject(extraSceneGeometryFilename);
+    extraSceneGeometry->BuildNormals(85.0);
+  }
+  else
+    extraSceneGeometry = NULL;
+
+  runSimulation = 1;
+}
+
+// Create the GUI 
+void initGLUI()
+{
+  // generate the UI
+  glui = GLUI_Master.create_glui( "Controls",0,windowWidth + 50, 0);
+
+  // scene option
+  GLUI_Panel * scene_option_panel = glui->add_panel("Scene&Parameters",GLUI_PANEL_EMBOSSED);
+  scene_option_panel->set_alignment(GLUI_ALIGN_LEFT);
+
+  glui->add_checkbox_to_panel(scene_option_panel,"WireFrame", &renderWireframe);
+  
+  //GLUI_RadioGroup * loadoption = glui->add_radiogroup_to_panel(scene_option_panel, &loadOption);
+  
+  GLUI_EditText * config_filename_text = glui->add_edittext_to_panel(scene_option_panel,"Config File Name",GLUI_EDITTEXT_TEXT,configFilename);
+  config_filename_text->w = config_filename_text->w + 15;
+  
+  glui->add_button_to_panel(scene_option_panel,"Load New Config File",4, update_simulation_status);
+  
+  glui->add_separator_to_panel(scene_option_panel);
+  glui->add_edittext_to_panel(scene_option_panel,"tensileStiffness",GLUI_EDITTEXT_FLOAT,&tensileStiffness);
+  glui->add_edittext_to_panel(scene_option_panel,"shearStiffness",GLUI_EDITTEXT_FLOAT,&shearStiffness);
+  glui->add_edittext_to_panel(scene_option_panel,"bendStiffnessU",GLUI_EDITTEXT_FLOAT,&bendStiffnessU);
+  glui->add_edittext_to_panel(scene_option_panel,"bendStiffnessV",GLUI_EDITTEXT_FLOAT,&bendStiffnessV);
+  glui->add_edittext_to_panel(scene_option_panel,"dampingMass",GLUI_EDITTEXT_FLOAT,&dampingMass);
+  glui->add_edittext_to_panel(scene_option_panel,"dampingStiffness",GLUI_EDITTEXT_FLOAT,&dampingStiffness);
+  glui->add_edittext_to_panel(scene_option_panel,"gravityForce",GLUI_EDITTEXT_FLOAT,&gravityForce);
+  glui->add_edittext_to_panel(scene_option_panel,"mousePower",GLUI_EDITTEXT_FLOAT,&mouseForceCoeff);
+  glui->add_checkbox_to_panel(scene_option_panel,"useRestAngles", &useRestAngles);
+  glui->add_button_to_panel(scene_option_panel,"Upload Parameters",0, update_simulation_status);
+  
+  glui->add_separator();
+  
+  // external force: wind
+  GLUI_Panel * extForce_panel = glui->add_panel("ExternalForce",GLUI_PANEL_EMBOSSED);
+  extForce_panel->set_alignment(GLUI_ALIGN_LEFT);
+  GLUI_Spinner * wind_x_spinner = glui->add_spinner_to_panel(extForce_panel, "WindX", GLUI_SPINNER_FLOAT, &wind_x);
+  GLUI_Spinner * wind_y_spinner = glui->add_spinner_to_panel(extForce_panel, "WindY", GLUI_SPINNER_FLOAT, &wind_y);
+  GLUI_Spinner * wind_z_spinner = glui->add_spinner_to_panel(extForce_panel, "WindZ", GLUI_SPINNER_FLOAT, &wind_z);
+  wind_x_spinner->set_float_limits(-10.0, 10.0);
+  wind_y_spinner->set_float_limits(-10.0, 10.0);
+  wind_z_spinner->set_float_limits(-10.0, 10.0);
+  wind_x_spinner->set_speed(0.1);
+  wind_y_spinner->set_speed(0.1);
+  wind_z_spinner->set_speed(0.1);
+  
+  // simulation control 
+  GLUI_Panel * simulation_panel = glui->add_panel("Simulation",GLUI_PANEL_EMBOSSED);
+  simulation_panel->set_alignment(GLUI_ALIGN_LEFT);
+  simulation_panel->draw_name(0,0);
+  glui->add_button_to_panel(simulation_panel,"Restart",3, update_simulation_status);
+  glui->add_button_to_panel(simulation_panel,"Run",1, update_simulation_status);
+  glui->add_button_to_panel(simulation_panel,"Pause",2, update_simulation_status);
+  glui->add_edittext_to_panel(simulation_panel,"Timestep [sec]",GLUI_EDITTEXT_FLOAT,&timeStep,0,update_timestep);
+
+  systemSolveStaticText = glui->add_statictext("System solve: ");
+  forceAssemblyStaticText = glui->add_statictext("Force assembly: ");
+
+  glui->add_separator();
+  
+  // end of the control
+  glui->add_button("Quit", 0, exit_buttonCallBack);
+  glui->sync_live();
+  glui->set_main_gfx_window( windowID );
+}
+
+int main(int argc, char* argv[])
+{
+  int numFixedArgs = 2;
+  
+  if ( argc != numFixedArgs )
+  {
+    printf("=== Cloth Simulator ===\n");
+    printf("Usage: %s [config file]\n", argv[0]);
+    printf("Please specify a configuration file\n");
+    return 1;
+  }
+  else
+  {
+    strncpy(configFilename, argv[1], strlen(argv[1]));
+  }
+  
+  // make window and size it properly
+  initGLUT(argc, argv, windowTitleBase, windowWidth, windowHeight, &windowID);
+  
+  // define background texture, set some openGL parameters
+  initGraphics(windowWidth, windowHeight);
+
+  // load info from config file
+  initConfigurations();
+  
+  initScene();
+  
+  initGLUI();
+  
+  glutMainLoop(); 
+  return 0;
+}
+
diff --git a/utilities/clothBW-rt/initGraphics.cpp b/utilities/clothBW-rt/initGraphics.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..367ddff8f1300acb81cb3f1ece1a3adf21e59be2
--- /dev/null
+++ b/utilities/clothBW-rt/initGraphics.cpp
@@ -0,0 +1,137 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Cloth simulator" application,                                        *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "initGraphics.h"
+
+#include <math.h>
+
+#ifndef M_PI
+  #define M_PI 3.141592653589793238462643
+#endif
+
+extern void displayFunction(void);
+extern void idleFunction(void);
+extern void reshape(int,int);
+extern void keyboardFunction(unsigned char key, int x, int y);
+extern void specialFunction(int key, int x, int y);
+extern void mouseButtonActivityFunction(int button, int state, int x, int y);
+extern void mouseMotionFunction(int x, int y);
+
+// initialize GLUT
+void initGLUT(int argc, char* argv[], char * windowTitle, int windowWidth, int windowHeight, int * windowID)
+{
+  // Initialize GLUT.
+  glutInit(&argc, argv);
+  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL);
+  //glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
+  glutInitWindowSize(windowWidth, windowHeight);
+  *windowID = glutCreateWindow(windowTitle);
+
+  // Setup GLUT callbacks.
+  glutDisplayFunc(displayFunction); 
+
+  GLUI_Master.set_glutIdleFunc(idleFunction);
+  GLUI_Master.set_glutKeyboardFunc(keyboardFunction);
+  GLUI_Master.set_glutSpecialFunc(specialFunction);
+  GLUI_Master.set_glutReshapeFunc(reshape);
+  GLUI_Master.set_glutMouseFunc(mouseButtonActivityFunction);
+    
+  glutMotionFunc(mouseMotionFunction);
+
+  // callback for mouse movement without any buttons pressed
+  //glutPassiveMotionFunc(MouseNoDrag);
+}
+
+void initCamera(double cameraRadius, double cameraLongitude, double cameraLattitude, double focusPosX, double focusPosY, double focusPosZ, double camera2WorldScalingFactor, double * zNear, double * zFar, SphericalCamera ** camera)
+{
+  double focusPos[3] = {focusPosX, focusPosY, focusPosZ};
+
+  *zNear = cameraRadius * 0.01;
+  *zFar = cameraRadius * 100;
+
+  double upPos[3] = {0,1,0};
+  *camera = new SphericalCamera(cameraRadius, 1.0 * cameraLongitude / 360 * (2*M_PI), 1.0 * cameraLattitude / 360 * (2*M_PI), focusPos,  upPos, 0.05, camera2WorldScalingFactor); 
+}
+
+void initGraphics(int windowWidth, int windowHeight)
+{
+   // clear to white
+  glClearColor(256.0 / 256, 256.0 / 256, 256.0 / 256, 0.0);
+
+  // clear to light blue
+  //glClearColor(233.0 / 256, 256.0 / 256, 256.0 / 256, 0.0);
+
+  // clear to gray
+  //glClearColor(196.0 / 256, 196.0 / 256, 196.0 / 256, 0.0);
+
+  // clear to brown
+  //glClearColor(255.0 / 256, 156.0 / 256, 17.0 / 256, 0.0);
+
+  // clear to medical blue
+  //glClearColor(148.0 / 256, 199.0 / 256, 211.0 / 256, 0.0);
+
+  glEnable(GL_DEPTH_TEST);
+  glEnable(GL_STENCIL_TEST);
+  glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+
+  glShadeModel(GL_SMOOTH);
+  glEnable(GL_POLYGON_SMOOTH);
+  glEnable(GL_LINE_SMOOTH);
+
+  reshape(windowWidth,windowHeight); 
+
+  printf ("Graphics initialization complete.\n"); 
+}
+
+// draw the cartesian axes.
+void drawAxes(double axisLength)
+{
+  glDisable(GL_LIGHTING);
+  glEnable(GL_COLOR_MATERIAL);
+    
+  glBegin(GL_LINES);
+  for (int i = 0; i<3; i++) 
+  {
+    float color[3] = { 0, 0, 0 };
+    color[i] = 1.0;
+    glColor3fv(color);
+        
+    float vertex[3] = {0, 0, 0};
+    vertex[i] = axisLength;
+    glVertex3fv(vertex);
+    glVertex3f(0, 0, 0);
+  } 
+  glEnd();
+}
+
diff --git a/utilities/clothBW-rt/initGraphics.h b/utilities/clothBW-rt/initGraphics.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3f1ccb69fa130c56e2df5a589559b747218997e
--- /dev/null
+++ b/utilities/clothBW-rt/initGraphics.h
@@ -0,0 +1,65 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Cloth simulator" application,                                        *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Andy Pierce, Yijing Li, Yu Yu Xu, Jernej Barbic         *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef _INITGRAPHICS_H_
+#define _INITGRAPHICS_H_
+
+#if defined(WIN32) || defined(_WIN32)
+  #include <windows.h>
+#endif
+
+#include "openGL-headers.h"
+#include "GL/glui.h"
+#include "camera.h"
+
+void initGLUT(int argc, char* argv[], char * windowTitle, int windowWidth, int windowHeight, int * windowID);
+
+void initCamera(double cameraRadius, 
+				double cameraLongitude, double cameraLattitude,
+				double focusPosX, double focusPosY, double focusPosZ,
+				double camera2WorldScalingFactor,
+				double * zNear, double * zFar, 
+				SphericalCamera ** camera);
+
+void initGraphics(int windowWidth, int windowHeight);
+
+void setupLights();
+
+// draw coordinate axes
+void drawAxes(double axisLength);
+
+void buildSphereDisplayList(GLuint * solidSphereList, GLuint * wireSphereList);
+
+#endif
+
diff --git a/utilities/computeDistanceField/computeDistanceField.cpp b/utilities/computeDistanceField/computeDistanceField.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b0faeddc582132d2dde27488832475842bed2729
--- /dev/null
+++ b/utilities/computeDistanceField/computeDistanceField.cpp
@@ -0,0 +1,362 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "distance field" driver , Copyright (C) 2007 CMU, 2018 USC            *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li                     *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <fstream>
+#include <iostream>
+#include <float.h>
+#include <vector>
+using namespace std;
+
+#include "distanceFieldCreator.h"
+#include "closestPointField.h"
+#include "objMesh.h"
+#include "getopts.h"
+
+/*
+  Computes signed or unsigned distance field for a given triangle mesh,
+  optionally using the pipeline from:
+
+  Hongyi Xu, Jernej Barbic:
+  Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014
+  Montreal, Canada
+*/
+
+int main( int argc, char** argv )
+{
+  bool printDetailedHelp = (argc >= 2) && (strcmp(argv[1],"-h") == 0);
+
+  if ( argc < 5 ) 
+  {
+    cout << "Computes signed or unsigned distance field for a given triangle mesh. Use -h for more help." << endl;
+    cout << "Usage: " << argv[0] << " [-h<help>] [obj file] [resolutionX] [resolutionY] [resolutionZ] "
+        "[-n<narrow band>] [-s<signed field>] [-o<output file>] [-m<signed field mode>] "
+        "[-b<xmin,ymin,zmin,xmax,ymax,zmax>] [-c<cube box>] [-e<box expansion ratio>] "
+        "[-d<max octree depth>] [-t<max #triangles per octree cell>] "
+        "[-w<band width>] "
+        "[-g<sigma>] [-G<sigma grid>] [-r<do not subtract sigma>] [-i<precomputed unsigned field>] "
+        "[-v<output voronoi diagram file>] [-p<compute closest filed>] " << endl;
+    if (!printDetailedHelp)
+      return 1;
+  }
+
+  if (printDetailedHelp)
+  {
+    cout << "  -n: compute only narrow band distance field; default: false" << endl;
+    cout << "  -s: compute signed distance field (requires manifold surface with no boundary); default: unsigned" << endl;
+    cout << "  -o: specify output file; default: out.dist " << endl;
+    cout << "  -m: signed field computation mode: " << endl;
+    cout << "      0: BASIC assume the input obj mesh is manifold and self-intersection-free " << endl;
+    cout << "      1: POLYGONSOUP handle non-manifold and/or self-intersecting meshes, using the SignedDistanceFieldFromPolygonSoup pipeline,\n"
+            "         as published in:\n"
+            "         Hongyi Xu, Jernej Barbic:\n"
+            "         Signed Distance Fields for Polygon Soup Meshes, Graphics Interface 2014, "
+        "Montreal, Canada" << endl;
+    cout << "      default: BASIC" << endl;
+    cout << "      2: AUTO use BASIC if mesh is manifold, otherwise POLYGONSOUP" << endl;
+
+    cout << "  ============== Bounding Box =============" << endl;
+    cout << "  -b: specify scene bounding box; default: automatic box with cubic cells" << endl;
+    cout << "  -c: force bounding box into a cube; default: automatic box with cubic cells " << endl;
+    cout << "  -e: expansion ratio for box (when automatic); default: 1.5" << endl;
+
+    cout << "  ================= Octree ================" << endl;
+    cout << "  -d: max octree depth to use (default: 10)" << endl;
+    cout << "  -t: max num triangles per octree cell (default: 15)" << endl;
+
+    cout << "  =============== Narrow Band =============" << endl;
+    cout << "  -w: the band width for narrow band distance field" << endl;
+
+    cout << "  ========= Polygon Soup Pipeline =========" << endl;
+    cout << "  -g: sigma value" << endl;
+    cout << "  -G: sigma grid value" << endl;
+    cout << "  only one of -g and -G is needed" << endl;
+    cout << "  -r: do not substract sigma when creating signed field (default: false)" << endl;
+    cout << "  -i: precomputed unsigned field for creating signed field (default: none)" << endl;
+
+    cout << "  ================ Extra ==================" << endl;
+    cout << "  -v: also compute voronoi diagram (defaut: not computed)" << endl;
+    cout << "  -p: also compute closest points (defaut: not computed)" << endl;
+
+    cout << "  ***  Output file is binary. Format: " << endl;
+    cout << "    - resolutionX,resolutionY,resolutionZ (three signed 4-byte integers (all equal), 12 bytes total)" << endl;
+    cout << "    - bminx,bminy,bminz (coordinates of the lower-left-front corner of the bounding box: (three double precision 8-byte real numbers , 24 bytes total)" << endl;
+    cout << "    - bmaxx,bmaxy,bmaxz (coordinates of the upper-right-back corner of the bounding box: (three double precision 8-byte real numbers , 24 bytes total)" << endl;
+    cout << "    - distance data (in single precision; data alignment: [0,0,0],...,[resolutionX,0,0],[0,1,0],...,[resolutionX,resolutionY,0],[0,0,1],...,[resolutionX,resolutionY,resolutionZ]; total num bytes: sizeof(float)*(resolutionX+1)*(resolutionY+1)*(resolutionZ+1))" << endl;
+
+    return 0;
+  }
+
+  int resolutionX = strtol(argv[2], NULL, 10);
+  int resolutionY = strtol(argv[3], NULL, 10);
+  int resolutionZ = strtol(argv[4], NULL, 10);
+  if ((resolutionX <= 0) || (resolutionY <= 0) || (resolutionZ <= 0))
+  {
+    printf("Invalid resolution.\n");
+    return 1;
+  }
+
+  char * objMeshname = argv[1];
+
+  bool doublePrecision = false;
+  bool narrowBandField = false;
+  bool signedField = false;
+  char outputFile[4096] = "out.dist";
+  int mode = 0;
+
+  bool useCubeBox = false;
+  char bboxString[4096] = "__default";
+  double expansionRatio = 1.5;
+  char expansionRatioString[4096] = "__none";
+
+  int maxTriCount = 15;
+  int maxDepth = 10;
+
+  double bandWidth = 5;
+  char bandWidthString[4096] = "__none";
+
+  char precomputedUnsignedDistanceFieldFilename[4096] = "__none";
+  double sigma = 0;
+  char sigmaString[1028] = "__none";
+  bool nonSubtractSigma = false;
+
+  char outputVoronoiDiagramFile[4096] = "__none";
+  bool closestPointField = false;
+
+  int sigmaGrid = 0;
+
+  opt_t opttable[] =
+  {
+    { "n", OPTBOOL, &narrowBandField},
+    { "m", OPTINT, &mode },
+    { "w", OPTSTR, bandWidthString},
+    { "b", OPTSTR, bboxString },
+    { "r", OPTBOOL, &nonSubtractSigma},
+    { "c", OPTBOOL, &useCubeBox },
+    { "d", OPTINT, &maxDepth },
+    { "e", OPTSTR, expansionRatioString},
+    { "o", OPTSTR, outputFile },
+    { "p", OPTBOOL, &closestPointField },
+    { "s", OPTBOOL, &signedField },
+    { "t", OPTINT, &maxTriCount },
+    { "v", OPTSTR, &outputVoronoiDiagramFile },
+    { "i", OPTSTR, precomputedUnsignedDistanceFieldFilename},
+    { "g", OPTSTR, sigmaString },
+    { "G", OPTINT, &sigmaGrid },
+    { NULL, 0, NULL }
+  };
+
+  argv += 4;
+  argc -= 4;
+  int optup = getopts(argc,argv,opttable);
+  if (optup != argc)
+  {
+    printf("Error parsing options. Error at option %s.\n",argv[optup]);
+    return 1;
+  }
+
+  if (mode < 0 || mode > 2)
+  {
+    printf("Invalid sign field computation mode: %d.\n", mode);
+    return 1;
+  }
+
+  if (strcmp(expansionRatioString, "__none") != 0)
+    expansionRatio = strtod(expansionRatioString, NULL);
+  if (expansionRatio < 1.0)
+  {
+    printf("Invalid expansion ratio: %G.\n", expansionRatio);
+    return 1;
+  }
+  printf("Expansion ratio is: %G.\n", expansionRatio);
+
+  if (strcmp(sigmaString, "__none") != 0 || sigmaGrid > 0)
+  {
+    sigma = strtod(sigmaString, NULL);
+    if (sigma <= 0.0 && sigmaGrid <= 0)
+    {
+      printf("Invalid sigma: %G.\n", sigma);
+      return 1;
+    }
+    printf("sigma is: %G.\n", sigma);
+  }
+  else if (mode == 1 && signedField) // no sigma given
+  {
+    printf("No sigma given when computing signed filed with POLYGON mode.\n");
+    return 1;
+  }
+
+  if (narrowBandField)
+  {
+    if (strcmp(bandWidthString, "__none") != 0)
+    {
+      bandWidth = strtod(bandWidthString, NULL);
+      if (bandWidth <= 0)
+      {
+        printf("Invalid narrow band width: %G.\n", bandWidth);
+        return 1;
+      }
+
+      if (mode == 1 && signedField && bandWidth <= sigma)
+      {
+        printf("Error: band width %G, smaller than sigma %G.\n", bandWidth, sigma);
+        return 1;
+      }
+    }
+    else
+    {
+      printf("Error: No band width is provided.\n");
+      return 1;
+    }
+  }
+
+  ObjMesh * objMesh = NULL;
+  try
+  {
+    objMesh = new ObjMesh(objMeshname);
+  }
+  catch(int exceptionCode)
+  {
+    printf("Error: could not load obj file %s (exception code %d).\n", objMeshname, exceptionCode);
+    exit(1);
+  }
+
+  printf("Data output format is: IEEE %s precision.\n", doublePrecision ? "double (64-bit)" : "single (32-bit)");
+  Vec3d * bmin = NULL;
+  Vec3d * bmax = NULL;
+  if (strcmp(bboxString,"__default") != 0)
+  {
+    printf("bbox string: %s\n", bboxString);
+    double xmin,ymin,zmin,xmax,ymax,zmax;
+    sscanf(bboxString,"%lf,%lf,%lf,%lf,%lf,%lf",&xmin,&ymin,&zmin,&xmax,&ymax,&zmax);
+    bmin = new Vec3d(xmin,ymin,zmin);
+    bmax = new Vec3d(xmax,ymax,zmax);
+    cout << "Scene bounding set to: " << bmin << " " << bmax << endl;
+    if ((xmin >= xmax) || (ymin >= ymax) || (zmin >= zmax))
+    {
+      cout << "Invalid bounding box.\n";
+      return 1;
+    }
+  }
+
+  cout << "UseCubeBox: " << useCubeBox << endl;
+  DistanceFieldCreator * distanceFieldCreator = new DistanceFieldCreator(objMesh, expansionRatio, useCubeBox, bmin, bmax);
+
+  char * inputUnsignedFieldFilename = NULL;
+  if (strcmp(precomputedUnsignedDistanceFieldFilename, "__none") != 0)
+    inputUnsignedFieldFilename = precomputedUnsignedDistanceFieldFilename;
+
+  DistanceFieldCreator::SignedFieldCreationMode creatorMode;
+  if (mode == 0)
+    creatorMode = DistanceFieldCreator::BASIC;
+  else if (mode == 1)
+    creatorMode = DistanceFieldCreator::POLYGONSOUP;
+  else
+    creatorMode = DistanceFieldCreator::AUTO;
+
+
+
+  if (sigma <= 0 && sigmaGrid > 0)
+  {
+    Vec3d bminTemp, bmaxTemp;
+    objMesh->getBoundingBox(1.0, &bminTemp, &bmaxTemp);
+
+    // set aspect ratio that corresponds to the resolutions
+    Vec3d bcenterTemp = 0.5 * (bminTemp + bmaxTemp);
+
+    cout << "Tight bounding box:" << endl << "  " << bminTemp << endl << "  " << bmaxTemp << endl;
+
+    Vec3d sideTemp = bmaxTemp - bminTemp;
+    if (sideTemp[0] / resolutionX < sideTemp[1] / resolutionY)
+    {
+      // increase x
+      sideTemp[0] = sideTemp[1] / resolutionY * resolutionX;
+    }
+    else
+    {
+      // increase y
+      sideTemp[1] = sideTemp[0] / resolutionX * resolutionY;
+    }
+
+    // now x,y are ok, must adjust z
+    if (sideTemp[1] / resolutionY < sideTemp[2] / resolutionZ)
+    {
+      // increase x and y
+      double factor = (sideTemp[2] / resolutionZ * resolutionY) / sideTemp[1];
+      sideTemp[1] *= factor;
+      sideTemp[0] *= factor;
+    }
+    else
+    {
+      // increase z
+      sideTemp[2] = sideTemp[1] / resolutionY * resolutionZ;
+    }
+
+    BoundingBox bbox(bminTemp, bmaxTemp);
+    bbox.setbmin(bcenterTemp - 0.5 * sideTemp);
+    bbox.setbmax(bcenterTemp + 0.5 * sideTemp);
+
+    Vec3d bmin_ = bbox.bmin();
+    Vec3d bmax_ = bbox.bmax();
+    sigma = sigmaGrid * (bmax_[0] - bmin_[0]) / resolutionX;
+  }
+
+  if (narrowBandField)
+  {
+    DistanceFieldNarrowBand * field = distanceFieldCreator->ComputeDistanceFieldNarrowBand(resolutionX, resolutionY, resolutionZ,
+        bandWidth, signedField, creatorMode, sigma, !nonSubtractSigma, maxTriCount, maxDepth, inputUnsignedFieldFilename);
+    cout << "Saving the distance field to " << outputFile << " ." << endl;
+    field->save(outputFile, doublePrecision);
+  }
+  else
+  {
+    bool computeVoronoiDiagram = (strcmp(outputVoronoiDiagramFile, "__none") != 0);
+    DistanceField * field = distanceFieldCreator->ComputeDistanceField(resolutionX, resolutionY, resolutionZ,
+        signedField, creatorMode, sigma, !nonSubtractSigma, computeVoronoiDiagram, maxTriCount, maxDepth, closestPointField,
+        inputUnsignedFieldFilename);
+    cout << "Saving the distance field to " << outputFile << " ." << endl;
+    field->save(outputFile, doublePrecision);
+
+    if (computeVoronoiDiagram)
+    {
+      cout << "Saving Voronoi diagram to " << outputVoronoiDiagramFile << " ." << endl;
+      field->saveVoronoiDiagram(outputVoronoiDiagramFile); 
+    }
+  }
+ 
+  return(0);
+}
+
diff --git a/src/util/displayObj/displayObj.cpp b/utilities/displayObj/displayObj.cpp
similarity index 95%
rename from src/util/displayObj/displayObj.cpp
rename to utilities/displayObj/displayObj.cpp
index 8c18eb6e0f30f9cd362facc8b478148642a43a6a..aea46fa6a895a313153dadda30dcd2ad2459d258 100644
--- a/src/util/displayObj/displayObj.cpp
+++ b/utilities/displayObj/displayObj.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * OBJ mesh visualization utility                                        *
- * Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                            *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -55,7 +59,7 @@
   #ifndef M_PI
     #define M_PI 3.141592654 
   #endif
-  #include "GL\glew.h"
+  #include "GL/glew.h"
 #endif
 
 #include "openGL-headers.h"
@@ -68,7 +72,7 @@
 #include "lighting.h"
 #include "glslPhong.h"
 #include "camera.h"
-#include "loadList.h"
+#include "listIO.h"
 
 std::set<int> * selectedVerticesSet;  // each object will have a set storing selected vertices
 std::set<int> * selectedGroupsSet; // each object will have a set storing selected groups
@@ -93,6 +97,7 @@ Vec3d lightBoxMin, lightBoxMax;
 int numLights =2;
 int showSpecularLight = 0;
 int displayRuntimeInfo = 0;
+double lineThickness = 2.0;
 
 char screenShotFilename[FILENAME_MAX] = "__default";
 #define SCREEN_SHOT_DEFAULT_WIDTH 800
@@ -176,7 +181,11 @@ void MakeDisplayList(unsigned int fileIndex)
     }
   }
   else
+  {
     renderMode = OBJMESHRENDER_CUSTOMCOLOR;
+    if (useFieldColors)
+      objMeshRenders[fileIndex]->setCustomColors(fieldColors);
+  }
 
   int geometryMode = OBJMESHRENDER_TRIANGLES;
   GLuint objMeshList = objMeshRenders[fileIndex]->createDisplayList(geometryMode, renderMode);
@@ -436,7 +445,7 @@ void Initialize()
   // init camera for 3d viewing
   // bogusFlag, r, Theta, Phi, focusX, focusY, focusZ, upX, upY, upZ, sensitivity, camera2worldScalingFactor
   //camera = new SphericalCamera(radius, 1.0 * 270 / 360 * (2*PI), 1.0 * 30 / 360 * (2*PI), centerV, upVector,  0.05, 1);
-  camera = new SphericalCamera(radius, 1.0 * 270 / 360 * (2*PI), 1.0 * 10 / 360 * (2*PI), centerV, upVector,  0.05, 1);
+  camera = new SphericalCamera(radius, 1.0 * 270 / 360 * (2*M_PI), 1.0 * 10 / 360 * (2*M_PI), centerV, upVector,  0.05, 1);
 
   // clear to white
   glClearColor(256.0 / 256, 256.0 / 256, 256.0 / 256, 0.0);
@@ -523,14 +532,6 @@ void MouseDrag(int x, int y)
 
   if ((g_iMiddleMouseButton) || (g_iAlt && g_iLeftMouseButton)) // handle camera zoom
     camera->ZoomIn(mouseDeltaY*radius*0.05);
-
-/*
-  if (g_iMiddleMouseButton)
-  {
-    camera->MoveIn(mouseDeltaY*radius*0.025);
-    camera->ZoomIn(mouseDeltaY*radius*0.025);
-  }
-*/
 }
 
 void MouseButtonActivity(int button, int state, int x, int y)
@@ -704,6 +705,16 @@ void KeyboardFunc (unsigned char key, int x, int y)
       showAxes = 1 - showAxes;
       break;
 
+    case ',': 
+      lineThickness *= 0.8;
+      printf("Line thickness is now %G .\n", lineThickness);
+      break;
+
+    case '.': 
+      lineThickness /= 0.8;
+      printf("Line thickness is now %G .\n", lineThickness);
+      break;
+
     case 't': 
       renderTextures = 1 - renderTextures;
       break;
@@ -892,7 +903,7 @@ void Display()
   // turns off stencil modifications
   glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
 
-  glLineWidth(2.0);
+  glLineWidth(lineThickness);
   if (showAxes)
   {
     glBegin(GL_LINES);
@@ -1125,6 +1136,7 @@ int main( int argc, char** argv )
   char renderNormalLengthString[4096] = "__default";
   char renderExternalVerticesBinaryFilename[4096] = "__default";
   char renderExternalVerticesZeroIndexedFilename[4096] = "__default";
+  char lineThicknessString[4096] = "__default";
   char renderExternalVerticesOneIndexedFilename[4096] = "__default";
   renderMaterials = false;
   bool useFaceNormals = false;
@@ -1147,6 +1159,7 @@ int main( int argc, char** argv )
     { (char*)"t", OPTBOOL, &renderToTerminal_ },
     { (char*)"v", OPTSTR, &renderExternalVerticesBinaryFilename },
     { (char*)"w", OPTBOOL, &displayEdges },
+    { (char*)"W", OPTSTR, &lineThicknessString },
     { (char*)"y", OPTBOOL, &autoRotateModelFlag },
     { (char*)"z", OPTBOOL, &rotateZUpToYUpFlag },
     { (char*)"0", OPTSTR, &renderExternalVerticesZeroIndexedFilename },
@@ -1181,6 +1194,11 @@ int main( int argc, char** argv )
     windowHeight = SCREEN_SHOT_DEFAULT_HEIGHT;    
   }
 
+  if (strcmp(lineThicknessString, "__default") != 0)
+  {
+    lineThickness = strtod(lineThicknessString, NULL);
+  }
+
   //windowWidth = 800;
   //windowHeight = 800;
 
@@ -1303,10 +1321,10 @@ int main( int argc, char** argv )
     }
     objMeshes.insert(std::make_pair(i,objMesh));
     objMeshRenders.insert(std::make_pair(i, new ObjMeshRender(objMesh)));
-    int textureMode = OBJMESHRENDER_GL_USEANISOTROPICFILTERING | OBJMESHRENDER_GL_USEMIPMAP | OBJMESHRENDER_GL_MODULATE;
+    //int textureMode = OBJMESHRENDER_GL_USEANISOTROPICFILTERING | OBJMESHRENDER_GL_USEMIPMAP | OBJMESHRENDER_GL_MODULATE;
     //int textureMode = OBJMESHRENDER_GL_USEMIPMAP | OBJMESHRENDER_GL_MODULATE;
     //int textureMode = OBJMESHRENDER_GL_USEMIPMAP | OBJMESHRENDER_GL_REPLACE;
-    //int textureMode = OBJMESHRENDER_GL_NOMIPMAP | OBJMESHRENDER_GL_MODULATE;
+    int textureMode = OBJMESHRENDER_GL_NOMIPMAP | OBJMESHRENDER_GL_MODULATE;
     int numFileTextures = objMeshRenders[i]->numTextures();
     numTextures += numFileTextures;
     if (numFileTextures > 0)
@@ -1334,8 +1352,8 @@ int main( int argc, char** argv )
   if (strcmp(renderExternalVerticesZeroIndexedFilename,"__default") != 0)
   {
     // parse the 0-indexed list
-    LoadList list;
-    int * vertexIndices;
+    ListIO list;
+    int * vertexIndices = NULL;
     list.load(renderExternalVerticesZeroIndexedFilename, &numExternalVertices, &vertexIndices);
     printf("Detected %d external vertices to render.\n", numExternalVertices);
     renderExternalVertices = true;
@@ -1354,8 +1372,8 @@ int main( int argc, char** argv )
   if (strcmp(renderExternalVerticesOneIndexedFilename,"__default") != 0)
   {
     // parse the 1-indexed list
-    LoadList list;
-    int * vertexIndices;
+    ListIO list;
+    int * vertexIndices = NULL;
     list.load(renderExternalVerticesOneIndexedFilename, &numExternalVertices, &vertexIndices);
     //list.printList(numExternalVertices, vertexIndices);
     printf("Detected %d external vertices to render.\n", numExternalVertices);
@@ -1375,7 +1393,9 @@ int main( int argc, char** argv )
   useFieldColors = false;
   if (strcmp(fieldColorFilename,"__default") != 0)
   {
+    renderMaterials = false;
     useFieldColors = true;
+    enableGLSLPhong = 0;
 
     if (renderObjList)
     {
@@ -1417,8 +1437,8 @@ int main( int argc, char** argv )
       fieldColors.push_back(JetColorMap(value));
     }
 
-    printf("Max field value was: %G\n",maxColor);
-    printf("Min field value was: %G\n",minColor);
+    printf("Min field value was: %G\n", minColor);
+    printf("Max field value was: %G\n", maxColor);
 
     free(colors);
   }
diff --git a/src/util/displayObj/objMergeFiles.cpp b/utilities/displayObj/objMergeFiles.cpp
similarity index 90%
rename from src/util/displayObj/objMergeFiles.cpp
rename to utilities/displayObj/objMergeFiles.cpp
index 1b035d24a6633022d7db867532503024a6810be4..5a60d87a4db518c230c478935a2a192102d00d19 100644
--- a/src/util/displayObj/objMergeFiles.cpp
+++ b/utilities/displayObj/objMergeFiles.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * Merges several obj files into one.                                    *
- * Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                            *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/utilities/editShapeARAP/editShapeARAP.cpp b/utilities/editShapeARAP/editShapeARAP.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..15cac7c810cab0e6eda911ba3aa16c802d1d02a2
--- /dev/null
+++ b/utilities/editShapeARAP/editShapeARAP.cpp
@@ -0,0 +1,1574 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Edit shape with ARAP" driver application,                            *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Hongyi Xu, Koki Nagano, Jernej Barbic        *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*  
+   An OpenGL driver to our "shapeEdit" ARAP library.
+   Allows the user to interactively deform a 3d model using the ARAP energy.
+*/ 
+
+#include "getopts.h"
+#include "sceneObjectDeformable.h"
+#include "objMesh.h"
+#include "performanceCounter.h"
+#include "configFile.h"
+#include "volumetricMeshLoader.h"
+#include <GL/glui.h>
+#include "lighting.h"
+#include "valueIndex.h"
+#include "listIO.h"
+#include "openGLHelper.h"
+#include "matrix.h"
+#include "arapDeformer.h"
+#include "matrixMultiplyMacros.h"
+#include "constrainedDOFs.h"
+#include "matrixIO.h"
+#ifdef USE_GLSL
+  #include "glslPhong.h"
+#endif
+#include "saveScreenShot.h"
+#include "camera.h"
+#include "matrix.h"
+#include "handleControl.h"
+#include "tetMesh.h"
+#include "generateSurfaceMesh.h"
+#include "handleRender.h"
+#include "sceneGroundPlane.h"
+#include "averagingBuffer.h"
+#include "barycentricCoordinates.h"
+#include "generateTetMeshFromCubicMesh.h"
+#include "simulationRecorder.h"
+#include "stringHelper.h"
+#include "cameraLighting.h"
+#include "inputDevice.h"
+#include "cameraChangeLoad.h"
+
+#include <stdlib.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <algorithm>
+#include <cassert>
+using namespace std;
+
+// graphics
+static char windowTitleBase[4096] = "DM";
+static int windowID;
+static int windowWidth = 1024;
+static int windowHeight = 576;
+static double zNear = 0.01, zFar = 10.0;
+static double cameraRadius = 1.0;
+Vec3d cameraFocusPosition(0.0);
+static InputDevice id;
+
+static int moveVerticesTogether = 0;
+
+static int renderTextInfo = 1;
+
+// glui
+static GLUI * glui;
+
+static double cameraLongitude, cameraLattitude;
+static SphericalCamera * camera = nullptr;
+string cameraPosFilename;
+static int renderWireframe = 1;
+static int renderEmbeddedWireframe = 0;
+static int renderDeformableObject = 1;
+static int renderEmbeddedObject = 1;
+static int renderAxes=1;
+static int renderGroundPlane = 0;
+static double lineThickness = 3.0;
+
+static ARAPDeformer * deformer = nullptr;
+
+static int numFixedDOFs = 0;
+static int * fixedDOFs = nullptr;
+static Lighting * lighting = nullptr;
+static CameraLighting * cameraLighting = nullptr;
+double cameraLightIntensity = 1.0;
+int lightOn[8] = {0, 0, 1, 1, 0, 0, 1, 1 };
+float lightIntensity = 0.5;
+int showSpecularLight = 0;
+string lightingConfigSavingFilename = "lighting.config";
+
+static SceneObjectDeformable * deformableMesh = nullptr, * comparedMesh = nullptr, * embeddedMesh = nullptr;
+static SceneObject * extraSceneGeometry = nullptr;
+static int buildExtraSceneGeometryNormals=1;
+static int renderExtraSceneGeometryShadow = 1;
+static int renderExtraSceneGeometry = 1;
+static int enableTextures = 1;
+static string groundPlaneString;
+static SceneGroundPlane * groundPlane = nullptr;
+
+#ifdef USE_GLSL
+  static GLSLPhong * glslPhong = nullptr;
+#endif
+static int enableGLSLPhong = 1;
+
+// config file
+static string configFilename;
+static string surfaceMeshFilename;
+static string volumetricMeshFilename;
+static string fixedVerticesFilename;
+static string initialHandlesFilename;
+// how many frames to deform initial handles to the input disp.
+// < 0 means no pre-disp frames
+static int numInitialHandlesPreDispFrames = 0;
+
+//static char deformableObjectFilename[4096];
+static char extraSceneGeometryFilename[4096];
+static string groundPlaneTextureFilename;
+static string lightingConfigFilename;
+static string embeddedMeshFilename;
+static string embeddedMeshInterpolationFilename;
+static vector<double> u_embed;
+
+static char backgroundColorString[4096] = "255 255 255";
+static int numSolverThreads;
+static int numOptIterations = 4;
+static int saveScreenToFile = 0;
+static int dumpScreenshots = 0;
+static string screenshotBaseName;
+static int numFramesToExit = -1; // negative means never exit automatically
+
+// simulation
+static double cpuLoad = 0;
+static int graphicFrame = 0;
+
+static PerformanceCounter titleBarCounter, explosionCounter, cpuLoadCounter;
+static int numFixedVertices;
+static int * fixedVertices;
+static int renderFixedVertices = 1;
+
+static PerformanceCounter worldCounter;
+static int renderWorldCounter = 1;
+// static int renderFPS = 1;
+
+static int renderVertices = 0;
+static int renderIKVertices = 1;
+
+// selected manipulation vertices
+typedef map<int, Vec3d> HandleMap;
+typedef HandleMap::iterator HandleIter;
+static HandleMap selectedIKVertices;
+
+static int n = 0;
+static AveragingBuffer fpsBuffer(5);
+static int dumpDeformation = 0;
+
+static bool interactiveDeformation = true;
+static int maxIterations = 20;
+static double iterationThreshold = 1e-5;
+
+static string outputConstraintsFilename = "outputConstraints.txt";
+static string initialDeformationFilename;
+
+static vector<double> disp, comparedDisp;
+
+static VolumetricMesh * volumetricMesh = nullptr;
+static CubicMesh * cubicMesh = nullptr;
+static TetMesh * tetMesh = nullptr;
+static ObjMesh * volSurfaceMesh = nullptr;
+
+static bool reverseHandle = false;
+static int numHiddenHandles = 50;
+static int numFrames = 1;
+
+static int currentFrame = 0;
+static bool verticalWindowSplit = true;
+static bool renderPrimaryWindow = true;
+static bool renderSecondaryWindow = false;
+static bool compareReducedWithFull = false;
+static BarycentricCoordinates * interpCoords = nullptr;
+static ConfigFile configFile;
+
+static map<int, Vec3d> initialHandleDisp;
+
+static FrameRecorder * dispRecorder = nullptr;
+static string dispRecordFilename;
+static HandleControl handleControl;
+static CameraChangeLoad cameraChangeLoad;
+static string cameraChangeLoadFilename;
+static int renderHandlesWithOffset = 0;
+static int globalFrameCount = -1;
+static int fullScreen = 0;
+static string handleObjMeshFilename;
+static ObjMesh * handleObjMesh = nullptr;
+static string handleObjMeshDispRecordFilename;
+static FrameRecorder * handleObjMeshRecorder = nullptr;
+static bool key_h_pressed = false;
+
+const static char * mouseControlStr[8] =
+{
+  "CTRL + click: and/remove a handle",
+  "(on Mac, use key 'h' + click)",
+  "Left click: show a nearby handle",
+  "Drag handle: change handle location",
+  "Middle + drag: zoom in/out",
+  "Right + drag: rotate camera",
+  "Keys 'e', 'w': toggle volumetric mesh",
+  "Keys 'E', 'W': toggle embedded mesh"
+};
+
+#define ADD_CONFIG(v) configFile.addOptionOptional(#v, &v, v)
+#define HAS_STRING(v) ((v).size() > 0)
+
+// Dump constraints to a file. The format is the same as for reading constraints from a file.
+static void saveConstraints()
+{
+  ofstream fout(outputConstraintsFilename.c_str(), ios::binary);
+  if(!fout)
+  {
+    cout << "Cannot open " << outputConstraintsFilename << endl;
+    return;
+  }
+
+  for(HandleIter it = selectedIKVertices.begin(); it != selectedIKVertices.end(); it++)
+  {
+    int vtx = it->first;
+    Vec3d disp = it->second;
+    fout << vtx << " " << disp[0] << " " << disp[1] << " " << disp[2] << endl;
+  }
+  fout.close();
+}
+
+static void ClearSelectedIKVertices()
+{
+  selectedIKVertices.clear();
+  handleControl.clearHandleSelection();
+}
+
+static void Sync_GLUI()
+{
+  glui->sync_live();
+}
+
+// displayFunction is always called before idleFunction
+static void idleFunction(void)
+{
+  cpuLoadCounter.StartCounter();
+  glutSetWindow(windowID);
+
+  globalFrameCount++;
+
+  // Because displayFunction() is called before idleFunction(), we dump animation for the rendered shape.
+  if (dumpDeformation == 1)
+  {
+    dumpDeformation = 0;
+    static int count = 0;
+    string filename = "def" + to_string(count) + ".u";
+    cout << "Writing deformation to " << filename << "..." << flush;
+    WriteMatrixToDisk_(filename.c_str(),3*n,1,&disp[0]);
+    cout << "Done" << endl;
+    count++;
+  }
+  else if (dumpDeformation == 2)
+  {
+    dumpDeformation = 0;
+    static int count = 0;
+
+    if (cubicMesh)
+    {
+      CubicMesh newCubicMesh(*cubicMesh);
+      newCubicMesh.applyDeformation(&disp[0]);
+      string filename = "cubicMesh" + to_string(count) + ".veg";
+      newCubicMesh.save(filename.c_str());
+      filename += ".obj";
+      deformableMesh->ResetDeformationToRest();
+      deformableMesh->AddVertexDeformations(&disp[0]);
+      deformableMesh->GetMesh()->save(filename);
+    } else if (tetMesh)
+    {
+      TetMesh newTetMesh(*tetMesh);
+      newTetMesh.applyDeformation(&disp[0]);
+      string filename = "tetMesh" + to_string(count) + ".veg";
+      newTetMesh.save(filename.c_str());
+      filename += ".obj";
+      deformableMesh->ResetDeformationToRest();
+      deformableMesh->AddVertexDeformations(&disp[0]);
+      deformableMesh->GetMesh()->save(filename);
+    }
+    if (embeddedMesh)
+    {
+      string filename = "embMesh" + to_string(count) + ".obj";
+      embeddedMesh->GetMesh()->save(filename);
+    }
+    count++;
+  }
+  if (dispRecorder)
+  {
+    dispRecorder->addFrame(&disp[0]);
+  }
+
+  if (handleObjMeshRecorder) // record the motion for the displacement on the idleFunc
+  {
+    vector<double> buffer;
+    for(const pair<int,Vec3d> & p : selectedIKVertices)
+    {
+      int vtx = p.first;
+      Vec3d pos = deformableMesh->GetSingleVertexRestPosition(vtx);
+      pos += p.second;
+      int numObjVtx = handleObjMesh->getNumVertices();
+      for(int i = 0; i < numObjVtx; i++)
+        for(int j = 0; j < 3; j++)
+          buffer.push_back(pos[j]);
+    }
+    handleObjMeshRecorder->addFrame(buffer.data());
+  }
+
+  // Because displayFunction() is called before idleFunction(), we have to give a +1 below.
+  if (numInitialHandlesPreDispFrames > 0 && globalFrameCount+1 <= numInitialHandlesPreDispFrames)
+  {
+    double fac = (globalFrameCount+1) / (double)numInitialHandlesPreDispFrames;
+    for(const pair<int, Vec3d> & p : initialHandleDisp)
+    {
+      selectedIKVertices[p.first] = fac * p.second;
+    }
+  }
+
+  if (cameraChangeLoadFilename.size() > 0)
+  {
+    cameraChangeLoad.controlCamera(globalFrameCount, *camera);
+  }
+
+  if (cameraLighting)
+  {
+    cameraLighting->SetLightIntensity(cameraLightIntensity);
+  }
+  else if (lighting)
+  {
+    for(int i = 0; i < 8; i++)
+    {
+      lighting->SetLightEnabled(i, lightOn[i]);
+      lighting->SetLightIntensity(i, lightIntensity);
+    }
+    lighting->SetSpecularEnabled(showSpecularLight);
+  }
+
+  deformer->updateHandles(selectedIKVertices);
+
+  // Take appropriate action in case the user is dragging a vertex.
+  auto processDrag = [&](int vertex, Vec3d posDiff)
+  {
+    if (!moveVerticesTogether)
+    {
+      auto IKPulledVertexIter = selectedIKVertices.find(vertex);
+      (IKPulledVertexIter->second) += posDiff;
+      if (len2(posDiff) > 0)
+        cout << IKPulledVertexIter->first << ": " << IKPulledVertexIter->second << endl;
+    }
+    else
+    {
+      for(auto iter = selectedIKVertices.begin(); iter != selectedIKVertices.end(); iter++)
+        (iter->second) += posDiff;
+    }
+  };
+  handleControl.processHandleMovement(id.getMousePosX(), id.getMousePosY(), id.shiftPressed(), processDrag);
+
+  if(interactiveDeformation)
+  {
+    deformer->deformOneIter(&disp[0], &disp[0]);
+  }
+  else
+  {
+    int scale = 1;
+    assert(maxIterations >= 1);
+    assert(iterationThreshold >= 0);
+
+    deformer->deform(&disp[0], &disp[0], iterationThreshold / scale, maxIterations * scale);
+  }
+
+  if (interpCoords && embeddedMesh && (renderEmbeddedObject || renderEmbeddedWireframe))
+  {
+    interpCoords->deform(&disp[0], &u_embed[0]);
+    embeddedMesh->SetVertexDeformations(&u_embed[0]);
+    embeddedMesh->BuildNormals();
+  }
+
+  // render the deformable object
+  if (renderDeformableObject || renderWireframe)
+  {
+    deformableMesh->SetVertexDeformations(&disp[0]);
+    deformableMesh->BuildNormals();
+  }
+
+  graphicFrame++;
+
+  // update title bar at 4 Hz
+  titleBarCounter.StopCounter();
+  double elapsedTime = titleBarCounter.GetElapsedTime();
+  if (elapsedTime >= 1.0 / 4)
+  {
+    titleBarCounter.StartCounter();
+    double fps = graphicFrame / elapsedTime;
+
+    fpsBuffer.addValue(fps);
+
+    // update menu bar
+    char windowTitle[4096];
+
+    sprintf(windowTitle, "Vertices: %d | %.1f FPS | iter %d ", n, fpsBuffer.getAverage(), globalFrameCount);
+
+    glutSetWindowTitle(windowTitle);
+    graphicFrame = 0;
+
+    //char ptext[96];
+    //sprintf(ptext, "Force assembly: %G", forceAssemblyTime);
+    //forceAssemblyStaticText->set_text(ptext);
+    //sprintf(ptext, "System solve: %G", systemSolveTime);
+    //systemSolveStaticText->set_text(ptext);
+
+    Sync_GLUI();
+  }
+
+  cpuLoadCounter.StopCounter();
+  double cpuTimePerGraphicsFrame = cpuLoadCounter.GetElapsedTime();
+  cpuLoad = cpuTimePerGraphicsFrame * fpsBuffer.getAverage();
+
+  if (saveScreenToFile || dumpScreenshots)
+  {
+    char defaultBaseName[] = "pic";
+    const char * baseName = defaultBaseName;
+    if (screenshotBaseName.size() > 0)
+      baseName = screenshotBaseName.c_str();
+
+    static int sprite = 0;
+    char s[100] = "";
+    sprintf(s,"%s.%04d.png",baseName,sprite);
+    Screenshot::SaveScreenshot(s, ImageIO::FORMAT_PNG,windowWidth, windowHeight);
+    sprite++;
+
+    saveScreenToFile=0; // save only once
+  }
+
+  if (numFramesToExit >= 0)
+  {
+    if (globalFrameCount+1 >= numFramesToExit)
+    {
+      cout << "Exiting after " << numFramesToExit << " frames" << endl;
+      exit(0);
+    }
+  }
+  glutPostRedisplay();
+}
+
+static void initScene()
+{
+  // initialize the rendering mesh for the deformable object
+  if(HAS_STRING(volumetricMeshFilename))
+  {
+    volumetricMesh = VolumetricMeshLoader::load(volumetricMeshFilename.c_str());
+    assert(volumetricMesh);
+    if (volumetricMesh->getElementType() == VolumetricMesh::TET)
+    {
+      tetMesh = dynamic_cast<TetMesh*>(volumetricMesh);
+    }
+    else if (volumetricMesh->getElementType() == VolumetricMesh::CUBIC)
+    {
+      cubicMesh = dynamic_cast<CubicMesh*>(volumetricMesh);
+    }
+    volSurfaceMesh = GenerateSurfaceMesh::ComputeMesh(volumetricMesh);
+    volSurfaceMesh->buildFaceNormals();
+    deformableMesh = new SceneObjectDeformable(volSurfaceMesh, true);
+  }
+  else if (HAS_STRING(surfaceMeshFilename))
+  {
+    deformableMesh = new SceneObjectDeformable(surfaceMeshFilename.c_str());
+  }
+
+  if (deformableMesh->HasTextures() && enableTextures)
+  {
+    deformableMesh->EnableTextures();
+    deformableMesh->SetUpTextures();
+  }
+  else
+    deformableMesh->DisableTextures();
+
+  n = deformableMesh->GetNumVertices();
+  //deformableMesh->EnableCustomColor();
+  //deformableMesh->SetCustomColor(vector<Vec3d>(n, Vec3d(1,0,0)));
+
+  disp.resize(3*n, 0.);
+  if(HAS_STRING(initialDeformationFilename))
+  {
+    Matrix<double> m(initialDeformationFilename.c_str());
+    assert(m.Getm() == 3*n && m.Getn() > 0);
+    memcpy(disp.data(), m.GetData(), sizeof(double) * m.Getm());
+  }
+
+  // init lighting
+  cameraLighting = new CameraLighting;
+  deformableMesh->ResetDeformationToRest();
+  deformableMesh->BuildNeighboringStructure();
+  deformableMesh->BuildNormals();
+  deformableMesh->SetMaterialAlpha(0.5);
+
+  // load/create fixed vertices
+  // 1-indexed notation
+  numFixedVertices = 0;
+  fixedVertices = nullptr;
+  if(fixedVerticesFilename.size() > 0)
+  {
+    if (ListIO::load(fixedVerticesFilename.c_str(), &numFixedVertices, &fixedVertices, 1) != 0)
+    {
+      printf("Error reading fixed vertices.\n");
+      exit(1);
+    }
+
+    printf("Loaded %d fixed vertices.\n", numFixedVertices);
+    //ListIO::print(numFixedVertices, fixedVertices);
+
+    // check if indices are within bounds
+    for(int i=0; i<numFixedVertices; i++)
+    {
+      if ((fixedVertices[i] < 0) || (fixedVertices[i] >= n))
+      {
+        printf("Error: encountered a boundary vertex out of bounds. Vertex: %d. Valid range: [0..%d).\n", fixedVertices[i], n);
+        exit(1);
+      }
+    }
+  }
+
+  numFixedDOFs = 3 * numFixedVertices;
+  fixedDOFs = (int*) malloc (sizeof(int) * numFixedDOFs);
+
+  for(int i=0; i<numFixedVertices; i++)
+  {
+    fixedDOFs[3*i+0] = 3 * (fixedVertices[i]) + 0;
+    fixedDOFs[3*i+1] = 3 * (fixedVertices[i]) + 1;
+    fixedDOFs[3*i+2] = 3 * (fixedVertices[i]) + 2;
+  }
+
+  cout << "Building ARAP shape deformer..." << endl;
+  if (tetMesh)
+    deformer = new ARAPDeformer(tetMesh, numFixedVertices, fixedVertices, numSolverThreads);
+  else if (cubicMesh)
+    deformer = new ARAPDeformer(cubicMesh, numFixedVertices, fixedVertices, numSolverThreads);
+  else
+    deformer = new ARAPDeformer(deformableMesh->GetMesh(), numFixedVertices, fixedVertices, numSolverThreads);
+
+  if (initialHandlesFilename.size() > 0)
+  {
+    ifstream fin(initialHandlesFilename);
+    assert(fin);
+    fin >> ws;
+    while(!fin.eof())
+    {
+      int vertex = -1;
+      Vec3d d(0.);
+      fin >> vertex >> d[0] >> d[1] >> d[2];
+      assert(vertex >= 0 && vertex < n);
+      initialHandleDisp.emplace(vertex, d);
+      assert(fin.fail() == false);
+      fin >> ws;
+    }
+
+    if (numInitialHandlesPreDispFrames <= 0)
+    {
+      selectedIKVertices.insert(initialHandleDisp.begin(), initialHandleDisp.end());
+    } else
+    {
+      for(const pair<int, Vec3d> & p : initialHandleDisp)
+        selectedIKVertices.emplace(p.first, Vec3d(0.0));
+    }
+  }
+
+  // Load any external geometry file (e.g. some static scene for decoration; usually there will be none).
+  printf("Loading extra scene geometry...\n");
+  if (strcmp(extraSceneGeometryFilename,"__none") != 0)
+  {
+    extraSceneGeometry = new SceneObjectDeformable(extraSceneGeometryFilename);
+    if (buildExtraSceneGeometryNormals)
+    {
+      extraSceneGeometry->BuildNeighboringStructure();
+      extraSceneGeometry->BuildNormals();
+    }
+    if (enableTextures)
+    {
+      if (extraSceneGeometry->SetUpTextures() != 0)
+        exit(1);
+    }
+  }
+  else
+    extraSceneGeometry = nullptr;
+
+  if (groundPlaneString.size() > 0)
+  {
+    groundPlane = new SceneGroundPlane(groundPlaneString.c_str(), groundPlaneTextureFilename.c_str());
+    renderGroundPlane = 1;
+  }
+
+  // set background color
+  int colorR, colorG, colorB;
+  sscanf(backgroundColorString, "%d %d %d", &colorR, &colorG, &colorB);
+  glClearColor(1.0 * colorR / 255, 1.0 * colorG / 255, 1.0 * colorB / 255, 0.0);
+  
+  #ifdef USE_GLSL
+    glslPhong = new GLSLPhong();
+  #endif
+
+  if(HAS_STRING(volumetricMeshFilename) && HAS_STRING(embeddedMeshFilename))
+  {
+    embeddedMesh = new SceneObjectDeformable(embeddedMeshFilename.c_str());
+    if (enableTextures && embeddedMesh->HasTextures())
+      embeddedMesh->SetUpTextures(SceneObject::MODULATE, SceneObject::NOMIPMAP,
+          SceneObject::USEANISOTROPICFILTERING, SceneObject::USETEXTURETRANSPARENCY);
+    else
+      embeddedMesh->DisableTextures();
+    // cout << "objRdrMesh has texture ? " << embeddedMesh->HasTextures() << endl;
+    embeddedMesh->ResetDeformationToRest();
+    embeddedMesh->BuildNeighboringStructure();
+    embeddedMesh->BuildNormals();
+    // embeddedMesh->SetMaterialAlpha(0.5);
+    u_embed.assign(embeddedMesh->GetNumVertices() * 3, 0.0);
+
+    if (HAS_STRING(embeddedMeshInterpolationFilename))
+    {
+      interpCoords = new BarycentricCoordinates(embeddedMeshInterpolationFilename);
+      assert((int)interpCoords->getNumLocations() == embeddedMesh->GetNumVertices());
+    }
+    else
+    {
+      cout << "Error: no embeding weights provided" << endl;
+      exit(1);
+    }
+  }
+
+  if (dispRecordFilename.size() > 0)
+  {
+    dispRecorder = new FrameRecorder(n*3, dispRecordFilename);
+  }
+
+  double cameraUp[3] = {0,1,0};
+
+  Vec3d cameraFocus;
+  Vec3d bmin, bmax;
+  deformableMesh->GetMesh()->computeBoundingBox();
+  deformableMesh->GetMesh()->getCubicBoundingBox(1.0, &bmin, &bmax);
+  Vec3d modelCenter = (bmin + bmax) / 2.0;
+  double modelRadius = deformableMesh->GetMesh()->getDiameter() / 2;
+
+  if (configFile.isOptionLoaded("cameraRadius") == false)
+  {
+    cameraRadius = modelRadius * 3;
+  }
+  if (configFile.isOptionLoaded("cameraFocusPosition"))
+    cameraFocus = cameraFocusPosition;
+  else
+    cameraFocus = modelCenter;
+  zNear = cameraRadius * 0.01;
+  zFar = cameraRadius * 100.0;
+
+  camera = new SphericalCamera(cameraRadius,
+            1.0 * cameraLongitude / 360 * (2.0 * PI),
+            1.0 * cameraLattitude / 360 * (2.0 * PI),
+            &cameraFocus[0], cameraUp, 0.05);
+  if (cameraPosFilename.size() > 0)
+    camera->LoadPosition(cameraPosFilename.c_str());
+  // else
+  //   camera->LoadPosition("cameraPos.txt");
+
+  // update lighting parameters
+  if (cameraLighting)
+  {
+    cameraLighting->SetLightIntensity(cameraLightIntensity);
+  }
+  else if (lighting)
+  {
+    for(int i = 0; i < 8; i++)
+    {
+      lighting->SetLightEnabled(i, lightOn[i]);
+      lighting->SetLightIntensity(i, lightIntensity);
+    }
+    lighting->SetSpecularEnabled(showSpecularLight);
+  }
+
+  if (cameraChangeLoadFilename.size() > 0)
+  {
+    int ret = cameraChangeLoad.load(cameraChangeLoadFilename);
+    assert(ret == 0);
+  }
+
+  if (handleObjMeshFilename.size() > 0)
+  {
+    handleObjMesh = new ObjMesh(handleObjMeshFilename);
+    cout << "Loaded handle mesh: " << handleObjMeshFilename << "." << endl;
+
+    if (handleObjMeshDispRecordFilename.size() > 0)
+    {
+      int numHandles = initialHandleDisp.size();
+      handleObjMeshRecorder = new FrameRecorder(numHandles * handleObjMesh->getNumVertices() * 3, handleObjMeshDispRecordFilename);
+    }
+  }
+
+  Sync_GLUI();
+
+  printf("Initialization completed.\n");
+}
+
+void initConfigurations()
+{
+  printf("Parsing configuration file %s...\n", configFilename.c_str());
+
+  // Specify the entries of the config file.
+
+  // At least one of the following three must be present:
+  ADD_CONFIG(fixedVerticesFilename);
+  ADD_CONFIG(groundPlaneTextureFilename);
+  ADD_CONFIG(initialHandlesFilename);
+  ADD_CONFIG(numInitialHandlesPreDispFrames);
+  ADD_CONFIG(dumpScreenshots);
+  ADD_CONFIG(screenshotBaseName);
+  ADD_CONFIG(numFramesToExit);
+  ADD_CONFIG(renderFixedVertices);
+  ADD_CONFIG(cameraPosFilename);
+  ADD_CONFIG(dispRecordFilename);
+  ADD_CONFIG(cameraLightIntensity);
+  ADD_CONFIG(lineThickness);
+  ADD_CONFIG(renderWireframe);
+  ADD_CONFIG(renderIKVertices);
+  ADD_CONFIG(renderDeformableObject);
+  ADD_CONFIG(renderEmbeddedWireframe);
+  ADD_CONFIG(renderHandlesWithOffset);
+  ADD_CONFIG(cameraChangeLoadFilename);
+  ADD_CONFIG(fullScreen);
+  ADD_CONFIG(handleObjMeshFilename);
+  ADD_CONFIG(handleObjMeshDispRecordFilename);
+  ADD_CONFIG(cameraFocusPosition);
+
+  configFile.addOptionOptional("numSolverThreads", &numSolverThreads, 1);
+  configFile.addOptionOptional("numOptIterations", &numOptIterations, 4);
+
+  configFile.addOptionOptional("windowWidth",&windowWidth,windowWidth);
+  configFile.addOptionOptional("windowHeight",&windowHeight,windowHeight);
+  configFile.addOptionOptional("cameraRadius",&cameraRadius,17.5);
+  configFile.addOptionOptional("cameraLongitude",&cameraLongitude,-60.0);
+  configFile.addOptionOptional("cameraLattitude",&cameraLattitude,20.0);
+  configFile.addOptionOptional("renderVertices",&renderVertices, renderVertices);
+  configFile.addOptionOptional("renderAxes",&renderAxes,renderAxes);
+  configFile.addOptionOptional("extraSceneGeometry",extraSceneGeometryFilename,"__none");
+  configFile.addOptionOptional("buildExtraSceneGeometryNormals", &buildExtraSceneGeometryNormals, buildExtraSceneGeometryNormals);
+  configFile.addOptionOptional("renderExtraSceneGeometryShadow", &renderExtraSceneGeometryShadow, renderExtraSceneGeometryShadow);
+
+  configFile.addOptionOptional("enableTextures",&enableTextures,enableTextures);
+  configFile.addOptionOptional("backgroundColor",backgroundColorString, backgroundColorString);
+
+  ADD_CONFIG(groundPlaneString);
+  configFile.addOptionOptional("renderWorldCounter",&renderWorldCounter, renderWorldCounter);
+  configFile.addOptionOptional("enableGLSLPhong",&enableGLSLPhong, enableGLSLPhong);
+
+  ADD_CONFIG(embeddedMeshFilename);
+  ADD_CONFIG(embeddedMeshInterpolationFilename);
+  ADD_CONFIG(interactiveDeformation);
+  ADD_CONFIG(maxIterations);
+  ADD_CONFIG(iterationThreshold);
+
+  ADD_CONFIG(outputConstraintsFilename);
+  ADD_CONFIG(initialDeformationFilename);
+  ADD_CONFIG(surfaceMeshFilename);
+  ADD_CONFIG(volumetricMeshFilename);
+  ADD_CONFIG(lightingConfigFilename);
+  ADD_CONFIG(numHiddenHandles);
+  ADD_CONFIG(numFrames);
+  ADD_CONFIG(compareReducedWithFull);
+
+  // parse the configuration file
+  if (configFile.parseOptions((char*)configFilename.c_str()) != 0)
+  {
+    printf("Error parsing options.\n");
+    exit(1);
+  }
+
+  // the config variables have now been loaded with their specified values
+
+  // informatively print the variables (with assigned values) that were just parsed
+  configFile.printOptions();
+}
+
+static void renderScene(int frame, bool renderHandle)
+{
+  // if GPU rendering, must set lighting
+  // else set lights directly via OpenGL (all done inside the following function)
+  if (cameraLighting)
+  {
+    cameraLighting->LightScene(camera);
+  }
+  else if (lighting)
+  {
+    deformableMesh->SetLighting(lighting);
+    lighting->LightScene();
+  }
+
+  glEnable(GL_LIGHTING);
+
+  glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);  //only when stencil pass and z-buffer pass, set stencil value to stencil reference
+  glStencilFunc(GL_ALWAYS, 0, ~(0u)); //always pass stencil test, stencil renference value is 0
+
+  #ifdef USE_GLSL
+    if (enableGLSLPhong)
+      glslPhong->Enable();
+  #endif
+
+  int allOffset = 0.0;
+  if (renderHandlesWithOffset) // we offset all the faces so that the handle points appear in front of everything
+    allOffset = 3;
+  PolygonOffsetFillState allFaceOffset(allOffset,allOffset);
+
+  if (renderGroundPlane && groundPlane)
+  {
+    glEnable(GL_LIGHTING);
+    glEnable(GL_TEXTURE_2D);
+    groundPlane->render();
+    glDisable(GL_TEXTURE_2D);
+  }
+
+  glStencilFunc(GL_ALWAYS, 1, ~(0u)); // always pass the stencil test, stencil renference value is 1
+
+  if (renderEmbeddedObject && embeddedMesh)
+    embeddedMesh->Render();
+
+  if (renderDeformableObject && deformableMesh)
+  {
+    //  cout << "set disp at frame " << currentFrame << endl;
+    bool doBlending = renderEmbeddedObject && (embeddedMesh || comparedMesh);
+    if (doBlending)
+    {
+      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      glEnable(GL_BLEND);
+      // PolygonOffsetFillState offsetState(1.0, 1.0);
+      glDrawBuffer(GL_NONE);
+      deformableMesh->Render();
+      glDrawBuffer(GL_BACK);
+    }
+
+    {
+      // PolygonFillOffsetState offsetState(1.0, 1.0);
+      if (volSurfaceMesh)
+        deformableMesh->EnableFlatFaces();
+
+      if (doBlending)
+      {
+        glEnable(GL_CULL_FACE);
+        glCullFace(GL_BACK);
+      }
+      // cout << "render vol mesh" << endl;
+      deformableMesh->Render();
+      if (doBlending)
+      {
+        glDisable(GL_CULL_FACE);
+      }
+    }
+
+    if (renderVertices)
+    {
+      glDisable(GL_LIGHTING);
+      glColor3f(0.5,0,0);
+      glPointSize(8.0);
+
+      deformableMesh->RenderVertices();
+
+      glEnable(GL_LIGHTING);
+    }
+
+    if (doBlending)
+      glDisable(GL_BLEND);
+  }
+
+  // render any extra scene geometry
+  glStencilFunc(GL_ALWAYS, 0, ~(0u));
+  if ((renderExtraSceneGeometry) && (extraSceneGeometry != nullptr))
+  {
+    extraSceneGeometry->Render();
+  }
+
+  glDisable(GL_TEXTURE_2D);
+  #ifdef USE_GLSL
+    glslPhong->Disable();
+  #endif
+
+  // render shadow
+  if (renderGroundPlane)
+  {
+    glColor3f(0.1,0.1,0.1);
+    glDisable(GL_LIGHTING);
+
+    if (renderDeformableObject && deformableMesh && groundPlane)
+      groundPlane->renderShadow(deformableMesh);
+
+    if ((renderExtraSceneGeometry) && (extraSceneGeometry) && groundPlane)
+      groundPlane->renderShadow(extraSceneGeometry);
+
+    glEnable(GL_LIGHTING);
+  }
+
+  glDisable(GL_LIGHTING);
+
+  glStencilFunc(GL_ALWAYS, 1, ~(0u));
+  glColor3f(0,0,0);
+  if (renderWireframe)
+  {
+    glLineWidth(lineThickness);
+    deformableMesh->RenderEdges();
+  }
+
+  if (renderEmbeddedWireframe)
+  {
+    glColor3f(0,0,0);
+    glLineWidth(lineThickness);
+    if (embeddedMesh)
+      embeddedMesh->RenderEdges();
+  }
+
+  glColor3f(0,0.5,0);
+  glPointSize(10.0);
+  if (renderVertices)
+    deformableMesh->RenderVertices();
+
+  #ifdef USE_GLSL
+    glslPhong->Disable();
+  #endif
+  allFaceOffset.restore();
+
+  glStencilFunc(GL_ALWAYS, 1, ~(0u));
+  if(renderIKVertices)
+  {
+    glPointSize(17.0);
+
+    glBegin(GL_POINTS);
+    glColor3f(0,0,1);
+    for(map<int,Vec3d> :: iterator iter = selectedIKVertices.begin(); iter != selectedIKVertices.end(); iter++)
+    {
+      double pulledVertexPos[3];
+      deformableMesh->GetSingleVertexRestPosition(iter->first,
+          &pulledVertexPos[0], &pulledVertexPos[1], &pulledVertexPos[2]);
+      pulledVertexPos[0] += iter->second[0];
+      pulledVertexPos[1] += iter->second[1];
+      pulledVertexPos[2] += iter->second[2];
+      glVertex3f(pulledVertexPos[0], pulledVertexPos[1], pulledVertexPos[2]);
+    }
+    glEnd();
+  }
+
+  // turn off stencil modifications
+  glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+  glColor3f(0,0,0);
+
+  if (renderAxes)
+  {
+    glLineWidth(1.0);
+    RenderAxes(1.0);
+  }
+
+  double pulledVertexPos[3];
+
+  // render the vertex currently being manipulated via IK
+  if (handleControl.isHandleSelected())
+  {
+    deformableMesh->GetSingleVertexPositionFromBuffer(handleControl.getSelectedHandle(), &pulledVertexPos[0], &pulledVertexPos[1], &pulledVertexPos[2]);
+    glColor3f(1,0,0);
+
+    glPointSize(8.0);
+    glBegin(GL_POINTS);
+    glVertex3f(pulledVertexPos[0], pulledVertexPos[1], pulledVertexPos[2]);
+    glEnd();
+
+    // render the move handle
+    Vec3d handlePosition;
+    deformableMesh->GetSingleVertexRestPosition(handleControl.getSelectedHandle(), &handlePosition[0], &handlePosition[1], &handlePosition[2]);
+    auto it = selectedIKVertices.find(handleControl.getSelectedHandle());
+    assert(it != selectedIKVertices.end());
+    handlePosition += it->second;
+
+    handleControl.renderHandle(camera, handlePosition, reverseHandle);
+  }
+
+  // render fixed vertices
+  if (renderFixedVertices)
+  {
+    glPointSize(18.0);
+    glColor3f(1,0,0);
+    glBegin(GL_POINTS);
+    for(int i=0; i<numFixedVertices; i++)
+    {
+      glColor3f(1,0,0);
+      double fixedVertexPos[3];
+      deformableMesh->GetSingleVertexRestPosition(fixedVertices[i],
+          &fixedVertexPos[0], &fixedVertexPos[1], &fixedVertexPos[2]);
+      glVertex3f(fixedVertexPos[0], fixedVertexPos[1], fixedVertexPos[2]);
+    }
+    glEnd();
+  }
+}
+
+static void displayFunction()
+{
+  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+  // Setup model transformations.
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+
+  camera->Look();
+
+  if (renderPrimaryWindow)
+  {
+    if (renderSecondaryWindow)
+    {
+      if (verticalWindowSplit)
+        glViewport(0, 0, windowWidth/2, windowHeight);
+      else
+        glViewport(0, windowHeight/2, windowWidth, windowHeight/2);
+    }
+    else
+      glViewport(0,0,windowWidth,windowHeight);
+
+    renderScene(currentFrame, true);
+  }
+
+  if (renderSecondaryWindow)
+  {
+    if (renderPrimaryWindow)
+    {
+      if (verticalWindowSplit)
+        glViewport(windowWidth/2,0,windowWidth/2,windowHeight);
+      else
+        glViewport(0,0,windowWidth,windowHeight/2);
+    }
+    else
+      glViewport(0,0,windowWidth,windowHeight);
+
+    renderScene(0, false);
+  }
+
+  glutSwapBuffers();
+}
+
+void reset_buttonCallBack(int code=0)
+{
+  ClearSelectedIKVertices();
+
+  Sync_GLUI();
+}
+
+void keyboardFunction(unsigned char key, int x, int y)
+{
+  Vec3d objectCenter;
+  id.setModifiers();
+  id.setMousePos(x, y);
+
+  switch (key)
+  {
+  case 'd':
+    dumpDeformation = 1;
+    break;
+
+  case 'D':
+    dumpDeformation = 2;
+    break;
+
+  case 9:
+  {
+    fullScreen = !fullScreen;
+    if (fullScreen)
+    {
+      cout << "Full screen" << endl;
+      glutFullScreen();
+    }
+    else
+    {
+      cout << "Normal screen" << endl;
+      glutReshapeWindow(800, 600);
+      glutPositionWindow(50, 50);
+    }
+    break;
+  }
+
+  case 27:
+    exit(0);
+    break;
+
+  case 'w':
+    renderWireframe = !renderWireframe;
+    break;
+
+  case 'W':
+    renderEmbeddedWireframe = !renderEmbeddedWireframe;
+    break;
+
+  case '?':
+    renderTextInfo = !renderTextInfo;
+    break;
+
+  case 'i':
+  {
+    double cameraX,cameraY,cameraZ;
+    camera->GetAbsWorldPosition(cameraX,cameraY,cameraZ);
+    printf("Camera is positioned at: %G %G %G\n",cameraX,cameraY,cameraZ);
+    printf("Camera radius is: %G \n",camera->GetRadius());
+    printf("Camera Phi is: %G \n",180.0/M_PI*camera->GetPhi());
+    printf("Camera Theta is: %G \n",180.0/M_PI*camera->GetTheta());
+    double focusPos[3];
+    camera->GetFocusPosition(focusPos);
+    printf("Camera focus position is: %G %G %G\n", focusPos[0], focusPos[1], focusPos[2]);
+  }
+  break;
+
+  case '\\':
+    camera->Reset();
+    break;
+
+  case 13:
+    break;
+
+  case '0':
+    reset_buttonCallBack(0); // hard reset
+    break;
+
+  case '9':
+    reset_buttonCallBack(1); // soft reset
+    break;
+
+  case 'a':
+    renderAxes = !renderAxes;
+    Sync_GLUI();
+    break;
+
+  case '[':
+    printf("Loading camera position.\n");
+    camera->LoadPosition("cameraPos.txt");
+    break;
+
+  case ']':
+    printf("Saving camera position.\n");
+    camera->SavePosition("cameraPos.txt");
+    break;
+
+  case 'b':
+    renderFixedVertices = !renderFixedVertices;
+    break;
+
+  case 'v':
+    renderIKVertices = !renderIKVertices;
+    break;
+
+  case 'V':
+    renderVertices = !renderVertices;
+    Sync_GLUI();
+    break;
+
+  case 'e':
+    renderDeformableObject = !renderDeformableObject;
+    break;
+
+  case 'E':
+    renderEmbeddedObject = !renderEmbeddedObject;
+    break;
+
+  case '!':
+    renderExtraSceneGeometry = !renderExtraSceneGeometry;
+    break;
+
+  case 't':
+    reverseHandle = !reverseHandle;
+    printf("Reverse handle is now %s .\n", reverseHandle ? "ON" : "OFF");
+    break;
+
+  case 'P':
+    enableGLSLPhong = !enableGLSLPhong;
+    printf("Phong shader is now %s.\n", enableGLSLPhong ? "ON" : "OFF");
+    break;
+
+  case 'g':
+    renderGroundPlane = !renderGroundPlane;
+    break;
+
+  case 'x':
+    saveScreenToFile = !saveScreenToFile;
+    break;
+
+  case 'y':
+    moveVerticesTogether = !moveVerticesTogether;
+    break;
+
+  case 'h':
+    key_h_pressed = true;
+    break;
+
+  case 'c': // dump constraints
+    cout << "Dump constraints to " << outputConstraintsFilename << "." << endl;
+    saveConstraints();
+    break;
+
+  case '-':
+    if (currentFrame > 0)
+      currentFrame--;
+    cout << "current frame " << currentFrame;
+    break;
+
+  case '=':
+    if (currentFrame+1 < numFrames)
+      currentFrame++;
+    cout << "current frame " << currentFrame;
+    break;
+  }
+}
+
+void specialFunction(int key, int x, int y)
+{
+  id.setModifiers();
+  id.setMousePos(x, y);
+
+  switch (key)
+  {
+  case GLUT_KEY_HOME:
+    break;
+
+  case GLUT_KEY_PAGE_UP:
+    break;
+
+  case GLUT_KEY_PAGE_DOWN:
+    break;
+
+  case GLUT_KEY_LEFT:
+    camera->MoveFocusRight(+0.03 * fabs(camera->GetRadius()));
+    break;
+
+  case GLUT_KEY_RIGHT:
+    camera->MoveFocusRight(-0.03 * fabs(camera->GetRadius()));
+    break;
+
+  case GLUT_KEY_DOWN:
+    camera->MoveFocusUp(+0.03 * fabs(camera->GetRadius()));
+    break;
+
+  case GLUT_KEY_UP:
+    camera->MoveFocusUp(-0.03 * fabs(camera->GetRadius()));
+    break;
+
+  case GLUT_KEY_END:
+    break;
+
+  case GLUT_KEY_INSERT:
+    break;
+
+  default:
+    break;
+  }
+}
+
+void reshape(int x, int y)
+{
+  windowWidth = x;
+  windowHeight = y;
+
+  glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
+  glLoadIdentity(); // Reset The Projection Matrix
+  gluPerspective(45.0f, 1.0 * windowWidth / windowHeight, cameraRadius*0.01f, 20*cameraRadius);
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+}
+
+void exitHandler()
+{
+  if (dispRecorder)
+    dispRecorder->save();
+  if (handleObjMeshRecorder)
+    handleObjMeshRecorder->save();
+}
+
+void mouseMotionFunction(int x, int y)
+{
+  int mouseDeltaX = x-id.getMousePosX();
+  int mouseDeltaY = y-id.getMousePosY();
+
+  id.setMousePos(x, y);
+
+  if (id.rightMouseButtonDown()) // right mouse button handles camera rotations
+  {
+    const double factor = 0.2;
+
+    camera->MoveRight(factor * mouseDeltaX);
+    camera->MoveUp(factor * mouseDeltaY);
+  }
+
+  if ((id.middleMouseButtonDown()) || (id.leftMouseButtonDown()&& id.altPressed()))// handle zoom in/out
+  {
+    const double factor = 0.1;
+    camera->ZoomIn(cameraRadius * factor * mouseDeltaY);
+  }
+}
+void mouseNoDrag(int x, int y)
+{
+  id.setMousePos(x, y);
+  if (handleControl.isHandleSelected())
+  {
+    Vec3d worldPos(0.0);
+    GLubyte stencilValue;
+    float zValue;
+    unprojectPointFromScreen(x,y, &worldPos[0], &stencilValue, &zValue);
+
+    if (stencilValue == 1)
+    {
+      handleControl.setMousePosition(worldPos);
+    }
+  }
+}
+
+void mouseButtonActivityFunction(int button, int state, int x, int y)
+{
+  id.setButton(button, state);
+  id.setMousePos(x,y);
+
+  Vec3d worldPos(0.0);
+  GLubyte stencilValue;
+  float zValue;
+
+  switch (button)
+  {
+  case GLUT_LEFT_BUTTON:
+  {
+    if (id.leftMouseButtonDown())
+      glutSetCursor(GLUT_CURSOR_CROSSHAIR);
+    else
+      glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
+
+    unprojectPointFromScreen(x,y, &worldPos[0], &stencilValue, &zValue);
+
+    auto getClosestHandle = [&]()
+    {
+      MinValueIndex vi;
+      for(map<int,Vec3d> :: iterator iter = selectedIKVertices.begin(); iter != selectedIKVertices.end(); iter++)
+      {
+        Vec3d pos(0.0);
+        deformableMesh->GetSingleVertexPositionFromBuffer(iter->first, &pos[0], &pos[1], &pos[2]);
+        vi.update(len2(pos - worldPos), iter->first);
+      }
+      return vi.index;
+    };
+
+    auto addOrRemoveHandle = [&]()
+    {
+      int clickedVertex = deformableMesh->GetClosestVertex(worldPos, nullptr, nullptr);
+      printf("(vertex IK handle select) Clicked on vertex: %d\n", clickedVertex);
+      map<int,Vec3d> :: iterator iter = selectedIKVertices.find(clickedVertex);
+      pair<int, bool> ret;
+      bool createNew = false;
+      if (iter == selectedIKVertices.end())
+      {
+        // select vertex
+        createNew = true;
+        double curPos[3];
+        deformableMesh->GetSingleVertexPositionFromBuffer(clickedVertex, &curPos[0], &curPos[1], &curPos[2]);
+        double restPos[3];
+        deformableMesh->GetSingleVertexRestPosition(clickedVertex, &restPos[0], &restPos[1], &restPos[2]);
+
+        Vec3d delta(curPos[0] - restPos[0], curPos[1] - restPos[1],curPos[2] - restPos[2]);
+        selectedIKVertices.insert(make_pair(clickedVertex, delta));
+        // set new constraint
+        printf("Create new handle: %d\n", clickedVertex);
+      }
+      else
+      {
+        // de-select vertex
+        selectedIKVertices.erase(iter);
+      }
+      return make_pair(clickedVertex, createNew);
+    };
+
+    handleControl.setMouseButtonActivity(id.leftMouseButtonDown(), stencilValue == 1, id.ctrlPressed() || key_h_pressed,
+        worldPos, zValue, getClosestHandle, addOrRemoveHandle);
+
+    break;
+  }
+
+  case GLUT_MIDDLE_BUTTON:
+
+    if (id.middleMouseButtonDown())
+      glutSetCursor(GLUT_CURSOR_UP_DOWN);
+    else
+      glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
+    break;
+
+  case GLUT_RIGHT_BUTTON:
+
+    if (id.rightMouseButtonDown())
+      glutSetCursor(GLUT_CURSOR_CYCLE);
+    else
+      glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
+    break;
+  }
+}
+
+void exit_buttonCallBack(int code)
+{
+  exit(0);
+}
+
+void gluiSaveLightConfigCallback(int)
+{
+  if (lighting)
+    lighting->SaveConfig(lightingConfigSavingFilename.c_str());
+}
+
+void initGLUI()
+{
+  // generate the UI
+  glui = GLUI_Master.create_glui("Controls", 0, windowWidth + 52, 0);
+
+  // ******** view ********
+
+  glui->add_checkbox("Display axes", &renderAxes);
+  glui->add_checkbox("Display wireframe", &renderWireframe);
+  glui->add_checkbox("Display vertices", &renderVertices);
+  glui->add_separator();
+
+  GLUI_Panel * instructions_panel =
+     glui->add_panel("Mouse buttons:", GLUI_PANEL_EMBOSSED);
+  instructions_panel->set_alignment(GLUI_ALIGN_LEFT);
+  
+  for(size_t i = 0; i < sizeof(mouseControlStr) / sizeof(const char *); i++)
+    glui->add_statictext_to_panel(instructions_panel, mouseControlStr[i]);
+
+  glui->add_separator();
+
+  glui->add_button("Reset", 0, reset_buttonCallBack);
+
+  glui->add_separator();
+  glui->add_button("Exit program", 0, exit_buttonCallBack);
+
+  Sync_GLUI();
+
+  glui->set_main_gfx_window(windowID);
+}
+
+void keyboardUpFunction(unsigned char key, int x, int y)
+{
+  // cout << "key " << key << " up" << endl;
+  switch(key)
+  {
+    case 'h':
+      key_h_pressed = false;
+      break;
+
+    default:
+      break;
+  }
+}
+
+void initGLUT(int argc, char* argv[], char * windowTitle, int windowWidth, int windowHeight, int * windowID)
+{
+  // Initialize GLUT.
+  glutInit(&argc, argv);
+  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL | GLUT_MULTISAMPLE);
+  glutInitWindowSize(windowWidth, windowHeight);
+  *windowID = glutCreateWindow(windowTitle);
+  glutPositionWindow(0,0);
+
+  // Setup GLUT callbacks.
+  GLUI_Master.set_glutDisplayFunc(displayFunction);
+  //glutDisplayFunc(displayFunction);
+
+  GLUI_Master.set_glutIdleFunc(idleFunction);
+  GLUI_Master.set_glutKeyboardFunc(keyboardFunction);
+  GLUI_Master.set_glutSpecialFunc(specialFunction);
+  GLUI_Master.set_glutReshapeFunc(reshape);
+  GLUI_Master.set_glutMouseFunc(mouseButtonActivityFunction);
+  glutKeyboardUpFunc(keyboardUpFunction);
+  glutMotionFunc(mouseMotionFunction);
+
+  // callback for mouse movement without any buttons pressed
+  glutPassiveMotionFunc(mouseNoDrag);
+}
+
+static void initGraphics(int windowWidth, int windowHeight)
+{
+   // clear to white
+  glClearColor(256.0 / 256, 256.0 / 256, 256.0 / 256, 0.0);
+
+  glEnable(GL_DEPTH_TEST);
+  glEnable(GL_STENCIL_TEST);
+  glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+
+  glShadeModel(GL_SMOOTH);
+  glEnable(GL_POLYGON_SMOOTH);
+  glEnable(GL_LINE_SMOOTH);
+
+  reshape(windowWidth,windowHeight);
+
+  printf ("Graphics initialization complete.\n");
+}
+
+int main(int argc, char* argv[])
+{
+  int numFixedArgs = 2;
+  if (argc < numFixedArgs)
+  {
+    printf("As-rigid-as-possible surface deformation editing.\n");
+    printf("Usage: %s [config file]\n", argv[0]);
+    return 1;
+  }
+
+  configFilename = argv[1];
+
+  printf("Starting application.\n");
+
+  printf("Loading scene configuration from %s.\n", configFilename.c_str());
+
+  initConfigurations();
+
+  atexit(exitHandler);
+
+  printf("Opening %d x %d window.\n", windowWidth, windowHeight);
+  initGLUT(argc, argv, windowTitleBase , windowWidth, windowHeight, &windowID);
+  initGraphics(windowWidth, windowHeight);
+
+  initGLUI();
+
+  initScene();
+
+  if (fullScreen)
+    glutFullScreen();
+
+  cout << "===================================================" << endl;
+  cout << "Mouse buttons:" << endl;
+  for(size_t i = 0; i < sizeof(mouseControlStr) / sizeof(const char *); i++)
+    cout << "- " << mouseControlStr[i] << endl;
+  
+  glutMainLoop();
+
+  return 0;
+}
+
diff --git a/utilities/finiteDifferenceTest/finiteDifferenceTest.cpp b/utilities/finiteDifferenceTest/finiteDifferenceTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e2d531b7164eb4af50fac541cb53dc3f2e89637b
--- /dev/null
+++ b/utilities/finiteDifferenceTest/finiteDifferenceTest.cpp
@@ -0,0 +1,439 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "finite difference tester" utility , Copyright (C) 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This driver utility tests the correctness of Vega's material classes.
+  It tests internal forces by performing finite differences on the energies,
+  and it tests stiffness matrices by performing finite differences on the
+  internal forces.
+*/
+#include "finiteDifferenceTester.h"
+
+#include "forceModel.h"
+#include "configFile.h"
+
+#include "tetMesh.h"
+#include "volumetricMesh.h"
+#include "volumetricMeshLoader.h"
+#include "generateMassMatrix.h"
+
+#include "clothBWFromObjMesh.h"
+#include "clothBWStencilForceModel.h"
+
+#include "matrix.h"
+#include "vectorHelper.h"
+
+#include "neoHookeanIsotropicMaterial.h"
+#include "StVKIsotropicMaterial.h"
+#include "MooneyRivlinIsotropicMaterial.h"
+#include "isotropicHyperelasticFEM.h"
+#include "isotropicHyperelasticFEMStencilForceModel.h"
+
+#include "corotationalLinearFEM.h"
+#include "corotationalLinearFEMStencilForceModel.h"
+
+#include "StVKFEM.h"
+#include "StVKElementABCDLoader.h"
+#include "StVKStencilForceModel.h"
+
+#include "massSpringSystem.h"
+#include "massSpringSystemFromObjMeshConfigFile.h"
+#include "massSpringSystemFromTetMeshConfigFile.h"
+#include "massSpringSystemFromCubicMeshConfigFile.h"
+#include "massSpringStencilForceModel.h"
+
+#include "stencilForceModel.h"
+#include "forceModelAssembler.h"
+
+#include "linearFEMStencilForceModel.h"
+
+#include <iostream>
+#include <cassert>
+#include <numeric>
+
+using namespace std;
+
+#define ADD_CONFIG(v) config.addOptionOptional(#v, &v, v)
+
+static double h = 0.0001;
+int n = 0, r = 0; // n: #vtx, r = n * 3
+
+static string volumetricMeshFilename;
+static string clothMeshFilename;
+static string animationFilename;
+
+static string deformableObjectMethod;
+static string invertibleMaterial;
+static int numInternalForceThreads = 1;
+
+static double surfaceDensity = 100;
+static double tensileStiffness = 4e6;
+static double shearStiffness = 2e6;
+static double bendStiffnessU = 0.2;
+static double bendStiffnessV = 0.2;
+static bool computeStShForce = true;
+static bool computeBendForce = true;
+static bool computeStShStiffness = true;
+static bool computeBendStiffness = true;
+static bool useRestAngle = true;
+static string customMassSpringSystem;
+static string massSpringSystemObjConfigFilename;
+static string massSpringSystemTetMeshConfigFilename;
+static string massSpringSystemCubicMeshConfigFilename;
+
+static int enableCompressionResistance = 0.;
+static double compressionResistance = 500;
+static double inversionThreshold = -1;
+
+static int corotationalLinearFEM_warp = 1;
+
+static ObjMesh * mesh = nullptr;
+static ClothBW * cloth = nullptr;
+static VolumetricMesh * volumetricMesh = nullptr;
+
+static ForceModel * forceModel = nullptr;
+static IsotropicMaterial * isotropicMaterial = nullptr;
+static IsotropicHyperelasticFEM * isotropicHyperelasticFEM = nullptr;
+CorotationalLinearFEM * corotationalLinearFEM = nullptr;
+StVKElementABCD * stVKPrecomputedIntegrals = nullptr;
+MassSpringSystem * massSpringSystem = nullptr;
+StVKFEM * stvkFEM = nullptr;
+
+static StencilForceModel *stencilForceModel = nullptr;
+static ForceModelAssembler *fmAsm = nullptr;
+
+static int numTestThreads = 1;
+static bool useFivePoint = true;
+static bool testStiffness = false;
+static bool hApproachZero = false;
+
+static Matrix<double> * animation = nullptr;
+static int numFrames = 0;
+
+static FiniteDifferenceTester * tester = nullptr;
+
+static void initConfig(const char * configFilename)
+{
+  ConfigFile config;
+
+  ADD_CONFIG(deformableObjectMethod);
+  ADD_CONFIG(invertibleMaterial);
+  ADD_CONFIG(volumetricMeshFilename);
+  ADD_CONFIG(clothMeshFilename);
+  ADD_CONFIG(surfaceDensity);
+  ADD_CONFIG(tensileStiffness);
+  ADD_CONFIG(shearStiffness);
+  ADD_CONFIG(bendStiffnessU);
+  ADD_CONFIG(bendStiffnessV);
+  ADD_CONFIG(computeStShForce);
+  ADD_CONFIG(computeBendForce);
+  ADD_CONFIG(computeStShStiffness);
+  ADD_CONFIG(computeBendStiffness);
+  ADD_CONFIG(useRestAngle);
+  ADD_CONFIG(animationFilename);
+  ADD_CONFIG(h);
+  ADD_CONFIG(numTestThreads);
+  ADD_CONFIG(numInternalForceThreads);
+  ADD_CONFIG(useFivePoint);
+  ADD_CONFIG(enableCompressionResistance);
+  ADD_CONFIG(compressionResistance);
+  ADD_CONFIG(inversionThreshold);
+  ADD_CONFIG(corotationalLinearFEM_warp);
+  ADD_CONFIG(testStiffness);
+  ADD_CONFIG(hApproachZero);
+  ADD_CONFIG(customMassSpringSystem);
+  ADD_CONFIG(massSpringSystemObjConfigFilename);
+  ADD_CONFIG(massSpringSystemTetMeshConfigFilename);
+  ADD_CONFIG(massSpringSystemCubicMeshConfigFilename);
+
+  if (config.parseOptions(configFilename) != 0)
+  {
+    cout << "Error parsing " << configFilename << endl;
+    exit(1);
+  }
+  config.printOptions();
+
+}
+
+static void initForceModel()
+{
+
+  if (deformableObjectMethod == "ClothBW")
+  {
+    assert(clothMeshFilename.size() > 0);
+    mesh = new ObjMesh(clothMeshFilename);
+    n = mesh->getNumVertices();
+    r = n * 3;
+    ClothBW::MaterialGroup group;
+    group.tensileStiffness = tensileStiffness;
+    group.shearStiffness = shearStiffness;
+    group.bendStiffnessU = bendStiffnessU;
+    group.bendStiffnessV = bendStiffnessV;
+    cloth = ClothBWFromObjMesh::GenerateClothBW(mesh, surfaceDensity, group, 0);
+    bool parameters[4];
+    parameters[0] = computeStShForce;
+    parameters[1] = computeBendForce;
+    parameters[2] = computeStShStiffness;
+    parameters[3] = computeBendStiffness;
+    cloth->SetComputationMode(parameters);
+    cloth->UseRestAnglesForBendingForces(useRestAngle);
+
+    stencilForceModel = new ClothBWStencilForceModel(cloth);
+  }
+  else if (deformableObjectMethod == "InvertibleFEM")
+  {
+    TetMesh * tetMesh = new TetMesh(volumetricMeshFilename.c_str());
+    n = tetMesh->getNumVertices();
+    r = 3 * n;
+    volumetricMesh = tetMesh;
+    if (invertibleMaterial == "StVK")
+      isotropicMaterial = new StVKIsotropicMaterial(tetMesh, enableCompressionResistance, compressionResistance);
+    else if (invertibleMaterial == "neoHookean")
+      isotropicMaterial = new NeoHookeanIsotropicMaterial(tetMesh, enableCompressionResistance, compressionResistance);
+    else if (invertibleMaterial == "MooneyRivlin")
+      isotropicMaterial = new MooneyRivlinIsotropicMaterial(tetMesh, enableCompressionResistance, compressionResistance);
+    assert(isotropicMaterial);
+
+    // create the invertible FEM deformable model
+    isotropicHyperelasticFEM = new IsotropicHyperelasticFEM(tetMesh, isotropicMaterial, inversionThreshold);
+    stencilForceModel = new IsotropicHyperelasticFEMStencilForceModel(isotropicHyperelasticFEM);
+  }
+  else if (deformableObjectMethod == "CLFEM")
+  {
+    TetMesh * tetMesh = new TetMesh(volumetricMeshFilename.c_str());
+    n = tetMesh->getNumVertices();
+    r = 3 * n;
+    volumetricMesh = tetMesh;
+    corotationalLinearFEM = new CorotationalLinearFEM(tetMesh);
+    stencilForceModel = new CorotationalLinearFEMStencilForceModel(corotationalLinearFEM);
+  }
+  else if (deformableObjectMethod == "StVK" || deformableObjectMethod == "LinearFEM")
+  {
+    volumetricMesh = VolumetricMeshLoader::load(volumetricMeshFilename.c_str());
+    assert(volumetricMesh);
+    n = volumetricMesh->getNumVertices();
+    r = 3 * n;
+
+    unsigned int loadingFlag = 0; // 0 = use low-memory version, 1 = use high-memory version
+    stVKPrecomputedIntegrals = StVKElementABCDLoader::load(volumetricMesh, loadingFlag);
+    assert(stVKPrecomputedIntegrals);
+
+    stvkFEM = new StVKFEM(volumetricMesh, stVKPrecomputedIntegrals);
+
+    if (deformableObjectMethod == "StVK")
+      stencilForceModel = new StVKStencilForceModel(stvkFEM);
+    else if (deformableObjectMethod == "LinearFEM")
+      stencilForceModel = new LinearFEMStencilForceModel(new StVKStencilForceModel(stvkFEM));
+  }
+  else if (deformableObjectMethod == "MassSpring")
+  {
+    if (massSpringSystemObjConfigFilename.size() > 0)
+    {
+      printf("Loading mass spring system from an obj file...\n");
+      MassSpringSystemFromObjMeshConfigFile massSpringSystemFromObjMeshConfigFile;
+      MassSpringSystemObjMeshConfiguration massSpringSystemObjMeshConfiguration;
+      if (massSpringSystemFromObjMeshConfigFile.GenerateMassSpringSystem(massSpringSystemObjConfigFilename.c_str(), &massSpringSystem, &massSpringSystemObjMeshConfiguration) != 0)
+      {
+        printf("Error initializing the mass spring system.\n");
+        exit(1);
+      }
+    }
+    else if (massSpringSystemTetMeshConfigFilename.size() > 0)
+    {
+      printf("Loading mass spring system from a tet mesh file...\n");
+      MassSpringSystemFromTetMeshConfigFile massSpringSystemFromTetMeshConfigFile;
+      MassSpringSystemTetMeshConfiguration massSpringSystemTetMeshConfiguration;
+      if (massSpringSystemFromTetMeshConfigFile.GenerateMassSpringSystem(massSpringSystemTetMeshConfigFilename.c_str(), &massSpringSystem, &massSpringSystemTetMeshConfiguration) != 0)
+      {
+        printf("Error initializing the mass spring system.\n");
+        exit(1);
+      }
+    }
+    else if (massSpringSystemCubicMeshConfigFilename.size() > 0)
+    {
+      printf("Loading mass spring system from a cubic mesh file...\n");
+      MassSpringSystemFromCubicMeshConfigFile massSpringSystemFromCubicMeshConfigFile;
+      MassSpringSystemCubicMeshConfiguration massSpringSystemCubicMeshConfiguration;
+      if (massSpringSystemFromCubicMeshConfigFile.GenerateMassSpringSystem(massSpringSystemCubicMeshConfigFilename.c_str(), &massSpringSystem, &massSpringSystemCubicMeshConfiguration) != 0)
+      {
+        printf("Error initializing the mass spring system.\n");
+        exit(1);
+      }
+    }
+    else if (customMassSpringSystem.size() > 0)
+    {
+      int numParticles;
+      double groupStiffness;
+      sscanf(customMassSpringSystem.c_str(), "chain,%d,%lf", &numParticles, &groupStiffness);
+      printf("Creating a chain mass-spring system with %d particles...\n", numParticles);
+
+      double * masses = (double*) malloc (sizeof(double) * numParticles);
+      for(int i=0; i<numParticles; i++)
+        masses[i] = 1.0;
+
+      double * restPositions = (double*) malloc (sizeof(double) * 3 * numParticles);
+      for(int i=0; i<numParticles; i++)
+      {
+        restPositions[3*i+0] = 0;
+        restPositions[3*i+1] = (numParticles == 1) ? 0.0 : 1.0 * i / (numParticles-1);
+        restPositions[3*i+2] = 0;
+      }
+      int * edges = (int*) malloc (sizeof(int) * 2 * (numParticles - 1));
+      for(int i=0; i<numParticles-1; i++)
+      {
+        edges[2*i+0] = i;
+        edges[2*i+1] = i+1;
+      }
+
+      int * edgeGroups = (int*) malloc (sizeof(int) * (numParticles - 1));
+      for(int i=0; i<numParticles-1; i++)
+        edgeGroups[i] = 0;
+      double groupDamping = 0;
+
+      massSpringSystem = new MassSpringSystem(numParticles, masses, restPositions, numParticles - 1, edges, edgeGroups, 1, &groupStiffness, &groupDamping, false);
+
+      free(edgeGroups);
+      free(edges);
+      free(restPositions);
+      free(masses);
+    }
+    assert(massSpringSystem);
+
+    n = massSpringSystem->GetNumParticles();
+    r = n * 3;
+
+    stencilForceModel = new MassSpringStencilForceModel(massSpringSystem);
+  }
+
+  assert(stencilForceModel != nullptr);
+  fmAsm = new ForceModelAssembler(stencilForceModel);
+
+  forceModel = fmAsm;
+}
+
+static void testAnimation(double & maxForceRelErr, double & maxStiffnessRelErr)
+{
+  maxStiffnessRelErr = 0;
+//  int maxRelErrFrame = 0;
+
+  for(int i = 0; i < numFrames; i++)
+  {
+    double * u = animation->GetData() + r * i;
+
+    double relForceErr = tester->testInternalForce(u);
+    if (maxForceRelErr < relForceErr)
+    {
+      maxForceRelErr = relForceErr;
+//      maxRelErrFrame = i;
+    }
+
+    if (hApproachZero == false)
+      cout << "Frame " << i << " force relerr: " << relForceErr * 100 << "%";
+
+    if (testStiffness)
+    {
+      double unsymErr = 0.0;
+      double relStiffErr = tester->testStiffnessMatrix(u, &unsymErr);
+      if (maxStiffnessRelErr < relStiffErr)
+        maxStiffnessRelErr = relStiffErr;
+
+      if (hApproachZero == false)
+        cout << ", stiffness relerr: " << relStiffErr * 100 << "% unsymErr: " << unsymErr * 100 << "%";
+    } // end if testStiffness
+    if (hApproachZero == false)
+      cout << endl;
+  }
+}
+
+
+int main(int argc, char ** argv)
+{
+  int numFixedArgs = 2;
+  if (argc < numFixedArgs)
+  {
+    cout << "Usage: " << argv[0] << ": <config filename>" << endl;
+    return 0;
+  }
+
+  initConfig(argv[1]);
+
+  initForceModel();
+
+  animation = new Matrix<double>(animationFilename.c_str());
+  assert(animation->Getm() == r);
+  numFrames = animation->Getn();
+  assert(numFrames > 0);
+  assert(numTestThreads >= 1);
+
+  vector<double> approachForceRelErr, approachStiffnessRelErr;
+  double maxForceRelErr = 0;
+  double maxStiffnessRelErr= 0;
+
+  FiniteDifferenceTester::Mode mode = (useFivePoint ? FiniteDifferenceTester::FIVE_POINT : FiniteDifferenceTester::TWO_POINT);
+  tester = new FiniteDifferenceTester(forceModel, h, mode, numTestThreads);
+
+  if (hApproachZero)
+    for(h = 1; h >= 1e-11; h = h / 10.)
+    {
+      tester->setTimestep(h);
+      maxForceRelErr = 0;
+      maxStiffnessRelErr = 0;
+      testAnimation(maxForceRelErr, maxStiffnessRelErr);
+
+      if (hApproachZero)
+      {
+        cout << "h = " << h << endl;
+        approachForceRelErr.push_back(maxForceRelErr);
+        approachStiffnessRelErr.push_back(maxStiffnessRelErr);
+      }
+      cout << "max force rel error " << maxForceRelErr * 100 << "% ";
+      if (testStiffness)
+      {
+        cout << "max stiffness rel error " << maxStiffnessRelErr * 100 << "%" << endl;
+      }
+    }
+  else
+    testAnimation(maxForceRelErr, maxStiffnessRelErr);
+
+  if (approachForceRelErr.size() > 0)
+  {
+    cout << "======================================" << endl;
+    for(size_t i = 0; i < approachForceRelErr.size(); i++)
+      cout << approachForceRelErr[i] * 100 << "%\t";
+    cout << endl;
+    for(size_t i = 0; i < approachStiffnessRelErr.size(); i++)
+      cout << approachStiffnessRelErr[i] * 100 << "%\t";
+    cout << endl;
+  }
+  return 0;
+}
+
diff --git a/utilities/finiteDifferenceTest/vectorHelper.cpp b/utilities/finiteDifferenceTest/vectorHelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99d06b285504f969ea81bbb2983cf92cfa41154e
--- /dev/null
+++ b/utilities/finiteDifferenceTest/vectorHelper.cpp
@@ -0,0 +1,75 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "finite difference tester" utility , Copyright (C) 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#include "vectorHelper.h"
+using namespace std;
+
+template<class T>
+bool saveToAscii(const std::vector<T> & v, std::ostream & out) 
+{
+  out << v.size() << " ";
+  if(out.fail()) 
+    return false;
+  for(size_t i = 0; i < v.size(); i++) 
+  {
+    out << v[i] << " ";
+    if(out.fail()) 
+      return false;
+  }
+  return true;
+}
+
+template<class T>
+bool loadFromAscii(std::vector<T> & v, std::istream & in) 
+{
+  size_t num = 0;
+  in >> num;
+  if(in.fail())  
+    return false;
+  v.reserve(v.size() + num);
+  for(size_t i = 0; i < num; i++) 
+  {
+    T k = T();
+    in >> k;
+    if(in.fail()) 
+      return false;
+    v.push_back(k);
+  }
+  return true;
+}
+
+template bool saveToAscii<int>(const std::vector<int> & v, std::ostream & out);
+template bool saveToAscii<double>(const std::vector<double> & v, std::ostream & out);
+
+template bool loadFromAscii<int>(std::vector<int> & v, std::istream & in);
+template bool loadFromAscii<double>(std::vector<double> & v, std::istream & in);
+
diff --git a/utilities/finiteDifferenceTest/vectorHelper.h b/utilities/finiteDifferenceTest/vectorHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..87da459ed5fe14386491a5fed2c1dd0340290919
--- /dev/null
+++ b/utilities/finiteDifferenceTest/vectorHelper.h
@@ -0,0 +1,121 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "finite difference tester" utility , Copyright (C) 2018 USC           *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code author: Yijing Li                                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+#ifndef VECTORHELPER_H
+#define VECTORHELPER_H
+
+#include <vector>
+#include <cassert>
+#include <cstring>
+#include <iostream>
+#include <cmath>
+#include <cassert>
+
+template<class T>
+inline void memset(std::vector<T> & v) 
+{
+  memset(v.data(), 0, sizeof(T) * v.size());
+}
+
+template<class T>
+inline void memcpy(std::vector<T> & v, const T * src) 
+{
+  assert(src);
+  memcpy(v.data(), src, sizeof(T) * v.size());
+}
+
+// free any memory allocated in the vector
+template<class T>
+inline void reset(std::vector<T> & v) 
+{
+  std::vector<T> tmp;
+  v.swap(tmp);
+}
+
+/////////////////////////////////////////////////////
+//                 Norm Computation                //
+/////////////////////////////////////////////////////
+
+// the Euclidean norm of the vector to the origin
+
+inline double squaredEuclideanNorm(const std::vector<double> & v) 
+{
+  double sum = 0.;
+  for(size_t i = 0; i < v.size(); i++)
+    sum += v[i] * v[i];
+  return sum;
+}
+
+inline double EuclideanNorm(const std::vector<double> & v) 
+{
+  return sqrt(squaredEuclideanNorm(v));
+}
+
+// the Euclidean distance between the two vector of the same size
+inline double squaredEuclideanDistance(const std::vector<double> & v1, const std::vector<double> & v2) 
+{
+  double sum = 0;
+  assert(v1.size() == v2.size());
+  for(size_t i = 0; i < v1.size(); i++) 
+  {
+    double value = (v1[i] - v2[i]);
+    sum += value * value;
+  }
+  return sum;
+}
+
+inline double EuclideanDistance(const std::vector<double> & v1, const std::vector<double> & v2) 
+{
+  return sqrt(squaredEuclideanDistance(v1,v2));
+}
+
+inline double EuclideanNorm(size_t r, const double * v) 
+{
+  double sum = 0;
+  for(size_t i = 0; i < r; i++)
+    sum += v[i] * v[i];
+  return sqrt(sum);
+}
+
+/////////////////////////////////////////////////////
+//         SAVE & LOAD IMPLEMENTATIONS             //
+/////////////////////////////////////////////////////
+// for use in saving/loading a class which includes vector as member var.
+// Same save/load file format as used in vega mayaPlugin
+template<class T>
+bool saveToAscii(const std::vector<T> & v, std::ostream & out);
+
+template<class T>
+bool loadFromAscii(std::vector<T> & v, std::istream & in);
+
+#endif
+
diff --git a/utilities/immersionMesher/immersionMesher.cpp b/utilities/immersionMesher/immersionMesher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0357b509741d3e85307d60b5b891c81524116601
--- /dev/null
+++ b/utilities/immersionMesher/immersionMesher.cpp
@@ -0,0 +1,240 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Immersion mesher" driver application,                                *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  This is an example driver for immersion meshing, i.e., generating a tet mesh with 
+  properly duplicated tets to mesh the space occupied by a self-intersecting input triangle mesh.
+  It calls routines from the immersionMeshing library.
+*/
+
+#include "containerHelper.h"
+#include "sceneObjectDeformable.h"
+#include "getopts.h"
+#include "initPredicates.h"
+#include "lighting.h"
+#include "listIO.h"
+#include "tetKey.h"
+#include "basicAlgorithms.h"
+#include "camera.h"
+#include "performanceCounter.h"
+#include "inputDevice.h"
+#include "commandLineParser.h"
+#include "saveScreenShot.h"
+#include "vec4d.h"
+#include "openGLHelper.h"
+#include "immersionMesher.h"
+#include "stringHelper.h"
+#ifdef WIN32
+  #include <windows.h>
+#endif
+#include <set>
+#include <stdlib.h>
+#include <stdio.h>
+#include <climits>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <memory>
+#include <math.h>
+#include <time.h>
+#include <GL/glui.h>
+
+//#define USE_GPERF_TOOLS
+#ifdef USE_GPERF_TOOLS
+  #include "gperftools/profiler.h"
+#endif
+
+using namespace std;
+
+#define PRINT_SWITCH(sw) printf(#sw " is %s\n", (sw) ? "ON" : "OFF")
+#define PRINT_VALUE(value) cout << #value << " is now " << value << endl
+
+const char * surfaceMeshFilename = nullptr;
+const char * tetMeshFilename = nullptr;
+
+static ObjMesh * uncutSurfaceMesh = nullptr;
+static ObjMesh * cutSurfaceMesh = nullptr;
+
+static ImmersionMesher im;
+static PerformanceCounter counter;
+
+static string finalTetMeshFilename = "finalTet.veg";
+static string finalBarycentricWeightsFilename = "finalTet.interp";
+
+static void runMesher()
+{
+  initPredicates(); // Initialize exact arithmetics.
+  srand(time(nullptr)); // Initialize random number generator.
+
+  // Load the input triangle mesh, and create the input "TriMeshGeo" object.
+  uncutSurfaceMesh = new ObjMesh(surfaceMeshFilename);
+  // Check if the input mesh has isolated vertices (there should be none).
+  int numVertices = uncutSurfaceMesh->getNumVertices();
+  uncutSurfaceMesh->removeIsolatedVertices(); 
+  if ((int)uncutSurfaceMesh->getNumVertices() != numVertices)
+  {
+    cout << "Error: input mesh has isolated vertices" << endl;
+    exit(1);
+  }
+  vector<Vec3i> uncutSurfaceTriangles;
+  uncutSurfaceMesh->exportTriangles(uncutSurfaceTriangles);
+  TriMeshGeo triMesh(uncutSurfaceMesh->getNumVertices(), &uncutSurfaceMesh->getPosition(0), move(uncutSurfaceTriangles));
+
+  // Load the input tet mesh, and create the input "TetMeshGeo" object.
+  TetMeshGeo tetMesh;
+  {
+    assert(tetMeshFilename);
+    TetMesh inputTetMesh(tetMeshFilename);
+    int numVertices, numTets, numEleVtx;
+    double * vertices;
+    int * tets;
+    inputTetMesh.exportMeshGeometry(&numVertices, &vertices, &numTets, &numEleVtx, &tets);
+    tetMesh = TetMeshGeo(numVertices, vertices, numTets, tets);
+    free(vertices);
+    free(tets);
+  }
+
+  PerformanceCounter pc;
+
+  // Create the output datastructures.
+  vector<TetMeshGeo> tetMeshes; // output tet meshes (see the immersionMesher library)
+  vector<BarycentricCoordinates> weights; // output barycentric embedding weights (see the immersionMesher library)
+  //vector<vector<TriMeshGeo>> allCellMeshes; // uncomment this if you also want to export the cell geometry (& need to then pass it into "run")
+  //vector<vector<BarycentricCoordinates>> allCellWeights;
+  // Run the mesher.
+  im.run(triMesh, tetMesh, tetMeshes, weights);
+
+  // cout << "Performance information:" << endl;
+  // cout << im.getProfiler().toString() << endl;
+
+  // Output the results to disk.
+  for(int i = 0; i < sizei(tetMeshes); i++)
+  {
+    // Create filenames for the output meshes.
+    string graphIDStr = "." + to_string(i);
+    string finalTetFilename = finalTetMeshFilename;
+    if (sizei(tetMeshes) > 1)
+    {
+      if (iendWith(finalTetFilename, ".veg"))
+        finalTetFilename = finalTetFilename.substr(0, finalTetFilename.size()-4) + graphIDStr + ".veg";
+      else
+        finalTetFilename = finalTetFilename + graphIDStr;
+    }
+    string interpFilename = finalBarycentricWeightsFilename;
+    if (sizei(tetMeshes) > 1)
+    {
+      if (iendWith(interpFilename, ".interp"))
+        interpFilename = interpFilename.substr(0, interpFilename.size()-7) + graphIDStr + ".interp";
+      else
+        interpFilename = interpFilename + graphIDStr;
+    }
+    //string allCellFilename = "allCellMesh.obj";
+    //if (sizei(tetMeshes) > 1) 
+    //  allCellFilename = "allCellMesh" + graphIDStr + ".obj";
+    //string allInterpFilename = "allCellMesh.interp";
+    //if (sizei(tetMeshes) > 1) 
+    //  allInterpFilename = "allCellMesh" + graphIDStr + ".interp";
+
+    // Save the outputs to disk.
+    tetMeshes[i].save(finalTetFilename);
+    weights[i].saveInterpolationWeights(interpFilename);
+    //allCellMeshes[i].save(allCellFilename);
+    //allCellWeights[i].saveInterpolationWeights(allInterpFilename);
+  }
+
+  pc.StopCounter();
+  cout << "Total  mesher execution time: " << pc.GetElapsedTime() << "s." << endl;
+
+  return;
+}
+
+void exitHandler()
+{
+  delete uncutSurfaceMesh;
+  delete cutSurfaceMesh;
+}
+
+int main (int argc, char ** argv)
+{
+  int numArgs = 3;
+  if (argc < numArgs)
+  {
+    cout << "Usage: " << argv[0] << " <obj mesh> <tet mesh> [-o <output tet mesh>] [-S] [-v] [-w <output barycentric weights>]" << endl;
+    cout << "Optional: " << endl;
+    cout << "  -o: Specify output tet mesh filename. Default: finalTet.veg." << endl;
+    cout << "  -S: Use CSG instead of the default implementation of [Li2018]. CSG is slower and equally precise, so [Li2018] is preferred." << endl;
+    cout << "  -v: be verbose." << endl;
+    cout << "  -w: Specify output filename to store the weights to embed the input obj mesh into the output tet mesh. Default: finalTet.interp." << endl;
+    return 0;
+  }
+
+  // Parse command-line arguments.
+
+  surfaceMeshFilename = argv[1];
+  tetMeshFilename = argv[2];
+  
+  CommandLineParser parser;
+  bool verbose = false;
+  bool useCSGForVirtualTets = false;
+
+  parser.addOption("v", verbose);
+  parser.addOption("S", useCSGForVirtualTets);
+  parser.addOption("o", finalTetMeshFilename);
+  parser.addOption("w", finalBarycentricWeightsFilename);
+
+  int ret = parser.parse(argc, argv, numArgs);
+  if (ret != argc)
+  {
+    cout << "Error parsing option " << argv[ret] << endl;
+    return 1;
+  }
+  im.setVerbose(verbose);
+  im.useCSGForVirtualTets(useCSGForVirtualTets);
+
+  cout << "Use CSG for virtual tets method ? " << useCSGForVirtualTets << endl;
+  cout << "Verbose ? " << verbose << endl;
+
+  #ifdef USE_GPERF_TOOLS
+    ProfilerStart("out.prof");
+  #endif
+    // execute the actual meshing operation, and save the results
+    runMesher();
+  #ifdef USE_GPERF_TOOLS
+    ProfilerStop();
+  #endif
+
+  atexit(exitHandler);
+  return(0);
+}
+
diff --git a/src/util/interactiveDeformableSimulator/initGraphics.cpp b/utilities/interactiveDeformableSimulator/initGraphics.cpp
old mode 100755
new mode 100644
similarity index 88%
rename from src/util/interactiveDeformableSimulator/initGraphics.cpp
rename to utilities/interactiveDeformableSimulator/initGraphics.cpp
index 331b96cbd12ba9b19cf25bf56b1bea538ef7399a..b29252514476a407aeb3b7db8025e492ec76fe0f
--- a/src/util/interactiveDeformableSimulator/initGraphics.cpp
+++ b/utilities/interactiveDeformableSimulator/initGraphics.cpp
@@ -1,21 +1,25 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Interactive deformable object simulator" driver application,         *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin, Daniel Schroeder          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -28,17 +32,17 @@
  *                                                                       *
  *************************************************************************/
 
+#include "initGraphics.h"
+
+#include <math.h>
+
 #ifndef M_PI
   #define M_PI 3.141592653589793238462643
 #endif
 
-#include <math.h>
-#include "initGraphics.h"
-
 extern void displayFunction(void);
 extern void idleFunction(void);
 extern void reshape(int,int);
-extern void handleMenu(int);
 extern void keyboardFunction(unsigned char key, int x, int y);
 extern void specialFunction(int key, int x, int y);
 extern void mouseButtonActivityFunction(int button, int state, int x, int y);
@@ -78,8 +82,6 @@ void initCamera(double cameraRadius, double cameraLongitude, double cameraLattit
 
   double upPos[3] = {0,1,0};
   *camera = new SphericalCamera(cameraRadius, 1.0 * cameraLongitude / 360 * (2*M_PI), 1.0 * cameraLattitude / 360 * (2*M_PI), focusPos,  upPos, 0.05, camera2WorldScalingFactor); 
-
-  (*camera)->SetOrigin(focusPos); 
 }
 
 void initGraphics(int windowWidth, int windowHeight)
diff --git a/src/util/interactiveDeformableSimulator/initGraphics.h b/utilities/interactiveDeformableSimulator/initGraphics.h
old mode 100755
new mode 100644
similarity index 79%
rename from src/util/interactiveDeformableSimulator/initGraphics.h
rename to utilities/interactiveDeformableSimulator/initGraphics.h
index 7be0eb72c02e27aea279aff87e2a1f3919a882eb..931a797b83af8146c9b9bfb6e47006a159f7e69e
--- a/src/util/interactiveDeformableSimulator/initGraphics.h
+++ b/utilities/interactiveDeformableSimulator/initGraphics.h
@@ -1,21 +1,25 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Interactive deformable object simulator" driver application,         *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin, Daniel Schroeder          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This library is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -31,7 +35,7 @@
 #ifndef _INITGRAPHICS_H_
 #define _INITGRAPHICS_H_
 
-#ifdef WIN32
+#if defined(WIN32) || defined(_WIN32)
   #include <windows.h>
 #endif
 
diff --git a/src/util/interactiveDeformableSimulator/interactiveDeformableSimulator.cpp b/utilities/interactiveDeformableSimulator/interactiveDeformableSimulator.cpp
old mode 100755
new mode 100644
similarity index 77%
rename from src/util/interactiveDeformableSimulator/interactiveDeformableSimulator.cpp
rename to utilities/interactiveDeformableSimulator/interactiveDeformableSimulator.cpp
index a408f5fc4f517f0bdd909da97d56763f752c1495..21fd684d250a108df31f8f28973e28150c5d14fa
--- a/src/util/interactiveDeformableSimulator/interactiveDeformableSimulator.cpp
+++ b/utilities/interactiveDeformableSimulator/interactiveDeformableSimulator.cpp
@@ -1,21 +1,25 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Interactive deformable object simulator" driver application,         *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic, Fun Shing Sin, Daniel Schroeder          *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -48,7 +52,7 @@ Supported materials:
 - rotated linear (corotational linear FEM)
 - Saint-Venant Kirchoff
 - invertible Saint-Venant Kirchoff
-- invertible neo-Hookean
+- invertible neo-Hookean 
 - invertible Mooney-Rivlin
 - mass-spring system
 
@@ -63,7 +67,7 @@ Supported materials:
 #include <float.h>
 using namespace std;
 
-#ifdef WIN32
+#if defined(WIN32) || defined(_WIN32)
   #include <windows.h>
 #endif
 
@@ -75,48 +79,57 @@ using namespace std;
 #include "initGraphics.h"
 #include "sceneObjectDeformable.h"
 #include "performanceCounter.h"
+
+#include "volumetricMeshLoader.h"
 #include "tetMesh.h"
+
+#include "StVKElementABCDLoader.h"
 #include "StVKCubeABCD.h"
 #include "StVKTetABCD.h"
 #include "StVKTetHighMemoryABCD.h"
+#include "StVKFEM.h"
+#include "StVKStencilForceModel.h"
+
 #include "implicitBackwardEulerSparse.h"
 #include "eulerSparse.h"
 #include "centralDifferencesSparse.h"
-#include "StVKInternalForces.h"
-#include "StVKStiffnessMatrix.h"
-#include "StVKInternalForcesMT.h"
-#include "StVKStiffnessMatrixMT.h"
-#include "StVKForceModel.h"
-#include "massSpringSystemForceModel.h"
+
 #include "corotationalLinearFEM.h"
-#include "corotationalLinearFEMMT.h"
-#include "corotationalLinearFEMForceModel.h"
-#include "linearFEMForceModel.h"
+#include "corotationalLinearFEMStencilForceModel.h"
+
 #include "isotropicHyperelasticFEM.h"
-#include "isotropicHyperelasticFEMMT.h"
-#include "isotropicHyperelasticFEMForceModel.h"
 #include "isotropicMaterial.h"
 #include "StVKIsotropicMaterial.h"
 #include "neoHookeanIsotropicMaterial.h"
 #include "MooneyRivlinIsotropicMaterial.h"
+#include "isotropicHyperelasticFEMStencilForceModel.h"
+
 #include "getIntegratorSolver.h"
-#include "volumetricMeshLoader.h"
-#include "StVKElementABCDLoader.h"
 #include "generateMeshGraph.h"
+#include "generateMassMatrix.h"
+
 #include "massSpringSystem.h"
-#include "massSpringSystemMT.h"
 #include "massSpringSystemFromObjMeshConfigFile.h"
 #include "massSpringSystemFromTetMeshConfigFile.h"
 #include "massSpringSystemFromCubicMeshConfigFile.h"
+#include "massSpringStencilForceModel.h"
+
+#include "linearFEMStencilForceModel.h"
+
+#include "forceModelAssembler.h"
+
 #include "graph.h"
 #include "renderSprings.h"
 #include "configFile.h"
-#include "GL/glui.h"
+
 #include "lighting.h"
-#include "loadList.h"
+#include "listIO.h"
 #include "matrixIO.h"
+#include "averagingBuffer.h"
 
-// graphics
+#include "GL/glui.h"
+
+// graphics 
 char windowTitleBase[4096] = "Real-time sim";
 void displayFunction(void);
 int windowID;
@@ -127,7 +140,7 @@ double zFar=10.0;
 double cameraRadius;
 double focusPositionX, focusPositionY, focusPositionZ;
 double cameraLongitude, cameraLattitude;
-SphericalCamera * camera = NULL;
+SphericalCamera * camera = nullptr;
 int g_iLeftMouseButton=0, g_iMiddleMouseButton=0, g_iRightMouseButton=0;
 int g_vMousePos[2] = {0,0};
 int shiftPressed=0;
@@ -145,10 +158,10 @@ int renderVertices = 0;
 int lockScene=0;
 int pauseSimulation=0;
 int singleStepMode=0;
-Lighting * lighting = NULL;
-SceneObjectDeformable * deformableObjectRenderingMesh = NULL;
-SceneObjectDeformable * secondaryDeformableObjectRenderingMesh = NULL;
-SceneObject * extraSceneGeometry = NULL;
+Lighting * lighting = nullptr;
+SceneObjectDeformable * deformableObjectRenderingMesh = nullptr;
+SceneObjectDeformable * secondaryDeformableObjectRenderingMesh = nullptr;
+SceneObject * extraSceneGeometry = nullptr;
 char groundPlaneString[128];
 double groundPlaneHeight;
 double groundPlaneLightHeight = 10.0;
@@ -164,7 +177,6 @@ char volumetricMeshFilename[4096];
 char customMassSpringSystem[4096];
 char deformableObjectMethod[4096];
 char fixedVerticesFilename[4096];
-char massMatrixFilename[4096];
 char massSpringSystemObjConfigFilename[4096];
 char massSpringSystemTetMeshConfigFilename[4096];
 char massSpringSystemCubicMeshConfigFilename[4096];
@@ -179,13 +191,16 @@ char implicitSolverMethod[4096];
 char solverMethod[4096];
 char extraSceneGeometryFilename[4096];
 char lightingConfigFilename[4096];
-float dampingMassCoef;
-float dampingStiffnessCoef;
-float dampingLaplacianCoef = 0.0;
-float deformableObjectCompliance = 1.0;
-float baseFrequency = 1.0;
-int maxIterations;
-double epsilon;
+float dampingMassCoef; // Rayleigh mass damping
+float dampingStiffnessCoef; // Rayleigh stiffness damping
+float dampingLaplacianCoef = 0.0; // Laplacian damping (rarely used)
+float deformableObjectCompliance = 1.0; // scales all user forces by the provided factor
+
+// adjusts the stiffness of the object to cause all frequencies scale by the provided factor:
+// keep it to 1.0 (except for experts)
+float frequencyScaling = 1.0; 
+int maxIterations; // for implicit integration
+double epsilon; // for implicit integration
 char backgroundColorString[4096] = "255 255 255";
 int numInternalForceThreads;
 int numSolverThreads;
@@ -199,20 +214,12 @@ int use1DNewmarkParameterFamily = 1;
 int substepsPerTimeStep = 1;
 double inversionThreshold;
 double fps = 0.0;
-const int fpsBufferSize = 5;
-int fpsHead = 0;
-double fpsBuffer[fpsBufferSize];
+AveragingBuffer fpsBuffer(5);
 double cpuLoad = 0;
 double forceAssemblyTime = 0.0;
-double forceAssemblyLocalTime = 0.0;
-const int forceAssemblyBufferSize = 50;
-int forceAssemblyHead = 0;
-double forceAssemblyBuffer[forceAssemblyBufferSize];
+AveragingBuffer forceAssemblyBuffer(50);
 double systemSolveTime = 0.0;
-double systemSolveLocalTime = 0.0;
-const int systemSolveBufferSize = 50;
-int systemSolveHead = 0;
-double systemSolveBuffer[systemSolveBufferSize];
+AveragingBuffer systemSolveBuffer(50);
 int enableTextures = 0;
 int staticSolver = 0;
 int graphicFrame = 0;
@@ -229,53 +236,62 @@ int subTimestepCounter = 0;
 int numFixedVertices;
 int * fixedVertices;
 int numForceLoads = 0;
-double * forceLoads = NULL;
-IntegratorBase * integratorBase = NULL;
-ImplicitNewmarkSparse * implicitNewmarkSparse = NULL;
-IntegratorBaseSparse * integratorBaseSparse = NULL;
-LinearSolver * linearSolver= NULL;
-ForceModel * forceModel = NULL;
-StVKInternalForces * stVKInternalForces = NULL;
-StVKStiffnessMatrix * stVKStiffnessMatrix = NULL;
-StVKForceModel * stVKForceModel = NULL;
-MassSpringSystemForceModel * massSpringSystemForceModel = NULL;
-CorotationalLinearFEMForceModel * corotationalLinearFEMForceModel = NULL;
+double * forceLoads = nullptr;
+IntegratorBase * integratorBase = nullptr;
+ImplicitNewmarkSparse * implicitNewmarkSparse = nullptr;
+IntegratorBaseSparse * integratorBaseSparse = nullptr;
+ForceModel * forceModel = nullptr;
 int enableCompressionResistance = 1;
 double compressionResistance = 500;
 int centralDifferencesTangentialDampingUpdateMode = 1;
-int positiveDefinite = 0;
 int addGravity=0;
 double g=9.81;
-VolumetricMesh * volumetricMesh = NULL;
-TetMesh * tetMesh = NULL;
-Graph * meshGraph = NULL;
+
+VolumetricMesh * volumetricMesh = nullptr;
+TetMesh * tetMesh = nullptr;
+Graph * meshGraph = nullptr;
+
+StVKFEM * stVKFEM = nullptr;
+
 enum massSpringSystemSourceType { OBJ, TETMESH, CUBICMESH, CHAIN, NONE } massSpringSystemSource = NONE;
 enum deformableObjectType { STVK, COROTLINFEM, LINFEM, MASSSPRING, INVERTIBLEFEM, UNSPECIFIED } deformableObject = UNSPECIFIED;
 enum invertibleMaterialType { INV_STVK, INV_NEOHOOKEAN, INV_MOONEYRIVLIN, INV_NONE } invertibleMaterial = INV_NONE;
 enum solverType { IMPLICITNEWMARK, IMPLICITBACKWARDEULER, EULER, SYMPLECTICEULER, CENTRALDIFFERENCES, UNKNOWN } solver = UNKNOWN;
-MassSpringSystem * massSpringSystem = NULL;
-RenderSprings * renderMassSprings = NULL;
-SparseMatrix * massMatrix = NULL;
-SparseMatrix * LaplacianDampingMatrix = NULL;
+
+StencilForceModel * stencilForceModel = nullptr;
+ForceModelAssembler *forceModelAssembler = nullptr;
+CorotationalLinearFEMStencilForceModel * corotationalLinearFEMStencilForceModel = nullptr;
+IsotropicHyperelasticFEMStencilForceModel * isotropicHyperelasticFEMStencilForceModel = nullptr;
+MassSpringStencilForceModel * massSpringStencilForceModel = nullptr;
+StVKStencilForceModel * stVKStencilForceModel = nullptr;
+LinearFEMStencilForceModel * linearFEMStencilForceModel = nullptr;
+
+MassSpringSystem * massSpringSystem = nullptr;
+RenderSprings * renderMassSprings = nullptr;
+SparseMatrix * massMatrix = nullptr;
+SparseMatrix * LaplacianDampingMatrix = nullptr;
+
 int n;
-double * u = NULL;
-double * uvel = NULL;
-double * uaccel = NULL;
-double * f_ext = NULL;
-double * f_extBase = NULL;
-double * uSecondary = NULL;
-double * uInitial = NULL;
-double * velInitial = NULL;
+double * u = nullptr;
+double * uvel = nullptr;
+double * uaccel = nullptr;
+double * f_ext = nullptr;
+double * f_extBase = nullptr;
+double * uSecondary = nullptr;
+double * uInitial = nullptr;
+double * velInitial = nullptr;
+
 // interpolation to secondary mesh
 int secondaryDeformableObjectRenderingMesh_interpolation_numElementVertices;
-int * secondaryDeformableObjectRenderingMesh_interpolation_vertices = NULL;
-double * secondaryDeformableObjectRenderingMesh_interpolation_weights = NULL;
+int * secondaryDeformableObjectRenderingMesh_interpolation_vertices = nullptr;
+double * secondaryDeformableObjectRenderingMesh_interpolation_weights = nullptr;
 
 // glui
 GLUI * glui;
 GLUI_Spinner * timeStep_spinner;
 GLUI_StaticText * systemSolveStaticText;
 GLUI_StaticText * forceAssemblyStaticText;
+
 void callAllUICallBacks();
 void Sync_GLUI();
 void stopDeformations_buttonCallBack(int code);
@@ -284,9 +300,9 @@ void stopDeformations_buttonCallBack(int code);
 void print_bitmap_string(float x, float y, float z, void * font, char * s)
 {
   glRasterPos3f(x,y,z);
-  if (s && strlen(s))
+  if (s && strlen(s)) 
   {
-    while (*s)
+    while (*s) 
     {
       glutBitmapCharacter(font, *s);
       s++;
@@ -300,10 +316,10 @@ void RenderGroundPlane(double groundPlaneHeight, double rPlane, double gPlane, d
   glEnable(GL_POLYGON_OFFSET_FILL);
   glPolygonOffset(1.0,1.0);
 
-  float planeAmbient[4] = { ambientFactor*rPlane, ambientFactor*gPlane, ambientFactor*bPlane, 1.0};
-  float planeDiffuse[4] = { diffuseFactor*rPlane, diffuseFactor*gPlane, diffuseFactor*bPlane, 1.0};
-  float planeSpecular[4] = { specularFactor*rPlane, specularFactor*gPlane, specularFactor*bPlane, 1.0};
-  float planeShininess = shininess;
+  float planeAmbient[4] = { (float)(ambientFactor*rPlane), (float)(ambientFactor*gPlane), (float)(ambientFactor*bPlane), 1.0};
+  float planeDiffuse[4] = { (float)(diffuseFactor*rPlane), (float)(diffuseFactor*gPlane), (float)(diffuseFactor*bPlane), 1.0};
+  float planeSpecular[4] = { (float)(specularFactor*rPlane), (float)(specularFactor*gPlane), (float)(specularFactor*bPlane), 1.0};
+  float planeShininess = shininess; 
   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, planeAmbient);
   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, planeDiffuse);
   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, planeSpecular);
@@ -331,16 +347,16 @@ void displayFunction(void)
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
   // setup model transformations
-  glMatrixMode(GL_MODELVIEW);
+  glMatrixMode(GL_MODELVIEW); 
   glLoadIdentity();
 
   camera->Look();
 
-  // set OpenGL lighting
+  // set OpenGL lighting 
   deformableObjectRenderingMesh->SetLighting(lighting);
 
   glEnable(GL_LIGHTING);
-
+  glPolygonOffset(0.0,0.0);
   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
 
   glStencilFunc(GL_ALWAYS, 0, ~(0u));
@@ -387,11 +403,11 @@ void displayFunction(void)
 
   // render any extra scene geometry
   glStencilFunc(GL_ALWAYS, 0, ~(0u));
-  if (extraSceneGeometry != NULL)
+  if (extraSceneGeometry != nullptr)
     extraSceneGeometry->Render();
 
   double ground[4] = {0,1,0,-groundPlaneHeight-0.01};
-  double light[4] = {0,groundPlaneLightHeight,0,1};
+  double light[4] = {0,groundPlaneLightHeight,0,1}; 
 
   glDisable(GL_TEXTURE_2D);
 
@@ -402,7 +418,7 @@ void displayFunction(void)
     glDisable(GL_LIGHTING);
     deformableObjectRenderingMesh->RenderShadow(ground, light);
 
-    if (extraSceneGeometry != NULL)
+    if (extraSceneGeometry != nullptr)
       extraSceneGeometry->RenderShadow(ground, light);
     glEnable(GL_LIGHTING);
     glCallList(displayListGround);
@@ -451,7 +467,7 @@ void displayFunction(void)
       glColor3f(1,0,0);
       double fixedVertexPos[3];
       deformableObjectRenderingMesh->GetSingleVertexRestPosition(fixedVertices[i],
-         &fixedVertexPos[0], &fixedVertexPos[1], &fixedVertexPos[2]);
+          &fixedVertexPos[0], &fixedVertexPos[1], &fixedVertexPos[2]);
 
       glEnable(GL_POLYGON_OFFSET_POINT);
       glPolygonOffset(-1.0,-1.0);
@@ -464,21 +480,21 @@ void displayFunction(void)
   }
 
   // render springs for mass-spring systems
-  if ((massSpringSystem != NULL) & renderSprings)
-  {
+  if ((massSpringSystem != nullptr) & renderSprings)
+  { 
     printf("rendering springs\n");
     glLineWidth(2.0);
     renderMassSprings->Render(massSpringSystem, u);
     glLineWidth(1.0);
-  }
+  } 
 
   // ==== bitmap routines below here
-  glMatrixMode(GL_MODELVIEW);
-  glPushMatrix();
-  glLoadIdentity();
-  glMatrixMode(GL_PROJECTION);
-  glPushMatrix();
-  glLoadIdentity();
+  glMatrixMode(GL_MODELVIEW); 
+  glPushMatrix(); 
+  glLoadIdentity(); 
+  glMatrixMode(GL_PROJECTION); 
+  glPushMatrix(); 
+  glLoadIdentity(); 
 
   // print info in case of integrator blow-up
   char s[4096];
@@ -500,9 +516,9 @@ void displayFunction(void)
     print_bitmap_string(X1,Y1,-1,GLUT_BITMAP_9_BY_15 ,s);
   }
 
-  glPopMatrix();
-  glMatrixMode(GL_MODELVIEW);
-  glPopMatrix();
+  glPopMatrix(); 
+  glMatrixMode(GL_MODELVIEW); 
+  glPopMatrix(); 
 
   glutSwapBuffers();
 }
@@ -520,7 +536,7 @@ void idleFunction(void)
   if ((!lockScene) && (!pauseSimulation) && (singleStepMode <= 1))
   {
     // determine force in case user is pulling on a vertex
-    if (g_iLeftMouseButton)
+    if (g_iLeftMouseButton) 
     {
       if (pulledVertex != -1)
       {
@@ -530,7 +546,7 @@ void idleFunction(void)
         double externalForce[3];
 
         camera->CameraVector2WorldVector_OrientationOnly3D(
-           forceX, forceY, 0, externalForce);
+            forceX, forceY, 0, externalForce);
 
         for(int j=0; j<3; j++)
           externalForce[j] *= deformableObjectCompliance;
@@ -601,25 +617,21 @@ void idleFunction(void)
 
     PerformanceCounter totalDynamicsCounter;
 
-    // timestep the dynamics
+    // timestep the dynamics 
     for(int i=0; i<substepsPerTimeStep; i++)
     {
       int code = integratorBase->DoTimestep();
-      printf("."); fflush(NULL);
+      printf("."); fflush(nullptr);
 
-      forceAssemblyLocalTime = integratorBaseSparse->GetForceAssemblyTime();
-      systemSolveLocalTime = integratorBaseSparse->GetSystemSolveTime();
+      double forceAssemblyLocalTime = integratorBaseSparse->GetForceAssemblyTime();
+      double systemSolveLocalTime = integratorBaseSparse->GetSystemSolveTime();
       //printf("Force assembly: %G\nSystem solve: %G\n", forceAssemblyTime, systemSolveTime);
 
-      // average forceAssemblyTime over last "forceAssemblyBufferSize" samples
-      forceAssemblyTime += 1.0 / forceAssemblyBufferSize * (forceAssemblyLocalTime - forceAssemblyBuffer[forceAssemblyHead]);
-      forceAssemblyBuffer[forceAssemblyHead] = forceAssemblyLocalTime;
-      forceAssemblyHead = (forceAssemblyHead + 1) % forceAssemblyBufferSize;
+      forceAssemblyBuffer.addValue(forceAssemblyLocalTime);
+      forceAssemblyTime = forceAssemblyBuffer.getAverage();
 
-      // average systemSolveTime over last "systemSolveBufferSize" samples
-      systemSolveTime += 1.0 / systemSolveBufferSize * (systemSolveLocalTime - systemSolveBuffer[systemSolveHead]);
-      systemSolveBuffer[systemSolveHead] = systemSolveLocalTime;
-      systemSolveHead = (systemSolveHead + 1) % systemSolveBufferSize;
+      systemSolveBuffer.addValue(systemSolveLocalTime);
+      systemSolveTime = systemSolveBuffer.getAverage();
 
       if (code != 0)
       {
@@ -661,7 +673,7 @@ void idleFunction(void)
     if (singleStepMode == 1)
       singleStepMode = 2;
 
-    printf("F"); fflush(NULL);
+    printf("F"); fflush(nullptr);
     graphicFrame++;
 
     if (lockAt30Hz)
@@ -679,7 +691,7 @@ void idleFunction(void)
   deformableObjectRenderingMesh->SetVertexDeformations(u);
 
   // interpolate deformations from volumetric mesh to rendering triangle mesh
-  if (secondaryDeformableObjectRenderingMesh != NULL)
+  if (secondaryDeformableObjectRenderingMesh != nullptr)
   {
     PerformanceCounter interpolationCounter;
     VolumetricMesh::interpolate(u, uSecondary, secondaryDeformableObjectRenderingMesh->Getn(), secondaryDeformableObjectRenderingMesh_interpolation_numElementVertices, secondaryDeformableObjectRenderingMesh_interpolation_vertices, secondaryDeformableObjectRenderingMesh_interpolation_weights);
@@ -692,8 +704,8 @@ void idleFunction(void)
   {
     // recompute normals
     PerformanceCounter normalsCounter;
-    deformableObjectRenderingMesh->BuildNormals();
-    if (secondaryDeformableObjectRenderingMesh != NULL)
+    deformableObjectRenderingMesh->BuildNormals(); 
+    if (secondaryDeformableObjectRenderingMesh != nullptr)
       secondaryDeformableObjectRenderingMesh->BuildNormals();
     normalsCounter.StopCounter();
     //printf("Recompute normals: %G\n", normalsCounter.GetElapsedTime());
@@ -713,15 +725,12 @@ void idleFunction(void)
   {
     titleBarCounter.StartCounter();
     double fpsLocal = graphicFrame / elapsedTime;
-
-    // average fps over last "fpsBufferSize" samples
-    fps += 1.0 / fpsBufferSize * (fpsLocal - fpsBuffer[fpsHead]);
-    fpsBuffer[fpsHead] = fpsLocal;
-    fpsHead = (fpsHead + 1) % fpsBufferSize;
+    fpsBuffer.addValue(fpsLocal);
+    fps = fpsBuffer.getAverage();
 
     //printf("Frames per second: %G\n", fps);
 
-    // update window title
+    // update window title 
     char windowTitle[4096] = "unknown defo model";
 
     if (deformableObject == STVK)
@@ -775,7 +784,7 @@ void idleFunction(void)
 
   cpuLoadCounter.StopCounter();
   double cpuTimePerGraphicsFrame = cpuLoadCounter.GetElapsedTime();
-  cpuLoad = cpuTimePerGraphicsFrame * fps;
+  cpuLoad = cpuTimePerGraphicsFrame * fps; 
 
   glutPostRedisplay();
 }
@@ -783,8 +792,6 @@ void idleFunction(void)
 // reacts to pressed keys
 void keyboardFunction(unsigned char key, int x, int y)
 {
-  double cameraX,cameraY,cameraZ;
-
   switch (key)
   {
     case 27:
@@ -800,12 +807,18 @@ void keyboardFunction(unsigned char key, int x, int y)
       break;
 
     case 'i':
+    {
+      double focusPos[3];
+      camera->GetFocusPosition(focusPos);
+      double cameraX,cameraY,cameraZ;
       camera->GetAbsWorldPosition(cameraX,cameraY,cameraZ);
       printf("Camera is positioned at: %G %G %G\n",cameraX,cameraY,cameraZ);
-      printf("Camera radius is: %G \n",camera->GetRadius());
-      printf("Camera Phi is: %G \n",180.0/M_PI*camera->GetPhi());
-      printf("Camera Theta is: %G \n",180.0/M_PI*camera->GetTheta());
-      break;
+      printf("Camera radius is: %G\n",camera->GetRadius());
+      printf("Camera Phi is: %G\n",180.0/M_PI*camera->GetPhi());
+      printf("Camera Theta is: %G\n",180.0/M_PI*camera->GetTheta());
+      printf("Camera focus is: %G %G %G\n", focusPos[0], focusPos[1], focusPos[2]);
+    }
+    break;
 
     case '\\':
       camera->Reset();
@@ -846,8 +859,9 @@ void keyboardFunction(unsigned char key, int x, int y)
 
     case '1':
       corotationalLinearFEM_warp = (corotationalLinearFEM_warp + 1) % (max_corotationalLinearFEM_warp + 1);
-      if(corotationalLinearFEMForceModel != NULL)
-        corotationalLinearFEMForceModel->SetWarp(corotationalLinearFEM_warp);
+      if(corotationalLinearFEMStencilForceModel != nullptr)
+        corotationalLinearFEMStencilForceModel->SetWarp(corotationalLinearFEM_warp);
+
       printf("CorotationalLinearFEM warp is now: %d\n", corotationalLinearFEM_warp);
       break;
 
@@ -857,7 +871,7 @@ void keyboardFunction(unsigned char key, int x, int y)
 
     case 'E':
       renderSecondaryDeformableObject = !renderSecondaryDeformableObject;
-      if (secondaryDeformableObjectRenderingMesh == NULL)
+      if (secondaryDeformableObjectRenderingMesh == nullptr)
         renderSecondaryDeformableObject = 0;
       break;
 
@@ -872,9 +886,9 @@ void keyboardFunction(unsigned char key, int x, int y)
 
     case 'P':
       if (singleStepMode > 0)
-        singleStepMode = 0;
+	singleStepMode = 0;
       else
-        singleStepMode = 1;
+	singleStepMode = 1;
       break;
 
     case 'u':
@@ -909,19 +923,19 @@ void specialFunction(int key, int x, int y)
   switch (key)
   {
     case GLUT_KEY_LEFT:
-      camera->MoveFocusRight(+0.1 * camera->GetRadius());
+      camera->MoveFocusRight(+0.1 * fabs(camera->GetRadius()));
     break;
 
     case GLUT_KEY_RIGHT:
-      camera->MoveFocusRight(-0.1 * camera->GetRadius());
+      camera->MoveFocusRight(-0.1 * fabs(camera->GetRadius()));
     break;
 
     case GLUT_KEY_DOWN:
-      camera->MoveFocusUp(+0.1 * camera->GetRadius());
+      camera->MoveFocusUp(+0.1 * fabs(camera->GetRadius()));
     break;
 
     case GLUT_KEY_UP:
-      camera->MoveFocusUp(-0.1 * camera->GetRadius());
+      camera->MoveFocusUp(-0.1 * fabs(camera->GetRadius()));
     break;
 
     case GLUT_KEY_PAGE_UP:
@@ -951,10 +965,10 @@ void reshape(int x, int y)
   windowWidth = x;
   windowHeight = y;
 
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
+  glMatrixMode(GL_PROJECTION); 
+  glLoadIdentity(); 
 
-  // compute the window aspect ratio
+  // compute the window aspect ratio 
   gluPerspective(45.0f, 1.0 * windowWidth / windowHeight, zNear, zFar);
 
   glMatrixMode(GL_MODELVIEW);
@@ -970,7 +984,7 @@ void mouseMotionFunction(int x, int y)
   g_vMousePos[0] = x;
   g_vMousePos[1] = y;
 
-  if (g_iLeftMouseButton) // left mouse button
+  if (g_iLeftMouseButton) // left mouse button 
   {
   }
 
@@ -1085,8 +1099,8 @@ void initSimulation()
      1.0 / virtualToPhysicalPositionFactor,
      &zNear, &zFar, &camera);
 
-  volumetricMesh = NULL;
-  massSpringSystem = NULL;
+  volumetricMesh = nullptr;
+  massSpringSystem = nullptr;
 
   // set deformable material type
   if (strcmp(volumetricMeshFilename, "__none") != 0)
@@ -1110,7 +1124,7 @@ void initSimulation()
   else if (strncmp(customMassSpringSystem, "chain", 5) == 0)
     massSpringSystemSource = CHAIN;
 
-  if ((massSpringSystemSource == OBJ) || (massSpringSystemSource == TETMESH) || (massSpringSystemSource == CUBICMESH) || (massSpringSystemSource == CHAIN))
+  if ((massSpringSystemSource == OBJ) || (massSpringSystemSource == TETMESH) || (massSpringSystemSource == CUBICMESH) || (massSpringSystemSource == CHAIN)) 
     deformableObject = MASSSPRING;
 
   if (deformableObject == UNSPECIFIED)
@@ -1119,183 +1133,149 @@ void initSimulation()
     exit(1);
   }
 
-  // load mesh
+  // load volumetric mesh
   if ((deformableObject == STVK) || (deformableObject == COROTLINFEM) || (deformableObject == LINFEM) || (deformableObject == INVERTIBLEFEM))
   {
     printf("Loading volumetric mesh from file %s...\n", volumetricMeshFilename);
 
-    volumetricMesh = VolumetricMeshLoader::load(volumetricMeshFilename);
-    if (volumetricMesh == NULL)
+    VolumetricMesh::fileFormatType fileFormat = VolumetricMesh::ASCII;
+    int verbose = 0;
+    volumetricMesh = VolumetricMeshLoader::load(volumetricMeshFilename, fileFormat, verbose);
+    if (volumetricMesh == nullptr)
     {
       printf("Error: unable to load the volumetric mesh from %s.\n", volumetricMeshFilename);
       exit(1);
     }
 
     n = volumetricMesh->getNumVertices();
-    printf("Num vertices: %d. Num elements: %d\n",n, volumetricMesh->getNumElements());
+    printf("Num vertices: %d. Num elements: %d\n", n, volumetricMesh->getNumElements());
     meshGraph = GenerateMeshGraph::Generate(volumetricMesh);
 
-    // load mass matrix
-    if (strcmp(massMatrixFilename, "__none") == 0)
-    {
-      printf("Error: mass matrix for the StVK deformable model not specified (%s).\n", massMatrixFilename);
-      exit(1);
-    }
-
-    printf("Loading the mass matrix from file %s...\n", massMatrixFilename);
-    // get the mass matrix
-    SparseMatrixOutline * massMatrixOutline;
-    try
-    {
-      massMatrixOutline = new SparseMatrixOutline(massMatrixFilename, 3); // 3 is expansion flag to indicate this is a mass matrix; and does 3x3 identity block expansion
-    }
-    catch(int exceptionCode)
-    {
-      printf("Error loading mass matrix %s.\n", massMatrixFilename);
-      exit(1);
-    }
-
-    massMatrix = new SparseMatrix(massMatrixOutline);
-    delete(massMatrixOutline);
+    // create the mass matrix
+    int inflate3Dim = true; // necessary so that the returned matrix is 3n x 3n
+    GenerateMassMatrix::computeMassMatrix(volumetricMesh, &massMatrix, inflate3Dim);
 
-    if (deformableObject == STVK || deformableObject == LINFEM)  //LINFEM constructed from stVKInternalForces
+    // create the internal forces for STVK and linear FEM materials
+    if (deformableObject == STVK || deformableObject == LINFEM)  // LINFEM is constructed from stVKInternalForces
     {
-      unsigned int loadingFlag = 0; // 0 = use low-memory version, 1 = use high-memory version
+      unsigned int loadingFlag = 0; // 0 = use the low-memory version, 1 = use the high-memory version
       StVKElementABCD * precomputedIntegrals = StVKElementABCDLoader::load(volumetricMesh, loadingFlag);
-      if (precomputedIntegrals == NULL)
+      if (precomputedIntegrals == nullptr)
       {
         printf("Error: unable to load the StVK integrals.\n");
         exit(1);
       }
 
-      printf("Generating internal forces and stiffness matrix models...\n"); fflush(NULL);
-      if (numInternalForceThreads == 0)
-        stVKInternalForces = new StVKInternalForces(volumetricMesh, precomputedIntegrals, addGravity, g);
-      else
-        stVKInternalForces = new StVKInternalForcesMT(volumetricMesh, precomputedIntegrals, addGravity, g, numInternalForceThreads);
-
-      if (numInternalForceThreads == 0)
-        stVKStiffnessMatrix = new StVKStiffnessMatrix(stVKInternalForces);
-      else
-        stVKStiffnessMatrix = new StVKStiffnessMatrixMT(stVKInternalForces, numInternalForceThreads);
+      printf("Generating internal forces and stiffness matrix models...\n"); fflush(nullptr);
+      stVKFEM = new StVKFEM(volumetricMesh, precomputedIntegrals, addGravity, g);
     }
   }
 
-  // load mass spring system (if any)
+  // load mass spring system (if any) 
   if (deformableObject == MASSSPRING)
   {
     switch (massSpringSystemSource)
     {
-      case OBJ:
-        {
-          printf("Loading mass spring system from an obj file...\n");
-          MassSpringSystemFromObjMeshConfigFile massSpringSystemFromObjMeshConfigFile;
-          MassSpringSystemObjMeshConfiguration massSpringSystemObjMeshConfiguration;
-          if (massSpringSystemFromObjMeshConfigFile.GenerateMassSpringSystem(massSpringSystemObjConfigFilename, &massSpringSystem, &massSpringSystemObjMeshConfiguration) != 0)
-          {
-            printf("Error initializing the mass spring system.\n");
-            exit(1);
-          }
-          strcpy(renderingMeshFilename, massSpringSystemObjMeshConfiguration.massSpringMeshFilename);
-        }
-        break;
+    case OBJ:
+    {
+      printf("Loading mass spring system from an obj file...\n");
+      MassSpringSystemFromObjMeshConfigFile massSpringSystemFromObjMeshConfigFile;
+      MassSpringSystemObjMeshConfiguration massSpringSystemObjMeshConfiguration;
+      if (massSpringSystemFromObjMeshConfigFile.GenerateMassSpringSystem(massSpringSystemObjConfigFilename, &massSpringSystem, &massSpringSystemObjMeshConfiguration) != 0)
+      {
+        printf("Error initializing the mass spring system.\n");
+        exit(1);
+      }
+      strcpy(renderingMeshFilename, massSpringSystemObjMeshConfiguration.massSpringMeshFilename);
+    }
+    break;
 
-      case TETMESH:
-        {
-          printf("Loading mass spring system from a tet mesh file...\n");
-          MassSpringSystemFromTetMeshConfigFile massSpringSystemFromTetMeshConfigFile;
-          MassSpringSystemTetMeshConfiguration massSpringSystemTetMeshConfiguration;
-          if (massSpringSystemFromTetMeshConfigFile.GenerateMassSpringSystem(massSpringSystemTetMeshConfigFilename, &massSpringSystem, &massSpringSystemTetMeshConfiguration) != 0)
-          {
-            printf("Error initializing the mass spring system.\n");
-            exit(1);
-          }
-          strcpy(renderingMeshFilename, massSpringSystemTetMeshConfiguration.surfaceMeshFilename);
-        }
-        break;
+    case TETMESH:
+    {
+      printf("Loading mass spring system from a tet mesh file...\n");
+      MassSpringSystemFromTetMeshConfigFile massSpringSystemFromTetMeshConfigFile;
+      MassSpringSystemTetMeshConfiguration massSpringSystemTetMeshConfiguration;
+      if (massSpringSystemFromTetMeshConfigFile.GenerateMassSpringSystem(massSpringSystemTetMeshConfigFilename, &massSpringSystem, &massSpringSystemTetMeshConfiguration) != 0)
+      {
+        printf("Error initializing the mass spring system.\n");
+        exit(1);
+      }
+      strcpy(renderingMeshFilename, massSpringSystemTetMeshConfiguration.surfaceMeshFilename);
+    }
+    break;
 
-      case CUBICMESH:
-        {
-          printf("Loading mass spring system from a cubic mesh file...\n");
-          MassSpringSystemFromCubicMeshConfigFile massSpringSystemFromCubicMeshConfigFile;
-          MassSpringSystemCubicMeshConfiguration massSpringSystemCubicMeshConfiguration;
-          if (massSpringSystemFromCubicMeshConfigFile.GenerateMassSpringSystem(massSpringSystemCubicMeshConfigFilename, &massSpringSystem, &massSpringSystemCubicMeshConfiguration) != 0)
-          {
-            printf("Error initializing the mass spring system.\n");
-            exit(1);
-          }
-          strcpy(renderingMeshFilename, massSpringSystemCubicMeshConfiguration.surfaceMeshFilename);
-        }
-        break;
+    case CUBICMESH:
+    {
+      printf("Loading mass spring system from a cubic mesh file...\n");
+      MassSpringSystemFromCubicMeshConfigFile massSpringSystemFromCubicMeshConfigFile;
+      MassSpringSystemCubicMeshConfiguration massSpringSystemCubicMeshConfiguration;
+      if (massSpringSystemFromCubicMeshConfigFile.GenerateMassSpringSystem(massSpringSystemCubicMeshConfigFilename, &massSpringSystem, &massSpringSystemCubicMeshConfiguration) != 0)
+      {
+        printf("Error initializing the mass spring system.\n");
+        exit(1);
+      }
+      strcpy(renderingMeshFilename, massSpringSystemCubicMeshConfiguration.surfaceMeshFilename);
+    }
+    break;
 
-      case CHAIN:
-        {
-          int numParticles;
-          double groupStiffness;
-          sscanf(customMassSpringSystem, "chain,%d,%lf", &numParticles, &groupStiffness);
-          printf("Creating a chain mass-spring system with %d particles...\n", numParticles);
+    case CHAIN:
+    {
+      int numParticles;
+      double groupStiffness;
+      sscanf(customMassSpringSystem, "chain,%d,%lf", &numParticles, &groupStiffness);
+      printf("Creating a chain mass-spring system with %d particles...\n", numParticles);
 
-          double * masses = (double*) malloc (sizeof(double) * numParticles);
-          for(int i=0; i<numParticles; i++)
-            masses[i] = 1.0;
+      double * masses = (double*) malloc (sizeof(double) * numParticles);
+      for(int i=0; i<numParticles; i++)
+        masses[i] = 1.0;
 
-          double * restPositions = (double*) malloc (sizeof(double) * 3 * numParticles);
-          for(int i=0; i<numParticles; i++)
-          {
-            restPositions[3*i+0] = 0;
-            restPositions[3*i+1] = (numParticles == 1) ? 0.0 : 1.0 * i / (numParticles-1);
-            restPositions[3*i+2] = 0;
-          }
-          int * edges = (int*) malloc (sizeof(int) * 2 * (numParticles - 1));
-          for(int i=0; i<numParticles-1; i++)
-          {
-            edges[2*i+0] = i;
-            edges[2*i+1] = i+1;
-          }
+      double * restPositions = (double*) malloc (sizeof(double) * 3 * numParticles);
+      for(int i=0; i<numParticles; i++)
+      {
+        restPositions[3*i+0] = 0;
+        restPositions[3*i+1] = (numParticles == 1) ? 0.0 : 1.0 * i / (numParticles-1);
+        restPositions[3*i+2] = 0;
+      }
+      int * edges = (int*) malloc (sizeof(int) * 2 * (numParticles - 1));
+      for(int i=0; i<numParticles-1; i++)
+      {
+        edges[2*i+0] = i;
+        edges[2*i+1] = i+1;
+      }
 
-          int * edgeGroups = (int*) malloc (sizeof(int) * (numParticles - 1));
-          for(int i=0; i<numParticles-1; i++)
-            edgeGroups[i] = 0;
-          double groupDamping = 0;
+      int * edgeGroups = (int*) malloc (sizeof(int) * (numParticles - 1));
+      for(int i=0; i<numParticles-1; i++)
+        edgeGroups[i] = 0;
+      double groupDamping = 0;
 
-          massSpringSystem = new MassSpringSystem(numParticles, masses, restPositions, numParticles - 1, edges, edgeGroups, 1, &groupStiffness, &groupDamping, addGravity);
+      massSpringSystem = new MassSpringSystem(numParticles, masses, restPositions, numParticles - 1, edges, edgeGroups, 1, &groupStiffness, &groupDamping, addGravity);
 
-          char s[96];
-          sprintf(s,"chain-%d.obj", numParticles);
-          massSpringSystem->CreateObjMesh(s);
-          strcpy(renderingMeshFilename, s);
+      char s[96];
+      sprintf(s,"chain-%d.obj", numParticles);
+      massSpringSystem->CreateObjMesh(s);
+      strcpy(renderingMeshFilename, s);
 
-          free(edgeGroups);
-          free(edges);
-          free(restPositions);
-          free(masses);
+      free(edgeGroups);
+      free(edges);
+      free(restPositions);
+      free(masses);
 
-          renderVertices = 1;
-        }
-        break;
+      renderVertices = 1;
+    }
+    break;
 
-      default:
-        printf("Error: mesh spring system configuration file was not specified.\n");
-        exit(1);
-        break;
+    default:
+      printf("Error: mesh spring system configuration file was not specified.\n");
+      exit(1);
+      break;
     }
 
     if (addGravity)
       massSpringSystem->SetGravity(addGravity, g);
 
-    if (numInternalForceThreads > 0)
-    {
-      printf("Launching threaded internal force evaluation: %d threads.\n", numInternalForceThreads);
-      MassSpringSystemMT * massSpringSystemMT = new MassSpringSystemMT(*massSpringSystem, numInternalForceThreads);
-      delete(massSpringSystem);
-      massSpringSystem = massSpringSystemMT;
-    }
-
     n = massSpringSystem->GetNumParticles();
 
     // create the mass matrix
-    massSpringSystem->GenerateMassMatrix(&massMatrix, 1);
-    delete(massMatrix);
     massSpringSystem->GenerateMassMatrix(&massMatrix);
 
     // create the mesh graph (used only for the distribution of user forces over neighboring vertices)
@@ -1317,11 +1297,11 @@ void initSimulation()
     deformableObjectRenderingMesh->SetUpTextures(SceneObject::MODULATE, SceneObject::NOMIPMAP);
   deformableObjectRenderingMesh->ResetDeformationToRest();
   deformableObjectRenderingMesh->BuildNeighboringStructure();
-  deformableObjectRenderingMesh->BuildNormals();
+  deformableObjectRenderingMesh->BuildNormals(); 
   deformableObjectRenderingMesh->SetMaterialAlpha(0.5);
 
-  // initialize the embedded triangle rendering mesh
-  secondaryDeformableObjectRenderingMesh = NULL;
+  // initialize the embedded triangle rendering mesh 
+  secondaryDeformableObjectRenderingMesh = nullptr;
   if (strcmp(secondaryRenderingMeshFilename, "__none") != 0)
   {
     secondaryDeformableObjectRenderingMesh = new SceneObjectDeformable(secondaryRenderingMeshFilename);
@@ -1329,7 +1309,7 @@ void initSimulation()
       secondaryDeformableObjectRenderingMesh->SetUpTextures(SceneObject::MODULATE, SceneObject::NOMIPMAP);
     secondaryDeformableObjectRenderingMesh->ResetDeformationToRest();
     secondaryDeformableObjectRenderingMesh->BuildNeighboringStructure();
-    secondaryDeformableObjectRenderingMesh->BuildNormals();
+    secondaryDeformableObjectRenderingMesh->BuildNormals(); 
 
     uSecondary = (double*) calloc (3 * secondaryDeformableObjectRenderingMesh->Getn(), sizeof(double));
 
@@ -1362,16 +1342,16 @@ void initSimulation()
     if (strcmp(fixedVerticesFilename, "__none") == 0)
     {
       numFixedVertices = 0;
-      fixedVertices = NULL;
+      fixedVertices = nullptr;
     }
     else
     {
-      if (LoadList::load(fixedVerticesFilename, &numFixedVertices,&fixedVertices) != 0)
+      if (ListIO::load(fixedVerticesFilename, &numFixedVertices,&fixedVertices) != 0)
       {
         printf("Error reading fixed vertices.\n");
         exit(1);
       }
-      LoadList::sort(numFixedVertices, fixedVertices);
+      ListIO::sort(numFixedVertices, fixedVertices);
     }
   }
   else
@@ -1382,7 +1362,7 @@ void initSimulation()
   }
 
   printf("Loaded %d fixed vertices. They are:\n",numFixedVertices);
-  LoadList::print(numFixedVertices,fixedVertices);
+  ListIO::print(numFixedVertices,fixedVertices);
   // create 0-indexed fixed DOFs
   int numFixedDOFs = 3 * numFixedVertices;
   int * fixedDOFs = (int*) malloc (sizeof(int) * numFixedDOFs);
@@ -1417,7 +1397,7 @@ void initSimulation()
   else if ((deformableObject == MASSSPRING) && (massSpringSystemSource == CHAIN))
   {
     uInitial = (double*) calloc (3*n, sizeof(double));
-    int numParticles = massSpringSystem->GetNumParticles();
+    int numParticles = massSpringSystem->GetNumParticles(); 
     for(int i=0; i<numParticles; i++)
     {
       uInitial[3*i+0] = 1.0 - ((numParticles == 1) ? 1.0 : 1.0 * i / (numParticles - 1));
@@ -1452,51 +1432,49 @@ void initSimulation()
     }
   }
 
-  // create force models, to be used by the integrator
-  printf("Creating force models...\n");
+  // create force model, to be used by the integrator
+  printf("Creating force model...\n");
   if (deformableObject == STVK)
   {
-    stVKForceModel = new StVKForceModel(stVKInternalForces, stVKStiffnessMatrix);
-    forceModel = stVKForceModel;
-    stVKForceModel->GetInternalForce(uInitial, u);
+    printf("Force model: STVK\n");
+    fflush(stdout);
+
+    stVKStencilForceModel = new StVKStencilForceModel(stVKFEM);
+    stencilForceModel = stVKStencilForceModel;
   }
 
   if (deformableObject == COROTLINFEM)
   {
-    TetMesh * tetMesh = dynamic_cast<TetMesh*>(volumetricMesh);
-    if (tetMesh == NULL)
-    {
-      printf("Error: the input mesh is not a tet mesh (CLFEM deformable model).\n");
-      exit(1);
-    }
+    printf("Force model: COROTLINFEM\n");
 
-    CorotationalLinearFEM * corotationalLinearFEM;
+    CorotationalLinearFEM * corotationalLinearFEM = new CorotationalLinearFEM(volumetricMesh);
 
-    if (numInternalForceThreads == 0)
-      corotationalLinearFEM = new CorotationalLinearFEM(tetMesh);
-    else
-      corotationalLinearFEM = new CorotationalLinearFEMMT(tetMesh, numInternalForceThreads);
+    corotationalLinearFEMStencilForceModel = new CorotationalLinearFEMStencilForceModel(corotationalLinearFEM);
+    corotationalLinearFEMStencilForceModel->SetWarp(corotationalLinearFEM_warp);
 
-    corotationalLinearFEMForceModel = new CorotationalLinearFEMForceModel(corotationalLinearFEM, corotationalLinearFEM_warp);
-    forceModel = corotationalLinearFEMForceModel;
+    stencilForceModel = corotationalLinearFEMStencilForceModel;
   }
 
   if (deformableObject == LINFEM)
   {
-    LinearFEMForceModel * linearFEMForceModel = new LinearFEMForceModel(stVKInternalForces);
-    forceModel = linearFEMForceModel;
+    printf("Force model: LINFEM\n");
+
+    assert(stVKStencilForceModel != nullptr);
+    linearFEMStencilForceModel = new LinearFEMStencilForceModel(stVKStencilForceModel);
+    stencilForceModel = linearFEMStencilForceModel;
   }
 
   if (deformableObject == INVERTIBLEFEM)
   {
+    printf("Force model: INVERTIBLEFEM\n");
     TetMesh * tetMesh = dynamic_cast<TetMesh*>(volumetricMesh);
-    if (tetMesh == NULL)
+    if (tetMesh == nullptr)
     {
       printf("Error: the input mesh is not a tet mesh (Invertible FEM deformable model).\n");
       exit(1);
     }
 
-    IsotropicMaterial * isotropicMaterial = NULL;
+    IsotropicMaterial * isotropicMaterial = nullptr;
 
     // create the invertible material model
     if (strcmp(invertibleMaterialString, "StVK") == 0)
@@ -1532,39 +1510,39 @@ void initSimulation()
     }
 
     // create the invertible FEM deformable model
-    IsotropicHyperelasticFEM * isotropicHyperelasticFEM;
-    if (numInternalForceThreads == 0)
-      isotropicHyperelasticFEM = new IsotropicHyperelasticFEM(tetMesh, isotropicMaterial, inversionThreshold, addGravity, g);
-    else
-      isotropicHyperelasticFEM = new IsotropicHyperelasticFEMMT(tetMesh, isotropicMaterial, inversionThreshold, addGravity, g, numInternalForceThreads);
-
-    // create force model for the invertible FEM class
-    IsotropicHyperelasticFEMForceModel * isotropicHyperelasticFEMForceModel = new IsotropicHyperelasticFEMForceModel(isotropicHyperelasticFEM);
-    forceModel = isotropicHyperelasticFEMForceModel;
+    IsotropicHyperelasticFEM * isotropicHyperelasticFEM = new IsotropicHyperelasticFEM(tetMesh, isotropicMaterial, inversionThreshold, addGravity, g);
+    isotropicHyperelasticFEMStencilForceModel = new IsotropicHyperelasticFEMStencilForceModel(isotropicHyperelasticFEM);
+    stencilForceModel = isotropicHyperelasticFEMStencilForceModel;
   }
 
   if (deformableObject == MASSSPRING)
   {
-    massSpringSystemForceModel = new MassSpringSystemForceModel(massSpringSystem);
-    forceModel = massSpringSystemForceModel;
+    printf("Force model: MASSSPRING\n");
+
+    massSpringStencilForceModel = new MassSpringStencilForceModel(massSpringSystem);
+    stencilForceModel = massSpringStencilForceModel;
 
     renderMassSprings = new RenderSprings();
   }
 
+  assert(stencilForceModel != nullptr);
+  forceModelAssembler = new ForceModelAssembler(stencilForceModel);
+  forceModel = forceModelAssembler;
+
   // initialize the integrator
   printf("Initializing the integrator, n = %d...\n", n);
   printf("Solver type: %s\n", solverMethod);
 
-  integratorBaseSparse = NULL;
+  integratorBaseSparse = nullptr;
   if (solver == IMPLICITNEWMARK)
   {
-    implicitNewmarkSparse = new ImplicitNewmarkSparse(3*n, timeStep, massMatrix, forceModel, positiveDefinite, numFixedDOFs, fixedDOFs,
+    implicitNewmarkSparse = new ImplicitNewmarkSparse(3*n, timeStep, massMatrix, forceModel, numFixedDOFs, fixedDOFs,
        dampingMassCoef, dampingStiffnessCoef, maxIterations, epsilon, newmarkBeta, newmarkGamma, numSolverThreads);
     integratorBaseSparse = implicitNewmarkSparse;
   }
   else if (solver == IMPLICITBACKWARDEULER)
   {
-    implicitNewmarkSparse = new ImplicitBackwardEulerSparse(3*n, timeStep, massMatrix, forceModel, positiveDefinite, numFixedDOFs, fixedDOFs,
+    implicitNewmarkSparse = new ImplicitBackwardEulerSparse(3*n, timeStep, massMatrix, forceModel, numFixedDOFs, fixedDOFs,
        dampingMassCoef, dampingStiffnessCoef, maxIterations, epsilon, numSolverThreads);
     integratorBaseSparse = implicitNewmarkSparse;
   }
@@ -1583,11 +1561,9 @@ void initSimulation()
     integratorBaseSparse = new CentralDifferencesSparse(3*n, timeStep, massMatrix, forceModel, numFixedDOFs, fixedDOFs, dampingMassCoef, dampingStiffnessCoef, centralDifferencesTangentialDampingUpdateMode, numSolverThreads);
   }
 
-  linearSolver = new CGSolver(integratorBaseSparse->GetSystemMatrix());
-  integratorBaseSparse->setLinearSolver(linearSolver);
   integratorBase = integratorBaseSparse;
 
-  if (integratorBase == NULL)
+  if (integratorBase == nullptr)
   {
     printf("Error: failed to initialize numerical integrator.\n");
     exit(1);
@@ -1599,23 +1575,13 @@ void initSimulation()
   integratorBase->SetState(uInitial, velInitial);
   integratorBase->SetTimestep(timeStep / substepsPerTimeStep);
 
-  if (implicitNewmarkSparse != NULL)
+  if (implicitNewmarkSparse != nullptr)
   {
     implicitNewmarkSparse->UseStaticSolver(staticSolver);
-    if (velInitial != NULL)
+    if (velInitial != nullptr)
       implicitNewmarkSparse->SetState(implicitNewmarkSparse->Getq(), velInitial);
   }
 
-  // clear fps buffer
-  for(int i=0; i<fpsBufferSize; i++)
-    fpsBuffer[i] = 0.0;
-
-  for(int i=0; i<forceAssemblyBufferSize; i++)
-    forceAssemblyBuffer[i] = 0.0;
-
-  for(int i=0; i<systemSolveBufferSize; i++)
-    systemSolveBuffer[i] = 0.0;
-
   // load any external geometry file (e.g. some static scene for decoration; usually there will be none)
   if (strcmp(extraSceneGeometryFilename,"__none") != 0)
   {
@@ -1623,7 +1589,7 @@ void initSimulation()
     extraSceneGeometry->BuildNormals(85.0);
   }
   else
-    extraSceneGeometry = NULL;
+    extraSceneGeometry = nullptr;
 
   // set up the ground plane (for rendering)
   renderGroundPlane = (strcmp(groundPlaneString, "__none") != 0);
@@ -1684,7 +1650,6 @@ void initConfigurations()
   configFile.addOptionOptional("secondaryRenderingMeshInterpolationFilename", secondaryRenderingMeshInterpolationFilename, "__none");
   configFile.addOptionOptional("useRealTimeNormals", &useRealTimeNormals, 0);
   configFile.addOptionOptional("fixedVerticesFilename", fixedVerticesFilename, "__none");
-  configFile.addOptionOptional("massMatrixFilename", massMatrixFilename, "__none");
   configFile.addOptionOptional("enableCompressionResistance", &enableCompressionResistance, enableCompressionResistance);
   configFile.addOptionOptional("compressionResistance", &compressionResistance, compressionResistance);
   configFile.addOption("timestep", &timeStep);
@@ -1696,7 +1661,7 @@ void initConfigurations()
   configFile.addOptionOptional("newmarkBeta", &newmarkBeta, newmarkBeta);
   configFile.addOptionOptional("newmarkGamma", &newmarkGamma, newmarkGamma);
   configFile.addOption("deformableObjectCompliance", &deformableObjectCompliance);
-  configFile.addOption("baseFrequency", &baseFrequency);
+  configFile.addOption("frequencyScaling", &frequencyScaling);
   configFile.addOptionOptional("forceNeighborhoodSize", &forceNeighborhoodSize, forceNeighborhoodSize);
   configFile.addOptionOptional("maxIterations", &maxIterations, 1);
   configFile.addOptionOptional("epsilon", &epsilon, 1E-6);
@@ -1797,14 +1762,14 @@ void syncTimestepWithGraphics_checkboxCallBack(int code)
     timeStep_spinner->enable();
 }
 
-void baseFrequency_spinnerCallBack(int code)
+void frequencyScaling_spinnerCallBack(int code)
 {
-  if (baseFrequency < 0)
-    baseFrequency = 0;
+  if (frequencyScaling < 0)
+    frequencyScaling = 0;
 
   glui->sync_live();
 
-  integratorBase->SetInternalForceScalingFactor(baseFrequency*baseFrequency);
+  integratorBase->SetInternalForceScalingFactor(frequencyScaling * frequencyScaling);
 }
 
 void newmarkBeta_spinnerCallBack(int code)
@@ -1823,7 +1788,7 @@ void newmarkBeta_spinnerCallBack(int code)
       newmarkGamma = 0.5;
   }
 
-  if (implicitNewmarkSparse != NULL)
+  if (implicitNewmarkSparse != nullptr)
   {
     implicitNewmarkSparse->SetNewmarkBeta(newmarkBeta);
     implicitNewmarkSparse->SetNewmarkGamma(newmarkGamma);
@@ -1843,7 +1808,7 @@ void newmarkGamma_spinnerCallBack(int code)
   if (use1DNewmarkParameterFamily)
     newmarkBeta = (newmarkGamma + 0.5) * (newmarkGamma + 0.5) / 4.0;
 
-  if (implicitNewmarkSparse != NULL)
+  if (implicitNewmarkSparse != nullptr)
   {
     implicitNewmarkSparse->SetNewmarkBeta(newmarkBeta);
     implicitNewmarkSparse->SetNewmarkGamma(newmarkGamma);
@@ -1858,7 +1823,7 @@ void newmark_checkboxuse1DNewmarkParameterFamilyCallBack(int code)
   {
     newmarkBeta = (newmarkGamma + 0.5) * (newmarkGamma + 0.5) / 4.0;
 
-    if (implicitNewmarkSparse != NULL)
+    if (implicitNewmarkSparse != nullptr)
     {
       implicitNewmarkSparse->SetNewmarkBeta(newmarkBeta);
       implicitNewmarkSparse->SetNewmarkGamma(newmarkGamma);
@@ -1897,7 +1862,7 @@ void timeStepSubdivisions_spinnerCallBack(int code)
   if (substepsPerTimeStep < 1)
     substepsPerTimeStep = 1;
 
-  integratorBase->SetTimestep(timeStep / substepsPerTimeStep);
+  integratorBase->SetTimestep(timeStep / substepsPerTimeStep); 
 
   glui->sync_live();
 }
@@ -1926,7 +1891,7 @@ void exit_buttonCallBack(int code)
 void callAllUICallBacks()
 {
   deformableObjectCompliance_spinnerCallBack(0);
-  baseFrequency_spinnerCallBack(0);
+  frequencyScaling_spinnerCallBack(0);
   timeStep_spinnerCallBack(0);
   syncTimestepWithGraphics_checkboxCallBack(0);
   rayleighMass_spinnerCallBack(0);
@@ -1944,13 +1909,13 @@ void initGLUI()
 
   glui = GLUI_Master.create_glui("Controls", 0, windowWidth + 52, 0);
 
-  glui->add_spinner("Deformable object compliance:",
-     GLUI_SPINNER_FLOAT, &deformableObjectCompliance, 0,
+  glui->add_spinner("Deformable object compliance:", 
+     GLUI_SPINNER_FLOAT, &deformableObjectCompliance, 0, 
      deformableObjectCompliance_spinnerCallBack );
   glui->add_spinner("Force kernel size:", GLUI_SPINNER_INT, &forceNeighborhoodSize);
 
-  glui->add_spinner("Frequency scaling:",
-     GLUI_SPINNER_FLOAT, &baseFrequency, 0, baseFrequency_spinnerCallBack );
+  glui->add_spinner("Frequency scaling:", 
+     GLUI_SPINNER_FLOAT, &frequencyScaling, 0, frequencyScaling_spinnerCallBack );
 
   // ******** newmark beta, gamma *********
 
@@ -1960,17 +1925,17 @@ void initGLUI()
        glui->add_panel("Newmark integrator parameters", GLUI_PANEL_EMBOSSED);
     newmark_panel->set_alignment(GLUI_ALIGN_LEFT);
 
-    glui->add_checkbox_to_panel(newmark_panel, "Link Beta and Gamma",
+    glui->add_checkbox_to_panel(newmark_panel, "Link Beta and Gamma", 
        &use1DNewmarkParameterFamily, 0, newmark_checkboxuse1DNewmarkParameterFamilyCallBack);
 
-    GLUI_Spinner * newmarkBeta_spinner =
-       glui->add_spinner_to_panel(newmark_panel,"Beta", GLUI_SPINNER_FLOAT,
-          &newmarkBeta, 0, newmarkBeta_spinnerCallBack);
+    GLUI_Spinner * newmarkBeta_spinner = 
+        glui->add_spinner_to_panel(newmark_panel,"Beta", GLUI_SPINNER_FLOAT,
+            &newmarkBeta, 0, newmarkBeta_spinnerCallBack);
     newmarkBeta_spinner->set_speed(0.1);
 
-    GLUI_Spinner * newmarkGamma_spinner =
-       glui->add_spinner_to_panel(newmark_panel,"Gamma", GLUI_SPINNER_FLOAT,
-          &newmarkGamma, 0, newmarkGamma_spinnerCallBack);
+    GLUI_Spinner * newmarkGamma_spinner = 
+        glui->add_spinner_to_panel(newmark_panel,"Gamma", GLUI_SPINNER_FLOAT,
+            &newmarkGamma, 0, newmarkGamma_spinnerCallBack);
     newmarkGamma_spinner->set_speed(0.1);
 
     glui->add_checkbox_to_panel(newmark_panel,"Static solver only", &staticSolver, 0,
@@ -2000,10 +1965,10 @@ void initGLUI()
      glui->add_panel("Timestep control", GLUI_PANEL_EMBOSSED);
   timeStep_panel->set_alignment(GLUI_ALIGN_LEFT);
 
-  glui->add_checkbox_to_panel(timeStep_panel, "Sync with graphics",
+  glui->add_checkbox_to_panel(timeStep_panel, "Sync with graphics", 
      &syncTimestepWithGraphics, 0, syncTimestepWithGraphics_checkboxCallBack);
 
-  timeStep_spinner =
+  timeStep_spinner = 
      glui->add_spinner_to_panel(timeStep_panel,"Timestep [sec]", GLUI_SPINNER_FLOAT, &timeStep, 0, timeStep_spinnerCallBack);
   timeStep_spinner->set_alignment(GLUI_ALIGN_LEFT);
 
@@ -2049,7 +2014,7 @@ void initGLUI()
 int main(int argc, char* argv[])
 {
   int numFixedArgs = 2;
-  if ( argc < numFixedArgs )
+  if ( argc < numFixedArgs ) 
   {
     printf("Real-time deformable object simulator.\n");
     printf("Usage: %s [config file]\n", argv[0]);
@@ -2060,7 +2025,7 @@ int main(int argc, char* argv[])
   char * configFilenameC = argv[1];
   opt_t opttable[] =
   {
-    { NULL, 0, NULL }
+    { nullptr, 0, nullptr }
   };
 
   argv += (numFixedArgs-1);
diff --git a/utilities/isosurfaceMesher/isosurfaceMesher.cpp b/utilities/isosurfaceMesher/isosurfaceMesher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..007883ce249036b6d6a72a688f5f036a4346d948
--- /dev/null
+++ b/utilities/isosurfaceMesher/isosurfaceMesher.cpp
@@ -0,0 +1,214 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "isosurfaceMesher" utility , Copyright (C) 2018 USC                   *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Driver for the IsosurfaceMesher. See the mesher library for more details.
+
+  Computes a quality triangular mesh of a distance field isosurface (level set),
+  using the following paper:
+
+  Steve Oudot, Laurent Rineau, Mariette Yvinec:
+  Meshing Volumes Bounded by Smooth Surfaces
+  Proceedings of the 14th International Meshing Roundtable 2005, pp 203-219
+ */
+
+#include <iostream>
+
+#include "tetMesh.h"
+#include "delaunayMesher.h"
+#include "distanceFieldCreator.h"
+#include "isosurfaceMesher.h"
+#include "performanceCounter.h"
+#include "getopts.h"
+#include "objMeshOrientable.h"
+#include "matrixIO.h"
+#include "predicates.h"
+using namespace std;
+
+int main(int argc, char **argv)
+{
+  int numFixedArg = 4;
+  if (argc < numFixedArg)
+  {
+    cout << "Computes a quality triangular mesh of a distance field isosurface (level set)" << endl;
+    cout << argv[0] << ": <distance field> <triangleSize> <out obj mesh> (-i<isoValue> -a<angle limit(in degrees, 0<a<=30)> "
+        "-e<epsilon(=1e-6)> -m<input is instead a surface mesh>, "
+        "-c <check Delaunay> -f <enforce to output a 2-manifold mesh> -n <input a narrowband distance field>)" << endl;
+    return 0;
+  }
+
+  initPredicates();
+  char * fieldFilename = argv[1];
+  char * surfaceMeshFilename = argv[1];
+  double triangleSize = atof(argv[2]);
+  char * objMeshFilename = argv[3];
+  double isoValue = 0;
+  bool inputSurfaceMesh = false;
+  double angleLimit = 30;
+  double epsilon = 1e-6;
+  bool checkDelaunay = false;
+  char angleLimitString[4096] = "30.0";
+  char isoValueString[4096] = "0.0";
+  char epsilonString[4096] = "1e-6";
+  bool narrowBand = false;
+  bool enforceManifold = true;
+  opt_t opttable[] =
+  {
+    { "a", OPTSTR, angleLimitString },
+    { "i", OPTSTR, isoValueString },
+    { "m", OPTBOOL, &inputSurfaceMesh },
+    { "e", OPTSTR, epsilonString },
+    { "c", OPTBOOL, &checkDelaunay },
+    { "n", OPTBOOL, &narrowBand },
+    { "f", OPTBOOL, &enforceManifold },
+    { NULL, 0, NULL }
+  };
+
+  argv += 3;
+  argc -= 3;
+  int optup = getopts(argc, argv, opttable);
+  if (optup != argc)
+  {
+    printf("Error parsing options. Error at option %d: %s.\n", optup, argv[optup + numFixedArg - 1]);
+    return 1;
+  }
+
+  angleLimit = strtod(angleLimitString, NULL);
+  isoValue = strtod(isoValueString, NULL);
+  epsilon = strtod(epsilonString, NULL);
+  PerformanceCounter pc;
+
+  DistanceFieldBase * field;
+  if (narrowBand)
+    field = new DistanceFieldNarrowBand;
+  else
+    field = new DistanceField;
+  ObjMesh * surfaceMesh = NULL;
+
+  if (angleLimit > 30)
+    angleLimit = 30;
+  else if (angleLimit < 0)
+    angleLimit = 1;
+  angleLimit = angleLimit * M_PI / 180.;
+  double radiusLimit = triangleSize;
+  if (radiusLimit < 0)
+    radiusLimit = 2 * field->diagonal();
+
+  if (inputSurfaceMesh == false)
+  {
+    int ret = field->load(fieldFilename);
+    if (ret != 0)
+    {
+      cout << "Failed to load distance field from " << fieldFilename << endl;
+      return 1;
+    }
+    pc.StopCounter();
+    cout << "Time to load distance field: " << pc.GetElapsedTime() << endl;
+
+    //  double gridX,gridY,gridZ;
+    //  field.getGridSpacing(&gridX, &gridY, &gridZ);
+    //  double gridSize = max(gridX, max(gridY, gridZ))pc.GetElapsedTime();;
+    //  cout << "gridSize: " << gridSize << endl;
+    //  //double triangleSize = 1.;
+    //
+    //  double absTriangleLimit = triangleSize * gridSize;
+    //  cout << "triangle size (world unit): " << absTriangleLimit << endl;
+
+    // convert to absolute value
+    cout << "radiusLimit: " << radiusLimit << endl;
+    cout << "isoValue: " << isoValue << endl;
+  }
+  else
+  {
+    surfaceMesh = new ObjMesh(surfaceMeshFilename);
+  }
+
+  bool outputManfold = false;
+  pc.StartCounter();
+  ObjMesh * mesh = NULL;
+
+  if (inputSurfaceMesh)
+  {
+    IsosurfaceMesher mesher(surfaceMesh);
+    mesher.checkDelaunayAfterComputation(checkDelaunay);
+
+    mesher.compute(isoValue, angleLimit, radiusLimit, 20, epsilon, 10000);
+    mesh = mesher.getMesh(outputManfold);
+  }
+  else
+  {
+    IsosurfaceMesher mesher(field);
+    mesher.checkDelaunayAfterComputation(checkDelaunay);
+    mesher.compute(isoValue, angleLimit, radiusLimit, 20, epsilon);
+    mesh = mesher.getMesh(outputManfold);
+  }
+  pc.StopCounter();
+  if (mesh == NULL)
+  {
+    cout << "Fail to generate isosurface mesh" << endl;
+    return 1;
+  }
+  cout << "Time to generate mesh: " << pc.GetElapsedTime() << endl;
+
+  if (enforceManifold)
+    IsosurfaceMesher::enforceManifoldnessAndOrientNormals(mesh);
+  else
+  {
+    try
+    {
+      ObjMeshOrientable orientedMesh(mesh);
+      cout << "Mesh is manifold" << endl;
+      if (orientedMesh.hasBoundary())
+      {
+        cout << "Mesh has boundary" << endl;
+      }
+      else
+      {
+        cout << "Mesh has no boundary" << endl;
+      }
+    }
+    catch (int)
+    {
+      cout << "Fail to orient the mesh" << endl;
+    }
+  }
+  cout << "Mesh size: #vtx " << mesh->getNumVertices() << " #face " << mesh->getNumFaces() << endl;
+  mesh->save(objMeshFilename);
+  cout << "Save to " << objMeshFilename << endl;
+  //delete mesh;
+  delete surfaceMesh;
+  delete mesh;
+  delete field;
+
+  return 0;
+}
+
diff --git a/src/util/largeModalDeformationFactory/canvas.cpp b/utilities/largeModalDeformationFactory/canvas.cpp
similarity index 94%
rename from src/util/largeModalDeformationFactory/canvas.cpp
rename to utilities/largeModalDeformationFactory/canvas.cpp
index 9d732c751e3e5604a089ff46ab605ec72f3318bd..4742c6cc5098e71f2fd5d1be636f25e1dfbf2803 100644
--- a/src/util/largeModalDeformationFactory/canvas.cpp
+++ b/utilities/largeModalDeformationFactory/canvas.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -54,8 +58,9 @@ BEGIN_EVENT_TABLE(MyGLCanvas, wxGLCanvas)
 END_EVENT_TABLE()
 
 MyGLCanvas::MyGLCanvas(PrecomputationState * precomputationState, UIState * uiState, MyFrame * parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name, int* attribList, const wxPalette& palette) :
-  wxGLCanvas(parent, id, pos, size, style | wxFULL_REPAINT_ON_RESIZE, name, attribList, palette) 
+  wxGLCanvas(parent, id, attribList, pos, size, style | wxFULL_REPAINT_ON_RESIZE, name, palette) 
 {
+  m_context = new wxGLContext(this);
   this->parent = parent;
   this->precomputationState = precomputationState;
   this->uiState = uiState;
@@ -111,6 +116,7 @@ MyGLCanvas::~MyGLCanvas()
     // remove display list
     glDeleteLists(renderingMeshDisplayList, 1);
   }
+  delete(m_context);
 }
 
 void MyGLCanvas::SetZBufferParams()
@@ -124,10 +130,12 @@ void MyGLCanvas::Reshape()
   int windowWidth, windowHeight;
   GetClientSize(&windowWidth, &windowHeight);
   SetZBufferParams();
-  
+
+/*
 #ifndef __WXMOTIF__
   if ( GetContext() )
 #endif
+*/
   {
     glViewport(0, 0, windowWidth, windowHeight);
 
@@ -145,14 +153,17 @@ void MyGLCanvas::Reshape()
 void MyGLCanvas::OnPaint(wxPaintEvent& event)
 {
   // must always be here
-  SetCurrent();
+  //SetCurrent();
+  wxGLCanvas::SetCurrent(*m_context);
   //wxPaintDC dc(this);
   wxPaintDC(this);
 
+/*
   #ifndef __WXMOTIF__
     if (!GetContext()) 
       return;
   #endif
+*/
 
   glClearColor(256.0 / 256, 256.0 / 256, 256.0 / 256, 0.0);
 
@@ -182,7 +193,7 @@ void MyGLCanvas::OnPaint(wxPaintEvent& event)
     time = elapsedTime.GetElapsedTime();
     double * ULinear = precomputationState->linearModalMatrix->GetMatrix();
     int n = precomputationState->linearModalMatrix->Getn();
-    double omega = 0.5 * 2 * PI;
+    double omega = 0.5 * 2 * M_PI;
     for(int i=0; i<3*n; i++)
       uLinear[i] = linearRenderingMagnitude * sin(omega * time) * ULinear[ELT(3*n, i, renderedLinearMode)];
 
@@ -202,7 +213,7 @@ void MyGLCanvas::OnPaint(wxPaintEvent& event)
     time = elapsedTime.GetElapsedTime();
     double * UNonLinear = precomputationState->nonLinearModalMatrix->GetMatrix();
     int n = precomputationState->nonLinearModalMatrix->Getn();
-    double omega = 0.5 * 2 * PI;
+    double omega = 0.5 * 2 * M_PI;
     for(int i=0; i<3*n; i++)
       uNonLinear[i] = nonLinearRenderingMagnitude * sin(omega * time) * UNonLinear[ELT(3*n, i, renderedNonLinearMode)];
 
@@ -308,9 +319,9 @@ void MyGLCanvas::OnPaint(wxPaintEvent& event)
 void MyGLCanvas::OnSize(wxSizeEvent& event)
 {
   // this is necessary to update the context on some platforms
-  wxGLCanvas::OnSize(event);
+  //wxGLCanvas::OnSize(event);
   
-  // Reset the OpenGL view aspect
+  // Reset the OpenGL view aspect ratio
   Reshape();
 }
 
@@ -322,7 +333,7 @@ void MyGLCanvas::OnEraseBackground(wxEraseEvent& event)
 
 void MyGLCanvas::OnMouse( wxMouseEvent& event )
 {
-  wxSize sz(GetClientSize());
+  //wxSize sz(GetClientSize());
   if( event.ButtonDown() )
   {
     if (event.LeftDown())
@@ -465,8 +476,8 @@ void MyGLCanvas::OnMouse( wxMouseEvent& event )
 
           for(int j=0; j<precomputationState->simulationMesh->getNumVertices(); j++)
           {
-            Vec3d * vertexPos = precomputationState->simulationMesh->getVertex(j);
-            Vec3d direction = (*vertexPos) - cameraPos;
+            Vec3d vertexPos = precomputationState->simulationMesh->getVertex(j);
+            Vec3d direction = vertexPos - cameraPos;
 
             bool insidePyramid = true;
             for(int jj=0; jj<4; jj++)
diff --git a/src/util/largeModalDeformationFactory/canvas.h b/utilities/largeModalDeformationFactory/canvas.h
similarity index 88%
rename from src/util/largeModalDeformationFactory/canvas.h
rename to utilities/largeModalDeformationFactory/canvas.h
index 9964384a1fcdef03cc19c17c6c887aa93ca5d83a..2173f6a39bca2600f6869e0e469ef82e51bfd96d 100644
--- a/src/util/largeModalDeformationFactory/canvas.h
+++ b/utilities/largeModalDeformationFactory/canvas.h
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -89,6 +93,8 @@ protected:
   PrecomputationState * precomputationState;
   UIState * uiState;
 
+  wxGLContext*	m_context;
+
   double zNear, zFar;
   double cameraRadius;
   SphericalCamera * camera;
diff --git a/src/util/largeModalDeformationFactory/convert.cpp b/utilities/largeModalDeformationFactory/convert.cpp
similarity index 94%
rename from src/util/largeModalDeformationFactory/convert.cpp
rename to utilities/largeModalDeformationFactory/convert.cpp
index 60be543d360756e57adfa8b8f7301f458eca69b1..58966c0d2316933533e5b8c0f7539124de17d1b5 100644
--- a/src/util/largeModalDeformationFactory/convert.cpp
+++ b/utilities/largeModalDeformationFactory/convert.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,7 +41,7 @@
 #include "matrixIO.h"
 
 /* XPM */
-static char *folder_open_xpm[] = {
+static const char *folder_open_xpm[] = {
 /* columns rows colors chars-per-pixel */
 "16 15 31 1",
 "6 c #9BACC2",
diff --git a/src/util/largeModalDeformationFactory/cubicPolynomials.cpp b/utilities/largeModalDeformationFactory/cubicPolynomials.cpp
similarity index 89%
rename from src/util/largeModalDeformationFactory/cubicPolynomials.cpp
rename to utilities/largeModalDeformationFactory/cubicPolynomials.cpp
index f05170d2726d7a220f6ebcc244e1f3b566bc2ef7..574675650ac2c43d5d3dbc9fd3a7bd43a797c369 100644
--- a/src/util/largeModalDeformationFactory/cubicPolynomials.cpp
+++ b/utilities/largeModalDeformationFactory/cubicPolynomials.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -34,11 +38,13 @@
 // cubic polynomial computation
 
 #include "StVKReducedInternalForces.h"
-#include "StVKReducedInternalForcesWX.h"
 #include "StVKCubeABCD.h"
 #include "matrixIO.h"
 #include "StVKElementABCDLoader.h"
 #include "largeModalDeformationFactory.h"
+#ifdef USE_TBB
+  #include <tbb/tbb.h>
+#endif
 
 void MyFrame::OnLoadCubicPolynomials(wxCommandEvent& event)
 {
@@ -197,22 +203,19 @@ void * MyFrame::CubicPolynomialsWorker(int * code, StVKReducedInternalForces **
 {
   // compute cubic polynomials
   StVKElementABCD * precomputedABCDIntegrals = StVKElementABCDLoader::load(precomputationState.simulationMesh);
-
-  if (uiState.numComputationThreads == 1)
-  {
+#ifdef USE_TBB
+  tbb::task_scheduler_init init(uiState.numComputationThreads);
+  // tbb::task_arena arena(uiState.numComputationThreads);
+  // arena.execute([&]
+  // {
+#endif
     *newCubicPolynomial = new StVKReducedInternalForces(
       precomputationState.rNonLin,
       precomputationState.nonLinearModalMatrix->GetMatrix(),
       precomputationState.simulationMesh, precomputedABCDIntegrals); 
-  }
-  else
-  {
-    *newCubicPolynomial = new StVKReducedInternalForcesWX(
-      precomputationState.rNonLin,
-      precomputationState.nonLinearModalMatrix->GetMatrix(),
-      precomputationState.simulationMesh, precomputedABCDIntegrals, false, uiState.numComputationThreads); 
-  }
-
+#ifdef USE_TBB
+  // });
+#endif
   delete(precomputedABCDIntegrals);
 
   computationRunning = -1;
diff --git a/src/util/largeModalDeformationFactory/fixedVertices.cpp b/utilities/largeModalDeformationFactory/fixedVertices.cpp
similarity index 88%
rename from src/util/largeModalDeformationFactory/fixedVertices.cpp
rename to utilities/largeModalDeformationFactory/fixedVertices.cpp
index 18f2fc720abb0e91c85864f1bdcb13f9a23ba43f..8d6d1c5e451e5a5fdd6e38865cc875ef99f30939 100644
--- a/src/util/largeModalDeformationFactory/fixedVertices.cpp
+++ b/utilities/largeModalDeformationFactory/fixedVertices.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -35,6 +39,8 @@
 
 #include "largeModalDeformationFactory.h"
 
+using namespace std;
+
 int MyFrame::LoadFixedVertices(wxString & fixedVerticesFilename)
 {
   int * newFixedVertices = NULL;
@@ -42,7 +48,7 @@ int MyFrame::LoadFixedVertices(wxString & fixedVerticesFilename)
 
   SetCursor(*wxHOURGLASS_CURSOR);
   const char * filename = fixedVerticesFilename.mb_str();
-  int code = LoadList::load((char*) filename, &newNumFixedVertices,&newFixedVertices);
+  int code = ListIO::load((char*) filename, &newNumFixedVertices,&newFixedVertices);
   SetCursor(*wxSTANDARD_CURSOR);
 
   if (code != 0)
@@ -139,7 +145,7 @@ void MyFrame::OnSaveFixedVertices(wxCommandEvent& event)
       //loadList.sort(numFixedVertices, fixedVertices);
       int offset = 1;
       const char * filename = vertexFilename.mb_str();
-      int code = LoadList::save((char*)filename, numFixedVertices, fixedVerticesC, offset);
+      int code = ListIO::save((char*)filename, numFixedVertices, fixedVerticesC, offset);
       free(fixedVerticesC);
       if (code != 0)
         this->errMsg( _T("Saving error"), _T("Unable to save fixed vertices to ") +  vertexFilename );
diff --git a/src/util/largeModalDeformationFactory/frequencies.cpp b/utilities/largeModalDeformationFactory/frequencies.cpp
similarity index 95%
rename from src/util/largeModalDeformationFactory/frequencies.cpp
rename to utilities/largeModalDeformationFactory/frequencies.cpp
index c23f33541a9862f2ebe144a8d31a6ea9c8c5bb43..ef23285379bc5544e94dda607d9205714ba02fbf 100644
--- a/src/util/largeModalDeformationFactory/frequencies.cpp
+++ b/utilities/largeModalDeformationFactory/frequencies.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,10 +44,12 @@
 #include "generateMassMatrix.h"
 #include "StVKStiffnessMatrix.h"
 #include "matrixIO.h"
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "volumetricMeshENuMaterial.h"
 #include "largeModalDeformationFactory.h"
 
+using namespace std;
+
 void MyFrame::OnLoadFrequencies(wxCommandEvent& event)
 {
   wxFileDialog *dlg = new wxFileDialog(this, _T("Load frequencies"), uiState.currentWorkingDirectory, _T(""), _T("Frequency Files(*.freq)|*.freq|All files(*.*)|*.*"), wxFD_OPEN /*| wxHIDE_READONLY*/, wxDefaultPosition);
diff --git a/src/util/largeModalDeformationFactory/icon.cpp b/utilities/largeModalDeformationFactory/icon.cpp
similarity index 95%
rename from src/util/largeModalDeformationFactory/icon.cpp
rename to utilities/largeModalDeformationFactory/icon.cpp
index b08898c8f55ebf0104738f47a537c9977694ef31..ae70f7ae259524bbda966012194859a5344facc9 100644
--- a/src/util/largeModalDeformationFactory/icon.cpp
+++ b/utilities/largeModalDeformationFactory/icon.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -34,7 +38,7 @@
 // the butterfly icon
 
 /* XPM */
-static char * iconBits[] = {
+static const char * iconBits[] = {
 "32 32 631 2",
 "  	c None",
 ". 	c #6A6363",
diff --git a/src/util/largeModalDeformationFactory/interpolate.cpp b/utilities/largeModalDeformationFactory/interpolate.cpp
similarity index 91%
rename from src/util/largeModalDeformationFactory/interpolate.cpp
rename to utilities/largeModalDeformationFactory/interpolate.cpp
index b5494c99832922ce476c9e325d89f9beabe010a9..99b0452b67627c8c0f9c3a76e44a3225537e8b6d 100644
--- a/src/util/largeModalDeformationFactory/interpolate.cpp
+++ b/utilities/largeModalDeformationFactory/interpolate.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/util/largeModalDeformationFactory/largeModalDeformationFactory.cpp b/utilities/largeModalDeformationFactory/largeModalDeformationFactory.cpp
similarity index 84%
rename from src/util/largeModalDeformationFactory/largeModalDeformationFactory.cpp
rename to utilities/largeModalDeformationFactory/largeModalDeformationFactory.cpp
index 927b00aae959ab0df30c392a5af4d5639a4dd1b6..a3ce04ca7fd3abdb0585e187fc19bf473d84c1f7 100644
--- a/src/util/largeModalDeformationFactory/largeModalDeformationFactory.cpp
+++ b/utilities/largeModalDeformationFactory/largeModalDeformationFactory.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,6 +40,7 @@
 #include "largeModalDeformationFactory.h"
 #include "icon.cpp"
 #include <wx/filename.h>
+using namespace std;
 
 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
   EVT_MENU_OPEN(MyFrame::OnMenuOpen)
@@ -132,9 +137,9 @@ bool MyApp::OnInit()
 {
   //#ifndef __WXMSW__
   #ifdef __APPLE__
-    if (std::freopen("stdout.txt", "w", stdout) == NULL)
+    if (freopen("stdout.txt", "w", stdout) == NULL)
       printf("Warning: failed to redirect output to stdout.txt\n");
-    if (std::freopen("stderr.txt", "w", stderr) == NULL)
+    if (freopen("stderr.txt", "w", stderr) == NULL)
       printf("Warning: failed to redirect output to stderr.txt\n");
   #endif
 
@@ -148,9 +153,9 @@ bool MyApp::OnInit()
 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
 : wxFrame((wxFrame *)NULL, -1, title, pos, size)
 {
-  versionString = _T("3.3");
+  versionString = _T("3.5");
   printf("Large modal deformation factory. Version %s .\n", (const char*)versionString.mb_str());
-  printf("By Jernej Barbic, CMU, MIT, USC, 2007-2013.\n");
+  printf("By Jernej Barbic, CMU, MIT, USC, 2007-2015.\n");
 
   printf("Initializing application...");
   precomputationState.renderingMeshAvailable = false;
@@ -260,14 +265,14 @@ MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
   menuInterpolate->Append( ID_SaveInterpolant, _T("&Export interpolant...") );
 
   wxMenu * menuMesh = new wxMenu;
-  menuMesh->Append( ID_LoadMesh, _T("&Load triangle mesh..."));
+  menuMesh->Append( ID_LoadMesh, _T("&Load triangle mesh (.obj)..."));
   menuMesh->Append( ID_RemoveTriangleMesh, _T("&Remove triangle mesh"));
   menuMesh->Append( ID_Voxelize, _T("&Voxelize...") );
   menuMesh->AppendSubMenu(menuInterpolate, 
     _T("&Interpolate to triangle mesh"), _T("Interpolate various matrices from simulation mesh to triangle mesh"));
 
   menuMesh->AppendSeparator();
-  menuMesh->Append( ID_LoadVoxelization, _T("L&oad simulation mesh..."));
+  menuMesh->Append( ID_LoadVoxelization, _T("L&oad simulation mesh (.veg)..."));
   menuMesh->Append( ID_SaveVoxelization, _T("&Save simulation mesh..."));
   menuMesh->Append( ID_ExportSurfaceMesh, _T("&Export surface of simulation mesh..."));
   menuMesh->Append( ID_MaterialProperties, _T("Set &material properties..."));
@@ -467,18 +472,17 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
 {
   wxString message = 
-    _T( "Modal Deformation Precomputation Utility.\n"
-        "Version ") + versionString + _T("\n") +
-    _T( "\n"
-        "Computes:\n"
-        "- Linear vibrational modes (both constrained and free models are supported)\n"
-        "- Modal derivatives\n"
-        "- Simulation basis (using linear modes and modal derivatives, or external simulation data)\n"
-        "- Reduced cubic polynomials for geometrically nonlinear simulations\n"
-        "- A cubic (voxel) mesh to the input triangle geometry\n"
-        "\n"
-        "By Jernej Barbic, CMU, MIT, USC, 2007-2013.\n"
-  );
+    _T( "Modal Deformation Precomputation Utility.\n")
+    _T("Version ") + versionString + _T("\n") +
+    _T( "\n")
+    _T("Computes:\n")
+    _T("- Linear vibrational modes (both constrained and free models are supported)\n")
+    _T("- Modal derivatives\n")
+    _T("- Simulation basis (using linear modes and modal derivatives, or external simulation data)\n")
+    _T("- Reduced cubic polynomials for geometrically nonlinear simulations\n")
+    _T("- A cubic (voxel) mesh to the input triangle geometry\n")
+    _T("\n")
+    _T("By Jernej Barbic, CMU, MIT, USC, 2007-2015.\n");
 
   wxMessageBox(message,
     _T("About Large Modal Deformation Factory"), wxOK | wxICON_INFORMATION, this);
@@ -487,18 +491,18 @@ void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
 void MyFrame::OnMouseHelp(wxCommandEvent& WXUNUSED(event))
 {
   wxString message = 
-  _T( "LEFT mouse button:\n"
-  "  click: constrain a vertex\n"
-  "  shift-click: toggle constraining a vertex\n"
-  "  drag: marquee-select constrained vertices\n"
-  "  shift-drag: marquee-toggle constraining vertices\n"
-  "  NOTE: The \"Fixed Vertices.Select\" menu flag must be activated\n"
-  "\n" 
-  "MIDDLE mouse button:\n"
-  "  drag: camera zoom in/out\n"
-  "\n"
-  "RIGHT mouse button:\n"
-  "  drag: change camera viewpoint\n");
+  _T("LEFT mouse button:\n")
+  _T("  click: constrain a vertex\n")
+  _T("  shift-click: toggle constraining a vertex\n")
+  _T("  drag: marquee-select constrained vertices\n")
+  _T("  shift-drag: marquee-toggle constraining vertices\n")
+  _T("  NOTE: The \"Fixed Vertices.Select\" menu flag must be activated\n")
+  _T("\n")
+  _T("MIDDLE mouse button:\n")
+  _T("  drag: camera zoom in/out\n")
+  _T("\n")
+  _T("RIGHT mouse button:\n")
+  _T("  drag: change camera viewpoint\n");
         
   wxMessageBox(message,
     _T("Mouse Help"), wxOK | wxICON_INFORMATION, this);
@@ -507,25 +511,25 @@ void MyFrame::OnMouseHelp(wxCommandEvent& WXUNUSED(event))
 void MyFrame::OnExampleHelp(wxCommandEvent& WXUNUSED(event))
 {
   wxString message = 
-    _T( "Example usage\n"
-        "Note: This text was also printed to your command prompt window. It is also available in \"instructions.txt\" (shortcut is in the Start menu).\n"
-        "=========================================================================================\n"
-        "\n"
-        "1. Load \"examples/simpleBridge.obj\" (using Mesh.Load triangle mesh)\n\n"
-        "2. Create a voxel simulation mesh (using Mesh.Voxelize, keep the resolution at the default (65))\n\n"
-        "3. (optional) Inspect the properties of the newly created simulation mesh (using Mesh.Information)\n\n"
-        "4. (optional) Switch the view to the triangle mesh (and later back to simulation mesh) using View.Triangle Mesh, and View.Simulation Mesh\n\n"
-        "5. (optional) Select some vertices that are to be permanently fixed (left-click with the mouse, use shift to add/subtract, you can also marquee select)\n\n"
-        "6. (optional) At any time, you can inspect the program text output in the text window (might be initially hidden behind the main program window)\n\n"
-        "7. Compute the linear modes (using Linear Modes.Compute; compute, say, 10 modes)\n\n"
-        "8. (optional) See the different modes and their frequencies by pressing the up arrow/down arrow on the toolbar\n\n"
-        "9. Compute modal derivatives (using the Simulation Basis.Modal derivatives.Compute modal derivatives)\n\n"
-        "10. Compute nonlinear modes (using Simulation Basis.Compute; compute, say, 15 modes)\n\n"
-        "11. Generate cubic polynomials (using Cubic polynomials.Compute; this part of the computation is multi-threaded, so it will work faster on a multi-core computer)\n\n"
-        "12. Prepare the real-time simulation (using Real-time Sim.Prepare real-time simulation)\n\n"
-        "13. Launch the real-time simulation (using Real-time Sim.Launch real-time simulation). Note: reducedDynamicSolver-rt must be in the path, check the .bat file.\n\n"
-        "14. Interact with the real-time simulation using the mouse (left-click on the model and pull on it)\n\n"
-        "15. Close the real-time simulation window\n" );
+    _T("Example usage\n")
+    _T("Note: This text was also printed to your command prompt window. It is also available in \"instructions.txt\" (shortcut is in the Start menu).\n")
+    _T("=========================================================================================\n")
+    _T("\n")
+    _T("1. Load \"examples/simpleBridge.obj\" (using Mesh.Load triangle mesh)\n\n")
+    _T("2. Create a voxel simulation mesh (using Mesh.Voxelize, keep the resolution at the default (65))\n\n")
+    _T("3. (optional) Inspect the properties of the newly created simulation mesh (using Mesh.Information)\n\n")
+    _T("4. (optional) Switch the view to the triangle mesh (and later back to simulation mesh) using View.Triangle Mesh, and View.Simulation Mesh\n\n")
+    _T("5. (optional) Select some vertices that are to be permanently fixed (left-click with the mouse, use shift to add/subtract, you can also marquee select)\n\n")
+    _T("6. (optional) At any time, you can inspect the program text output in the text window (might be initially hidden behind the main program window)\n\n")
+    _T("7. Compute the linear modes (using Linear Modes.Compute; compute, say, 10 modes)\n\n")
+    _T("8. (optional) See the different modes and their frequencies by pressing the up arrow/down arrow on the toolbar\n\n")
+    _T("9. Compute modal derivatives (using the Simulation Basis.Modal derivatives.Compute modal derivatives)\n\n")
+    _T("10. Compute nonlinear modes (using Simulation Basis.Compute; compute, say, 15 modes)\n\n")
+    _T("11. Generate cubic polynomials (using Cubic polynomials.Compute; this part of the computation is multi-threaded, so it will work faster on a multi-core computer)\n\n")
+    _T("12. Prepare the real-time simulation (using Real-time Sim.Prepare real-time simulation)\n\n")
+    _T("13. Launch the real-time simulation (using Real-time Sim.Launch real-time simulation). Note: reducedDynamicSolver-rt must be in the path, check the .bat file.\n\n")
+    _T("14. Interact with the real-time simulation using the mouse (left-click on the model and pull on it)\n\n")
+    _T("15. Close the real-time simulation window\n");
         
   printf("%s", (const char*)message.mb_str());
 
@@ -553,7 +557,7 @@ void MyFrame::CreateOpenGLWindow()
 void MyFrame::InitOpenGL()
 {
   myGLCanvas->Show();
-  myGLCanvas->SetCurrent();
+  //myGLCanvas->SetCurrent();
   myGLCanvas->InitOpenGL();
   wxSafeYield();
 }
diff --git a/src/util/largeModalDeformationFactory/largeModalDeformationFactory.h b/utilities/largeModalDeformationFactory/largeModalDeformationFactory.h
similarity index 94%
rename from src/util/largeModalDeformationFactory/largeModalDeformationFactory.h
rename to utilities/largeModalDeformationFactory/largeModalDeformationFactory.h
index 72f65d925c277e7ff771d3aebd03cc5c287a3b0f..e31321c899df25b4d54fec71b23b9fcfe1942c7f 100644
--- a/src/util/largeModalDeformationFactory/largeModalDeformationFactory.h
+++ b/utilities/largeModalDeformationFactory/largeModalDeformationFactory.h
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -39,13 +43,13 @@
 #define _LARGEMODALDEFORMATIONFACTORY_H_
 
 #include <set>
-using namespace std;
+#include <string>
 
 #include "wx/wx.h" 
 #include "wx/spinctrl.h" 
 
 #include "objMesh.h"
-#include "loadList.h"
+#include "listIO.h"
 #include "volumetricMesh.h"
 #include "modalMatrix.h"
 #include "StVKReducedInternalForces.h"
@@ -182,8 +186,8 @@ protected:
   void UpdateMenus();
   void CreateOpenGLWindow();
   void InitOpenGL();
-  string int2string(int n);
-  string double2string(double d);
+  std::string int2string(int n);
+  std::string double2string(double d);
   double MaxModeMagnitude(ModalMatrix * modalMatrix);
   void DeleteRenderingMesh();
   int LoadSimulationMesh(wxString & meshFilename);
diff --git a/src/util/largeModalDeformationFactory/largeModalDeformationFactory.icns b/utilities/largeModalDeformationFactory/largeModalDeformationFactory.icns
similarity index 100%
rename from src/util/largeModalDeformationFactory/largeModalDeformationFactory.icns
rename to utilities/largeModalDeformationFactory/largeModalDeformationFactory.icns
diff --git a/src/util/largeModalDeformationFactory/linearModes.cpp b/utilities/largeModalDeformationFactory/linearModes.cpp
similarity index 96%
rename from src/util/largeModalDeformationFactory/linearModes.cpp
rename to utilities/largeModalDeformationFactory/linearModes.cpp
index 54290ee79a088df4271ff7292a171c5e926844f6..98668c66a3efce8eebb1faa0fefe6da1f3200a84 100644
--- a/src/util/largeModalDeformationFactory/linearModes.cpp
+++ b/utilities/largeModalDeformationFactory/linearModes.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,7 +44,7 @@
 #include "StVKStiffnessMatrix.h"
 #include "StVKCubeABCD.h"
 #include "matrixIO.h"
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "StVKElementABCDLoader.h"
 #include "ARPACKSolver.h"
 #include "largeModalDeformationFactory.h"
@@ -51,6 +55,8 @@
   #define ISNAN isnan
 #endif
 
+using namespace std;
+
 void MyFrame::SetAutoRenderingMagnitude(ModalMatrix * modalMatrix)
 {
   // determine rendered magnitude
@@ -506,7 +512,7 @@ void * MyFrame::LinearModesWorker(
   {
     // insert zero rows into the computed modes
     int oneIndexed = 1;
-    InsertRows(n3, &modesTemp[numRetainedDOFs*i], &((*modes_)[n3*i]), 
+    ConstrainedDOFs::InsertDOFs(n3, &modesTemp[numRetainedDOFs*i], &((*modes_)[n3*i]), 
       3 * numConstrainedVertices, constrainedDOFs, oneIndexed);
   }
 
@@ -641,7 +647,7 @@ void MyFrame::ExportMassMatrix(bool fullMatrix)
     return;
   }
 
-  wxFileDialog *dlg = new wxFileDialog(this, _T("Export stiffness matrix"), uiState.currentWorkingDirectory, _T(""), _T("Mass Matrix Files(*.M)|*.M|All files(*.*)|*.*"), wxFD_SAVE /*| wxHIDE_READONLY*/, wxDefaultPosition);
+  wxFileDialog *dlg = new wxFileDialog(this, _T("Export mass matrix"), uiState.currentWorkingDirectory, _T(""), _T("Mass Matrix Files(*.M)|*.M|All files(*.*)|*.*"), wxFD_SAVE /*| wxHIDE_READONLY*/, wxDefaultPosition);
   if ( dlg->ShowModal() == wxID_OK )
   {
     wxString massMatrixFilename( dlg->GetPath() );
diff --git a/src/util/largeModalDeformationFactory/main.cpp b/utilities/largeModalDeformationFactory/main.cpp
similarity index 77%
rename from src/util/largeModalDeformationFactory/main.cpp
rename to utilities/largeModalDeformationFactory/main.cpp
index 2a9fec34e86f26e651cf9a59eabb02d93fca26f8..245a1ab2b8d4b0926142468d3fb59a71a5ffdef9 100644
--- a/src/util/largeModalDeformationFactory/main.cpp
+++ b/utilities/largeModalDeformationFactory/main.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/util/largeModalDeformationFactory/modalDerivatives.cpp b/utilities/largeModalDeformationFactory/modalDerivatives.cpp
similarity index 90%
rename from src/util/largeModalDeformationFactory/modalDerivatives.cpp
rename to utilities/largeModalDeformationFactory/modalDerivatives.cpp
index db2697ba8d728d6023ca9dffce183c76851b64a3..b12c2b04aacc14ece8a6a9351e5979b0a4ec1d53 100644
--- a/src/util/largeModalDeformationFactory/modalDerivatives.cpp
+++ b/utilities/largeModalDeformationFactory/modalDerivatives.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,19 +41,14 @@
 #include "StVKCubeABCD.h"
 #include "StVKHessianTensor.h"
 #include "StVKElementABCDLoader.h"
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "matrixIO.h"
 #include "matrixPCA.h"
 #include "computeStiffnessMatrixNullspace.h"
 #include "largeModalDeformationFactory.h"
 #include "sparseSolverAvailability.h"
 #include "sparseSolvers.h"
-
-// for faster computation, uncomment the following line (enable USE_OPENMP macro) if you want to use OpenMP (and add -fopenmp to the compiler line)
-//#define USE_OPENMP
-#ifdef USE_OPENMP
-  #include <omp.h>
-#endif
+using namespace std;
 
 void MyFrame::OnLoadModalDerivatives(wxCommandEvent& event)
 {
@@ -400,7 +399,7 @@ void MyFrame::ComputeModalDerivatives(int * code, double ** modalDerivatives)
   // constrain rhs
   double * rhsConstrained = (double*) malloc (sizeof(double) * numRetainedDOFs * precomputationState.numDeriv);
   for(int i=0; i<precomputationState.numDeriv; i++)
-    RemoveRows(n3, &rhsConstrained[numRetainedDOFs*i], 
+    ConstrainedDOFs::RemoveDOFs(n3, &rhsConstrained[numRetainedDOFs*i], 
       &rhs[n3*i], 3 * numConstrainedVertices, constrainedDOFs, oneIndexed);
 
   free(rhs);
@@ -415,13 +414,12 @@ void MyFrame::ComputeModalDerivatives(int * code, double ** modalDerivatives)
   LinearSolver * solver;
 
   #ifdef PARDISO_SOLVER_IS_AVAILABLE
-    int positiveDefinite = 0;
-    int directIterative = 0;
+    //int directIterative = 0;
     int numThreads = wxThread::GetCPUCount();
-    PardisoSolver * pardisoSolver = new PardisoSolver(stiffnessMatrix, numThreads, positiveDefinite, directIterative);
-    pardisoSolver->ComputeCholeskyDecomposition(stiffnessMatrix);
+    PardisoSolver * pardisoSolver = new PardisoSolver(stiffnessMatrix, numThreads, PardisoSolver::REAL_SYM_INDEFINITE);
+    pardisoSolver->FactorMatrix(stiffnessMatrix);
     solver = pardisoSolver;
-  #elif SPOOLES_SOLVER_IS_AVAILABLE
+  #elif defined(SPOOLES_SOLVER_IS_AVAILABLE)
     int numThreads = wxThread::GetCPUCount();
     if (numThreads > 1)
       solver = new SPOOLESSolverMT(stiffnessMatrix, numThreads);
@@ -431,14 +429,17 @@ void MyFrame::ComputeModalDerivatives(int * code, double ** modalDerivatives)
     solver = new CGSolver(stiffnessMatrix);
   #endif
 
-  #ifdef USE_OPENMP
-    #pragma omp parallel for
+  #ifdef PARDISO_SOLVER_IS_AVAILABLE
+    // with Pardiso, we cannot parallelize the multiple solves using OpenMP, as Pardiso is not thread-safe in this way
+    printf("Solving for modal derivatives using PARDISO.\n"); fflush(NULL);
+    pardisoSolver->SolveLinearSystemMultipleRHS(modalDerivativesConstrained, rhsConstrained, precomputationState.numDeriv);
+  #else
+    for(int i=0; i< precomputationState.numDeriv; i++)
+    {
+      printf("Solving for derivative #%d out of %d.\n", i + 1, precomputationState.numDeriv); fflush(NULL);
+      solver->SolveLinearSystem(&modalDerivativesConstrained[ELT(numRetainedDOFs, 0, i)], &rhsConstrained[ELT(numRetainedDOFs, 0, i)]);
+    }
   #endif
-  for(int i=0; i< precomputationState.numDeriv; i++)
-  {
-    printf("Solving for derivative #%d out of %d.\n", i + 1, precomputationState.numDeriv); fflush(NULL);
-    solver->SolveLinearSystem(&modalDerivativesConstrained[ELT(numRetainedDOFs, 0, i)], &rhsConstrained[ELT(numRetainedDOFs, 0, i)]);
-  }
 
   free(rhsConstrained);
   delete(solver);
@@ -449,7 +450,7 @@ void MyFrame::ComputeModalDerivatives(int * code, double ** modalDerivatives)
   // insert zero rows into the computed derivatives
   for(int i=0; i<precomputationState.numDeriv; i++)
   {
-    InsertRows(n3, &modalDerivativesConstrained[numRetainedDOFs*i], 
+    ConstrainedDOFs::InsertDOFs(n3, &modalDerivativesConstrained[numRetainedDOFs*i], 
       &((*modalDerivatives)[n3*i]), 
       3 * numConstrainedVertices, constrainedDOFs, oneIndexed);
   }
@@ -504,7 +505,7 @@ void MyFrame::RemoveSixRigidModes(int numVectors, double * x)
   double * defoPos6 = (double*) malloc (sizeof(double) * n3);
   for(int i=0; i<n3/3; i++)
   {
-    Vec3d restPos = *((precomputationState.simulationMesh)->getVertex(i));
+    Vec3d restPos = (precomputationState.simulationMesh)->getVertex(i);
     for(int j=0; j<3; j++)
       defoPos6[3*i+j] = restPos[j];
   }
diff --git a/src/util/largeModalDeformationFactory/nonlinearModes.cpp b/utilities/largeModalDeformationFactory/nonlinearModes.cpp
similarity index 95%
rename from src/util/largeModalDeformationFactory/nonlinearModes.cpp
rename to utilities/largeModalDeformationFactory/nonlinearModes.cpp
index 7ab5315543ea7eb2bbcda4a306a78df246b39744..84e9025180564b238ccf161f43758cb5b554eb9b 100644
--- a/src/util/largeModalDeformationFactory/nonlinearModes.cpp
+++ b/utilities/largeModalDeformationFactory/nonlinearModes.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -37,7 +41,7 @@
 #include "generateMassMatrix.h"
 #include "StVKStiffnessMatrix.h"
 #include "StVKHessianTensor.h"
-#include "insertRows.h"
+#include "constrainedDOFs.h"
 #include "sparseSolvers.h"
 #include "matrixIO.h"
 #include "matrixPCA.h"
@@ -182,10 +186,10 @@ void MyFrame::OnComputeNonLinearModes(wxCommandEvent& event)
      )
   {
     this->errMsg( _T("Cannot compute the simulation basis"),  
-      _T("You must compute/provide at least one of the following:\n"
-         "  (1) linear modes, frequencies, and modal derivatives, OR\n"
-         "  (2) external simulation data.\n"
-      ) );
+      _T("You must compute/provide at least one of the following:\n")
+      _T("  (1) linear modes, frequencies, and modal derivatives, OR\n")
+      _T("  (2) external simulation data.\n")
+      );
       return;
   }
 
diff --git a/src/util/largeModalDeformationFactory/renderingMesh.cpp b/utilities/largeModalDeformationFactory/renderingMesh.cpp
similarity index 87%
rename from src/util/largeModalDeformationFactory/renderingMesh.cpp
rename to utilities/largeModalDeformationFactory/renderingMesh.cpp
index b8a713f90a2923d144bb808a24d03d7b897f6fb3..5557a090e0a82985e2d2e360cb007eb3ec432457 100644
--- a/src/util/largeModalDeformationFactory/renderingMesh.cpp
+++ b/utilities/largeModalDeformationFactory/renderingMesh.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,6 +40,8 @@
 #include "generateSurfaceMesh.h"
 #include "largeModalDeformationFactory.h"
 
+using namespace std;
+
 void MyFrame::OnLoadMesh(wxCommandEvent& event)
 {
   wxFileDialog *dlg = new wxFileDialog(this, _T("Load mesh"), uiState.currentWorkingDirectory, _T(""), _T("Obj Files(*.obj)|*.obj|All files(*.*)|*.*"), wxFD_OPEN /*| wxHIDE_READONLY*/, wxDefaultPosition); 
diff --git a/src/util/largeModalDeformationFactory/runtime.cpp b/utilities/largeModalDeformationFactory/runtime.cpp
similarity index 95%
rename from src/util/largeModalDeformationFactory/runtime.cpp
rename to utilities/largeModalDeformationFactory/runtime.cpp
index 32f457f2274eab82df6c7d51ccb8ac8872d6cf31..f4c55821f0c2aae8d59cee0c491038dcc8b3dc53 100644
--- a/src/util/largeModalDeformationFactory/runtime.cpp
+++ b/utilities/largeModalDeformationFactory/runtime.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -40,7 +44,9 @@
 #include "generateMassMatrix.h"
 #include "largeModalDeformationFactory.h"
 
-static char * folder_open_xpm_2[] = {
+using namespace std;
+
+static const char * folder_open_xpm_2[] = {
 /* columns rows colors chars-per-pixel */
 "16 15 31 1",
 "6 c #9BACC2",
@@ -328,7 +334,7 @@ int MyFrame::PreprocessSingleSimulation(wxString & projectName, int numLinearMod
       3 * precomputationState.nonLinearModalMatrix->Getn(),
       precomputationState.nonLinearModalMatrix->Getr(),
       precomputationState.nonLinearModalMatrix->GetMatrix());
-    code = code;
+    code = code + 1;
 
     // save cubic polynomials
     wxString cubicPolynomialsFilename = projectName + _T(".cub");
@@ -750,7 +756,9 @@ void MyFrame::OnBatchPreprocessManySimulations(wxCommandEvent & event)
     if (fscanf(fin, "%s %s %d %d\n", meshFiles[i], bouFiles[i], &numLinearModes[i], &numNonLinearModes[i]) != 4)
     {
       wxString errM = _T("Incorrect line in the script file, model #");
-      errM += i + _T(" .");
+      char numStr[128] = "";
+      sprintf(numStr,"%i .",i);
+      errM += wxString(numStr, wxConvUTF8);
       this->errMsg(errM, errM);
       fclose(fin);
       PreprocessManySimulationsDeallocateHelper(numModels, meshFiles, bouFiles, numLinearModes, numNonLinearModes);
@@ -769,7 +777,9 @@ void MyFrame::OnBatchPreprocessManySimulations(wxCommandEvent & event)
     if ((numLinearModes[i] <= 0) || (numLinearModes[i] > 16384))
     {
       wxString errM = _T("Incorrect number of linear modes for model #");
-      errM += i + _T(" .");
+      char numStr[128] = "";
+      sprintf(numStr,"%i .",i);
+      errM += wxString(numStr, wxConvUTF8);
       this->errMsg(errM, errM);
       PreprocessManySimulationsDeallocateHelper(numModels, meshFiles, bouFiles, numLinearModes, numNonLinearModes);
       return;
@@ -778,7 +788,9 @@ void MyFrame::OnBatchPreprocessManySimulations(wxCommandEvent & event)
     if ((numNonLinearModes[i] <= 0) || (numNonLinearModes[i] > 16384))
     {
       wxString errM = _T("Incorrect number of nonlinear modes for model #");
-      errM += i + _T(" .");
+      char numStr[128] = "";
+      sprintf(numStr,"%i .",i);
+      errM += wxString(numStr, wxConvUTF8);
       this->errMsg(errM, errM);
       PreprocessManySimulationsDeallocateHelper(numModels, meshFiles, bouFiles, numLinearModes, numNonLinearModes);
       return;
@@ -847,10 +859,8 @@ void MyFrame::OnPrepareRuntimeSimulation(wxCommandEvent& event)
   if (!precomputationState.renderingMeshAvailable)
   {
     wxMessageDialog * dlg = new wxMessageDialog(this, 
-      _T(
-         "Click OK to use the surface mesh of the simulation mesh as the rendering mesh.\n\n"
-         "Click CANCEL to load your own rendering mesh (via the \"Mesh.Load Triangle Mesh\" menu option)."
-        ),
+      _T("Click OK to use the surface mesh of the simulation mesh as the rendering mesh.\n\n")
+      _T("Click CANCEL to load your own rendering mesh (via the \"Mesh.Load Triangle Mesh\" menu option)."),
       _T("Rendering mesh has not been specified"),
       wxOK | wxCANCEL);
 
@@ -1049,7 +1059,7 @@ void MyFrame::OnPrepareRuntimeSimulation(wxCommandEvent& event)
   free(K);
   delete(massMatrix);
 
-  fprintf(fout, "*baseFrequency\n");
+  fprintf(fout, "*frequencyScaling\n");
   if (uiState.scaleRealTimeTo1HzCheckBox)
     fprintf(fout, "%G\n\n", 1.0 / (precomputationState.frequencies)[precomputationState.numRigidModes]);
   else
@@ -1149,12 +1159,10 @@ void MyFrame::OnLaunchRuntimeSimulation(wxCommandEvent& event)
 void MyFrame::OnTuneRuntimeSimulation(wxCommandEvent& event)
 {
   wxString message = 
-	  _T( 
-  "You can tune the real-time simulation from within the real-time GUI (activated by choosing \"Launch real-time simulation\").\n\n"
-  "There are essentially two parameters to tune:\n"
-  "  1. You can set how stiff the object feels by adjusting the \"strength\" of your mouse forces via the \"Deformable object compliance\" parameter.\n"
-  "  2. You can make the object oscillate faster/slower by altering the \"Base frequency\" parameter.\n"
-   );
+	  _T("You can tune the real-time simulation from within the real-time GUI (activated by choosing \"Launch real-time simulation\").\n\n")
+    _T("There are essentially two parameters to tune:\n")
+    _T("  1. You can set how stiff the object feels by adjusting the \"strength\" of your mouse forces via the \"Deformable object compliance\" parameter.\n")
+    _T("  2. You can make the object oscillate faster/slower by altering the \"Base frequency\" parameter.\n");
         
   wxMessageBox(message,
     _T("Tuning the real-time simulation"), wxOK | wxICON_INFORMATION, this);
diff --git a/src/util/largeModalDeformationFactory/simulationMesh.cpp b/utilities/largeModalDeformationFactory/simulationMesh.cpp
similarity index 94%
rename from src/util/largeModalDeformationFactory/simulationMesh.cpp
rename to utilities/largeModalDeformationFactory/simulationMesh.cpp
index d3085cef4563e6ee014a194740614b4ef69aa814..02d80527134d3d363cb92622d8d00d3a713c185f 100644
--- a/src/util/largeModalDeformationFactory/simulationMesh.cpp
+++ b/utilities/largeModalDeformationFactory/simulationMesh.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -39,6 +43,8 @@
 #include "objMeshOffsetVoxels.h"
 #include "largeModalDeformationFactory.h"
 
+using namespace std;
+
 void MyFrame::OnVoxelize(wxCommandEvent& event)
 {
   char cubicMeshResolutionStringC[256];
@@ -56,8 +62,9 @@ void MyFrame::OnVoxelize(wxCommandEvent& event)
        wxALIGN_CENTER, _T( "staticText"));
 
   int maxResolution = 16384;
+  int resCtlSize = 90;
   wxSpinCtrl * resolutionControl = new wxSpinCtrl(dlg, -1, 
-      wxEmptyString, wxDefaultPosition, wxSize(70,-1), wxSP_ARROW_KEYS, 
+      wxEmptyString, wxDefaultPosition, wxSize(resCtlSize,-1), wxSP_ARROW_KEYS, 
       1, maxResolution, uiState.cubicMeshResolution, _T("wxSpinCtrl"));
   resolutionControl->SetValue(uiState.cubicMeshResolution);
 
@@ -209,7 +216,7 @@ void MyFrame::OnSaveSimulationMesh(wxCommandEvent& event)
     if( !meshFilename.empty() )
     {
       const char * filename = meshFilename.mb_str();
-      int code = precomputationState.simulationMesh->save((char*)filename);
+      int code = precomputationState.simulationMesh->saveToAscii((char*)filename);
       if (code != 0)
         this->errMsg( _T("Saving error"), 
           _T("Unable to save simulation mesh to ") +  meshFilename );
@@ -346,11 +353,9 @@ void MyFrame::OnMaterialProperties(wxCommandEvent & event)
   if (precomputationState.simulationMesh->getNumMaterials() > 1)
   {
     wxMessageDialog * confirmationDialog = new wxMessageDialog
-      (this, _T(
-        "Warning: existing simulation mesh has more than one material. "
-        "This dialog can only edit one material; other materials will be deleted. "
-        "Do you want to continue?"
-        ),
+      (this, _T("Warning: existing simulation mesh has more than one material. ")
+      _T("This dialog can only edit one material; other materials will be deleted. ")
+      _T("Do you want to continue?"),
       _T("More than one material encountered"), 
       wxYES_NO | wxICON_QUESTION);
 
diff --git a/src/util/largeModalDeformationFactory/sketch.cpp b/utilities/largeModalDeformationFactory/sketch.cpp
similarity index 86%
rename from src/util/largeModalDeformationFactory/sketch.cpp
rename to utilities/largeModalDeformationFactory/sketch.cpp
index 12baaa0b481fb95a937afcf2222da04c2a5342ff..1b983a79812994b9b5e25a4fb66c5c73d213d13c 100644
--- a/src/util/largeModalDeformationFactory/sketch.cpp
+++ b/utilities/largeModalDeformationFactory/sketch.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/util/largeModalDeformationFactory/states.h b/utilities/largeModalDeformationFactory/states.h
similarity index 87%
rename from src/util/largeModalDeformationFactory/states.h
rename to utilities/largeModalDeformationFactory/states.h
index 2f70a314c57665e8d40fd8964c556c7118697703..0940240f93d37ef96b85ceca8c0141245ac6c151 100644
--- a/src/util/largeModalDeformationFactory/states.h
+++ b/utilities/largeModalDeformationFactory/states.h
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -89,7 +93,7 @@ typedef struct
 
   // fixed vertices
   bool fixedVerticesAvailable;
-  set<int> fixedVertices;
+  std::set<int> fixedVertices;
   
   // simulation mesh
   bool simulationMeshAvailable;
diff --git a/src/util/largeModalDeformationFactory/view.cpp b/utilities/largeModalDeformationFactory/view.cpp
similarity index 95%
rename from src/util/largeModalDeformationFactory/view.cpp
rename to utilities/largeModalDeformationFactory/view.cpp
index e85e6fe606ba30930278426f85fbbe98fd9766ef..101bb5c93e9a38f84b1cfbba7f8bd81718fa1eea 100644
--- a/src/util/largeModalDeformationFactory/view.cpp
+++ b/utilities/largeModalDeformationFactory/view.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Large Modal Deformation Factory",                                    *
  * a pre-processing utility for model reduction of                       *
  * deformable objects undergoing large deformations.                     *
  *                                                                       *
- *  Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                           *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
diff --git a/src/util/reducedDynamicSolver-rt/initGraphics.cpp b/utilities/reducedDynamicSolver-rt/initGraphics.cpp
similarity index 89%
rename from src/util/reducedDynamicSolver-rt/initGraphics.cpp
rename to utilities/reducedDynamicSolver-rt/initGraphics.cpp
index 6e2b5333bcb56c28142f692d638939fe0d51a293..05265d0a43f738bb408d9a56e1c99401fd546401 100644
--- a/src/util/reducedDynamicSolver-rt/initGraphics.cpp
+++ b/utilities/reducedDynamicSolver-rt/initGraphics.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Reduced deformable dynamics" real-time driver application.           *
  * Uses model reduction to rapidly simulate deformable objects           *
  * undergoing large deformations.                                        *
  *                                                                       *
- * Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                            *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -36,13 +40,12 @@
    StVK simulation.
 */
 
-#include "initGraphics.h"
 #include "macros.h"
+#include "initGraphics.h"
 
 extern void displayFunction(void);
 extern void idleFunction(void);
 extern void reshape(int,int);
-extern void handleMenu(int);
 extern void keyboardFunction(unsigned char key, int x, int y);
 extern void mouseButtonActivityFunction(int button, int state, int x, int y);
 extern void mouseMotionFunction(int x, int y);
@@ -90,8 +93,6 @@ void initCamera(double cameraRadius,
 	     1.0 * cameraLongitude / 360 * (2*PI), 
              1.0 * cameraLattitude / 360 * (2*PI), 
              focusPos,  upPos, 0.05, camera2WorldScalingFactor); 
-
-  (*camera)->SetOrigin(focusPos); // focusPos is zero
 }
 
 // Sets up graphics pipeline
diff --git a/src/util/reducedDynamicSolver-rt/initGraphics.h b/utilities/reducedDynamicSolver-rt/initGraphics.h
similarity index 81%
rename from src/util/reducedDynamicSolver-rt/initGraphics.h
rename to utilities/reducedDynamicSolver-rt/initGraphics.h
index 6e79c594cf81f5755ef9375b409c3d6392b125f3..90747da4260d077ab07b531f1c8b991a6ea7eef1 100644
--- a/src/util/reducedDynamicSolver-rt/initGraphics.h
+++ b/utilities/reducedDynamicSolver-rt/initGraphics.h
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Reduced deformable dynamics" real-time driver application.           *
  * Uses model reduction to rapidly simulate deformable objects           *
  * undergoing large deformations.                                        *
  *                                                                       *
- * Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                            *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -39,7 +43,7 @@
 #ifndef _INITGRAPHICS_H_
 #define _INITGRAPHICS_H_
 
-#ifdef WIN32
+#if defined(WIN32) || defined(_WIN32)
   #include <windows.h>
 #endif
 
diff --git a/src/util/reducedDynamicSolver-rt/realtime/A/A.URendering.float b/utilities/reducedDynamicSolver-rt/realtime/A/A.URendering.float
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/A/A.URendering.float
rename to utilities/reducedDynamicSolver-rt/realtime/A/A.URendering.float
diff --git a/src/util/reducedDynamicSolver-rt/realtime/A/A.config b/utilities/reducedDynamicSolver-rt/realtime/A/A.config
similarity index 92%
rename from src/util/reducedDynamicSolver-rt/realtime/A/A.config
rename to utilities/reducedDynamicSolver-rt/realtime/A/A.config
index df029942b9206451ff5b586ddbce9aa580753154..4729f2e851ad21dce3d3256ea7da7a2ba9d0d711 100644
--- a/src/util/reducedDynamicSolver-rt/realtime/A/A.config
+++ b/utilities/reducedDynamicSolver-rt/realtime/A/A.config
@@ -10,18 +10,21 @@ realtime/A/A.cub
 *deformableObjectCompliance
 50
 
-*baseFrequency
+*frequencyScaling
 0.02
 
 *dampingMassCoef
 0.0
 
 *dampingStiffnessCoef
-0.003
+0.01
 
 *substepsPerTimeStep
 5
 
+*plasticThreshold
+1E12
+
 *renderWireframe
 1
 
diff --git a/src/util/reducedDynamicSolver-rt/realtime/A/A.cub b/utilities/reducedDynamicSolver-rt/realtime/A/A.cub
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/A/A.cub
rename to utilities/reducedDynamicSolver-rt/realtime/A/A.cub
diff --git a/src/util/reducedDynamicSolver-rt/realtime/A/A.lighting b/utilities/reducedDynamicSolver-rt/realtime/A/A.lighting
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/A/A.lighting
rename to utilities/reducedDynamicSolver-rt/realtime/A/A.lighting
diff --git a/src/util/reducedDynamicSolver-rt/realtime/A/A.obj b/utilities/reducedDynamicSolver-rt/realtime/A/A.obj
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/A/A.obj
rename to utilities/reducedDynamicSolver-rt/realtime/A/A.obj
diff --git a/src/util/reducedDynamicSolver-rt/realtime/A/A.obj.mtl b/utilities/reducedDynamicSolver-rt/realtime/A/A.obj.mtl
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/A/A.obj.mtl
rename to utilities/reducedDynamicSolver-rt/realtime/A/A.obj.mtl
diff --git a/src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.URendering.float b/utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.URendering.float
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.URendering.float
rename to utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.URendering.float
diff --git a/src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.config b/utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.config
similarity index 93%
rename from src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.config
rename to utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.config
index f3d1411589fdfc231b92ff125f3a26f6ac380158..ee39c3cfbf0ee5dce044f4dbcc108c70edde87dc 100644
--- a/src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.config
+++ b/utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.config
@@ -10,18 +10,21 @@ realtime/simpleBridge/simpleBridge.cub
 *deformableObjectCompliance
 33605.2
 
-*baseFrequency
+*frequencyScaling
 0.25
 
 *dampingMassCoef
 0.0
 
 *dampingStiffnessCoef
-0.003
+0.01
 
 *substepsPerTimeStep
 5
 
+*plasticThreshold
+1E11
+
 *renderWireframe
 1
 
diff --git a/src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.cub b/utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.cub
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.cub
rename to utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.cub
diff --git a/src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.lighting b/utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.lighting
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.lighting
rename to utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.lighting
diff --git a/src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.obj b/utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.obj
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.obj
rename to utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.obj
diff --git a/src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.obj.mtl b/utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.obj.mtl
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.obj.mtl
rename to utilities/reducedDynamicSolver-rt/realtime/simpleBridge/simpleBridge.obj.mtl
diff --git a/src/util/reducedDynamicSolver-rt/reducedDynamicSolver-rt.cpp b/utilities/reducedDynamicSolver-rt/reducedDynamicSolver-rt.cpp
similarity index 89%
rename from src/util/reducedDynamicSolver-rt/reducedDynamicSolver-rt.cpp
rename to utilities/reducedDynamicSolver-rt/reducedDynamicSolver-rt.cpp
index 1ef0bae7d3ddb0570ae925224c8de89698855eea..0250810a92bb73797d2c7a0ee70020a06b8cda62 100644
--- a/src/util/reducedDynamicSolver-rt/reducedDynamicSolver-rt.cpp
+++ b/utilities/reducedDynamicSolver-rt/reducedDynamicSolver-rt.cpp
@@ -1,24 +1,28 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "Reduced deformable dynamics" real-time driver application.           *
  * Uses model reduction to rapidly simulate deformable objects           *
  * undergoing large deformations.                                        *
  *                                                                       *
- * Copyright (C) 2007 CMU, 2009 MIT, 2013 USC                            *
+ * Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                            *
  *                                                                       *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -62,7 +66,9 @@ using namespace std;
 
 #include "initGraphics.h"
 #include "sceneObjectReducedCPU.h"
-#include "sceneObjectReducedGPU.h"
+#ifdef USE_CG
+  #include "sceneObjectReducedGPU.h"
+#endif
 #include "performanceCounter.h"
 #include "implicitNewmarkDense.h"
 #include "implicitBackwardEulerDense.h"
@@ -121,7 +127,9 @@ PerformanceCounter titleBarCounter;
 PerformanceCounter explosionCounter;
 Lighting * lighting = NULL;
 SceneObjectReduced * deformableObjectRenderingMeshReduced = NULL;
-SceneObjectReducedGPU * deformableObjectRenderingMeshGPU = NULL;
+#ifdef USE_CG
+  SceneObjectReducedGPU * deformableObjectRenderingMeshGPU = NULL;
+#endif
 SceneObjectReducedCPU * deformableObjectRenderingMeshCPU = NULL;
 SceneObject * extraSceneGeometry = NULL;
 ModalMatrix * renderingModalMatrix = NULL;
@@ -133,6 +141,9 @@ ReducedStVKForceModel * reducedStVKForceModel;
 ReducedLinearStVKForceModel * reducedLinearStVKForceModel;
 double lowestFrequency;
 
+int plasticDeformationsEnabled = 0;
+float plasticThreshold = 1E9;
+
 void stopDeformations_buttonCallBack(int code);
 
 PerformanceCounter cpuLoadCounter;
@@ -157,7 +168,7 @@ GLUI * glui;
 GLUI_Spinner * timeStep_spinner;
 GLUI_Checkbox * renderOnGPU_checkbox;
 float deformableObjectCompliance = 1.0;
-float baseFrequency = 1.0;
+float frequencyScaling = 1.0;
 int sceneID=0;
 
 void callAllUICallBacks();
@@ -190,6 +201,7 @@ void displayFunction(void)
   // Setup model transformations.
   glMatrixMode(GL_MODELVIEW); 
   glLoadIdentity();
+  glPolygonOffset(0.0,0.0);
 
   camera->Look();
 
@@ -633,42 +645,44 @@ void initScene()
   fq = (double*) calloc (r, sizeof(double));
   fqBase = (double*) calloc (r, sizeof(double));
 
-  // initialize the GPU rendering class for the deformable object
-  try
-  {
-    deformableObjectRenderingMeshGPU = new SceneObjectReducedGPU(deformableObjectFilename, renderingModalMatrix); // uses GPU to compute u=Uq
-  }
-  catch(int exceptionCode)
-  {
-    printf("Warning: unable to initialize GPU rendering for deformations (code: %d). Using CPU instead.\n", exceptionCode);
-    deformableObjectRenderingMeshGPU = NULL;
-    renderOnGPU = 0;
-  }
-
   // initialize the CPU rendering class for the deformable object
   deformableObjectRenderingMeshCPU = new SceneObjectReducedCPU(deformableObjectFilename, renderingModalMatrix); // uses CPU to compute u=Uq
-
   // prepare textures (if necessary)
   if (enableTextures)
   {
-    if (deformableObjectRenderingMeshGPU != NULL)
-    {
-      deformableObjectRenderingMeshGPU->SetUpTextures(SceneObject::MODULATE, SceneObject::USEMIPMAP);
-      deformableObjectRenderingMeshGPU->EnableTextures();
-    }
     deformableObjectRenderingMeshCPU->SetUpTextures(SceneObject::MODULATE, SceneObject::USEMIPMAP);
     deformableObjectRenderingMeshCPU->EnableTextures();
   }
 
-  // create a buffer used when searching for the 3D vertex closest to the click location
-  if (deformableObjectRenderingMeshGPU != NULL)
-    uClosestVertexBuffer = (double*) 
-      malloc (sizeof(double) * 3 * deformableObjectRenderingMeshGPU->Getn());
-
-  if (renderOnGPU)
-    deformableObjectRenderingMeshReduced = deformableObjectRenderingMeshGPU;
-  else
+  #ifdef USE_CG
+    // initialize the GPU rendering class for the deformable object
+    try
+    {
+      deformableObjectRenderingMeshGPU = new SceneObjectReducedGPU(deformableObjectFilename, renderingModalMatrix); // uses GPU to compute u=Uq
+      // create a buffer used when searching for the 3D vertex closest to the click location
+      uClosestVertexBuffer = (double*) malloc (sizeof(double) * 3 * deformableObjectRenderingMeshGPU->Getn());
+      
+      // prepare textures (if necessary)
+      if (enableTextures)
+      {
+        deformableObjectRenderingMeshGPU->SetUpTextures(SceneObject::MODULATE, SceneObject::USEMIPMAP);
+        deformableObjectRenderingMeshGPU->EnableTextures();
+      }
+    }
+    catch(int exceptionCode)
+    {
+      printf("Warning: unable to initialize GPU rendering for deformations (code: %d). Using CPU instead.\n", exceptionCode);
+      deformableObjectRenderingMeshGPU = NULL;
+      renderOnGPU = 0;
+    }
+    if (renderOnGPU)
+      deformableObjectRenderingMeshReduced = deformableObjectRenderingMeshGPU;
+    else
+      deformableObjectRenderingMeshReduced = deformableObjectRenderingMeshCPU;
+  #else
+    renderOnGPU = 0;
     deformableObjectRenderingMeshReduced = deformableObjectRenderingMeshCPU;
+  #endif
 
   deformableObjectRenderingMeshReduced->ResetDeformationToRest();
 
@@ -708,12 +722,12 @@ void initScene()
   else
     extraSceneGeometry = NULL;
 
+/*
   // compute lowest frequency of the system (smallest eigenvalue of K)
   double * K = (double*) malloc (sizeof(double) * r * r);
   double * zero = (double*) calloc (r, sizeof(double));
   stVKReducedStiffnessMatrix->Evaluate(zero, K);
 
-/*
   // find smallest eigenvalue of K
   Matrix<double> KM(r, r, K, false, false);
   Matrix<double> EigenVectors(r,r);
@@ -763,8 +777,10 @@ void initConfigurations()
   configFile.addOption("dampingMassCoef",&dampingMassCoef);
   configFile.addOption("dampingStiffnessCoef",&dampingStiffnessCoef);
 
+  configFile.addOptionOptional("plasticThreshold", &plasticThreshold, plasticThreshold);
+
   configFile.addOption("deformableObjectCompliance",&deformableObjectCompliance);
-  configFile.addOption("baseFrequency",&baseFrequency);
+  configFile.addOption("frequencyScaling",&frequencyScaling);
 
   configFile.addOptionOptional("cameraRadius",&cameraRadius,17.5);
   configFile.addOptionOptional("focusPositionX",&focusPositionX,0.0);
@@ -844,14 +860,14 @@ void syncTimeStepWithGraphics_checkboxCallBack(int code)
     timeStep_spinner->enable();
 }
 
-void baseFrequency_spinnerCallBack(int code)
+void frequencyScaling_spinnerCallBack(int code)
 {
-  if (baseFrequency < 0)
-    baseFrequency = 0;
+  if (frequencyScaling < 0)
+    frequencyScaling = 0;
 
   glui->sync_live();
 
-  implicitNewmarkDense->SetInternalForceScalingFactor(baseFrequency * baseFrequency);
+  implicitNewmarkDense->SetInternalForceScalingFactor(frequencyScaling * frequencyScaling);
 }
 
 void newmarkBeta_spinnerCallBack(int code)
@@ -936,11 +952,23 @@ void timeStepSubdivisions_spinnerCallBack(int code)
   glui->sync_live();
 }
 
+void plasticDeformationsEnabled_checkboxCallBack(int code)
+{
+  implicitNewmarkDense->SetPlasticThreshold(plasticThreshold);
+  implicitNewmarkDense->UsePlasticDeformations(plasticDeformationsEnabled);
+
+  glui->sync_live();
+}
+
 void renderOnGPU_checkBoxCallBack(int code)
 {
-  if (renderOnGPU && (deformableObjectRenderingMeshGPU != NULL))
+  if (renderOnGPU)
   {
+#ifdef USE_CG
     deformableObjectRenderingMeshReduced = deformableObjectRenderingMeshGPU;
+#else
+    printf("Error: sceneObjectReducedGPU not compiled. Install Nvidia CG to enable.");
+#endif
   }
   else
   {
@@ -960,6 +988,7 @@ void forceModel_checkBoxCallBack(int code)
 void stopDeformations_buttonCallBack(int code)
 {
   implicitNewmarkDense->ResetToRest();
+  plasticDeformationsEnabled_checkboxCallBack(0);
 }
 
 void staticSolver_checkboxCallBack(int code)
@@ -976,7 +1005,7 @@ void exit_buttonCallBack(int code)
 void callAllUICallBacks()
 {
   deformableObjectCompliance_spinnerCallBack(0);
-  baseFrequency_spinnerCallBack(0);
+  frequencyScaling_spinnerCallBack(0);
   timeStep_spinnerCallBack(0);
   syncTimeStepWithGraphics_checkboxCallBack(0);
   rayleighMass_spinnerCallBack(0);
@@ -996,7 +1025,7 @@ void initGLUI()
 
   glui->add_spinner("Deformable object compliance:", GLUI_SPINNER_FLOAT, &deformableObjectCompliance, 0, deformableObjectCompliance_spinnerCallBack );
 
-  glui->add_spinner("Frequency scaling:", GLUI_SPINNER_FLOAT, &baseFrequency, 0, baseFrequency_spinnerCallBack);
+  glui->add_spinner("Frequency scaling:", GLUI_SPINNER_FLOAT, &frequencyScaling, 0, frequencyScaling_spinnerCallBack);
 
   // ******** newmark beta, gamma ********
   
@@ -1054,8 +1083,17 @@ void initGLUI()
 
   glui->add_spinner_to_panel(timeStep_panel,"Substeps per timestep", GLUI_SPINNER_INT, &substepsPerTimeStep, 0, timeStepSubdivisions_spinnerCallBack);
 
-  renderOnGPU_checkbox = 
-    glui->add_checkbox("Compute u=Uq on GPU", &renderOnGPU, 0, renderOnGPU_checkBoxCallBack);
+  // ******* plastic deformations ********
+
+  GLUI_Panel * plasticDeformationsPanel = glui->add_panel("Plastic deformations", GLUI_PANEL_EMBOSSED);
+  plasticDeformationsPanel->set_alignment(GLUI_ALIGN_LEFT);
+  glui->add_checkbox_to_panel(plasticDeformationsPanel, "Enable", &plasticDeformationsEnabled, 0, plasticDeformationsEnabled_checkboxCallBack);
+  glui->add_column_to_panel(plasticDeformationsPanel, 0);
+  glui->add_spinner_to_panel(plasticDeformationsPanel, "Threshold:", GLUI_SPINNER_FLOAT, &plasticThreshold, 0, plasticDeformationsEnabled_checkboxCallBack);
+
+  // *** u = Uq ***
+
+  renderOnGPU_checkbox = glui->add_checkbox("Compute u=Uq on GPU", &renderOnGPU, 0, renderOnGPU_checkBoxCallBack);
 
   glui->add_separator();
 
@@ -1105,8 +1143,10 @@ int main(int argc, char* argv[])
   initScene();
 
   // disable the GPU UI switch if no GPU support for vertex texture fetches
-  if (deformableObjectRenderingMeshGPU == NULL)
-    renderOnGPU_checkbox->disable();
+  #ifdef USE_CG
+    if (deformableObjectRenderingMeshGPU == NULL)
+  #endif
+      renderOnGPU_checkbox->disable();
 
   glutMainLoop(); 
 
diff --git a/src/util/reducedDynamicSolver-rt/run_A b/utilities/reducedDynamicSolver-rt/run_A
old mode 100755
new mode 100644
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/run_A
rename to utilities/reducedDynamicSolver-rt/run_A
diff --git a/src/util/reducedDynamicSolver-rt/run_simpleBridge b/utilities/reducedDynamicSolver-rt/run_simpleBridge
old mode 100755
new mode 100644
similarity index 100%
rename from src/util/reducedDynamicSolver-rt/run_simpleBridge
rename to utilities/reducedDynamicSolver-rt/run_simpleBridge
diff --git a/utilities/tetMesher/tetMesher.cpp b/utilities/tetMesher/tetMesher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8cf9184214d43218b32bdf0fa4a9b969e57dab63
--- /dev/null
+++ b/utilities/tetMesher/tetMesher.cpp
@@ -0,0 +1,129 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "tet mesher" driver , Copyright (C) 2018 USC                          *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Danyong Zhao, Yijing Li, Jernej Barbic                  *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This library is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This library is distributed in the hope that it will be useful,       *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*
+  Driver for constrained 3D Delaunay tet mesher with refinement.
+  See the mesher library for more info.
+
+  The input is a manifold and self-intersection-free triangle mesh,
+  and the output is a quality tet mesh of the volume enclosed by the input mesh
+  that conforms to the input mesh.
+  This mesh is generated using constrained Delaunay tetrahedralization,
+  and by inserting new vertices (``Steiner vertices'') into the mesh as needed
+  to satisy the quality conditions.
+
+  Note: The tet mesher is experimental. It works in most cases, but
+  may generate sliver tets with degenerate inputs, such as all input points lying
+  on the same sphere in 3D.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <fstream>
+#include <iostream>
+using namespace std;
+
+#include "getopts.h"
+#include "objMesh.h"
+#include "tetMesher.h"
+#include "initPredicates.h"
+
+int main( int argc, char** argv )
+{
+  if ( argc < 3 ) 
+  {
+    std::cout << "Creates a tet mesh, by meshing the space occupied by the given input manifold surface mesh." << std::endl;
+    std::cout << "Usage: " << argv[0] << " [input surface mesh file] [output tet mesh filename] [-a alpha] [-q refinement quality] [-s max Steiner vertices] [-d minimal dihedral angles the tetmesh can have] [-t maximal seconds the refine process takes]" << std::endl;
+    std::cout << "Refinement quality is a scalar. It must be greater or equal to 1.0. The lower the number, the more the mesh will be refined. Default is 1.1." << std::endl;
+    std::cout << "Max Steiner points controls the maximum number of inserted vertices. Default: unbounded." << std::endl;
+    std::cout << "If a point is closer than alpha times the average edge length (in input mesh), skip adding this Steiner point." << std::endl;
+    return 1;
+  }
+  initPredicates();
+
+  char * inputSurfaceMeshFilename = argv[1];
+  char * ouputTetMeshFilename = argv[2];
+  char refinementQualityString[4096] = "1.1";
+  char alphaString[4096] = "1.0";
+  int maxSteinerVertices = -1;
+  char minDihedralString[4096] = "0.0";
+  char maxTimeSecondsString[4096] = "-1.0";
+
+  opt_t opttable[] =
+  {
+    { "a", OPTSTR, alphaString },
+    { "q", OPTSTR, refinementQualityString },
+    { "s", OPTINT, &maxSteinerVertices },
+    { "d", OPTSTR, minDihedralString },
+    { "t", OPTSTR, maxTimeSecondsString },
+    { NULL, 0, NULL }
+  };
+
+  argv += 2;
+  argc -= 2;
+  int optup = getopts(argc,argv,opttable);
+  if (optup != argc)
+  {
+    printf("Error parsing options. Error at option %s.\n",argv[optup]);
+  }
+
+  double refinementQuality = strtod(refinementQualityString, NULL);
+  cout << "Refinement quality is: " << refinementQuality << std::endl;
+
+  double alpha = strtod(alphaString, NULL);
+  cout << "Alpha is: " << alpha << std::endl;
+
+  double minDihedral = strtod(minDihedralString, NULL);
+	cout << "Minimal dihedral is: " << minDihedral << endl;
+
+	double maxTimeSeconds = strtod(maxTimeSecondsString, NULL);
+
+  ObjMesh * inputSurfaceMesh = new ObjMesh(inputSurfaceMeshFilename);
+
+  printf("Running the tet mesher...\n");fflush(NULL);
+  TetMesh * tetMesh = TetMesher().compute(inputSurfaceMesh, refinementQuality, alpha, minDihedral, maxSteinerVertices, maxTimeSeconds);
+  
+  if (tetMesh == NULL)
+  {
+    printf("Error running the mesher.\n");
+  }
+  else
+  {
+    printf("Saving the output mesh to %s.\n", ouputTetMeshFilename);
+    tetMesh->save(ouputTetMeshFilename);
+  }
+  delete tetMesh;
+  delete inputSurfaceMesh;
+  return(0);
+}
+
diff --git a/utilities/virtualTetsDriver/virtualTetsDriver.cpp b/utilities/virtualTetsDriver/virtualTetsDriver.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8961a4a63b87989cfbc4b3ccaf998ad5b1de3f0c
--- /dev/null
+++ b/utilities/virtualTetsDriver/virtualTetsDriver.cpp
@@ -0,0 +1,135 @@
+/*************************************************************************
+ *                                                                       *
+ * Vega FEM Simulation Library Version 4.0                               *
+ *                                                                       *
+ * "Virtual tets" driver application,                                    *
+ *  Copyright (C) 2007 CMU, 2009 MIT, 2018 USC                           *
+ *                                                                       *
+ * All rights reserved.                                                  *
+ *                                                                       *
+ * Code authors: Yijing Li, Jernej Barbic                                *
+ * http://www.jernejbarbic.com/vega                                      *
+ *                                                                       *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
+ *           Doug L. James, Jovan Popovic                                *
+ *                                                                       *
+ * Funding: National Science Foundation, Link Foundation,                *
+ *          Singapore-MIT GAMBIT Game Lab,                               *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
+ *                                                                       *
+ * This utility is free software; you can redistribute it and/or         *
+ * modify it under the terms of the BSD-style license that is            *
+ * included with this library in the file LICENSE.txt                    *
+ *                                                                       *
+ * This utility 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 file     *
+ * LICENSE.TXT for more details.                                         *
+ *                                                                       *
+ *************************************************************************/
+
+/*  
+  This is an example driver for generating a virtualized tet mesh 
+  based on the given input tet mesh and input manifold nearly self-intersecting triangle mesh.
+  It calls routines from the virtualTets library.
+*/
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <functional>
+#include <vector>
+#include "virtualTets.h"
+#include "virtualTets-via-csg.h"
+#include "commandLineParser.h"
+#include "filterIterator.h"
+#include "predicates.h"
+#include "exactOctree.h"
+#include "tetMesh.h"
+#include "objMesh.h"
+#include "profiler.h"
+using namespace std;
+
+int main(int argc, char ** argv)
+{
+  initPredicates();
+
+  int numFixedArgs = 4;
+  if (argc < numFixedArgs) 
+  {
+    cout << "Usage: " << argv[0] << " <tet mesh> <tri mesh> <output virtualized tet mesh> -w <output barycentric weight file>" << endl;
+    return 0;
+  }
+
+  char * tetMeshFilename = argv[1];
+  char * triMeshFilename = argv[2];
+  char * outputFilename = argv[3];
+  string weightFilename;
+
+  CommandLineParser parser;
+  parser.addOption("w", weightFilename);
+
+  int ret = parser.parse(argc, argv, 4);
+  if (ret != argc) 
+  {
+    cout << "Failure parsing option: " << argv[ret] << endl;
+    return 1;
+  }
+
+  // load the tet mesh and convert it to "TetMeshGeo" object
+  TetMesh vegaTetMesh(tetMeshFilename);
+  int numVertices, numTets, numEleVtx;
+  double * vertices;
+  int * tets;
+  vegaTetMesh.exportMeshGeometry(&numVertices, &vertices, &numTets, &numEleVtx, &tets);
+  TetMeshGeo tetMesh(numVertices, vertices, numTets, tets);
+  free(vertices);
+  free(tets);
+
+  // load the triangle mesh and convert it to "TriMeshGeo" object
+  ObjMesh objMesh(triMeshFilename);
+  int numTriangles;
+  int * triangles;
+  objMesh.exportGeometry(&numVertices, &vertices, &numTriangles, &triangles);
+  TriMeshGeo triMesh(numVertices, vertices, numTriangles, triangles);
+  free(vertices);
+  free(triangles);
+
+  // check if the input mesh is self-intersecting
+  ExactTriMeshOctree triMeshOctree;
+  int maxDepth = 5;
+  int maxNumTrianglesPerNode = 10;
+  triMeshOctree.build(triMesh, maxDepth, maxNumTrianglesPerNode);
+  vector<pair<int,int>> selfIintersectVector;
+  triMeshOctree.selfIntersectionExact(triMesh, selfIintersectVector);
+  if (selfIintersectVector.size() > 0) 
+  {
+    cout << "Error: the input mesh is self-intersecting." << endl;
+    return 1;
+  }
+
+  // perform the virtualization
+  BarycentricCoordinates bc;
+  TetMeshGeo newTetMesh;
+  try 
+  {
+    newTetMesh = createVirtualTetsMesh(tetMesh, triMesh, &bc);
+  } catch(int) 
+  {
+    cout << "Failed to run virtual tets algorithm" << endl;
+    return 1;
+  }
+  assert(newTetMesh.numTets() > 0);
+
+  // save to disk
+  newTetMesh.save(outputFilename);
+  if (weightFilename.size() > 0) 
+    bc.saveInterpolationWeights(weightFilename);
+
+  return 0;
+}
+
diff --git a/src/util/volumetricMeshUtilities/generateInterpolant.cpp b/utilities/volumetricMeshUtilities/generateInterpolant.cpp
similarity index 81%
rename from src/util/volumetricMeshUtilities/generateInterpolant.cpp
rename to utilities/volumetricMeshUtilities/generateInterpolant.cpp
index b8ab48a9ef6b26170c618fe8643fca150438bd14..82196c20d9ff659f3a0be650f5adfa09a1f457f7 100644
--- a/src/util/volumetricMeshUtilities/generateInterpolant.cpp
+++ b/utilities/volumetricMeshUtilities/generateInterpolant.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "generateInterpolant" utility , Copyright (C) 2007 CMU, 2009 MIT,     *
- *                                               2013 USC                *
+ *                                               2018 USC                *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -42,7 +46,7 @@ using namespace std;
 #include "matrixIO.h"
 #include "matrixMacros.h"
 #include "getopts.h"
-#include "loadList.h"
+#include "listIO.h"
 
 /*
   Builds an interpolant that can be used to efficiently transfer deformations from
@@ -67,7 +71,7 @@ int main(int argc, char ** argv)
   char outputElementFilename[4096] = "__none";
   char zeroThresholdString[4096] = "__none";
 
-  vega::opt_t opttable[] =
+  opt_t opttable[] =
   {
     { (char*)"s", OPTSTR, &outputElementFilename },
     { (char*)"z", OPTSTR, &zeroThresholdString },
@@ -89,7 +93,7 @@ int main(int argc, char ** argv)
   else
     threshold = strtod(zeroThresholdString, NULL);
 
-  vega::VolumetricMesh * volumetricMesh = vega::VolumetricMeshLoader::load(meshFile);
+  VolumetricMesh * volumetricMesh = VolumetricMeshLoader::load(meshFile);
   
   if (volumetricMesh == NULL)
   {
@@ -103,13 +107,13 @@ int main(int argc, char ** argv)
   printf("Num vertices: %d\n", n);
   printf("Num elements: %d\n", nel);
 
-  vega::ObjMesh * objMesh = new vega::ObjMesh(objMeshname);
+  ObjMesh * objMesh = new ObjMesh(objMeshname);
 
   int numInterpolationLocations = objMesh->getNumVertices();
   double * interpolationLocations = (double*) malloc (sizeof(double) * 3 * numInterpolationLocations);
   for(int i=0; i< numInterpolationLocations; i++)
   {
-    vega::Vec3d pos = objMesh->getPosition(i);
+    Vec3d pos = objMesh->getPosition(i);
     interpolationLocations[3*i+0] = pos[0];
     interpolationLocations[3*i+1] = pos[1];
     interpolationLocations[3*i+2] = pos[2];
@@ -118,17 +122,15 @@ int main(int argc, char ** argv)
   int * vertices;
   double * weights;
 
-  vector<int> elementList;
-  vector<int> * elementListp = NULL;
+  int * elementList;
+  int ** elementListp = NULL;
   if (strcmp(outputElementFilename, "__none") != 0)
   {
     elementListp = &elementList;    
   }
 
   int verbose = 1;
-  int numExternalVertices;
-  numExternalVertices = volumetricMesh->generateInterpolationWeights(
-    numInterpolationLocations, interpolationLocations, &vertices, &weights, threshold, elementListp, verbose);
+  volumetricMesh->generateInterpolationWeights(numInterpolationLocations, interpolationLocations, &vertices, &weights, threshold, elementListp, verbose);
 
   printf("Saving weights to %s...\n", outputFilename); fflush(NULL);
   volumetricMesh->saveInterpolationWeights(outputFilename, numInterpolationLocations, volumetricMesh->getNumElementVertices(), vertices, weights);
@@ -136,16 +138,17 @@ int main(int argc, char ** argv)
   if (strcmp(outputElementFilename, "__none") != 0)
   {
     set<int> uniqueElementSet;
-    for(unsigned int i=0; i<elementList.size(); i++)
+    for(int i=0; i<numInterpolationLocations; i++)
       uniqueElementSet.insert(elementList[i]);
 
     vector<int> uniqueElementList;
     for(set<int>::iterator iter = uniqueElementSet.begin(); iter != uniqueElementSet.end(); iter++)
       uniqueElementList.push_back(*iter);
 
-    vega::LoadList saveList;
+    ListIO saveList;
     sort(uniqueElementList.begin(), uniqueElementList.end());
-    saveList.save(outputElementFilename, uniqueElementList.size(), &uniqueElementList[0], 1);
+    int oneIndexed = 1;
+    saveList.save(outputElementFilename, uniqueElementList.size(), &uniqueElementList[0], oneIndexed);
   }
   printf("\n");
 
diff --git a/src/util/volumetricMeshUtilities/generateInterpolationMatrix.cpp b/utilities/volumetricMeshUtilities/generateInterpolationMatrix.cpp
similarity index 83%
rename from src/util/volumetricMeshUtilities/generateInterpolationMatrix.cpp
rename to utilities/volumetricMeshUtilities/generateInterpolationMatrix.cpp
index df1e4e58f9492b62b7be9003f425d0f399d265d6..bfc7f5f9d137a4686653729f1ed51187d9a93cf2 100644
--- a/src/util/volumetricMeshUtilities/generateInterpolationMatrix.cpp
+++ b/utilities/volumetricMeshUtilities/generateInterpolationMatrix.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "generateInterpolationMatrix" utility , Copyright (C) 2007 CMU,       *
- *                                             2009 MIT, 2013 USC        *
+ *                                             2009 MIT, 2018 USC        *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -64,7 +68,7 @@ int main(int argc, char ** argv)
 
   char zeroThresholdString[4096] = "__none";
   char interpolantFile[4096] = "__none";
-  vega::opt_t opttable[] =
+  opt_t opttable[] =
   {
     { (char*)"i", OPTSTR, &interpolantFile },
     { (char*)"z", OPTSTR, &zeroThresholdString },
@@ -86,7 +90,7 @@ int main(int argc, char ** argv)
   else
     threshold = strtod(zeroThresholdString, NULL);
 
-  vega::VolumetricMesh * volumetricMesh = vega::VolumetricMeshLoader::load(meshFile);
+  VolumetricMesh * volumetricMesh = VolumetricMeshLoader::load(meshFile);
 
   if (volumetricMesh == NULL)
   {
@@ -100,12 +104,12 @@ int main(int argc, char ** argv)
   printf("Num vertices: %d\n",n);
   printf("Num elements: %d\n",nel);
 
-  vega::ObjMesh * objMesh = new vega::ObjMesh(objMeshname);
+  ObjMesh * objMesh = new ObjMesh(objMeshname);
   int numInterpolationLocations = objMesh->getNumVertices();
   double * interpolationLocations = (double*) malloc (sizeof(double) * 3 * numInterpolationLocations);
   for(int i=0; i< numInterpolationLocations; i++)
   {
-    vega::Vec3d pos = objMesh->getPosition(i);
+    Vec3d pos = objMesh->getPosition(i);
     interpolationLocations[3*i+0] = pos[0];
     interpolationLocations[3*i+1] = pos[1];
     interpolationLocations[3*i+2] = pos[2];
@@ -131,8 +135,8 @@ int main(int argc, char ** argv)
     }    
   }
 
-  vega::SparseMatrix * A;
-  vega::GenerateInterpolationMatrix::generate(numInterpolationLocations, volumetricMesh->getNumElementVertices(), vertices, weights, &A, volumetricMesh->getNumVertices());
+  SparseMatrix * A;
+  GenerateInterpolationMatrix::generate(numInterpolationLocations, volumetricMesh->getNumElementVertices(), vertices, weights, &A, volumetricMesh->getNumVertices());
 
   A->Save(outputFilename);
 
diff --git a/src/util/volumetricMeshUtilities/generateMassMatrix.cpp b/utilities/volumetricMeshUtilities/generateMassMatrix.cpp
similarity index 75%
rename from src/util/volumetricMeshUtilities/generateMassMatrix.cpp
rename to utilities/volumetricMeshUtilities/generateMassMatrix.cpp
index b97ae9be70079239ba99e8d49231fa91aed65b56..1634d9b555f5006c37dddb50cb2dda708ad86adc 100644
--- a/src/util/volumetricMeshUtilities/generateMassMatrix.cpp
+++ b/utilities/volumetricMeshUtilities/generateMassMatrix.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "generateMassMatrix" utility , Copyright (C) 2007 CMU, 2009 MIT,      *
- *                                              2013 USC                 *
+ *                                              2018 USC                 *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code authors: Jernej Barbic                                           *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -48,12 +52,12 @@ int main(int argc, char ** argv)
     return 1;
   }
 
-  vega::VolumetricMesh * volumetricMesh = vega::VolumetricMeshLoader::load(argv[1]);
+  VolumetricMesh * volumetricMesh = VolumetricMeshLoader::load(argv[1]);
   printf("The number of vertices is: %d\n", volumetricMesh->getNumVertices());
   printf("The number of elements is: %d\n", volumetricMesh->getNumElements());
 
-  vega::SparseMatrix * massMatrix;
-  vega::GenerateMassMatrix::computeMassMatrix(volumetricMesh, &massMatrix, false);
+  SparseMatrix * massMatrix;
+  GenerateMassMatrix::computeMassMatrix(volumetricMesh, &massMatrix, false);
 
   massMatrix->Save(argv[2]);
 
diff --git a/src/util/volumetricMeshUtilities/generateSurfaceMesh.cpp b/utilities/volumetricMeshUtilities/generateSurfaceMesh.cpp
similarity index 78%
rename from src/util/volumetricMeshUtilities/generateSurfaceMesh.cpp
rename to utilities/volumetricMeshUtilities/generateSurfaceMesh.cpp
index 26fb50f04f73ce5e6f761234243f7675f7986bf2..7112fec0ed48c119431478b9039ceba42c024434 100644
--- a/src/util/volumetricMeshUtilities/generateSurfaceMesh.cpp
+++ b/utilities/volumetricMeshUtilities/generateSurfaceMesh.cpp
@@ -1,20 +1,24 @@
 /*************************************************************************
  *                                                                       *
- * Vega FEM Simulation Library Version 2.0                               *
+ * Vega FEM Simulation Library Version 4.0                               *
  *                                                                       *
  * "generateSurfaceMesh" utility , Copyright (C) 2007 CMU, 2009 MIT,     *
- *                                               2013 USC                *
+ *                                               2018 USC                *
  * All rights reserved.                                                  *
  *                                                                       *
  * Code author: Jernej Barbic                                            *
- * http://www.jernejbarbic.com/code                                      *
+ * http://www.jernejbarbic.com/vega                                      *
  *                                                                       *
- * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder,             *
+ * Research: Jernej Barbic, Hongyi Xu, Yijing Li,                        *
+ *           Danyong Zhao, Bohan Wang,                                   *
+ *           Fun Shing Sin, Daniel Schroeder,                            *
  *           Doug L. James, Jovan Popovic                                *
  *                                                                       *
  * Funding: National Science Foundation, Link Foundation,                *
  *          Singapore-MIT GAMBIT Game Lab,                               *
- *          Zumberge Research and Innovation Fund at USC                 *
+ *          Zumberge Research and Innovation Fund at USC,                *
+ *          Sloan Foundation, Okawa Foundation,                          *
+ *          USC Annenberg Foundation                                     *
  *                                                                       *
  * This utility is free software; you can redistribute it and/or         *
  * modify it under the terms of the BSD-style license that is            *
@@ -60,7 +64,7 @@ int main( int argc, char** argv )
 
   bool outputTriangleMesh = false;
 
-  vega::opt_t opttable[] =
+  opt_t opttable[] =
   {
     { (char*)"t", OPTBOOL, &outputTriangleMesh },
     { NULL, 0, NULL }
@@ -73,10 +77,10 @@ int main( int argc, char** argv )
   if (outputTriangleMesh)
     printf("Triangle mesh will be triangulated (if needed).\n");
 
-  vega::VolumetricMesh * mesh = vega::VolumetricMeshLoader::load(meshFile);
+  VolumetricMesh * mesh = VolumetricMeshLoader::load(meshFile);
 
-  vega::GenerateSurfaceMesh generateSurfaceMesh;
-  vega::ObjMesh * objMesh = generateSurfaceMesh.ComputeMesh(mesh, outputTriangleMesh);
+  GenerateSurfaceMesh generateSurfaceMesh;
+  ObjMesh * objMesh = generateSurfaceMesh.ComputeMesh(mesh, outputTriangleMesh);
   objMesh->save(outputFile);
 
   return 0;