diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ec75b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/build +/OpenSubdiv +/libigl diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..abb60c8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.8.6) + +## The path to the FindEigen file. +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") + +## Build the library. +add_subdirectory(lib) + +## Build the GUI +add_subdirectory(subdivgui) diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..c8c9e7d --- /dev/null +++ b/INSTALL @@ -0,0 +1,33 @@ +## Install the OpenSubdiv 2.x dependency + +mkdir -p OpenSubdiv +git clone https://github.com/PixarAnimationStudios/OpenSubdiv.git OpenSubdiv +( + cd OpenSubdiv + && + git reset --hard 25cee425f32758a7e0e8812da628007a8eeecce6 + && + mkdir build + && + cd build + && + cmake -DCMAKE_BUILD_TYPE=Release .. + && + make + ) + + +## To compile Release or Debug + +mkdir -p build/Release +mkdir -p build/Debug +cd build + +cd Release +cmake -DCMAKE_BUILD_TYPE=Release ../.. +make +cd .. + +cd Debug +cmake -DCMAKE_BUILD_TYPE=Debug ../.. +make diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..37ec93a --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/cmake/FindEigen3.cmake b/cmake/FindEigen3.cmake new file mode 100644 index 0000000..9c546a0 --- /dev/null +++ b/cmake/FindEigen3.cmake @@ -0,0 +1,81 @@ +# - Try to find Eigen3 lib +# +# This module supports requiring a minimum version, e.g. you can do +# find_package(Eigen3 3.1.2) +# to require version 3.1.2 or newer of Eigen3. +# +# Once done this will define +# +# EIGEN3_FOUND - system has eigen lib with correct version +# EIGEN3_INCLUDE_DIR - the eigen include directory +# EIGEN3_VERSION - eigen version + +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# Redistribution and use is allowed according to the terms of the 2-clause BSD license. + +if(NOT Eigen3_FIND_VERSION) + if(NOT Eigen3_FIND_VERSION_MAJOR) + set(Eigen3_FIND_VERSION_MAJOR 2) + endif(NOT Eigen3_FIND_VERSION_MAJOR) + if(NOT Eigen3_FIND_VERSION_MINOR) + set(Eigen3_FIND_VERSION_MINOR 91) + endif(NOT Eigen3_FIND_VERSION_MINOR) + if(NOT Eigen3_FIND_VERSION_PATCH) + set(Eigen3_FIND_VERSION_PATCH 0) + endif(NOT Eigen3_FIND_VERSION_PATCH) + + set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") +endif(NOT Eigen3_FIND_VERSION) + +macro(_eigen3_check_version) + file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) + + string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") + set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") + set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") + set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") + + set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) + if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK FALSE) + else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK TRUE) + endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + + if(NOT EIGEN3_VERSION_OK) + + message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " + "but at least version ${Eigen3_FIND_VERSION} is required") + endif(NOT EIGEN3_VERSION_OK) +endmacro(_eigen3_check_version) + +if (EIGEN3_INCLUDE_DIR) + + # in cache already + _eigen3_check_version() + set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) + +else (EIGEN3_INCLUDE_DIR) + + find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library + PATHS + ${CMAKE_INSTALL_PREFIX}/include + ${KDE4_INCLUDE_DIR} + PATH_SUFFIXES eigen3 eigen + ) + + if(EIGEN3_INCLUDE_DIR) + _eigen3_check_version() + endif(EIGEN3_INCLUDE_DIR) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) + + mark_as_advanced(EIGEN3_INCLUDE_DIR) + +endif(EIGEN3_INCLUDE_DIR) + diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000..702aebb --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 2.8.6) + +## We need C++11 +set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++") + +## We need Eigen +find_package(Eigen3 REQUIRED) +include_directories( "${EIGEN3_INCLUDE_DIR}" ) + +## We need OpenSubdiv +set(OpenSubdiv_DIR "${CMAKE_SOURCE_DIR}/OpenSubdiv") + +find_library(OPENSUBDIV_LIBRARY libosdCPU.a "${CMAKE_SOURCE_DIR}/OpenSubdiv/build/lib") +include_directories("${OpenSubdiv_DIR}/opensubdiv") + +## To prevent duplicate compilation for the static and dynamic libraries +# http://www.cmake.org/Wiki/CMake/Tutorials/Object_Library +add_library(subdivision_skinning_common_objects + OBJECT + subdivision_matrices.cpp + subdivision_matrices_eigen.cpp + subdivision_matrices_osd.cpp + subdivision_matrices_osd_eigen.cpp + subdivision_skinning.cpp + subdivision_engine.cpp + subdivision_limit_mesh.cpp + save_obj.cpp + Timing.cpp + ) + +## To prevent duplicate compilation for the static and dynamic libraries +# http://www.cmake.org/Wiki/CMake/Tutorials/Object_Library +add_library(subdivision_skinning_library_sources + OBJECT + subdivision_skinning_wrapper.cpp + ) +add_library(subdivision_skinning_library_static STATIC $ $) +add_library(subdivision_skinning_library_shared SHARED $ $) + +set_target_properties(subdivision_skinning_library_static PROPERTIES OUTPUT_NAME subdivision_skinning CLEAN_DIRECT_OUTPUT 1) +set_target_properties(subdivision_skinning_library_shared PROPERTIES OUTPUT_NAME subdivision_skinning CLEAN_DIRECT_OUTPUT 1) +target_link_libraries( + subdivision_skinning_library_shared + "${OPENSUBDIV_LIBRARY}" +) diff --git a/lib/ColorMap.h b/lib/ColorMap.h new file mode 100644 index 0000000..3ffdec4 --- /dev/null +++ b/lib/ColorMap.h @@ -0,0 +1,94 @@ +#ifndef __ColorMap_h__ +#define __ColorMap_h__ + +template < typename value_t > struct ColorMap +{ + typedef float color_component_t; + struct Color + { + color_component_t r; + color_component_t g; + color_component_t b; + + Color() : r(0), g(0), b(0) {} + Color( color_component_t r0, color_component_t g0, color_component_t b0 ) : r(r0), g(g0), b(b0) {} + + static Color lerp( const Color& a, const Color& b, value_t t ) + { + return Color( + a.r + t*(b.r-a.r), + a.g + t*(b.g-a.g), + a.b + t*(b.b-a.b) + ); + } + }; + + // The minimum value. + value_t min_value; + // The maximum value. + value_t max_value; + // The 0-th entry corresponds to values <= min_value. + // The last entry corresponds to values >= max_value. + std::vector< Color > colormap; + + ColorMap( value_t min, value_t max ) + : min_value( min ), max_value( max ) + { + SetWhiteToRed(); + } + + ColorMap() + : min_value( 0 ), max_value( 1 ) + { + SetWhiteToRed(); + } + + void SetWhiteToRed() + { + colormap.resize(2); + colormap[0] = Color( 1,1,1 ); + colormap[1] = Color( 1,0,0 ); + } + + // Returns the computed color. + Color Value2Color( const value_t val ) + { + assert( colormap.size() >= 2 ); + assert( max_value >= min_value ); + + /// 1 Find 'val' as a fraction from min_value to max_value. + /// 2 Find the percent's index into colormap. + /// 3 Find the percent between the two surrounding colors. + /// 4 Lerp the two surrounding colors in the color map. + + static const double eps = 1e-7; + + if( fabs( max_value - min_value ) < eps ) + { + return Color::lerp( colormap.at(0), colormap.at(1), .5 ); + } + + /// 1 + const value_t fraction = std::max( value_t(0), std::min( value_t(1), ( val - min_value )/(max_value - min_value) ) ); + /// 2 + const value_t real_index = fraction*( colormap.size()-1 ); + const int lower_index = std::max( 0, std::min( int(colormap.size())-2, int( real_index ) ) ); + /// 3 + value_t remaining_fraction = real_index - lower_index; + assert( remaining_fraction > -eps ); + assert( remaining_fraction < 1+eps ); + remaining_fraction = std::max( value_t(0), std::min( value_t(1), remaining_fraction ) ); + /// 4 + return Color::lerp( colormap.at( lower_index ), colormap.at( lower_index+1 ), remaining_fraction ); + } + // Directly puts the output into a pointer to three color_component_t's. + void Value2Color( const value_t val, color_component_t* rgb_out ) + { + const Color result = Value2Color( val ); + rgb_out[0] = result.r; + rgb_out[1] = result.g; + rgb_out[2] = result.b; + } +}; + +#endif /* __ColorMap_h__ */ diff --git a/lib/Timing.cpp b/lib/Timing.cpp new file mode 100644 index 0000000..38f5f8d --- /dev/null +++ b/lib/Timing.cpp @@ -0,0 +1,61 @@ +#include "Timing.hpp" + +#include +#include // gettimeofday(), getrusage() +#include // getrusage() + +#define TICTOC 1 + +static double timeofday() +{ + timeval tp ; + gettimeofday( &tp, NULL ) ; + double result = (double)tp.tv_sec + 1e-6 * (double)tp.tv_usec ; + return result ; +} +static double cpuusage() +{ + struct rusage rus ; + getrusage( RUSAGE_SELF, &rus ) ; + double user = (double)rus.ru_utime.tv_sec + 1e-6 * (double)rus.ru_utime.tv_usec ; + double sys = (double)rus.ru_stime.tv_sec + 1e-6 * (double)rus.ru_stime.tv_usec ; + + return user + sys ; +} + +static const int TICTOC_STACK_SIZE = 25 ; +static double sTicStart[ TICTOC_STACK_SIZE ] = { 0.0 } ; +static int sTicStartIndex = -1 ; +void tic( const char* txt ) +{ +#if TICTOC + sTicStartIndex += 1 ; + if( sTicStartIndex >= TICTOC_STACK_SIZE ) + { + sTicStartIndex = TICTOC_STACK_SIZE - 1 ; + std::cerr << "tic()/toc() max depth reached. Re-using top of stack.\n"; + } + + if( txt ) std::cout << '[' << txt << "] begins\n"; + + // sTicStart[ sTicStartIndex ] = timeofday() ; + sTicStart[ sTicStartIndex ] = cpuusage() ; +#endif +} +void toc( const char* txt ) +{ +#if TICTOC + if( sTicStartIndex >= 0 ) + { + // double curtime = timeofday() ; + double curtime = cpuusage() ; + if( txt ) std::cout << '[' << txt << "] "; + std::cout << "elapsed cpu time: " << (curtime - sTicStart[ sTicStartIndex ]) << std::endl ; + sTicStartIndex -= 1 ; + } + else + { + std::cerr << "Called toc() with no matching tic().\n"; + } +#endif +} diff --git a/lib/Timing.hpp b/lib/Timing.hpp new file mode 100644 index 0000000..b01d33f --- /dev/null +++ b/lib/Timing.hpp @@ -0,0 +1,15 @@ +#ifndef __Timing_hpp__ +#define __Timing_hpp__ + +void tic( const char* label = 0 ) ; +void toc( const char* label = 0 ) ; + +struct Tick +{ + Tick( const char* alabel = 0 ) : label( alabel ) { tic( label ); } + ~Tick() { toc( label ); } + + const char* label; +}; + +#endif /* __Timing_hpp__ */ diff --git a/lib/save_obj.cpp b/lib/save_obj.cpp new file mode 100644 index 0000000..58fef46 --- /dev/null +++ b/lib/save_obj.cpp @@ -0,0 +1,43 @@ +#include "save_obj.h" + +#include +#include + +namespace save_obj +{ +void save_mesh( const std::string& out_path, const std::vector< std::vector< int > >& faces, const std::vector< std::vector< real_t > >& vertices, const std::string& header_message ) +{ + std::ofstream out( out_path ); + save_mesh( out, faces, vertices, header_message ); + std::cout << "Saved a mesh to: " << out_path << '\n'; +} +void save_mesh( std::ostream& out, const std::vector< std::vector< int > >& faces, const std::vector< std::vector< real_t > >& vertices, const std::string& header_message ) +{ + // Save an optional header message. + out << header_message; + + // Save vertices. + for( const auto& vert: vertices ) + { + out << "v"; + for( const auto& coord: vert ) + { + out << ' ' << coord; + } + out << '\n'; + } + + out << '\n'; + + for( const auto& f: faces ) + { + out << 'f'; + for( const auto& vi : f ) + { + // Vertices are 1-indexed. + out << ' ' << (vi+1); + } + out << '\n'; + } +} +} diff --git a/lib/save_obj.h b/lib/save_obj.h new file mode 100644 index 0000000..8a84947 --- /dev/null +++ b/lib/save_obj.h @@ -0,0 +1,15 @@ +#ifndef __save_obj_h__ +#define __save_obj_h__ + +#include +#include +#include + +namespace save_obj +{ + typedef float real_t; + void save_mesh( const std::string& outpath, const std::vector< std::vector< int > >& faces, const std::vector< std::vector< float > >& vertices, const std::string& header_message = "" ); + void save_mesh( std::ostream& out, const std::vector< std::vector< int > >& faces, const std::vector< std::vector< float > >& vertices, const std::string& header_message = "" ); +} + +#endif /* __save_obj_h__ */ diff --git a/lib/subdivision_engine.cpp b/lib/subdivision_engine.cpp new file mode 100644 index 0000000..9231d73 --- /dev/null +++ b/lib/subdivision_engine.cpp @@ -0,0 +1,342 @@ +/* +clang++ -std=c++11 -stdlib=libc++ -g -I../../opensubdiv -I../../regression subdivision_matrices_eigen.cpp subdivision_matrices.cpp subdivision_engine.cpp -o subdivision -DSUBDIVISION_ENGINE_MAIN -I/opt/local/include/eigen3 +*/ + +#include "subdivision_engine.h" +#include +#include +#include + +const float EPS = 1e-7; +const int resolution = 10; +#define CUBE_VS 0.000000, -1.414214, 1.000000, 1.414214, 0.000000, 1.000000, -1.414214, 0.000000, 1.000000, 0.000000, 1.414214, 1.000000, -1.414214, 0.000000, -1.000000, 0.000000, 1.414214, -1.000000, 0.000000, -1.414214, -1.000000, 1.414214, 0.000000, -1.000000 +#define CUBE_FACES { {0, 1, 3, 2}, {2, 3, 5, 4}, {4, 5, 7, 6}, {6, 7, 1, 0}, {1, 7, 5, 3}, {6, 0, 2, 4} } + +#define TORUS_VS 1.25052, 0.517982, 0.353553, 0.597239, 0.247384, 0.353553, 0.597239, 0.247384, -0.353553, 1.25052, 0.517982, -0.353553, 0.517982, 1.25052, 0.353553, 0.247384, 0.597239, 0.353553, 0.247384, 0.597239, -0.353553, 0.517982, 1.25052, -0.353553, -0.517982, 1.25052, 0.353553, -0.247384, 0.597239, 0.353553, -0.247384, 0.597239, -0.353553, -0.517982, 1.25052, -0.353553, -1.25052, 0.517982, 0.353553, -0.597239, 0.247384, 0.353553, -0.597239, 0.247384, -0.353553, -1.25052, 0.517982, -0.353553, -1.25052, -0.517982, 0.353553, -0.597239, -0.247384, 0.353553, -0.597239, -0.247384, -0.353553, -1.25052, -0.517982, -0.353553, -0.517982, -1.25052, 0.353553, -0.247384, -0.597239, 0.353553, -0.247384, -0.597239, -0.353553, -0.517982, -1.25052, -0.353553, 0.517982, -1.25052, 0.353553, 0.247384, -0.597239, 0.353553, 0.247384, -0.597239, -0.353553, 0.517982, -1.25052, -0.353553, 1.25052, -0.517982, 0.353553, 0.597239, -0.247384, 0.353553, 0.597239, -0.247384, -0.353553, 1.25052, -0.517982, -0.353553 +#define TORUS_FACES { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } + +namespace subdivision_matrix +{ + + MatrixXX_t shepard( const MatrixX3_t& positions, const MatrixX3_t& handle_positions ) + { + int row_num = positions.rows(); + int col_num = handle_positions.rows(); + + MatrixXX_t weights( row_num, col_num ); + + for ( int i = 0; i < row_num; i++ ) { + int flag = -1; + for ( int j = 0; j < col_num; j++ ) { + vertex_t diff = positions.row( i ) - handle_positions.row( j ); + weights( i, j ) = diff.dot( diff ); + // if a position is on the handle, mark the handle's index + if ( weights( i, j ) <= EPS ) + flag = j; + } + // if a position is on any handle, make its weight 1.0. + if ( flag >= 0 ) { + for ( int j = 0; j < col_num; j++ ) { + weights( i, j ) = ( flag == j ? 1.0 : 0.0 ); + } + continue; + } + + for ( int j = 0; j < col_num; j++ ) { + weights( i, j ) = 1.0 / weights( i, j ); + } + weights.row( i ) /= weights.row( i ).sum(); + + } + + return weights; + } + +//-------------------------------------- PREPARE METHODS -------------------------------------- + void prepare( const subdivision_control_mesh& mesh, const MatrixX3_t& handle_positions, const char* weight_function, + std::vector< MatrixX4_t > & W_Array ) + { + /// 1 Get the sparse subdivision coefficients at many places around the mesh. + /// 2 Compute the weights and areas for every point around the mesh. + + /// 1 + std::vector< real_t > us, vs; + createUVs( resolution, resolution, us, vs ); + + SparseMatrix_t M_matrices, Du_matrices, Dv_matrices; + compute_subdivision_coefficients_for_mesh( + mesh.vs.rows(), + mesh.faces, + us, vs, + M_matrices, &Du_matrices, &Dv_matrices ); + + + std::cout << M_matrices.rows() << ' ' << M_matrices.cols() << std::endl; + + prepare( mesh.vs, M_matrices, Du_matrices, Dv_matrices, handle_positions, weight_function, W_Array ); + } + + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, + const MatrixXX_t& weights, std::vector< MatrixX4_t > & W_Array ) + { + /// areas is a column vector whose number of rows equals to the number of all positions. + DiagonalMatrix_t areas( M_matrices.rows() ); + // std::cout << "areas size: " << M_matrices.rows() << '\n'; + areas.setIdentity(); + prepare( vs, M_matrices, areas, weights, W_Array ); + } + + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const DiagonalMatrix_t& areas, + const MatrixXX_t& weights, std::vector< MatrixX4_t > & W_Array ) + { + /// Compute the weights and areas for every point around the mesh. + /// areas is a column vector whose number of rows equals to the number of all positions. + + /// build system. + const MatrixXX_t system = ( M_matrices.transpose() * areas * M_matrices ).eval(); + + /// set up LLT solver and examine if the system can be inverted. + Eigen::LLT< MatrixXX_t > llt; + llt.compute( system ); + if ( Eigen::Success == llt.info () ) + ; //std::cout << "succeed." << std::endl; + else if ( Eigen::NumericalIssue == llt.info () ) + std::cout << "numerical issue." << std::endl; + + + /// set up an identity matrix for compute the inverse of system. + MatrixX4_t controls( vs.rows(), 4 ); + controls.leftCols( 3 ) = vs; + controls.col( 3 ).setOnes(); + + // std::cout << "M_matrices rows: " << M_matrices.rows() << '\n'; + // std::cout << "M_matrices cols: " << M_matrices.cols() << '\n'; + // std::cout << "weights size: " << weights.col(0).asDiagonal().rows() << '\n'; + + for( int k = 0; k < weights.cols(); k++ ) { + const MatrixX4_t extra = ( controls.transpose() * M_matrices.transpose() * areas * weights.col(k).asDiagonal() * M_matrices ).transpose(); + W_Array.push_back( llt.solve( extra ) ); + } + + return; + } + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const DiagonalMatrix_t& areas, + const MatrixX3_t handle_positions, const char* weight_function, + std::vector< MatrixX4_t > & W_Array ) + { + /// Compute the weights and areas for every point around the mesh. + /// areas is a column vector whose number of rows equals to the number of all positions. + + MatrixX3_t positions( M_matrices.rows(), 3 ); + positions = M_matrices * vs; + + /// weights are a dense matrix whose number of rows equales to the number of all positions, + /// and the number of columns equals to the number of handles. + MatrixXX_t weights; + if( strcmp( weight_function, "harmonic" ) == 0) { + // weights = harmonic( mesh, positions, handle_positions ); + + } else if ( strcmp( weight_function, "shepard" ) == 0 ) { + weights = shepard( positions, handle_positions ); + } + + prepare( vs, M_matrices, areas, weights, W_Array ); + } + + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const SparseMatrix_t& Du_matrices, const SparseMatrix_t& Dv_matrices, + const MatrixX3_t& handle_positions, const char* weight_function, + std::vector< MatrixX4_t > & W_Array ) + { + /// areas is a column vector whose number of rows equals to the number of all positions. + DiagonalMatrix_t areas( M_matrices.rows() ); + MatrixX3_t du_list = Du_matrices * vs; + MatrixX3_t dv_list = Dv_matrices * vs; + Eigen::Matrix< real_t, 1, 3 > vec1, vec2; + for( int i = 0; i < M_matrices.rows(); ++i ) + { + vec1 = du_list.row( i ); + vec2 = dv_list.row( i ); + // Divide by the number of rows, which is like multiplying by dt. + areas.diagonal()[i] = ( vec1.cross( vec2 ).norm() ) / M_matrices.rows(); + } + + prepare( vs, M_matrices, areas, handle_positions, weight_function, W_Array ); + } + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, + const MatrixX3_t& handle_positions, const char* weight_function, + std::vector< MatrixX4_t > & W_Array ) + { + /// areas is a column vector whose number of rows equals to the number of all positions. + DiagonalMatrix_t areas( M_matrices.rows() ); + areas.setIdentity(); + prepare( vs, M_matrices, areas, handle_positions, weight_function, W_Array ); + } + + void naive_prepare( const MatrixX3_t& vs, const MatrixX3_t& handle_positions, + std::vector< MatrixX4_t > & W_Array ) + { + MatrixXX_t weights = shepard( vs, handle_positions ); + + // build the homogeneous coordinates for control points. + MatrixX4_t controls( vs.rows(), 4 ); + controls.leftCols( 3 ) = vs; + controls.col( 3 ).setOnes(); + for( int k = 0; k < weights.cols(); k++ ) { + MatrixXX_t wi( weights.rows(), 4 ); + for ( int i = 0; i < 4; i++ ) + wi.col(i) = weights.col(k); + + W_Array.push_back( controls.cwiseProduct( wi ) ); + } +// std::cout << "naive prepare finished\n"; + } + +//-------------------------------------- PREPARE METHODS END-------------------------------------- + + void solve( const std::vector< MatrixX4_t >& W_Array, + const std::vector< transform_t >& transforms, + MatrixX3_t & result ) + { + + assert( W_Array.size() == transforms.size() ); + + MatrixXX_t rhs( W_Array[0] ); + rhs.setZero(); + + for( int i = 0; i < transforms.size(); i++ ) + { + rhs += ( W_Array[i] * transforms[i].transpose() ); + } + + result = rhs.leftCols( 3 ); + + return; + } + + + void compute_target_surface( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, + const MatrixX3_t& handle_positions, const std::vector< transform_t >& transforms, + MatrixX3_t& result ) + { + MatrixX3_t positions( M_matrices.rows(), 3 ); + positions = M_matrices * vs; + + std::vector< MatrixX4_t > W_Array; + naive_prepare( positions, handle_positions, W_Array ); + + solve( W_Array, transforms, result ); + +// std::cout << "target surface computed.\n"; + } + + VectorX_t piecewise_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ) + { + MatrixX3_t diff_mat = deformed_surface - target_surface; + VectorX_t result = diff_mat.rowwise().norm(); + + return result; + } + + real_t norm_of_piecewise_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ) + { + MatrixX3_t diff_mat = deformed_surface - target_surface; + return diff_mat.norm(); + } + + + real_t hausdorff_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ) + { + assert( target_surface.rows() == deformed_surface.rows() ); + int num = target_surface.rows(); + + VectorX_t min_diffs( num, 1 ); + for( int i = 0; i < num; i++ ) { + VectorX_t diffs = ( deformed_surface.rowwise() - target_surface.row(i) ).rowwise().norm(); + min_diffs(i) = diffs.minCoeff(); + } + + return min_diffs.maxCoeff(); + } +} + +#ifdef SUBDIVISION_ENGINE_MAIN +void usage( const char* argv0 ) +{ + std::cerr << "Usage: " << argv0 << " test_subdivision_engine\n"; + exit( -1 ); +} + +void print_mesh( const subdivision_matrix::subdivision_control_mesh& mesh ); + +void test( const char *type ) +{ + subdivision_matrix::MatrixX3_t handle_positions; + handle_positions.resize(2,3); + handle_positions << 0.8, 0.8, 0.7, + 0.2, -0.3, -0.2; + + // make a mesh + subdivision_matrix::subdivision_control_mesh mesh; + + // use the cube! has problem! +// mesh.vs.resize( 8, 3 ); +// mesh.vs << CUBE_VS; +// mesh.faces = CUBE_FACES; + + mesh.vs.resize( 32, 3 ); + mesh.vs << TORUS_VS; + mesh.faces = TORUS_FACES; + + + std::vector< subdivision_matrix::MatrixX4_t > W_Array; + if ( strcmp( type, "naive") == 0 ) + subdivision_matrix::naive_prepare( mesh.vs, handle_positions, W_Array ); + else + subdivision_matrix::prepare( mesh, handle_positions, "shepard", W_Array ); + + for (auto W_i : W_Array ) + std::cout << W_i << std::endl; + + std::vector< subdivision_matrix::transform_t > transforms; + subdivision_matrix::transform_t t1, t2; + t1.setIdentity(); + t2.setIdentity(); + transforms.push_back( t1 ); + transforms.push_back( t2 ); + + subdivision_matrix::MatrixX3_t new_controls; + subdivision_matrix::solve( W_Array, transforms, new_controls ); + + std::cout << "result\n" << new_controls << std::endl; +} + +int main( int argc, char* argv[] ) +{ + using std::string; + +// if( argc != 1 ) usage( argv[0] ); + + if( argc == 1 ) + test( "ours" ); + else + test( argv[1]); + + return 0; +} +#endif +void print_mesh( const subdivision_matrix::subdivision_control_mesh& mesh ) +{ + std::cout << "vertices\n"; + std::cout << mesh.vs << std::endl; + std::cout << "faces\n"; + int face_offset = 0; + for( const auto& num_face_vertices : mesh.faces.first ) + { + for( int fvi = 0; fvi < num_face_vertices; ++fvi ) + { + std::cout << mesh.faces.second.at( face_offset + fvi ) << ' '; + } + face_offset += num_face_vertices; + std::cout << '\n'; + } +} diff --git a/lib/subdivision_engine.h b/lib/subdivision_engine.h new file mode 100644 index 0000000..708e7d8 --- /dev/null +++ b/lib/subdivision_engine.h @@ -0,0 +1,57 @@ +#ifndef __subdivision_engine_h__ +#define __subdivision_engine_h__ + +#include "subdivision_matrices_eigen.h" +#include "subdivision_matrices_osd_eigen.h" +#include +#include +#include "Timing.hpp" + +namespace subdivision_matrix +{ + // Declare the mesh structure. + struct subdivision_control_mesh + { + // This should be an N-by-3 dense matrix. + MatrixX3_t vs; + // This is a vector of faces. + faces_t faces; + }; + + typedef Eigen::DiagonalMatrix< real_t, Eigen::Dynamic > DiagonalMatrix_t; + + void prepare( const subdivision_control_mesh& mesh, const MatrixX3_t& handle_positions, const char * weight_function, + std::vector< MatrixX4_t > & W_Array ); + + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const SparseMatrix_t& Du_matrices, const SparseMatrix_t& Dv_matrices, + const MatrixX3_t& handle_positions, const char* weight_function, + std::vector< MatrixX4_t > & W_Array ); + + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, + const MatrixX3_t& handle_positions, const char* weight_function, + std::vector< MatrixX4_t > & W_Array ); + + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, const DiagonalMatrix_t& areas, + const MatrixXX_t& weights, std::vector< MatrixX4_t > & W_Array ); + + void prepare( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, + const MatrixXX_t& weights, std::vector< MatrixX4_t > & W_Array ); + + void solve( const std::vector< MatrixX4_t >& W_Array, const std::vector< transform_t >& transforms, MatrixX3_t & result ); + + // naive approach + void naive_prepare( const MatrixX3_t& vs, const MatrixX3_t& handle_positions, + std::vector< MatrixX4_t > & W_Array ); + + void compute_target_surface( const MatrixX3_t& vs, const SparseMatrix_t& M_matrices, + const MatrixX3_t& handle_positions, const std::vector< transform_t >& transforms, + MatrixX3_t& result ); + + VectorX_t piecewise_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ); + + real_t norm_of_piecewise_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ); + + real_t hausdorff_distance( const MatrixX3_t& target_surface, const MatrixX3_t& deformed_surface ); +} + +#endif diff --git a/lib/subdivision_limit_mesh.cpp b/lib/subdivision_limit_mesh.cpp new file mode 100644 index 0000000..61fb94f --- /dev/null +++ b/lib/subdivision_limit_mesh.cpp @@ -0,0 +1,260 @@ +// c++ -g -std=c++11 -I/Users/yotam/Work/ext/OpenSubdiv/opensubdiv -I/Users/yotam/Work/ext/OpenSubdiv/regression subdivision_limit_mesh.cpp subdivision_matrices.cpp -o subdivision_limit_mesh -DSUBDIVISION_LIMIT_MESH_MAIN + +#include "subdivision_limit_mesh.h" +#include "save_obj.h" + +#include +#include + +namespace subdivision_limit_mesh +{ + +void deduplicate_vertices_and_indices( subdivision_matrix::MatrixX3_t duplicated_vertices, std::vector< std::vector< real_t > >& unique_vertices, std::vector< std::vector< int > >& limit_quad_faces_out ); +void efficient_deduplicate_vertices_and_indices( int num_u, int num_v, subdivision_matrix::MatrixX3_t duplicated_vertices, std::vector< std::vector< real_t > >& unique_vertices, std::vector< std::vector< int > >& limit_quad_faces_out ) ; + +// The same as subdivision_matrices::createUVs() but samples 0 and 1. +// This is a helper function for compute_quad_limit_mesh_for_mesh(). +void createUVsWithZeroAndOne( int NUM_U, int NUM_V, std::vector< real_t >& us, std::vector< real_t >& vs ) +{ + assert( NUM_U > 0 ); + assert( NUM_V > 0 ); + + us.clear(); + vs.clear(); + + us.reserve( NUM_U * NUM_V ); + vs.reserve( NUM_U * NUM_V ); + + /// 1 Create the u and v samples, which are identical to the ones in createUVs() + /// but have 0 and 1 at the beginning and end. + std::vector< real_t > u_samples, v_samples; + u_samples.reserve( NUM_U + 2 ); + u_samples.push_back( 0. ); + for( int ui = 0; ui < NUM_U; ++ui ) + { + // Sample from .5 to .95, so that each sample has an equal piece of area. + const real_t u = real_t( ui + 0.5 )/NUM_U; + u_samples.push_back( u ); + } + u_samples.push_back( 1.0 ); + assert( u_samples.size() == NUM_U + 2 ); + + v_samples.reserve( NUM_U + 2 ); + v_samples.push_back( 0. ); + for( int vi = 0; vi < NUM_V; ++vi ) + { + // Sample from .5 to .95, so that each sample has an equal piece of area. + const real_t v = real_t( vi + 0.5 )/NUM_V; + v_samples.push_back( v ); + } + v_samples.push_back( 1.0 ); + assert( v_samples.size() == NUM_V + 2 ); + + /// 2 Put the Cartesian product in us and vs. + for( int ui = 0; ui < u_samples.size(); ++ui ) + { + const real_t u = u_samples.at(ui); + for( int vi = 0; vi < v_samples.size(); ++vi ) + { + const real_t v = v_samples.at(vi); + + us.push_back( u ); + vs.push_back( v ); + } + } +} + +void compute_quad_limit_mesh_for_mesh( + const subdivision_matrix::MatrixX3_t& vertices, + OpenSubdiv::HbrMesh* mesh, + int num_u, int num_v, + std::vector< std::vector< real_t > >& limit_vertices_out, + std::vector< std::vector< int > >& limit_quad_faces_out + ) +{ + /// 1 createUVs just like subdivision_matrices::createUVs() except with 0 and 1 in each row/column. + /// 2 Extract the M matrix for computing limit vertex positions. + /// 3 Calculate the positions. + /// 4 Create a quad mesh for every "num_u+2 x num_v+2" vertices (they sample a control mesh face). + /// 5 De-duplicate the vertices. + + /// 1 + std::vector< real_t > us, vs; + createUVsWithZeroAndOne( num_u, num_v, us, vs ); + + + /// 2 + subdivision_matrix::SparseMatrix_t positions; + subdivision_matrix::compute_subdivision_coefficients_for_mesh( + vertices.rows(), mesh, + us, vs, + positions + ); + + + /// 3 + subdivision_matrix::MatrixX3_t intermediate_vertices = positions * vertices; +// std::cout << intermediate_vertices.rows() << " " << intermediate_vertices.size() << std::endl; +// limit_vertices_out = positions * vertices; + + + /// 4 + // Because of how createUVsWithZeroAndOne() works, we know that + // every "num_u+2 x num_v+2" vertices sample a control mesh quad face in a grid. + // Create faces for the sub-quads that make up this grid. + // The order of elements in 'limit_vertices_out' is, for each control mesh quad face: + // (0,0), (0,.5/num_v), ..., (0,1), (.5/num_u,0), (.5/num_u,.5/num_v), ..., (.5/num_u,1), ... + // In other words, every 'num_v+2' vertices forms a column (strip of constant u value). + // The number of vertices should be an integer multiple of the number of control mesh quad faces + // (n.b. the number of control mesh quad faces may be increased by extraordinary vertex sub-faces). + assert( intermediate_vertices.rows() % ( (num_u+2)*(num_v+2) ) == 0 ); + for( int off = 0; off < intermediate_vertices.rows(); off += (num_u+2)*(num_v+2) ) + { + // Iterate over each quad and add it to the faces. + for( int u_off = 1; u_off < num_u + 2; ++u_off ) + { + for( int v_off = 1; v_off < num_v + 2; ++v_off ) + { + // TODO: Add the face: + // (off + (u_off-1,v_off-1)), (off + (u_off-1,v_off)), (off + (u_off,v_off)), (off + (u_off,v_off-1)), clockwise + // To convert from (off + (ui,vi)) to an actual vertex index, + // it is something like: off + ui*(num_u+2) + vi. + std::vector< int > quad{(off + (u_off-1)*(num_u+2) + v_off-1), + (off + (u_off-1)*(num_u+2) + v_off), + (off + u_off*(num_u+2) + v_off), + (off + u_off*(num_u+2) + v_off-1)}; + limit_quad_faces_out.push_back( quad ); + } + + } + } + + /// 5 + // TODO: De-duplicate vertices with the same position. +// deduplicate_vertices_and_indices( intermediate_vertices, limit_vertices_out, limit_quad_faces_out ); + efficient_deduplicate_vertices_and_indices( num_u+2, num_v+2, intermediate_vertices, limit_vertices_out, limit_quad_faces_out ); + /// 6 Save the vertices and faces to an obj file. + save_obj::save_mesh( "model.obj", limit_quad_faces_out, limit_vertices_out ); + + for( auto quad: limit_quad_faces_out ) + { + for( auto index : quad ) + std::cout << index << " "; + std::cout << std::endl; + } + /* test + for( int i=0; i v; + for( int j=0; j >& unique_vertices, std::vector< std::vector< int > >& limit_quad_faces_out ) +{ + int old_num = duplicated_vertices.rows(); + int old2new [ old_num ]; + std::vector< real_t > vertex { 0, 0, 0 }; + + for( int i=0; i >& unique_vertices, std::vector< std::vector< int > >& limit_quad_faces_out ) +{ + int old_num = duplicated_vertices.rows(); + int old2new [ old_num ]; + struct lookup { + int index; + std::vector< real_t > pos; + }; + std::vector< lookup > lookup_list; + + for( int i=0; i vertex { duplicated_vertices(i,0), duplicated_vertices(i,1), duplicated_vertices(i,2) }; + + int res = i % ( num_u*num_v ); + if( res < num_v or res >= (num_u-1)*num_v or res % num_v == 0 or ( (res+1) % num_v ) == 0 ) { + int p = 0; + for( auto element : lookup_list ) { + std::vector< real_t > pos = element.pos; + if ( pos[0] == vertex[0] and pos[1] == vertex[1] and pos[2] == vertex[2] ) + break; + p++; + } + if( p != lookup_list.size() ) + old2new[ i ] = lookup_list[p].index; + else { + unique_vertices.push_back( vertex ); + old2new[ i ] = unique_vertices.size()-1; + + lookup new_lookup; + new_lookup.index = old2new[ i ]; + new_lookup.pos = vertex; + lookup_list.push_back( new_lookup ); + } + + } + else { + unique_vertices.push_back( vertex ); + old2new[ i ] = unique_vertices.size()-1; + } + } + + // now re-write the vertex indices in faces. + + for( int i=0; i* mesh, + int num_u, int num_v, + std::vector< std::vector< real_t > >& vertices_out, + std::vector< std::vector< int > >& faces_out + ); +} + +#ifdef SUBDIVISION_LIMIT_MESH_HEADER_ONLY +#include "subdivision_limit_mesh.cpp" +#endif + +#endif /* __subdivision_limit_mesh_h__ */ diff --git a/lib/subdivision_matrices.cpp b/lib/subdivision_matrices.cpp new file mode 100644 index 0000000..f287f19 --- /dev/null +++ b/lib/subdivision_matrices.cpp @@ -0,0 +1,446 @@ +// c++ -g -std=c++11 -I/Users/yotam/Work/ext/OpenSubdiv/opensubdiv -I/Users/yotam/Work/ext/OpenSubdiv/regression subdivision_matrices.cpp -o subdivision_matrices -DSUBDIVISION_MATRICES_MAIN + +#include "subdivision_matrices.h" + +#include +#include + +namespace +{ + +/* +Given the number of vertices in the mesh 'num_vertices' +and a vector of faces 'faces', each element of which is a vector of vertex indices, +returns a new mesh object suitable for passing to +Free the resulting mesh with "delete" when finished with it. +*/ +OpenSubdiv::HbrMesh* newMeshFromNumVerticesAndFaces( int num_vertices, const subdivision_matrix::faces_t& faces ) +{ + assert( num_vertices > 0 ); + + // Following: http://graphics.pixar.com/opensubdiv/forum.html?place=msg%2Fopensubdiv%2FzKq9vG_azHQ%2FDb2K7L23BykJ + + static OpenSubdiv::HbrCatmarkSubdivision CATMULL_CLARK; + + auto mesh = new OpenSubdiv::HbrMesh( &CATMULL_CLARK ); + + OpenSubdiv::FarStencilFactoryVertex default_vertex_value; + for( int vi = 0; vi < num_vertices; ++vi ) mesh->NewVertex( vi, default_vertex_value ); + + /// TODO Q: Is this necessary? + // int ptex_index = 0; + int face_offset = 0; + for( const auto& num_face_vertices : faces.first ) + { + OpenSubdiv::HbrFace* face = mesh->NewFace( num_face_vertices, &faces.second[face_offset], 0 ); + + /* + face->SetPtexIndex( ptex_index ); + if( f.size() != 4 ) { + ptex_index += f.size(); + } else { + ptex_index += 1; + } + */ + + face_offset += num_face_vertices; + } + + mesh->SetInterpolateBoundaryMethod( OpenSubdiv::HbrMesh::k_InterpolateBoundaryAlwaysSharp ); + + mesh->Finish(); + + return mesh; +} + +} + +namespace subdivision_matrix +{ + +/* +Given a desired number of "u" and "v" parameters sampling the range [0,1]x[0,1], +returns in the output vectors 'us' and 'vs' coordinates sampling the range +in equal intervals. +*/ +void createUVs( int NUM_U, int NUM_V, std::vector< real_t >& us, std::vector< real_t >& vs ) +{ + assert( NUM_U > 0 ); + assert( NUM_V > 0 ); + + us.clear(); + vs.clear(); + + us.reserve( NUM_U * NUM_V ); + vs.reserve( NUM_U * NUM_V ); + + /// XXX NOTE: Keep this in sync with subdivision_limit_mesh::createUVsWithZeroAndOne() + for( int ui = 0; ui < NUM_U; ++ui ) + { + // Sample from .5 to .95, so that each sample has an equal piece of area. + const real_t u = real_t( ui + 0.5 )/NUM_U; + // const real_t u = real_t( ui )/(NUM_U-1); + for( int vi = 0; vi < NUM_V; ++vi ) + { + // Sample from .5 to .95, so that each sample has an equal piece of area. + const real_t v = real_t( vi + 0.5 )/NUM_V; + // const real_t v = real_t( vi )/(NUM_V-1); + + us.push_back( u ); + vs.push_back( v ); + } + } +} + +/* +Given a mesh, +two same-length vectors 'us' and 'vs' where the ( us[i], vs[i] ) are the uv locations, +and an optional integer parameter 'reflevel' ("In most cases only approximate evaluation +is done by linearly interpolating between limit values after refLevel subdivision steps."), +returns a FarStencilTables object suitable for calling UpdateValues() and UpdateDerivs(). +*/ +OpenSubdiv::FarStencilTables precomputeStencils( + OpenSubdiv::HbrMesh* mesh, + const std::vector< float >& us, + const std::vector< float >& vs, + int reflevel = 5 + ) +{ + assert( mesh ); + assert( us.size() == vs.size() ); + assert( !us.empty() ); + assert( reflevel > 0 ); + + OpenSubdiv::FarStencilTables controlStencils; + + OpenSubdiv::FarStencilTablesFactory<> factory(mesh); + + // std::cout << "Stencil num faces: " << mesh->GetNumFaces() << '\n'; + + // Check if we are using catmull-clark subdivision. + // const bool catmark = 0 != dynamic_cast< OpenSubdiv::HbrCatmarkSubdivision* >( mesh->GetSubdivision() ); + + // XXXX UDPATE: mesh->GetNumFaces() changes during calls to AppendStencils()! + // We end up with lots of duplicate stencils if we don't store the + // initial value of GetNumFaces(). Better yet, store GetNumCoarseFaces(). + // I checked the HbrMesh code and the initial non-NULL faces are all + // coarse. I'm not sure how a NULL face could get into the structure, + // so let's assert that faces aren't NULL. + const int nfaces = mesh->GetNumCoarseFaces(); + for( int i = 0; i < nfaces; ++i ) { + + const OpenSubdiv::HbrFace* f = mesh->GetFace(i); + assert( f ); + + const int nv = f->GetNumVertices(); + + // TODO Q: Why doesn't this die with loop subdivision? + // if( catmark && nv!=4 ) { + if( nv!=4 ) { + + // if the face is not a quad, we have to iterate over sub-quad(rants) + for (int j=0; jGetNumVertices(); ++j) { + + // std::cout << "Non-quad face with " << nv << " vertices.\n"; + + factory.SetCurrentFace(i,j); + + const int appended = factory.AppendStencils( &controlStencils, us.size(), &us[0], &vs[0], reflevel ); + // std::cout << "Appended " << appended << " stencils.\n"; + } + } else { + + factory.SetCurrentFace(i); + + const int appended = factory.AppendStencils( &controlStencils, us.size(), &us[0], &vs[0], reflevel ); + // std::cout << "Appended " << appended << " stencils.\n"; + } + } + + return controlStencils; +} + +/* +Given OpenSubdiv::FarStencilTables as returned by precomputeStencils() +and the number of control points 'num_control_points' +fills 'positions_out' with sparse vectors such that the position +for the i-th uv value in the original call to precomputeStencils() can be obtained by: + \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second + +The optional parameters 'du_out' and 'dv_out', if specified, are similar to 'positions_out' +except that the above summation results in du and dv. +*/ +void sparseVectorsForPositionsAndDerivativesFromFarStencilTables( + const OpenSubdiv::FarStencilTables& controlStencils, + int num_control_points, + std::vector< sparse_vector_t >& positions_out, + std::vector< sparse_vector_t >* du_out = nullptr, + std::vector< sparse_vector_t >* dv_out = nullptr + ) +{ + /// 1 Create a vector of control points, where each control point is a sparse vector containing only its integer index. + /// 2 Create a vector of output coefficients, whose length is the total number + /// of all positions on the mesh we are evaluating. + /// 3 Create adapters for passing to OpenSubdiv::FarStencilTables.UpdateValues(). + /// 4 Call OpenSubdiv::FarStencilTables.UpdateValues(). + /// 5 Repeat with OpenSubdiv::FarStencilTables.UpdateDerivs(). + + assert( num_control_points > 0 ); + // The derivative outputs, du_out and dv_out, + // must be either both null or both given. + assert( ( du_out == nullptr ) == ( dv_out == nullptr ) ); + // du_out and dv_out must be distinct unless they are both null. + assert( du_out != dv_out || du_out == nullptr ); + + /// 1 + // controlPoints are just indices! + std::vector< sparse_vector_t > controlPoints( num_control_points ); + static const real_t kMagicValue{ 31337 }; + for( int i = 0; i < num_control_points; ++i ) controlPoints[i].push_back( std::make_pair( i, kMagicValue ) ); + + + /// 2 + positions_out.clear(); + positions_out.resize( controlStencils.GetNumStencils() ); + + + /// 3 + struct SparseVectorAdapter + { + sparse_vector_t* data; + + void Clear() { assert( data ); data->clear(); } + void AddWithWeight( const SparseVectorAdapter& other, real_t value ) + { + assert( data ); + assert( other.data ); + assert( other.data->size() == 1 ); + assert( (*other.data)[0].second == kMagicValue ); + + const index_t index = (*other.data)[0].first; + data->push_back( std::make_pair( index, value ) ); + } + }; + + std::vector< SparseVectorAdapter > controlPoints_adapters( controlPoints.size() ); + for( int i = 0; i < controlPoints_adapters.size(); ++i ) controlPoints_adapters[i].data = &controlPoints[i]; + + std::vector< SparseVectorAdapter > positions_adapters( positions_out.size() ); + for( int i = 0; i < positions_adapters.size(); ++i ) positions_adapters[i].data = &positions_out[i]; + + + /// 4 + controlStencils.UpdateValues< SparseVectorAdapter >( &controlPoints_adapters[0], &positions_adapters[0] ); + + + if( du_out && dv_out ) + { + du_out->clear(); + dv_out->clear(); + du_out->resize( controlStencils.GetNumStencils() ); + dv_out->resize( controlStencils.GetNumStencils() ); + + std::vector< SparseVectorAdapter > du_adapters( du_out->size() ); + std::vector< SparseVectorAdapter > dv_adapters( dv_out->size() ); + for( int i = 0; i < du_adapters.size(); ++i ) du_adapters[i].data = &(*du_out)[i]; + for( int i = 0; i < du_adapters.size(); ++i ) dv_adapters[i].data = &(*dv_out)[i]; + + controlStencils.UpdateDerivs< SparseVectorAdapter >( &controlPoints_adapters[0], &du_adapters[0], &dv_adapters[0] ); + } +} + +/* +Given the number of vertices in the mesh 'num_vertices' +and a vector of faces 'faces', each element of which is a vector of vertex indices, and +two same-length vectors 'us' and 'vs' where the ( us[i], vs[i] ) are the uv locations, +fills 'positions_out' with sparse vectors such that the position +for the i-th uv value in the original call to precomputeStencils() can be obtained by: + \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second + +The optional parameters 'du_out' and 'dv_out', if specified, are similar to 'positions_out' +except that the above summation results in du and dv. +*/ +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + const faces_t& faces, + const std::vector< real_t >& us, + const std::vector< real_t >& vs, + std::vector< sparse_vector_t >& positions_out, + std::vector< sparse_vector_t >* du_out, + std::vector< sparse_vector_t >* dv_out + ) +{ + auto mesh = newMeshFromNumVerticesAndFaces( num_vertices, faces ); + compute_subdivision_coefficients_for_mesh( num_vertices, mesh, us, vs, positions_out, du_out, dv_out ); + delete mesh; +} + +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + OpenSubdiv::HbrMesh* mesh, + const std::vector< real_t >& us, + const std::vector< real_t >& vs, + std::vector< sparse_vector_t >& positions_out, + std::vector< sparse_vector_t >* du_out, + std::vector< sparse_vector_t >* dv_out + ) +{ + std::vector< float > real_us, real_vs; + for ( auto u : us ) + real_us.push_back( u ); + for ( auto v : vs ) + real_vs.push_back( v ); + + auto precomputed = precomputeStencils( mesh, real_us, real_vs ); + sparseVectorsForPositionsAndDerivativesFromFarStencilTables( precomputed, num_vertices, positions_out, du_out, dv_out ); +} + +} // ~namespace subdivision_matrix() + +#ifdef SUBDIVISION_MATRICES_MAIN +void print_sparse_vectors( const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::string& label ); + +#define TORUS 32, { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } +#define CUBE 8, { {0, 1, 3, 2}, {2, 3, 5, 4}, {4, 5, 7, 6}, {6, 7, 1, 0}, {1, 7, 5, 3}, {6, 0, 2, 4} } + +void test_newMesh_sparseVectors( int resolution, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) +{ + using namespace subdivision_matrix; + + auto mesh = newMeshFromNumVerticesAndFaces( + // Cube + // CUBE + // Torus + TORUS + ); + + std::vector< float > us, vs; + createUVs( resolution, resolution, us, vs ); + OpenSubdiv::FarStencilTables controlStencils = precomputeStencils( mesh, us, vs ); + + // std::vector< sparse_vector_t > position_coeffs; + std::vector< sparse_vector_t > du_coeffs; + std::vector< sparse_vector_t > dv_coeffs; + sparseVectorsForPositionsAndDerivativesFromFarStencilTables( controlStencils, mesh->GetNumVertices(), position_coeffs, &du_coeffs, &dv_coeffs ); + + print_sparse_vectors( position_coeffs, "position_coeffs: " ); + print_sparse_vectors( du_coeffs, "du_coeffs: " ); + print_sparse_vectors( dv_coeffs, "dv_coeffs: " ); + + delete mesh; +} + +void test_compute_coefficients( int resolution, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) +{ + using namespace subdivision_matrix; + + std::vector< real_t > us, vs; + createUVs( resolution, resolution, us, vs ); + + // std::vector< sparse_vector_t > position_coeffs; + std::vector< sparse_vector_t > du_coeffs; + std::vector< sparse_vector_t > dv_coeffs; + compute_subdivision_coefficients_for_mesh( + // Cube + // CUBE, + // Torus + TORUS, + us, vs, + position_coeffs, &du_coeffs, &dv_coeffs + ); + + print_sparse_vectors( position_coeffs, "position_coeffs: " ); + print_sparse_vectors( du_coeffs, "du_coeffs: " ); + print_sparse_vectors( dv_coeffs, "dv_coeffs: " ); +} + +// -I/path/to/regression +#include +#include +#include +void test_shape_utils( int resolution, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) +{ + using namespace subdivision_matrix; + + // Create a torus. + std::vector orgPositions; + auto mesh = simpleHbr( catmark_torus.c_str(), kCatmark, orgPositions, true); + // auto mesh = simpleHbr( catmark_cube.c_str(), kCatmark, orgPositions, true); + + std::vector< float > us, vs; + createUVs( resolution, resolution, us, vs ); + OpenSubdiv::FarStencilTables controlStencils = precomputeStencils( mesh, us, vs ); + + +// auto p_weights = controlStencils.GetWeights(); +// auto du_weights = controlStencils.GetDuWeights(); +// auto dv_weights = controlStencils.GetDvWeights(); + + + // std::vector< sparse_vector_t > position_coeffs; + std::vector< sparse_vector_t > du_coeffs; + std::vector< sparse_vector_t > dv_coeffs; + sparseVectorsForPositionsAndDerivativesFromFarStencilTables( controlStencils, mesh->GetNumVertices(), position_coeffs, &du_coeffs, &dv_coeffs ); + + print_sparse_vectors( position_coeffs, "position_coeffs: " ); + print_sparse_vectors( du_coeffs, "du_coeffs: " ); + print_sparse_vectors( dv_coeffs, "dv_coeffs: " ); +} + +#include +#include +void usage( const char* argv0 ) +{ + std::cerr << "Usage: " << argv0 << " test_shape_utils|test_compute_coefficients|test_newMesh_sparseVectors resolution\n"; + exit( -1 ); +} +int main( int argc, char* argv[] ) +{ + using std::string; + + if( argc != 3 ) usage( argv[0] ); + + const int resolution = atoi( argv[2] ); + if( resolution < 0 ) usage( argv[0] ); + + const string test_name = string(argv[1]); + + std::vector< subdivision_matrix::sparse_vector_t > position_coeffs; + if( string("test_shape_utils") == test_name ) + { + test_shape_utils( resolution, position_coeffs ); + } + else if( string("test_compute_coefficients") == test_name ) + { + test_compute_coefficients( resolution, position_coeffs ); + } + else if( string("test_newMesh_sparseVectors") == test_name ) + { + test_newMesh_sparseVectors( resolution, position_coeffs ); + } + else + { + usage( argv[0] ); + } + + // TODO: Do something with position_coeffs + + return 0; +} + +void print_sparse_vectors( const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::string& label = "" ) +{ + std::cout << label << "{"; + for( const auto vec : sparse_vectors ) + { + std::cout << "\n { "; + for( const auto p : vec ) + { + std::cout << "{ " << p.first << ", " << p.second << " }, "; + } + std::cout << " },"; + } + std::cout << "}\n"; +} + +#endif diff --git a/lib/subdivision_matrices.h b/lib/subdivision_matrices.h new file mode 100644 index 0000000..d6e5db3 --- /dev/null +++ b/lib/subdivision_matrices.h @@ -0,0 +1,58 @@ +#ifndef __subdivision_matrices_h__ +#define __subdivision_matrices_h__ + +#include "subdivision_matrix_types.h" +#include + +// -I/path/to/OpenSubDiv +#include +#include + +namespace subdivision_matrix +{ + +/* +Given the number of vertices in the mesh 'num_vertices' +and a vector of faces 'faces', each element of which is a vector of vertex indices, and +two same-length vectors 'us' and 'vs' where the ( us[i], vs[i] ) are the uv locations, +fills 'positions_out' with sparse vectors such that the position +for the i-th uv value in the original call to precomputeStencils() can be obtained by: + \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second + +The optional parameters 'du_out' and 'dv_out', if specified, are similar to 'positions_out' +except that the above summation results in du and dv. +*/ +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + const faces_t& faces, + const std::vector< real_t >& us, + const std::vector< real_t >& vs, + std::vector< sparse_vector_t >& positions_out, + std::vector< sparse_vector_t >* du_out = nullptr, + std::vector< sparse_vector_t >* dv_out = nullptr + ); +// The same as above, except with an HbrMesh pointer +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + OpenSubdiv::HbrMesh* mesh, + const std::vector< real_t >& us, + const std::vector< real_t >& vs, + std::vector< sparse_vector_t >& positions_out, + std::vector< sparse_vector_t >* du_out = nullptr, + std::vector< sparse_vector_t >* dv_out = nullptr + ); + +/* +Given a desired number of "u" and "v" parameters sampling the range [0,1]x[0,1], +returns in the output vectors 'us' and 'vs' coordinates sampling the range +in equal intervals. +*/ +void createUVs( int num_u, int num_v, std::vector< real_t >& us, std::vector< real_t >& vs ); + +} + +#ifdef SUBDIVISION_MATRICES_HEADER_ONLY +#include "subdivision_matrices.cpp" +#endif + +#endif /* __subdivision_matrices_h__ */ diff --git a/lib/subdivision_matrices_eigen.cpp b/lib/subdivision_matrices_eigen.cpp new file mode 100644 index 0000000..310e2a8 --- /dev/null +++ b/lib/subdivision_matrices_eigen.cpp @@ -0,0 +1,205 @@ +// c++ -g -std=c++11 -I../OpenSubdiv/opensubdiv -I../OpenSubdiv/regression subdivision_matrices_eigen.cpp subdivision_matrices.cpp -o subdivision_matrices_eigen -DSUBDIVISION_MATRICES_EIGEN_MAIN -I/usr/local/include/eigen3 + +#include "subdivision_matrices_eigen.h" + +#include + +namespace +{ +/* +Given the number of vertices 'num_vertices', +a vector of sparse_vector_t 'sparse_vectors' containing a sequence of ( index, coefficient ) pairs, +and an output Eigen::SparseMatrix 'matrix', +fills 'matrix' such that the matrix multiplication of 'matrix' times a num_vertices-by-K matrix of control points +yields the positions specified by 'sparse_vectors'. +*/ +void convert_vector_of_sparse_vectors_to_matrix( int num_vertices, const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, subdivision_matrix::SparseMatrix_t& matrix ) +{ + assert( num_vertices > 0 ); + + // Clear the output matrix. + matrix.resize( sparse_vectors.size(), num_vertices ); + matrix.setZero(); + + // We will fill the matrix with a vector of triplets. + std::vector< Eigen::Triplet< subdivision_matrix::real_t > > triplets; + + // Count the number of triplets we will need. + int nnz = 0; + for( const auto& sp : sparse_vectors ) nnz += sp.size(); + triplets.reserve( nnz ); + + // Convert 'sparse_vectors' to triplets. + for( int row = 0; row < sparse_vectors.size(); ++row ) + { + for( const auto p : sparse_vectors[row] ) + { + triplets.push_back( { row, p.first, p.second } ); + } + } + + matrix.setFromTriplets( triplets.begin(), triplets.end() ); +} +} + +namespace subdivision_matrix +{ + +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + const faces_t& faces, + const std::vector< real_t >& us, + const std::vector< real_t >& vs, + SparseMatrix_t& positions_out, + SparseMatrix_t* du_out, + SparseMatrix_t* dv_out + ) +{ + // Intermediary output vectors. + std::vector< sparse_vector_t > position_coeffs; + std::vector< sparse_vector_t > du_coeffs; + std::vector< sparse_vector_t > dv_coeffs; + + // Call the function we're wrapping. + compute_subdivision_coefficients_for_mesh( + num_vertices, + faces, + us, vs, + position_coeffs, + du_out ? &du_coeffs : nullptr, + dv_out ? &dv_coeffs : nullptr + ); + + convert_vector_of_sparse_vectors_to_matrix( num_vertices, position_coeffs, positions_out ); + if( du_out ) + { + assert( dv_out ); + + convert_vector_of_sparse_vectors_to_matrix( num_vertices, du_coeffs, *du_out ); + convert_vector_of_sparse_vectors_to_matrix( num_vertices, dv_coeffs, *dv_out ); + } +} + +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + OpenSubdiv::HbrMesh* mesh, + const std::vector< real_t >& us, + const std::vector< real_t >& vs, + SparseMatrix_t& positions_out, + SparseMatrix_t* du_out, + SparseMatrix_t* dv_out + ) +{ + // Intermediary output vectors. + std::vector< sparse_vector_t > position_coeffs; + std::vector< sparse_vector_t > du_coeffs; + std::vector< sparse_vector_t > dv_coeffs; + + // Call the function we're wrapping. + compute_subdivision_coefficients_for_mesh( + num_vertices, + mesh, + us, vs, + position_coeffs, + du_out ? &du_coeffs : nullptr, + dv_out ? &dv_coeffs : nullptr + ); + + // NOTE: We don't call mesh->GetNumVertices() because that may not be the number of coarse vertices. + convert_vector_of_sparse_vectors_to_matrix( num_vertices, position_coeffs, positions_out ); + if( du_out ) + { + assert( dv_out ); + + convert_vector_of_sparse_vectors_to_matrix( num_vertices, du_coeffs, *du_out ); + convert_vector_of_sparse_vectors_to_matrix( num_vertices, dv_coeffs, *dv_out ); + } +} + +} + +#ifdef SUBDIVISION_MATRICES_EIGEN_MAIN +#include + +#define TORUS 32, { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } + +template< typename T > +void print_dims( const std::string& prefix, const T& m ) +{ + std::cerr << prefix << m.rows() << "x" << m.cols() << '\n'; +} + +#include +void save_compute_coefficients( int resolution, std::ostream& out ) +{ + using namespace subdivision_matrix; + + std::vector< real_t > us, vs; + createUVs( resolution, resolution, us, vs ); + + SparseMatrix_t position_coeffs; + compute_subdivision_coefficients_for_mesh( + // Cube + // CUBE, + // Torus + TORUS, + us, vs, + position_coeffs + ); + + std::vector< std::vector< real_t > > positions_orig( { { 1.25052, 0.517982, 0.353553 }, { 0.597239, 0.247384, 0.353553 }, { 0.597239, 0.247384, -0.353553 }, { 1.25052, 0.517982, -0.353553 }, { 0.517982, 1.25052, 0.353553 }, { 0.247384, 0.597239, 0.353553 }, { 0.247384, 0.597239, -0.353553 }, { 0.517982, 1.25052, -0.353553 }, { -0.517982, 1.25052, 0.353553 }, { -0.247384, 0.597239, 0.353553 }, { -0.247384, 0.597239, -0.353553 }, { -0.517982, 1.25052, -0.353553 }, { -1.25052, 0.517982, 0.353553 }, { -0.597239, 0.247384, 0.353553 }, { -0.597239, 0.247384, -0.353553 }, { -1.25052, 0.517982, -0.353553 }, { -1.25052, -0.517982, 0.353553 }, { -0.597239, -0.247384, 0.353553 }, { -0.597239, -0.247384, -0.353553 }, { -1.25052, -0.517982, -0.353553 }, { -0.517982, -1.25052, 0.353553 }, { -0.247384, -0.597239, 0.353553 }, { -0.247384, -0.597239, -0.353553 }, { -0.517982, -1.25052, -0.353553 }, { 0.517982, -1.25052, 0.353553 }, { 0.247384, -0.597239, 0.353553 }, { 0.247384, -0.597239, -0.353553 }, { 0.517982, -1.25052, -0.353553 }, { 1.25052, -0.517982, 0.353553 }, { 0.597239, -0.247384, 0.353553 }, { 0.597239, -0.247384, -0.353553 }, { 1.25052, -0.517982, -0.353553 } } ); + Eigen::Matrix< real_t, 3, Eigen::Dynamic > positions; + positions.resize( 3, positions_orig.size() ); + for( int row = 0; row < positions.rows(); ++row ) + for( int col = 0; col < positions.cols(); ++col ) + { + positions( row, col ) = positions_orig.at(col).at(row); + } + + print_dims( "position_coeffs is ", position_coeffs ); + print_dims( "positions is ", positions ); + + Eigen::MatrixXf all_pos = positions * position_coeffs.transpose(); + print_dims( "all_pos is ", all_pos ); + // out << all_pos << '\n'; + + for( int i = 0; i < position_coeffs.rows(); ++i ) + { + // std::cerr << position_coeffs.row(i) << '\n'; + + Eigen::VectorXf pos = positions * position_coeffs.row(i).transpose(); + out << "v " << pos(0) << ' ' << pos(1) << ' ' << pos(2) << '\n'; + } +} + +#include +#include +void usage( const char* argv0 ) +{ + std::cerr << "Usage: " << argv0 << " save_compute_coefficients resolution\n"; + exit( -1 ); +} +int main( int argc, char* argv[] ) +{ + using std::string; + + if( argc != 3 ) usage( argv[0] ); + + const int resolution = atoi( argv[2] ); + if( resolution < 0 ) usage( argv[0] ); + + const string test_name = string(argv[1]); + + if( string("save_compute_coefficients") == test_name ) + { + std::ofstream out( "torus-eigen.obj" ); + save_compute_coefficients( resolution, out ); + } + else + { + usage( argv[0] ); + } + + return 0; +} +#endif diff --git a/lib/subdivision_matrices_eigen.h b/lib/subdivision_matrices_eigen.h new file mode 100644 index 0000000..e19ad32 --- /dev/null +++ b/lib/subdivision_matrices_eigen.h @@ -0,0 +1,48 @@ +#ifndef __subdivision_matrices_eigen_h__ +#define __subdivision_matrices_eigen_h__ + +#include "subdivision_matrices.h" +#include +#include + +namespace subdivision_matrix +{ + +/* +Given the number of vertices in the mesh 'num_vertices' +and a vector of faces 'faces', each element of which is a vector of vertex indices, and +two same-length vectors 'us' and 'vs' where the ( us[i], vs[i] ) are the uv locations +with which to sample every face, +fills 'positions_out' with a matrix such that +the matrix multiplication of 'positions_out' times a num_vertices-by-K matrix of control points +yields the positions. + +The optional parameters 'du_out' and 'dv_out', if specified, are similar to 'positions_out' +except that the matrix multiplication results in du and dv vectors. +*/ +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + const faces_t& faces, + const std::vector< real_t >& us, + const std::vector< real_t >& vs, + SparseMatrix_t& positions_out, + SparseMatrix_t* du_out = nullptr, + SparseMatrix_t* dv_out = nullptr + ); +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + OpenSubdiv::HbrMesh* mesh, + const std::vector< real_t >& us, + const std::vector< real_t >& vs, + SparseMatrix_t& positions_out, + SparseMatrix_t* du_out = nullptr, + SparseMatrix_t* dv_out = nullptr + ); + +} + +#ifdef SUBDIVISION_MATRICES_HEADER_ONLY +#include "subdivision_matrices_eigen.cpp" +#endif + +#endif /* __subdivision_matrices_eigen_h__ */ diff --git a/lib/subdivision_matrices_osd.cpp b/lib/subdivision_matrices_osd.cpp new file mode 100644 index 0000000..f4da7c5 --- /dev/null +++ b/lib/subdivision_matrices_osd.cpp @@ -0,0 +1,393 @@ +// c++ -g -std=c++11 -I../../opensubdiv -I../../regression subdivision_matrices_osd.cpp -o subdivision_matrices_osd -DSUBDIVISION_MATRICES_OSD_MAIN ../../build/lib/libosdCPU.a + +#include "subdivision_matrices_osd.h" + +#include +#include +#include // unique_ptr +#include // fill + +#include +#include +#include +#include + +namespace +{ + +/* +Given the number of vertices in the mesh 'num_vertices' +and a vector of faces 'faces', each element of which is a vector of vertex indices, +returns a new mesh object suitable for passing to +Free the resulting mesh with "delete" when finished with it. +*/ +OpenSubdiv::HbrMesh* newMeshFromNumVerticesAndFaces( int num_vertices, const subdivision_matrix::faces_t& faces ) +{ + assert( num_vertices > 0 ); + + // Following: http://graphics.pixar.com/opensubdiv/forum.html?place=msg%2Fopensubdiv%2FzKq9vG_azHQ%2FDb2K7L23BykJ + + static OpenSubdiv::HbrCatmarkSubdivision CATMULL_CLARK; + + auto mesh = new OpenSubdiv::HbrMesh( &CATMULL_CLARK ); + + OpenSubdiv::OsdVertex default_vertex_value; + for( int vi = 0; vi < num_vertices; ++vi ) mesh->NewVertex( vi, default_vertex_value ); + + /// TODO Q: Is this necessary? + // int ptex_index = 0; + int face_offset = 0; + for( const auto& num_face_vertices : faces.first ) + { + OpenSubdiv::HbrFace* face = mesh->NewFace( num_face_vertices, &faces.second[face_offset], 0 ); + + /* + face->SetPtexIndex( ptex_index ); + if( f.size() != 4 ) { + ptex_index += f.size(); + } else { + ptex_index += 1; + } + */ + + face_offset += num_face_vertices; + } + + mesh->SetInterpolateBoundaryMethod( OpenSubdiv::HbrMesh::k_InterpolateBoundaryAlwaysSharp ); + + mesh->Finish(); + + return mesh; +} + +} + +namespace subdivision_matrix +{ + +/* +Given the number of vertices in the mesh 'num_vertices' +and a vector of faces 'faces', each element of which is a vector of vertex indices, and +a positive integer 'level' indicating the level of refinement, +fills 'positions_out' with sparse vectors such that the position +for the i-th uv value in the original call to precomputeStencils() can be obtained by: + \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second + +The optional parameter 'quad_faces_out', if specified, is a sequence of quad faces +obtained by subdivision, where each face is four indices into 'positions_out'. +*/ +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + const faces_t& faces, + const int level, + std::vector< sparse_vector_t >& positions_out, + std::vector< index_t >* quad_faces_out + ) +{ + auto mesh = newMeshFromNumVerticesAndFaces( num_vertices, faces ); + compute_subdivision_coefficients_for_mesh( num_vertices, mesh, level, positions_out, quad_faces_out ); + delete mesh; +} + +void compute_subdivision_coefficients_for_mesh( + int num_coarse_vertices, + OpenSubdiv::HbrMesh* hmesh, + const int level, + std::vector< sparse_vector_t >& positions_out, + std::vector< index_t >* quad_faces_out + ) +{ + assert( num_coarse_vertices == hmesh->GetNumVertices() ); + + assert( level > 0 ); + positions_out.clear(); + if( quad_faces_out ) quad_faces_out->clear(); + + std::unique_ptr< OpenSubdiv::FarMesh > fmesh; + // Adaptive would give us limit vertex positions, however: + // Adaptive is only supported for Catmull-Clark subdivision. + // Adaptive doesn't support getting quads. + // So, we use non-adaptive aka uniform subdivision. + { + // The comment in "osdutil/refiner.cpp" says: + // XXX:gelder + // Had problems with patchArrayVector in patchTables not working + // unless firstLevel is passed as 1 here. Bug in refiner? + OpenSubdiv::FarMeshFactory factory( hmesh, level, false /* adaptive */, 1 /* firstLevel */ ); + fmesh.reset( factory.Create() ); + } + + + // Following "osdutil/refiner.cpp" + + /// Refiner::Initialize() + // Subdivision tables describe the addition steps with coefficients + // needed to perform subdivision + const OpenSubdiv::FarSubdivisionTables* ftable = fmesh->GetSubdivisionTables(); + + + const int kNumDimensions = std::min( 100, num_coarse_vertices ); + { + /// UniformEvaluator::Initialize() + std::unique_ptr< OpenSubdiv::OsdCpuComputeContext > _computeContext( OpenSubdiv::OsdCpuComputeContext::Create(fmesh->GetSubdivisionTables(), fmesh->GetVertexEditTables()) ); + // The vertex buffer must have the FarMesh's vertex count, which includes + // the refined vertices, not the HbrMesh's vertex count, which only has + // the coarse vertices. + std::unique_ptr< OpenSubdiv::OsdCpuVertexBuffer > _vertexBuffer( OpenSubdiv::OsdCpuVertexBuffer::Create( kNumDimensions, fmesh->GetNumVertices() ) ); + + for( int coarse_index = 0; coarse_index < num_coarse_vertices; coarse_index += kNumDimensions ) + { + /// UniformEvaluator::SetCoarsePositions() + // Each vertex stores its indicator function. + // The identity matrix is the indicator function. + // We are iterating vertex-by-vertex, so set the coarse_index column of the + // identity matrix. + { + // vbuffer: v0d0 v0d1 v0d2 ... v0dN v1d0 v1d1 v1d2 ... v1dN v2d0 v2d1 ... + float* vbuffer = _vertexBuffer->BindCpuBuffer(); + // TODO Q: Do I need to fill it with zeros, or can I just + // unset the last iteration's 1? + // In other words, does Refine() overwrite these values? + std::fill( vbuffer, vbuffer + kNumDimensions * num_coarse_vertices, 0. ); + // A slice of 'kNumDimensions' columns of the identity matrix: + for( int i = coarse_index; i < std::min( coarse_index + kNumDimensions, num_coarse_vertices ); ++i ) + { + vbuffer[ i-coarse_index + i*kNumDimensions ] = 1.; + } + } + + /// UniformEvaluator::Refine() + { + OpenSubdiv::OsdCpuComputeController cpuComputeController; + cpuComputeController.Refine( + _computeContext.get(), + fmesh->GetKernelBatches(), + _vertexBuffer.get() + ); + // TODO Q: Is this Synchronize() call necessary? + cpuComputeController.Synchronize(); + } + + + /// UniformEvaluator::GetRefinedPositions() + { + const int numRefinedVerts = ftable->GetNumVertices(level); + const int firstVertexOffset = ftable->GetFirstVertexOffset(level); + const float* vbuffer = _vertexBuffer->BindCpuBuffer(); + + if (numRefinedVerts == 0) { + std::cerr << "ERROR: No refinement occurred.\n"; + return; + } + + if( 0 == coarse_index ) + { + // We clear it upon entry to this function. + assert( positions_out.empty() ); + positions_out.resize( numRefinedVerts ); + for( int vi = 0; vi < numRefinedVerts; ++vi ) positions_out.at( vi ).reserve( 16 ); + } + else + { + assert( positions_out.size() == numRefinedVerts ); + } + + const float* off = vbuffer + firstVertexOffset*kNumDimensions; + const float kEps = 1e-10; + for( int vi = 0; vi < numRefinedVerts; ++vi ) + { + for( int i = coarse_index; i < std::min( coarse_index + kNumDimensions, num_coarse_vertices ); ++i ) + { + const auto& val = off[ i - coarse_index ]; + if( fabs( val ) > kEps ) + { + positions_out.at( vi ).push_back( std::make_pair( i, val ) ); + } + } + off += kNumDimensions; + } + } + } + } + + // Faces + if( quad_faces_out ) + { + // We clear it upon entry to this function. + assert( quad_faces_out->empty() ); + + // From "osdutil/refiner.cpp"'s Refiner::Initialize(): + // Find quads array at given level + const OpenSubdiv::FarPatchTables* ptables = fmesh->GetPatchTables(); + const OpenSubdiv::FarPatchTables::PatchArrayVector& parrays = ptables->GetPatchArrayVector(); + // parrays doesn't contain base mesh, so it starts with level==1 + assert( level-1 < parrays.size() ); + const OpenSubdiv::FarPatchTables::PatchArray& parray = parrays[level-1]; + + // Global index of the first point in this array + const int firstVertexOffset = ftable->GetFirstVertexOffset(level); + + + /// From "osdutil/refiner.cpp"'s Refiner::GetRefinedQuads(): + const int numUniformQuads = (int) parray.GetNumPatches(); + quad_faces_out->resize(numUniformQuads * 4); + + const unsigned int *quadIndices = ptables->GetFaceVertices( level ); + for( int i = 0; i < numUniformQuads*4; ++i ) + { + quad_faces_out->at(i) = quadIndices[i] - firstVertexOffset; + } + } +} + +} // ~namespace subdivision_matrix() + +#ifdef SUBDIVISION_MATRICES_OSD_MAIN +void print_sparse_vectors( const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::string& label ); +void print_faces( const std::vector< subdivision_matrix::index_t >& faces, const std::string& label ); +void print_OBJ( const std::vector& original_positions, const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::vector< subdivision_matrix::index_t >& faces, const std::string& label ); + +#define TORUS 32, { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } +#define CUBE 8, { {0, 1, 3, 2}, {2, 3, 5, 4}, {4, 5, 7, 6}, {6, 7, 1, 0}, {1, 7, 5, 3}, {6, 0, 2, 4} } + +void test_compute_coefficients( int level, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) +{ + using namespace subdivision_matrix; + + // std::vector< sparse_vector_t > position_coeffs; + std::vector< index_t > faces; + compute_subdivision_coefficients_for_mesh( + // Cube + // CUBE, + // Torus + TORUS, + level, + position_coeffs, &faces + ); + + print_sparse_vectors( position_coeffs, "position_coeffs: " ); + print_faces( faces, "faces: " ); +} + +// -I/path/to/regression +#include +#include +#include +void test_shape_utils( int level, std::vector< subdivision_matrix::sparse_vector_t >& position_coeffs ) +{ + using namespace subdivision_matrix; + + // Create a torus. + std::vector orgPositions; + auto mesh = simpleHbr( catmark_torus.c_str(), kCatmark, orgPositions, true); + // auto mesh = simpleHbr( catmark_cube.c_str(), kCatmark, orgPositions, true); + + // std::vector< sparse_vector_t > position_coeffs; + std::vector< index_t > faces; + compute_subdivision_coefficients_for_mesh( + mesh->GetNumVertices(), + mesh, + level, + position_coeffs, &faces + ); + + print_sparse_vectors( position_coeffs, "position_coeffs: " ); + print_faces( faces, "faces: " ); + print_OBJ( orgPositions, position_coeffs, faces, "reconstructed OBJ" ); +} + +#include +#include +void usage( const char* argv0 ) +{ + std::cerr << "Usage: " << argv0 << " test_shape_utils|test_compute_coefficients level\n"; + exit( -1 ); +} +int main( int argc, char* argv[] ) +{ + using std::string; + + if( argc != 3 ) usage( argv[0] ); + + const int level = atoi( argv[2] ); + if( level <= 0 ) usage( argv[0] ); + + const string test_name = string(argv[1]); + + std::vector< subdivision_matrix::sparse_vector_t > position_coeffs; + if( string("test_shape_utils") == test_name ) + { + test_shape_utils( level, position_coeffs ); + } + else if( string("test_compute_coefficients") == test_name ) + { + test_compute_coefficients( level, position_coeffs ); + } + else + { + usage( argv[0] ); + } + + // TODO: Do something with position_coeffs + + return 0; +} + +void print_sparse_vectors( const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::string& label = "" ) +{ + std::cout << label << "{"; + for( const auto vec : sparse_vectors ) + { + std::cout << "\n { "; + for( const auto p : vec ) + { + std::cout << "{ " << p.first << ", " << p.second << " }, "; + } + std::cout << "},"; + } + std::cout << "}\n"; +} +void print_faces( const std::vector< subdivision_matrix::index_t >& faces, const std::string& label ) +{ + std::cout << label << "{\n"; + for( int fi = 0; fi < faces.size(); ++fi ) + { + if( fi > 0 && fi % 4 == 0 ) std::cout << " },\n"; + if( fi % 4 == 0 ) std::cout << " {"; + std::cout << ' ' << faces.at(fi); + } + std::cout << "}\n"; +} +#include +void print_OBJ( const std::vector& original_positions, const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, const std::vector< subdivision_matrix::index_t >& faces, const std::string& label ) +{ + std::cout << "# OBJ: " << label << '\n'; + + assert( original_positions.size() % 3 == 0 ); + std::vector< std::valarray< float > > ovs( original_positions.size() / 3, { 0., 0., 0. } ); + for( int i = 0; i < ovs.size(); ++i ) + { + assert( ovs.at( i ).size() == 3 ); + ovs.at( i )[ 0 ] = original_positions.at( 3*i + 0 ); + ovs.at( i )[ 1 ] = original_positions.at( 3*i + 1 ); + ovs.at( i )[ 2 ] = original_positions.at( 3*i + 2 ); + } + + for( int i = 0; i < sparse_vectors.size(); ++i ) + { + std::valarray< float > pos( { 0., 0., 0. } ); + for( const auto& i_w : sparse_vectors.at( i ) ) + { + pos += i_w.second * ovs.at( i_w.first ); + } + std::cout << "v " << pos[0] << ' ' << pos[1] << ' ' << pos[2] << '\n'; + } + + assert( faces.size() % 4 == 0 ); + for( int i = 0; i+3 < faces.size(); i += 4 ) + { + std::cout << "f " << (faces[i+0]+1) << ' ' << (faces[i+1]+1) << ' ' << (faces[i+2]+1) << ' ' << (faces[i+3]+1) << '\n'; + } +} + +#endif diff --git a/lib/subdivision_matrices_osd.h b/lib/subdivision_matrices_osd.h new file mode 100644 index 0000000..3fc5e43 --- /dev/null +++ b/lib/subdivision_matrices_osd.h @@ -0,0 +1,48 @@ +#ifndef __subdivision_matrices_osd_h__ +#define __subdivision_matrices_osd_h__ + +#include "subdivision_matrix_types.h" +#include + +// -I/path/to/OpenSubDiv +#include +#include + +namespace subdivision_matrix +{ + +/* +Given the number of vertices in the mesh 'num_vertices' +and a vector of faces 'faces', each element of which is a vector of vertex indices, and +a positive integer 'level' indicating the level of refinement, +fills 'positions_out' with sparse vectors such that the position +for the i-th uv value in the original call to precomputeStencils() can be obtained by: + \sum_j control_points[ positions_out[i][j].first ] * positions_out[i][j].second + +The optional parameter 'faces_out', if specified, is a sequence of quad faces +obtained by subdivision, where each face is four indices into 'positions_out': + face0_vertex0 face0_vertex1 face0_vertex2 face0_vertex3 face1_vertex0 face1_vertex1 face1_vertex2 face1_vertex3 ... +*/ +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + const faces_t& faces, + const int level, + std::vector< sparse_vector_t >& positions_out, + std::vector< index_t >* quad_faces_out = nullptr + ); +// The same as above, except with an HbrMesh pointer +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + OpenSubdiv::HbrMesh* mesh, + const int level, + std::vector< sparse_vector_t >& positions_out, + std::vector< index_t >* quad_faces_out = nullptr + ); + +} + +#ifdef SUBDIVISION_MATRICES_HEADER_ONLY +#include "subdivision_matrices_osd.cpp" +#endif + +#endif /* __subdivision_matrices_osd_h__ */ diff --git a/lib/subdivision_matrices_osd_eigen.cpp b/lib/subdivision_matrices_osd_eigen.cpp new file mode 100644 index 0000000..cf0379b --- /dev/null +++ b/lib/subdivision_matrices_osd_eigen.cpp @@ -0,0 +1,192 @@ +// c++ -g -std=c++11 -I../OpenSubdiv/opensubdiv -I../OpenSubdiv/regression subdivision_matrices_osd_eigen.cpp subdivision_matrices.cpp -o subdivision_matrices_osd_eigen -DSUBDIVISION_MATRICES_OSD_EIGEN_MAIN -I/usr/local/include/eigen3 + +#include "subdivision_matrices_osd_eigen.h" + +#include + +namespace +{ +/* +Given the number of vertices 'num_vertices', +a vector of sparse_vector_t 'sparse_vectors' containing a sequence of ( index, coefficient ) pairs, +and an output Eigen::SparseMatrix 'matrix', +fills 'matrix' such that the matrix multiplication of 'matrix' times a num_vertices-by-K matrix of control points +yields the positions specified by 'sparse_vectors'. +*/ +void convert_vector_of_sparse_vectors_to_matrix( int num_vertices, const std::vector< subdivision_matrix::sparse_vector_t >& sparse_vectors, subdivision_matrix::SparseMatrix_t& matrix ) +{ + assert( num_vertices > 0 ); + + // Clear the output matrix. + matrix.resize( sparse_vectors.size(), num_vertices ); + matrix.setZero(); + + // We will fill the matrix with a vector of triplets. + std::vector< Eigen::Triplet< subdivision_matrix::real_t > > triplets; + + // Count the number of triplets we will need. + int nnz = 0; + for( const auto& sp : sparse_vectors ) nnz += sp.size(); + triplets.reserve( nnz ); + + // Convert 'sparse_vectors' to triplets. + for( int row = 0; row < sparse_vectors.size(); ++row ) + { + for( const auto p : sparse_vectors[row] ) + { + triplets.push_back( { row, p.first, p.second } ); + } + } + + matrix.setFromTriplets( triplets.begin(), triplets.end() ); +} +void convert_vector_of_quad_indices_to_matrix( const std::vector< subdivision_matrix::index_t >& quad_faces, Eigen::Matrix< subdivision_matrix::index_t, Eigen::Dynamic, 4 >& quad_faces_out ) +{ + assert( quad_faces.size() % 4 == 0 ); + + quad_faces_out.setZero( quad_faces.size()/4, 4 ); + + for( int i = 0; i+3 < quad_faces.size(); i += 4 ) + { + quad_faces_out( i/4, i%4 ) = quad_faces.at(i); + } +} + +} + +namespace subdivision_matrix +{ + +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + const faces_t& faces, + const int level, + SparseMatrix_t& positions_out, + std::vector< index_t >* quad_faces_out + ) +{ + // Intermediary output vectors. + std::vector< sparse_vector_t > position_coeffs; + std::vector< index_t > quad_faces; + + // Call the function we're wrapping. + compute_subdivision_coefficients_for_mesh( + num_vertices, + faces, + level, + position_coeffs, + quad_faces_out + ); + + convert_vector_of_sparse_vectors_to_matrix( num_vertices, position_coeffs, positions_out ); +} + +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + OpenSubdiv::HbrMesh* mesh, + const int level, + SparseMatrix_t& positions_out, + std::vector< index_t >* quad_faces_out + ) +{ + // Intermediary output vectors. + std::vector< sparse_vector_t > position_coeffs; + std::vector< index_t > quad_faces; + + // Call the function we're wrapping. + compute_subdivision_coefficients_for_mesh( + num_vertices, + mesh, + level, + position_coeffs, + quad_faces_out + ); + + // NOTE: We don't call mesh->GetNumVertices() because that may not be the number of coarse vertices. + convert_vector_of_sparse_vectors_to_matrix( num_vertices, position_coeffs, positions_out ); +} + +} + +#ifdef SUBDIVISION_MATRICES_OSD_EIGEN_MAIN +#include + +#define TORUS 32, { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } + +template< typename T > +void print_dims( const std::string& prefix, const T& m ) +{ + std::cerr << prefix << m.rows() << "x" << m.cols() << '\n'; +} + +#include +void save_compute_coefficients( int level, std::ostream& out ) +{ + using namespace subdivision_matrix; + + SparseMatrix_t position_coeffs; + compute_subdivision_coefficients_for_mesh( + // Cube + // CUBE, + // Torus + TORUS, + level, + position_coeffs + ); + + std::vector< std::vector< real_t > > positions_orig( { { 1.25052, 0.517982, 0.353553 }, { 0.597239, 0.247384, 0.353553 }, { 0.597239, 0.247384, -0.353553 }, { 1.25052, 0.517982, -0.353553 }, { 0.517982, 1.25052, 0.353553 }, { 0.247384, 0.597239, 0.353553 }, { 0.247384, 0.597239, -0.353553 }, { 0.517982, 1.25052, -0.353553 }, { -0.517982, 1.25052, 0.353553 }, { -0.247384, 0.597239, 0.353553 }, { -0.247384, 0.597239, -0.353553 }, { -0.517982, 1.25052, -0.353553 }, { -1.25052, 0.517982, 0.353553 }, { -0.597239, 0.247384, 0.353553 }, { -0.597239, 0.247384, -0.353553 }, { -1.25052, 0.517982, -0.353553 }, { -1.25052, -0.517982, 0.353553 }, { -0.597239, -0.247384, 0.353553 }, { -0.597239, -0.247384, -0.353553 }, { -1.25052, -0.517982, -0.353553 }, { -0.517982, -1.25052, 0.353553 }, { -0.247384, -0.597239, 0.353553 }, { -0.247384, -0.597239, -0.353553 }, { -0.517982, -1.25052, -0.353553 }, { 0.517982, -1.25052, 0.353553 }, { 0.247384, -0.597239, 0.353553 }, { 0.247384, -0.597239, -0.353553 }, { 0.517982, -1.25052, -0.353553 }, { 1.25052, -0.517982, 0.353553 }, { 0.597239, -0.247384, 0.353553 }, { 0.597239, -0.247384, -0.353553 }, { 1.25052, -0.517982, -0.353553 } } ); + Eigen::Matrix< real_t, 3, Eigen::Dynamic > positions; + positions.resize( 3, positions_orig.size() ); + for( int row = 0; row < positions.rows(); ++row ) + for( int col = 0; col < positions.cols(); ++col ) + { + positions( row, col ) = positions_orig.at(col).at(row); + } + + print_dims( "position_coeffs is ", position_coeffs ); + print_dims( "positions is ", positions ); + + Eigen::MatrixXf all_pos = positions * position_coeffs.transpose(); + print_dims( "all_pos is ", all_pos ); + // out << all_pos << '\n'; + + for( int i = 0; i < position_coeffs.rows(); ++i ) + { + // std::cerr << position_coeffs.row(i) << '\n'; + + Eigen::VectorXf pos = positions * position_coeffs.row(i).transpose(); + out << "v " << pos(0) << ' ' << pos(1) << ' ' << pos(2) << '\n'; + } +} + +#include +#include +void usage( const char* argv0 ) +{ + std::cerr << "Usage: " << argv0 << " save_compute_coefficients level\n"; + exit( -1 ); +} +int main( int argc, char* argv[] ) +{ + using std::string; + + if( argc != 3 ) usage( argv[0] ); + + const int level = atoi( argv[2] ); + if( level < 1 ) usage( argv[0] ); + + const string test_name = string(argv[1]); + + if( string("save_compute_coefficients") == test_name ) + { + std::ofstream out( "torus-eigen.obj" ); + save_compute_coefficients( level, out ); + } + else + { + usage( argv[0] ); + } + + return 0; +} +#endif diff --git a/lib/subdivision_matrices_osd_eigen.h b/lib/subdivision_matrices_osd_eigen.h new file mode 100644 index 0000000..8a0db90 --- /dev/null +++ b/lib/subdivision_matrices_osd_eigen.h @@ -0,0 +1,44 @@ +#ifndef __subdivision_matrices_osd_eigen_h__ +#define __subdivision_matrices_osd_eigen_h__ + +#include "subdivision_matrices_osd.h" +#include +#include + +namespace subdivision_matrix +{ + +/* +Given the number of vertices in the mesh 'num_vertices' +and a vector of faces 'faces', each element of which is a vector of vertex indices, and +a positive integer 'level' indicating the level of refinement, +fills 'positions_out' with a matrix such that +the matrix multiplication of 'positions_out' times a num_vertices-by-K matrix of control points +yields the positions. + +The optional parameter 'faces_out', if specified, is a sequence of quad faces +obtained by subdivision, where each face is four indices into 'positions_out': + face0_vertex0 face0_vertex1 face0_vertex2 face0_vertex3 face1_vertex0 face1_vertex1 face1_vertex2 face1_vertex3 ... +*/ +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + const faces_t& faces, + const int level, + SparseMatrix_t& positions_out, + std::vector< index_t >* quad_faces_out = nullptr + ); +void compute_subdivision_coefficients_for_mesh( + int num_vertices, + OpenSubdiv::HbrMesh* mesh, + const int level, + SparseMatrix_t& positions_out, + std::vector< index_t >* quad_faces_out = nullptr + ); + +} + +#ifdef SUBDIVISION_MATRICES_HEADER_ONLY +#include "subdivision_matrices_osd_eigen.cpp" +#endif + +#endif /* __subdivision_matrices_osd_eigen_h__ */ diff --git a/lib/subdivision_matrix_types.h b/lib/subdivision_matrix_types.h new file mode 100644 index 0000000..d224d15 --- /dev/null +++ b/lib/subdivision_matrix_types.h @@ -0,0 +1,32 @@ +#ifndef __subdivision_matrix_types_h__ +#define __subdivision_matrix_types_h__ + +#include +#include +#include +#include // pair + +namespace subdivision_matrix +{ + +typedef float real_t; +// typedef double long_real_t; +typedef int index_t; +typedef std::vector< std::pair< index_t, real_t > > sparse_vector_t; + +// typedef Eigen::Matrix< real_t, Eigen::Dynamic, Eigen::Dynamic > Matrix_t; +typedef Eigen::SparseMatrix< real_t, Eigen::RowMajor > SparseMatrix_t; + +typedef Eigen::Matrix< real_t, 1, 3> vertex_t; + +typedef Eigen::Matrix< real_t, Eigen::Dynamic, Eigen::Dynamic > MatrixXX_t; +typedef Eigen::Matrix< real_t, Eigen::Dynamic, 4 > MatrixX4_t; +typedef Eigen::Matrix< real_t, Eigen::Dynamic, 3 > MatrixX3_t; +typedef Eigen::Matrix< real_t, Eigen::Dynamic, 1 > VectorX_t; + +typedef std::pair< std::vector< int >, std::vector< int > > faces_t; +typedef Eigen::Matrix< real_t, 4, 4 > transform_t; + +} + +#endif /* __subdivision_matrix_types_h__ */ diff --git a/lib/subdivision_skinning.cpp b/lib/subdivision_skinning.cpp new file mode 100644 index 0000000..fb30d25 --- /dev/null +++ b/lib/subdivision_skinning.cpp @@ -0,0 +1,450 @@ +#include "subdivision_skinning.h" +#include "subdivision_engine.h" +#include "subdivision_limit_mesh.h" + +#include +#include +#include +#include "ColorMap.h" + +#define TORUS_VS 1.25052, 0.517982, 0.353553, 0.597239, 0.247384, 0.353553, 0.597239, 0.247384, -0.353553, 1.25052, 0.517982, -0.353553, 0.517982, 1.25052, 0.353553, 0.247384, 0.597239, 0.353553, 0.247384, 0.597239, -0.353553, 0.517982, 1.25052, -0.353553, -0.517982, 1.25052, 0.353553, -0.247384, 0.597239, 0.353553, -0.247384, 0.597239, -0.353553, -0.517982, 1.25052, -0.353553, -1.25052, 0.517982, 0.353553, -0.597239, 0.247384, 0.353553, -0.597239, 0.247384, -0.353553, -1.25052, 0.517982, -0.353553, -1.25052, -0.517982, 0.353553, -0.597239, -0.247384, 0.353553, -0.597239, -0.247384, -0.353553, -1.25052, -0.517982, -0.353553, -0.517982, -1.25052, 0.353553, -0.247384, -0.597239, 0.353553, -0.247384, -0.597239, -0.353553, -0.517982, -1.25052, -0.353553, 0.517982, -1.25052, 0.353553, 0.247384, -0.597239, 0.353553, 0.247384, -0.597239, -0.353553, 0.517982, -1.25052, -0.353553, 1.25052, -0.517982, 0.353553, 0.597239, -0.247384, 0.353553, 0.597239, -0.247384, -0.353553, 1.25052, -0.517982, -0.353553 +#define TORUS_FACES { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } + +namespace +{ + const int kResolution = 10; + const int kLevel = 3; +} + +namespace subdivision_skinning +{ + struct engine_t { + int num_of_controls; + subdivision_matrix::subdivision_control_mesh mesh; +// subdivision_matrix::MatrixX3_t handle_positions; + subdivision_matrix::SparseMatrix_t M_matrices; + std::vector< subdivision_matrix::MatrixX4_t > W_matrices; + std::vector< subdivision_matrix::MatrixX4_t > naive_W_matrices; + std::vector< subdivision_matrix::MatrixX4_t > target_W_matrices; + }; + + // delete the result when you are finished with it. + engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const handles_t& handle_positions, const char* weight_function, OpenSubdiv::HbrMesh* hmesh ) + { + Tick *tick1 = new Tick( "preparation" ); + assert( vertices.size() % 3 == 0 ); + int size = vertices.size() / 3; + + // build subdivision_matrix::mesh + subdivision_matrix::subdivision_control_mesh mesh; + mesh.faces = faces; + mesh.vs.resize( size, 3 ); + for( int i = 0; i < size; i++ ) + mesh.vs.row( i ) << vertices[i*3], vertices[i*3+1], vertices[i*3+2]; + std::cout << "vertices #: " << size << " faces #: " << faces.first.size() << std::endl; + + // build engine. + engine_t* engine = new engine_t; + engine->num_of_controls = size; + engine->mesh = mesh; + + std::vector< subdivision_matrix::real_t > us, vs; + int resolution = kResolution; + subdivision_matrix::createUVs( resolution, resolution, us, vs ); + + subdivision_matrix::SparseMatrix_t M_matrices, Du_matrices, Dv_matrices; + if( hmesh ) { + subdivision_matrix::compute_subdivision_coefficients_for_mesh( + mesh.vs.rows(), + hmesh, + us, vs, + M_matrices, &Du_matrices, &Dv_matrices ); + } else { + subdivision_matrix::compute_subdivision_coefficients_for_mesh( + mesh.vs.rows(), + mesh.faces, + us, vs, + M_matrices, &Du_matrices, &Dv_matrices ); + } + + engine->M_matrices = M_matrices; + + // std::vector< std::vector< subdivision_matrix::real_t > > limit_vertices_out; + // std::vector< std::vector< int > > limit_quad_faces_out; + // subdivision_limit_mesh::compute_quad_limit_mesh_for_mesh( mesh.vs, hmesh, resolution, resolution, limit_vertices_out, limit_quad_faces_out ); + + // Convert the handle positions' type. + assert( handle_positions.cols() == 3 ); + subdivision_matrix::MatrixX3_t subdivision_handles( handle_positions.rows(), 3 ); + + for( int i = 0; i < handle_positions.rows(); i++ ) + subdivision_handles.row( i ) << handle_positions(i,0), + handle_positions(i,1), + handle_positions(i,2); +// engine->handle_positions = subdivision_handles; + // prepare the precompute matrices and save them in engine. + delete tick1; + + Tick *tick2 = new Tick( "Pre-compute naive controls." ); + subdivision_matrix::naive_prepare( mesh.vs, subdivision_handles, engine->naive_W_matrices ); + delete tick2; + + Tick *tick3 = new Tick( "Pre-compute our controls." ); + subdivision_matrix::prepare( mesh.vs, M_matrices, Du_matrices, Dv_matrices, + subdivision_handles, weight_function, + engine->W_matrices ); + delete tick3; + + Tick *tick4 = new Tick( "Pre-compute target surface." ); + subdivision_matrix::naive_prepare( M_matrices*mesh.vs, subdivision_handles, engine->target_W_matrices ); + delete tick4; + + + return engine; + } + // delete the result when you are finished with it. + engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const handles_t& handle_positions, const char* weight_function, OpenSubdiv::HbrMesh* hmesh, std::vector< subdivision_matrix::index_t >* quad_faces_out ) + { + Tick *tick1 = new Tick( "preparation" ); + assert( vertices.size() % 3 == 0 ); + int size = vertices.size() / 3; + + // build subdivision_matrix::mesh + subdivision_matrix::subdivision_control_mesh mesh; + mesh.faces = faces; + mesh.vs.resize( size, 3 ); + for( int i = 0; i < size; i++ ) + mesh.vs.row( i ) << vertices[i*3], vertices[i*3+1], vertices[i*3+2]; + std::cout << "vertices #: " << size << " faces #: " << faces.first.size() << std::endl; + + // build engine. + engine_t* engine = new engine_t; + engine->num_of_controls = size; + engine->mesh = mesh; + + subdivision_matrix::SparseMatrix_t M_matrices; + if( hmesh ) { + subdivision_matrix::compute_subdivision_coefficients_for_mesh( + mesh.vs.rows(), + hmesh, + kLevel, + M_matrices, + quad_faces_out + ); + } else { + subdivision_matrix::compute_subdivision_coefficients_for_mesh( + mesh.vs.rows(), + mesh.faces, + kLevel, + M_matrices, + quad_faces_out + ); + } + + engine->M_matrices = M_matrices; + + // std::vector< std::vector< subdivision_matrix::real_t > > limit_vertices_out; + // std::vector< std::vector< int > > limit_quad_faces_out; + // subdivision_limit_mesh::compute_quad_limit_mesh_for_mesh( mesh.vs, hmesh, resolution, resolution, limit_vertices_out, limit_quad_faces_out ); + + // Convert the handle positions' type. + assert( handle_positions.cols() == 3 ); + subdivision_matrix::MatrixX3_t subdivision_handles( handle_positions.rows(), 3 ); + + for( int i = 0; i < handle_positions.rows(); i++ ) + subdivision_handles.row( i ) << handle_positions(i,0), + handle_positions(i,1), + handle_positions(i,2); +// engine->handle_positions = subdivision_handles; + // prepare the precompute matrices and save them in engine. + delete tick1; + + Tick *tick2 = new Tick( "Pre-compute naive controls." ); + subdivision_matrix::naive_prepare( mesh.vs, subdivision_handles, engine->naive_W_matrices ); + delete tick2; + + Tick *tick3 = new Tick( "Pre-compute our controls." ); + subdivision_matrix::prepare( mesh.vs, M_matrices, + subdivision_handles, weight_function, + engine->W_matrices ); + delete tick3; + + Tick *tick4 = new Tick( "Pre-compute target surface." ); + subdivision_matrix::naive_prepare( M_matrices*mesh.vs, subdivision_handles, engine->target_W_matrices ); + delete tick4; + + + return engine; + } + // delete the result when you are finished with it. + engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const subdivision_matrix::SparseMatrix_t& M_matrices, const subdivision_matrix::MatrixXX_t& weights ) + { + Tick* tick1 = new Tick( "preparation" ); + + assert( vertices.size() % 3 == 0 ); + const int size = vertices.size() / 3; + + // build engine. + engine_t* engine = new engine_t; + engine->num_of_controls = size; + + // build subdivision_matrix::mesh + subdivision_matrix::subdivision_control_mesh& mesh = engine->mesh; + mesh.faces = faces; + mesh.vs.resize( size, 3 ); + for( int i = 0; i < size; i++ ) + { + mesh.vs.row( i ) << vertices[i*3], vertices[i*3+1], vertices[i*3+2]; + } + std::cout << "vertices #: " << size << " faces #: " << faces.first.size() << std::endl; + + engine->M_matrices = M_matrices; + delete tick1; + + + Tick* tick3 = new Tick( "Pre-compute our controls." ); + subdivision_matrix::prepare( mesh.vs, M_matrices, weights, engine->W_matrices ); + delete tick3; + + return engine; + } + + void set_new_handle_positions( engine_t* engine, const handles_t& handle_positions, const char* weight_function ) + { + assert( engine ); + + // Convert the handle positions' type. + assert( handle_positions.cols() == 3 ); + subdivision_matrix::MatrixX3_t subdivision_handles = handle_positions; + // engine->handle_positions = subdivision_handles; + + // prepare the precompute matrices and save them in engine. + Tick *tick2 = new Tick( "Pre-compute naive controls." ); + subdivision_matrix::naive_prepare( engine->mesh.vs, subdivision_handles, engine->naive_W_matrices ); + delete tick2; + + Tick *tick3 = new Tick( "Pre-compute our controls." ); + subdivision_matrix::prepare( engine->mesh.vs, engine->M_matrices, + subdivision_handles, weight_function, + engine->W_matrices ); + delete tick3; + + Tick *tick4 = new Tick( "Pre-compute target surface." ); + subdivision_matrix::naive_prepare( engine->M_matrices*engine->mesh.vs, subdivision_handles, engine->target_W_matrices ); + delete tick4; + } + + void update_control_vertices_given_handle_transforms( const engine_t* engine, const std::vector< transform_t >& transforms, vertices_t& vertices_out ) + { + assert( engine ); + assert( transforms.size() > 0 ); + assert( transforms.size() == engine->W_matrices.size() ); + +// Tick *tick = new Tick( "update." ); + // Convert the transforms' type. + std::vector< subdivision_matrix::transform_t > subdivision_transforms; + for ( auto transform : transforms ) { + assert( transform.cols() == 4 && transform.rows() == 4 ); + + subdivision_matrix::transform_t converted_transform; + for ( int i = 0; i < 4; i++ ) + converted_transform.row(i) << transform(i,0), transform(i,1), transform(i,2), transform(i,3); + + subdivision_transforms.push_back( converted_transform ); + } + + const int nverts = engine->num_of_controls; + + subdivision_matrix::MatrixX3_t deformed_controls( nverts, 3 ); + subdivision_matrix::solve( engine->W_matrices, subdivision_transforms, deformed_controls ); + + vertices_out.resize( nverts * 3 ); + for( int vi = 0; vi < nverts; ++vi ) + { + vertices_out[ 3*vi + 0 ] = deformed_controls(vi, 0); + vertices_out[ 3*vi + 1 ] = deformed_controls(vi, 1); + vertices_out[ 3*vi + 2 ] = deformed_controls(vi, 2); + } +// delete tick; + /* + subdivision_matrix::MatrixX3_t b_min(1, 3), b_max(1, 3); + b_min = engine->mesh.vs.colwise().minCoeff(); + b_max = engine->mesh.vs.colwise().maxCoeff(); + real_t unit = real_t( (b_max - b_min).norm() ); + + subdivision_matrix::MatrixX3_t target = engine->M_matrices * engine->mesh.vs; + subdivision_matrix::real_t our_hausdorff; + our_hausdorff = subdivision_matrix::hausdorff_distance( target, engine->M_matrices*deformed_controls ); + std::cout << "our hausdorff percentage:\n" << our_hausdorff / unit <<"\n\n"; + */ + + } + + + void update_control_vertices_given_handle_transforms( const engine_t* engine, const std::vector< transform_t >& transforms, vertices_t& vertices_out, const char* approach, vertices_t& g_targetPositions, vertices_t& g_targetColors ) + { + assert( engine ); + assert( transforms.size() > 0 ); + assert( transforms.size() == engine->W_matrices.size() ); + + // Convert the transforms' type. + std::vector< subdivision_matrix::transform_t > subdivision_transforms; + for ( auto transform : transforms ) { + assert( transform.cols() == 4 && transform.rows() == 4 ); + + subdivision_matrix::transform_t converted_transform; + for ( int i = 0; i < 4; i++ ) + converted_transform.row(i) << transform(i,0), transform(i,1), transform(i,2), transform(i,3); + + subdivision_transforms.push_back( converted_transform ); + } + + const int nverts = engine->num_of_controls; + + // compute the deformed control points' positions. + subdivision_matrix::MatrixX3_t deformed_controls( nverts, 3 ), naive_result( nverts, 3 ), result( nverts, 3 ); + + Tick *tick1 = new Tick( "update naive controls." ); + subdivision_matrix::solve( engine->naive_W_matrices, subdivision_transforms, naive_result ); + delete tick1; + + + Tick *tick2 = new Tick( "update our controls." ); + subdivision_matrix::solve( engine->W_matrices, subdivision_transforms, result ); + delete tick2; + + if ( strcmp( approach, "naive") == 0 ) + deformed_controls = naive_result; + else + deformed_controls = result; + + Tick *tick3 = new Tick( "update target surface." ); + subdivision_matrix::MatrixX3_t target_positions; + subdivision_matrix::solve( engine->target_W_matrices, subdivision_transforms, target_positions ); + delete tick3; + +/* + subdivision_matrix::MatrixX3_t our_positions = engine->M_matrices * result; + subdivision_matrix::MatrixX3_t naive_positions = engine->M_matrices * naive_result; + subdivision_matrix::real_t our_diff + = subdivision_matrix::norm_of_piecewise_distance( target_positions, our_positions ); + subdivision_matrix::real_t naive_diff + = subdivision_matrix::norm_of_piecewise_distance( target_positions, naive_positions ); + std::cout << "naive vs ours:\n" << naive_diff << ' ' << our_diff << "\n\n"; +*/ + + Tick *tick4 = new Tick( "compute piecewise distance." ); + subdivision_matrix::VectorX_t our_diffs + = subdivision_matrix::piecewise_distance( target_positions, engine->M_matrices*result ); + subdivision_matrix::VectorX_t naive_diffs + = subdivision_matrix::piecewise_distance( target_positions, engine->M_matrices*naive_result ); + + subdivision_matrix::VectorX_t diff_values; + if ( strcmp( approach, "naive") == 0 ) + diff_values = naive_diffs; + else + diff_values = our_diffs; + delete tick4; + +#ifdef HAUSDORFF_COMPARISON + subdivision_matrix::MatrixX3_t target = engine->M_matrices * engine->mesh.vs; + subdivision_matrix::real_t naive_hausdorff, our_hausdorff; + naive_hausdorff = subdivision_matrix::hausdorff_distance( target, engine->M_matrices*naive_result ); + our_hausdorff = subdivision_matrix::hausdorff_distance( target, engine->M_matrices*result ); + std::cout << "naive hausdorff vs our hausdorff:\n" << naive_hausdorff << " " << our_hausdorff <<"\n\n"; +#endif + // Convert the new control points' positions from matrix to an one-row array. + vertices_out.resize( nverts * 3 ); + g_targetPositions.resize( target_positions.size() ); + g_targetColors.resize( target_positions.size() ); + + for( int vi = 0; vi < nverts; ++vi ) + { + vertices_out[ 3*vi + 0 ] = deformed_controls(vi, 0); + vertices_out[ 3*vi + 1 ] = deformed_controls(vi, 1); + vertices_out[ 3*vi + 2 ] = deformed_controls(vi, 2); + } + + subdivision_matrix::MatrixX3_t b_min(1, 3), b_max(1, 3); + b_min = engine->mesh.vs.colwise().minCoeff(); + b_max = engine->mesh.vs.colwise().maxCoeff(); + + real_t unit = real_t( (b_max - b_min).norm() ); + + std::cout << unit << std::endl; + ColorMap< real_t > colorMap(0, 0.1*unit); + for( int vi = 0; vi < target_positions.rows(); ++vi ) + { + g_targetPositions[ 3*vi + 0 ] = target_positions(vi, 0); + g_targetPositions[ 3*vi + 1 ] = target_positions(vi, 1); + g_targetPositions[ 3*vi + 2 ] = target_positions(vi, 2); + colorMap.Value2Color( diff_values( vi ), &g_targetColors[ 3*vi ] ); + + } + // XXX END Debugging: Apply the first transform to all vertices. + } + void delete_engine_t( engine_t* engine ) + { + delete engine; + } + + void limit_vertices_for_initial_sample_locations( const engine_t* engine, vertices_t& limit_vertices_out ) + { + assert( engine ); + + subdivision_matrix::MatrixX3_t limit_vertices = engine->M_matrices * engine->mesh.vs; + + limit_vertices_out.clear(); + limit_vertices_out.resize( limit_vertices.rows()*3, -31337 ); + for( int i = 0; i < limit_vertices.rows(); ++i ) + { + limit_vertices_out.at( 3*i + 0 ) = limit_vertices( i, 0 ); + limit_vertices_out.at( 3*i + 1 ) = limit_vertices( i, 1 ); + limit_vertices_out.at( 3*i + 2 ) = limit_vertices( i, 2 ); + } + } +} + +#ifdef SUBDIVISION_SKINNING_MAIN +void test_our_approach() +{ + float vs[] = { TORUS_VS }; + std::vector vertices (vs, vs + sizeof(vs) / sizeof(float) ); + subdivision_skinning::faces_t faces = TORUS_FACES; + + subdivision_skinning::handles_t handle_positions; + handle_positions.resize(2,3); + handle_positions << 0.8, 0.8, 0.7, + 0.2, -0.3, -0.2; + + const char* weight_function = "shepard"; + + subdivision_skinning::engine_t *engine = subdivision_skinning::new_engine_for_control_mesh( vertices, faces, handle_positions, weight_function); + + std::vector< subdivision_skinning::transform_t > transforms; + subdivision_skinning::transform_t t1, t2; + t1.setIdentity(); + t2.setIdentity(); + transforms.push_back( t1 ); + transforms.push_back( t2 ); + + subdivision_skinning::vertices_t vertices_out; + + update_control_vertices_given_handle_transforms( engine, transforms, vertices_out ); + + for ( auto v : vertices_out ) + std::cout << v << ' '; + std::cout << std::endl; +} + + +int main( int argc, char* argv[] ) +{ + if ( argc < 2 || argv[1].strcmp( "ours" ) == 0 ) + test_our_approach(); + else if ( argv[1].strcmp( "naive" ) == 0 ) + test_naive_approach(); + + return 0; +} +#endif diff --git a/lib/subdivision_skinning.h b/lib/subdivision_skinning.h new file mode 100644 index 0000000..d4a66fe --- /dev/null +++ b/lib/subdivision_skinning.h @@ -0,0 +1,54 @@ +#ifndef __subdivision_skinning_h__ +#define __subdivision_skinning_h__ + +#include +#include + +// -I/path/to/OpenSubDiv +#include +#include + +#include "subdivision_engine.h" + +namespace subdivision_skinning +{ + typedef float real_t; + // A sequence of vertex values: x0 y0 z0 x1 y1 z1 ... + typedef std::vector< real_t > vertices_t; + // Each face in a faces_t is a sequence of vertex indices. + using subdivision_matrix::faces_t; + // An H-by-3 matrix with an x,y,z position for each handle. + typedef Eigen::MatrixXf handles_t; + // A transform is a 4x4 matrix. + typedef Eigen::Matrix4f transform_t; + + // Forward declare the engine structure. + struct engine_t; + + // Precomputes and returns a new engine_t for the given subdivision surface. + // The resulting pointer can be passed to update_control_vertices_given_handle_transforms(). + // To destroy the engine_t and free the memory associated with it, call delete_engine_t(). + // The optional HbrMesh* provides tagging information. + engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const handles_t& handle_positions, const char* weight_function, OpenSubdiv::HbrMesh* mesh = 0 ); + engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const handles_t& handle_positions, const char* weight_function, OpenSubdiv::HbrMesh* mesh, std::vector< subdivision_matrix::index_t >* quad_faces_out = 0 ); + engine_t* new_engine_for_control_mesh( const vertices_t& vertices, const faces_t& faces, const subdivision_matrix::SparseMatrix_t& M_matrices, const subdivision_matrix::MatrixXX_t& weights ); + // Given an existing 'engine_t', modifies it to use the new 'handle_positions' and 'weight_function'. + // Subsequent calls to update_control_vertices_given_handle_transforms() must pass + // 'handle_positions.rows()' transforms. + void set_new_handle_positions( engine_t* engine, const handles_t& handle_positions, const char* weight_function ); + // Given a set of transform_t's, one for each handle_position, + // returns in 'vertices_out' updated positions for the control points of the subdivision surface. + void update_control_vertices_given_handle_transforms( const engine_t* engine, const std::vector< transform_t >& transforms, vertices_t& vertices_out ); + // update with test and all kinds of comparisons. + void update_control_vertices_given_handle_transforms( const engine_t* engine, const std::vector< transform_t >& transforms, vertices_t& vertices_out, const char* approach, vertices_t& g_targetPositions, vertices_t& g_targetColors ); + // Frees the memory associated with an engine_t. + void delete_engine_t( engine_t* engine ); + // naive approach wrapper + + // Upon return, replaces 'limit_vertices_out' with limit vertex positions + // for all of our sample locations, based on the initial control mesh. + // This is useful for an experiment. + void limit_vertices_for_initial_sample_locations( const engine_t* engine, vertices_t& limit_vertices_out ); +} + +#endif /* __subdivision_skinning_h__ */ diff --git a/lib/subdivision_skinning_wrapper.cpp b/lib/subdivision_skinning_wrapper.cpp new file mode 100644 index 0000000..0d0b229 --- /dev/null +++ b/lib/subdivision_skinning_wrapper.cpp @@ -0,0 +1,203 @@ +#include "subdivision_skinning.h" +#include "subdivision_matrices_osd_eigen.h" + +#include // std::copy + +// Static definitions. +namespace +{ + const int kDefaultLevel = 4; + + struct subdivision_evaluator_t + { + subdivision_skinning::vertices_t vertices; + subdivision_skinning::faces_t faces; + subdivision_matrix::SparseMatrix_t M_matrices; + std::vector< subdivision_matrix::index_t > quad_faces; + }; + + struct subdivision_skinning_engine_t + { + subdivision_matrix::SparseMatrix_t M_matrices; + int num_transforms; + subdivision_skinning::engine_t* engine; + + subdivision_skinning_engine_t() : engine( 0 ) {} + ~subdivision_skinning_engine_t() + { + if( engine ) subdivision_skinning::delete_engine_t( engine ); + engine = 0; + } + }; +} + + +extern "C" { + +#include "subdivision_skinning_wrapper.h" + +void* new_subdivision_evaluator( int num_vertices, const subdivision_evaluator_real_t* vertices, int num_faces, const int* faces, int level ) +{ + assert( num_vertices > 0 ); + assert( num_faces > 0 ); + assert( vertices ); + assert( faces ); + assert( level > 0 ); + + // Create the result object. + subdivision_evaluator_t* result = new subdivision_evaluator_t; + + // Copy the vertices. + result->vertices.resize( num_vertices*3 ); + std::copy( vertices, vertices + num_vertices*3, result->vertices.begin() ); + + // Copy the faces. + result->faces.first.reserve( num_faces ); + result->faces.second.reserve( num_faces*4 ); + for( int fi = 0; fi < num_faces; ++fi ) + { + // Number of vertices in the face: + result->faces.first.push_back( 0 ); + for( int vi = 0; vi < 4; ++vi ) + { + const int vertex_index = faces[ 4*fi + vi ]; + if( -1 != vertex_index ) + { + result->faces.second.push_back( vertex_index ); + result->faces.first.back() += 1; + } + } + assert( result->faces.first.back() == 3 || result->faces.first.back() == 4 ); + } + + // Create M_matrices and quads. + subdivision_matrix::compute_subdivision_coefficients_for_mesh( + num_vertices, + result->faces, + level, + result->M_matrices, + &result->quad_faces + ); + + return result; +} +void delete_subdivision_evaluator( void* subdivision_evaluator ) +{ + delete static_cast< subdivision_evaluator_t* >( subdivision_evaluator ); +} + +int num_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator_in ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + + assert( subdivision_evaluator->quad_faces.size() % 4 == 0 ); + return subdivision_evaluator->quad_faces.size() / 4; +} +void get_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator_in, int* refined_quad_faces_out ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + + assert( refined_quad_faces_out ); + std::copy( subdivision_evaluator->quad_faces.begin(), subdivision_evaluator->quad_faces.end(), refined_quad_faces_out ); +} + +int num_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator_in ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + + return subdivision_evaluator->M_matrices.rows(); +} +void get_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator_in, subdivision_evaluator_real_t* refined_vertices_out ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + assert( refined_vertices_out ); + + assert( subdivision_evaluator->vertices.size() == subdivision_evaluator->M_matrices.cols()*3 ); + const Eigen::Map< const Eigen::Matrix< subdivision_skinning::vertices_t::value_type, Eigen::Dynamic, 3, Eigen::RowMajor > > vs( &subdivision_evaluator->vertices[0], subdivision_evaluator->M_matrices.cols(), 3 ); + Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, 3, Eigen::RowMajor > > out( refined_vertices_out, subdivision_evaluator->M_matrices.rows(), 3 ); + out = ( subdivision_evaluator->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); +} +void get_refined_vertices_of_subdivision_evaluator_with_control_vertices( const void* subdivision_evaluator_in, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + assert( vertex_dimension > 0 ); + assert( control_vertices ); + assert( refined_vertices_out ); + + const Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > vs( control_vertices, subdivision_evaluator->M_matrices.cols(), vertex_dimension ); + Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > out( refined_vertices_out, subdivision_evaluator->M_matrices.rows(), vertex_dimension ); + out = ( subdivision_evaluator->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); +} + +void* new_subdivision_skinning_engine( const void* subdivision_evaluator_in, int num_transforms, const subdivision_evaluator_real_t* weights_in ) +{ + assert( num_transforms > 0 ); + assert( weights_in ); + assert( subdivision_evaluator_in ); + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + + subdivision_skinning_engine_t* result = new subdivision_skinning_engine_t; + result->num_transforms = num_transforms; + result->M_matrices = subdivision_evaluator->M_matrices; + const subdivision_matrix::MatrixXX_t weights = Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( weights_in, subdivision_evaluator->M_matrices.rows(), num_transforms ).cast< subdivision_matrix::real_t >(); + result->engine = subdivision_skinning::new_engine_for_control_mesh( subdivision_evaluator->vertices, subdivision_evaluator->faces, subdivision_evaluator->M_matrices, weights ); + + return result; +} +void delete_subdivision_skinning_engine( void* subdivision_skinning_engine ) +{ + delete static_cast< subdivision_skinning_engine_t* >( subdivision_skinning_engine ); +} + +void compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( const void* subdivision_skinning_engine_in, const subdivision_evaluator_real_t* transforms_in, subdivision_evaluator_real_t* control_mesh_vertices_out ) +{ + const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine_in ); + assert( engine ); + assert( engine->engine ); + assert( transforms_in ); + assert( control_mesh_vertices_out ); + + // Copy the transforms. + std::vector< subdivision_skinning::transform_t > transforms( engine->num_transforms ); + for( int i = 0; i < engine->num_transforms; ++i ) + { + transforms.at(i) = Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, 4, 4, Eigen::RowMajor > >( transforms_in + 16*i, 4, 4 ); + } + + // Solve for the new control mesh vertices. + subdivision_skinning::vertices_t control_mesh_vertices; + update_control_vertices_given_handle_transforms( engine->engine, transforms, control_mesh_vertices ); + + // Copy to the output. + assert( control_mesh_vertices.size() == engine->M_matrices.cols()*3 ); + // Copy (and convert floating point formats if needed). + Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, 3, Eigen::RowMajor > >( control_mesh_vertices_out, engine->M_matrices.cols(), 3 ) = + Eigen::Map< Eigen::Matrix< subdivision_skinning::vertices_t::value_type, Eigen::Dynamic, 3, Eigen::RowMajor > >( &control_mesh_vertices[0], engine->M_matrices.cols(), 3 ); +} + +int num_refined_vertices_of_subdivision_skinning_engine( const void* subdivision_skinning_engine ) +{ + const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine ); + assert( engine ); + + return engine->M_matrices.rows(); +} +void get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( const void* subdivision_skinning_engine, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ) +{ + const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine ); + assert( engine ); + assert( vertex_dimension > 0 ); + assert( control_vertices ); + assert( refined_vertices_out ); + + const Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > vs( control_vertices, engine->M_matrices.cols(), vertex_dimension ); + Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > out( refined_vertices_out, engine->M_matrices.rows(), vertex_dimension ); + out = ( engine->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); +} + +} diff --git a/lib/subdivision_skinning_wrapper.h b/lib/subdivision_skinning_wrapper.h new file mode 100644 index 0000000..a328059 --- /dev/null +++ b/lib/subdivision_skinning_wrapper.h @@ -0,0 +1,102 @@ +typedef float subdivision_evaluator_real_t; + +/* +Given a control mesh with 'num_vertices' 3D vertices, +3*num_vertices values 'vertices' arranged: + x0 y0 z0 x1 y1 z1 ... +'num_faces' triangle or quad faces, +4*num_faces indices into the num_vertices arranged: + face0_vertex0 face0_vertex1 face0_vertex2 face0_vertex3 face1_vertex0 face1_vertex1 face1_vertex2 face1_vertex3 ... +and a positive integer level representing the number of subdivision steps to perform (e.g. 4), +creates and returns an opaque subdivision_evaluator*. +When finished, the memory used by the subdivision_evaluator* must be freed using delete_subdivision_evaluator(). +NOTE: For a triangle face, set the face's last vertex to -1. +*/ +void* new_subdivision_evaluator( int num_vertices, const subdivision_evaluator_real_t* vertices, int num_faces, const int* faces, int level ); +// Frees the memory associated with the subdivision_evaluator created by new_subdivision_evaluator(). +void delete_subdivision_evaluator( void* subdivision_evaluator ); + +// Returns the number of quad faces generated by the subdivision_evaluator. +int num_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator ); +/* +Given a 'subdivision_evaluator', +upon return, places 'num_refined_quad_faces_of_subdivision_evaluator()' quad faces in 'refined_quad_faces_out'. +The quads are arranged: + face0_vertex0 face0_vertex1 face0_vertex2 face0_vertex3 face1_vertex0 face1_vertex1 face1_vertex2 face1_vertex3 ... +NOTE: The 'refined_quad_faces_out' parameter must point to (at least) 4*num_refined_quad_faces_of_subdivision_evaluator() integers of memory. +NOTE: All faces are quads, so there will be no -1 indices. + This is in contrast to new_subdivision_evaluator(), which can take triangles or quads. +*/ +void get_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator, int* refined_quad_faces_out ); + +// Returns the number of refined vertices generated by the subdivision_evaluator. +int num_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator ); +/* +Given a 'subdivision_evaluator', +upon return, places 'num_refined_vertices_of_subdivision_evaluator()' 3D vertex values in 'refined_vertices_out'. +The vertices are arranged: + x0 y0 z0 x1 y1 z1 ... +NOTE: The 'refined_vertices_out' parameter must point to (at least) 3*num_refined_vertices_of_subdivision_evaluator() subdivision_evaluator_real_t of memory. +*/ +void get_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator, subdivision_evaluator_real_t* refined_vertices_out ); +/* +Given a 'subdivision_evaluator' and +a 'vertex_dimension'-dimensional control mesh 'control_vertices' of the same quantity +and arrangement as was passed to new_subdivision_evaluator(): + (in 3D) x0 y0 z0 x1 y1 z1 ... + (in general) vertex0_coordinate0 vertex0_coordinate1 ... vertex0_coordinate_vertex_dimension-1 vertex1_coordinate0 vertex1_coordinate1 ... +Upon return, places 'num_refined_vertices_of_subdivision_evaluator()' 'vertex_dimension'-dimensional +vertex values in 'refined_vertices_out'. The vertices are arranged: + (in 3D) x0 y0 z0 x1 y1 z1 ... + (in general) vertex0_coordinate0 vertex0_coordinate1 ... vertex0_coordinate_vertex_dimension-1 vertex1_coordinate0 vertex1_coordinate1 ... +NOTE: The 'refined_vertices_out' parameter must point to (at least) 'vertex_dimension*num_refined_vertices_of_subdivision_evaluator()' subdivision_evaluator_real_t of memory. +NOTE: 'vertex_dimension' must be positive (greater than zero). +*/ +void get_refined_vertices_of_subdivision_evaluator_with_control_vertices( const void* subdivision_evaluator, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ); + +/* +Given a subdivision_evaluator* and num_refined_vertices_of_subdivision_evaluator()-by-'num_transforms' +'weights', which are values arranged: + vertex0_transform0 vertex0_transform1 vertex0_transform2 ... vertex1_transform0 vertex1_transform1 vertex1_transform2 ... +creates and returns an opaque subdivision_skinning_engine*. +When finished, the memory must be freed by delete_subdivision_skinning_engine(). +NOTE: The subdivision_skinning_engine does *not* depend on the 'subdivision_evaluator'. + You may call delete_subdivision_evaluator() to free the subdivision_evaluator + once you have created the subdivision_skinning_engine(). +*/ +void* new_subdivision_skinning_engine( const void* subdivision_evaluator, int num_transforms, const subdivision_evaluator_real_t* weights ); +// Frees the memory associated with the subdivision_skinning_engine created by new_subdivision_skinning_engine(). +void delete_subdivision_skinning_engine( void* subdivision_skinning_engine ); + +/* +Given a subdivision_skinning_engine* and +a set of 'transforms', +returns in 'control_mesh_vertices_out' 3D control mesh vertices +that provide the best fitting refined subdivision surface to the linear blend +skin deformation specified by the weights and 'transforms'. +The parameter 'transforms' contains 4x4 matrices stacked vertically into a very tall matrix. +The matrix is provided in row-major order, and the number of transforms +is the parameter 'num_transforms' passed to new_subdivision_skinning_engine(). +*/ +void compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( const void* subdivision_skinning_engine, const subdivision_evaluator_real_t* transforms, subdivision_evaluator_real_t* control_mesh_vertices_out ); + +// Given a 'subdivision_skinning_engine', +// returns the number of refined vertices generated by the subdivision_skinning_engine. +// NOTE: The returned value will be the same as would be returned by +// 'num_refined_vertices_of_subdivision_evaluator()' with the 'subdivision_evaluator' +// used to create the 'subdivision_skinning_engine'. +int num_refined_vertices_of_subdivision_skinning_engine( const void* subdivision_skinning_engine ); +/* +Given a 'subdivision_skinning_engine' and +a 'vertex_dimension'-dimensional control mesh 'control_vertices' of the same quantity +and arrangement as was passed to new_subdivision_evaluator(): + (in 3D) x0 y0 z0 x1 y1 z1 ... + (in general) vertex0_coordinate0 vertex0_coordinate1 ... vertex0_coordinate_vertex_dimension-1 vertex1_coordinate0 vertex1_coordinate1 ... +Upon return, places 'num_refined_vertices_of_subdivision_skinning_engine()' 'vertex_dimension'-dimensional +vertex values in 'refined_vertices_out'. The vertices are arranged: + (in 3D) x0 y0 z0 x1 y1 z1 ... + (in general) vertex0_coordinate0 vertex0_coordinate1 ... vertex0_coordinate_vertex_dimension-1 vertex1_coordinate0 vertex1_coordinate1 ... +NOTE: The 'refined_vertices_out' parameter must point to (at least) 'vertex_dimension*num_refined_vertices_of_subdivision_skinning_engine()' subdivision_evaluator_real_t of memory. +NOTE: 'vertex_dimension' must be positive (greater than zero). +*/ +void get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( const void* subdivision_skinning_engine, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ); diff --git a/lib/subdivision_skinning_wrapper_test.cpp b/lib/subdivision_skinning_wrapper_test.cpp new file mode 100644 index 0000000..a1e3be1 --- /dev/null +++ b/lib/subdivision_skinning_wrapper_test.cpp @@ -0,0 +1,264 @@ +// c++ -g subdivision_skinning_wrapper_test.cpp -o subdivision_skinning_wrapper_test ../build/Release/src/libsubdivision_skinning.a ../OpenSubdiv/build/lib/libosdCPU.a + +#include +#include +#include +#include +#include // atoi +#include +#include + +#include // std::fill + +extern "C" +{ +#include "subdivision_skinning_wrapper.h" +} + +namespace +{ + +// The Torus data from OpenSubdiv +subdivision_evaluator_real_t TORUS_VS[] = { 1.25052, 0.517982, 0.353553, 0.597239, 0.247384, 0.353553, 0.597239, 0.247384, -0.353553, 1.25052, 0.517982, -0.353553, 0.517982, 1.25052, 0.353553, 0.247384, 0.597239, 0.353553, 0.247384, 0.597239, -0.353553, 0.517982, 1.25052, -0.353553, -0.517982, 1.25052, 0.353553, -0.247384, 0.597239, 0.353553, -0.247384, 0.597239, -0.353553, -0.517982, 1.25052, -0.353553, -1.25052, 0.517982, 0.353553, -0.597239, 0.247384, 0.353553, -0.597239, 0.247384, -0.353553, -1.25052, 0.517982, -0.353553, -1.25052, -0.517982, 0.353553, -0.597239, -0.247384, 0.353553, -0.597239, -0.247384, -0.353553, -1.25052, -0.517982, -0.353553, -0.517982, -1.25052, 0.353553, -0.247384, -0.597239, 0.353553, -0.247384, -0.597239, -0.353553, -0.517982, -1.25052, -0.353553, 0.517982, -1.25052, 0.353553, 0.247384, -0.597239, 0.353553, 0.247384, -0.597239, -0.353553, 0.517982, -1.25052, -0.353553, 1.25052, -0.517982, 0.353553, 0.597239, -0.247384, 0.353553, 0.597239, -0.247384, -0.353553, 1.25052, -0.517982, -0.353553 }; +// #define TORUS_FACES { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } +int TORUS_FACES[] = { 4, 5, 1, 0, 5, 6, 2, 1, 6, 7, 3, 2, 7, 4, 0, 3, 8, 9, 5, 4, 9, 10, 6, 5, 10, 11, 7, 6, 11, 8, 4, 7, 12, 13, 9, 8, 13, 14, 10, 9, 14, 15, 11, 10, 15, 12, 8, 11, 16, 17, 13, 12, 17, 18, 14, 13, 18, 19, 15, 14, 19, 16, 12, 15, 20, 21, 17, 16, 21, 22, 18, 17, 22, 23, 19, 18, 23, 20, 16, 19, 24, 25, 21, 20, 25, 26, 22, 21, 26, 27, 23, 22, 27, 24, 20, 23, 28, 29, 25, 24, 29, 30, 26, 25, 30, 31, 27, 26, 31, 28, 24, 27, 0, 1, 29, 28, 1, 2, 30, 29, 2, 3, 31, 30, 3, 0, 28, 31 }; + +void print_OBJ( std::ostream& out, int num_vs, subdivision_evaluator_real_t* vs, int num_faces, int* quad_faces, const std::string& header_message = "" ) +{ + // Save an optional header message. + out << "# OBJ: " << header_message << '\n'; + + // Save vertices. + for( int i = 0; i < num_vs; ++i ) + { + out << "v " << vs[3*i+0] << ' ' << vs[3*i+1] << ' ' << vs[3*i+2] << '\n'; + } + + out << '\n'; + + for( int i = 0; i < num_faces; ++i ) + { + // Vertices are 1-indexed. + out << "f " << (1+quad_faces[ 4*i + 0 ]) << ' ' << (1+quad_faces[ 4*i + 1 ]) << ' ' << (1+quad_faces[ 4*i + 2 ]); + if( -1 != quad_faces[ 4*i + 3 ] ) out << ' ' << (1+quad_faces[ 4*i + 3 ]) << '\n'; + else out << '\n'; + } +} +void save_OBJ( const std::string& out_path, int num_vs, subdivision_evaluator_real_t* vs, int num_faces, int* quad_faces, const std::string& header_message = "" ) +{ + std::ofstream out( out_path ); + print_OBJ( out, num_vs, vs, num_faces, quad_faces, header_message ); + std::cout << "Saved a mesh to: " << out_path << '\n'; +} + +bool load_OBJ( std::istream& in, int* num_vs_out, subdivision_evaluator_real_t** vs_out, int* num_faces_out, int** quad_faces_out ) +{ + std::string line; + std::istringstream linestr; + + std::vector< subdivision_evaluator_real_t > vs; + std::vector< int > faces; + + int line_number = 0; + std::string type; + while( !( in >> std::ws ).eof() ) + { + line_number += 1; + + std::getline( in, line ); + if( line.empty() ) continue; + + linestr.clear(); + linestr.str( line ); + linestr >> type; + + if( type == std::string("v") ) + { + subdivision_evaluator_real_t x(-31337), y(-31337), z(-31337); + linestr >> x >> y >> z; + vs.push_back( x ); + vs.push_back( y ); + vs.push_back( z ); + if( !linestr ) + { + std::cerr << "Invalid vertex encountered on line " << line_number << ".\n"; + return false; + } + } + else if( type == std::string("f") ) + { + int num_face_vertices = 0; + while( !( linestr >> std::ws ).eof() ) + { + std::string vi; + linestr >> vi; + num_face_vertices += 1; + + // Now look for a slash, in case there's a bundle. + int slash = vi.find( "/" ); + if( std::string::npos == slash ) + { + faces.push_back( atoi( vi.c_str() ) ); + } + else + { + faces.push_back( atoi( vi.substr( 0, slash ).c_str() ) ); + } + // OBJ are 1-indexed, and a negative number indexes from the end. + // Nothing should equal 0. + if( faces.back() == 0 ) + { + std::cerr << "Invalid face vertex index of 0 encountered on line " << line_number << ".\n"; + return false; + } + } + + if( !( num_face_vertices == 3 || num_face_vertices == 4 ) ) + { + std::cerr << "Invalid face without three or four vertices encountered on line " << line_number << ".\n"; + return false; + } + // A triangle has a -1 as the fourth vertex index. + // UPDATE: push_back a 0, because + if( 3 == num_face_vertices ) faces.push_back( 0 ); + } + } + + assert( vs.size() % 3 == 0 ); + *num_vs_out = vs.size()/3; + *vs_out = new subdivision_evaluator_real_t[ vs.size() ]; + std::copy( vs.begin(), vs.end(), *vs_out ); + + assert( faces.size() % 4 == 0 ); + *num_faces_out = faces.size()/4; + *quad_faces_out = new int[ faces.size() ]; + for( int i = 0; i < faces.size(); ++i ) + { + (*quad_faces_out)[i] = + faces[i] >= 0 + ? faces[i] - 1 + // Negative indices add to the back. + : vs.size() + faces[i] + ; + } + + return true; +} +bool load_OBJ( const std::string& in_path, int* num_vs_out, subdivision_evaluator_real_t** vs_out, int* num_faces_out, int** quad_faces_out ) +{ + std::ifstream in( in_path ); + return load_OBJ( in, num_vs_out, vs_out, num_faces_out, quad_faces_out ); +} + +} + + +int main( int argc, const char* argv[] ) +{ + subdivision_evaluator_real_t* vs = &TORUS_VS[0]; + int* faces = &TORUS_FACES[0]; + int num_vs = sizeof( TORUS_VS )/sizeof( subdivision_evaluator_real_t )/3; + int num_faces = sizeof( TORUS_FACES )/sizeof( int )/4; + + if( argc == 2 ) + { + const bool success = load_OBJ( argv[1], &num_vs, &vs, &num_faces, &faces ); + if( !success ) return -1; + } + + std::cout << "Number of vertices: " << num_vs << '\n'; + std::cout << "Number of faces: " << num_faces << '\n'; + + save_OBJ( "original.obj", num_vs, vs, num_faces, faces ); + + void* eval = new_subdivision_evaluator( num_vs, vs, num_faces, faces, 4 ); + + const int num_refined_faces = num_refined_quad_faces_of_subdivision_evaluator( eval ); + int* refined_faces = new int[ num_refined_faces*4 ]; + get_refined_quad_faces_of_subdivision_evaluator( eval, refined_faces ); + + const int num_refined_vs = num_refined_vertices_of_subdivision_evaluator( eval ); + subdivision_evaluator_real_t* refined_vs = new subdivision_evaluator_real_t[ num_refined_vs*3 ]; + get_refined_vertices_of_subdivision_evaluator( eval, refined_vs ); + + save_OBJ( "original-refined.obj", num_refined_vs, refined_vs, num_refined_faces, refined_faces ); + + // Test the non-3 dimensional path by duplicating submitting 6-dimensional + // vertices xyzxyz: + { + std::vector< subdivision_evaluator_real_t > control_vs6( num_vs*6 ); + for( int i = 0; i < num_vs; ++i ) + for( int c = 0; c < 3; ++c ) + { + control_vs6[ 6*i + 3 + c ] = control_vs6[ 6*i + c ] = vs[ 3*i + c ]; + } + std::vector< subdivision_evaluator_real_t > refined_vs6( num_refined_vs*6 ); + get_refined_vertices_of_subdivision_evaluator_with_control_vertices( eval, 6, &control_vs6[0], &refined_vs6[0] ); + // Verify the output: + subdivision_evaluator_real_t total_diff = 0.; + for( int i = 0; i < num_refined_vs; ++i ) + for( int c = 0; c < 3; ++c ) + { + total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs6[ 6*i + 3 + c ] ); + total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs[ 3*i + c ] ); + } + std::cout << "Total difference of 6-dimensional versus 3-dimensional (should be 0): " << total_diff << '\n'; + } + + subdivision_evaluator_real_t* weights = new subdivision_evaluator_real_t[ num_refined_vs ]; + std::fill( weights, weights + num_refined_vs, 1. ); + void* engine = new_subdivision_skinning_engine( eval, 1, weights ); + + subdivision_evaluator_real_t* vs_modified = new subdivision_evaluator_real_t[ num_vs*3 ]; + /// This produces identical output as input: + // const subdivision_evaluator_real_t transforms[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; + /// This produces output whose coordinates are scaled by 2: + const subdivision_evaluator_real_t transforms[] = { 2,0,0,0, 0,2,0,0, 0,0,2,0, 0,0,0,2 }; + compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( engine, (subdivision_evaluator_real_t*)transforms, vs_modified ); + save_OBJ( "modified.obj", num_vs, vs_modified, num_faces, faces ); + + const int num_refined_vs2 = num_refined_vertices_of_subdivision_skinning_engine( engine ); + std::cout << "num_refined_vs eval: " << num_refined_vs << '\n'; + std::cout << "num_refined_vs engine: " << num_refined_vs2 << '\n'; + + get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 3, vs_modified, refined_vs ); + save_OBJ( "modified-refined.obj", num_refined_vs, refined_vs, num_refined_faces, refined_faces ); + + // Test the non-3 dimensional path by duplicating submitting 6-dimensional + // vertices xyzxyz: + { + std::vector< subdivision_evaluator_real_t > control_vs6( num_vs*6 ); + for( int i = 0; i < num_vs; ++i ) + for( int c = 0; c < 3; ++c ) + { + control_vs6[ 6*i + 3 + c ] = control_vs6[ 6*i + c ] = vs_modified[ 3*i + c ]; + } + std::vector< subdivision_evaluator_real_t > refined_vs6( num_refined_vs*6 ); + get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 6, &control_vs6[0], &refined_vs6[0] ); + // Verify the output: + subdivision_evaluator_real_t total_diff = 0.; + for( int i = 0; i < num_refined_vs; ++i ) + for( int c = 0; c < 3; ++c ) + { + total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs6[ 6*i + 3 + c ] ); + total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs[ 3*i + c ] ); + } + std::cout << "Total difference of 6-dimensional versus 3-dimensional (should be 0): " << total_diff << '\n'; + } + + delete_subdivision_skinning_engine( engine ); + delete_subdivision_evaluator( eval ); + + delete [] vs_modified; + delete [] refined_faces; + delete [] refined_vs; + delete [] weights; + + if( argc == 2 ) + { + delete[] vs; + delete[] faces; + } + + return 0; +} diff --git a/subdivgui/CMakeLists.txt b/subdivgui/CMakeLists.txt new file mode 100644 index 0000000..97be05d --- /dev/null +++ b/subdivgui/CMakeLists.txt @@ -0,0 +1,73 @@ +cmake_minimum_required(VERSION 2.8.6) + +## CGAL +find_package(CGAL REQUIRED) +## This conflicts with many of my directives if it isn't first. +include(${CGAL_USE_FILE}) +# This is absolutely necessary for Exact Construction +set(CMAKE_C_FLAGS "-frounding-math -fsignaling-nans") +#set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${CGAL_LIBRARIES} ${CGAL_3RD_PARTY_LIBRARIES}) + +## We need Eigen +find_package(Eigen3 REQUIRED) +include_directories( "${EIGEN3_INCLUDE_DIR}" ) + +## We need libigl +set(LIBIGL "${CMAKE_SOURCE_DIR}/libigl") +include_directories("${LIBIGL}/include") + +## We don't have/want MOSEK +add_definitions(-DIGL_NO_MOSEK) + +## AntTweakBar +include_directories("${LIBIGL}/external/AntTweakBar/include") +find_library(ANTTWEAKBAR_LIB AntTweakBar "${LIBIGL}/external/AntTweakBar/lib") +set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${ANTTWEAKBAR_LIB}) + +## Embree +include_directories("${LIBIGL}/external/embree") +include_directories("${LIBIGL}/external/embree/embree") +find_library(EMBREE_MAIN_LIB NAMES embree PATHS "${LIBIGL}/external/embree/build") +find_library(EMBREE_SYS_LIB NAMES sys PATHS "${LIBIGL}/external/embree/build") +set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${EMBREE_MAIN_LIB} ${EMBREE_SYS_LIB}) + +## Tetgen +include_directories("${LIBIGL}/external/tetgen") +find_library(TETGEN_LIB tet "${LIBIGL}/external/tetgen") +set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${TETGEN_LIB}) + +## YImage +include_directories("${LIBIGL}/external/yimg") +find_library(YIMG_LIB yimg "${LIBIGL}/external/yimg") +find_package(PNG REQUIRED) +set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${YIMG_LIB} ${PNG_LIBRARIES}) + +## OpenGL/GLU/GLUT +find_package(OpenGL REQUIRED) +find_package(GLUT REQUIRED) +#include_directories("${OPENGL_INCLUDE_DIRS}") +#include_directories("${GLUT_INCLUDE_DIRS}") +## I don't have Alec's patched GLUT with GLUT_ACTIVE_COMMAND +add_definitions(-DGLUT_ACTIVE_COMMAND=8) +set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES}) + +## Our library +include_directories("${CMAKE_SOURCE_DIR}/lib") +find_library(SUBDIVISION_SKINNING_LIB subdivision_skinning "${CMAKE_BINARY_DIR}/lib") +## OpenSubdiv library is defined by the CMakeList from our lib directory, which +## is processed first. +set(SUBDIVGUI_LIBS ${SUBDIVGUI_LIBS} ${SUBDIVISION_SKINNING_LIB} ${OPENSUBDIV_LIBRARY}) + +## We need C++11. Put this directive after CGAL's include. +set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++") + +add_executable(subdivgui + subdivgui.cpp + clean.cpp + robust_bbw.cpp + subdiv_weights.cpp + Mesh.cpp + ) +target_link_libraries(subdivgui + ${SUBDIVGUI_LIBS} + ) diff --git a/subdivgui/Makefile b/subdivgui/Makefile new file mode 100644 index 0000000..14f30a7 --- /dev/null +++ b/subdivgui/Makefile @@ -0,0 +1,110 @@ +.PHONY: all + +GG=clang++ +#CFLAGS += -DIGL_STATIC_LIBRARY +CFLAGS += -Wall -Wno-deprecated-declarations -Wno-deprecated-register +#CFLAGS += -O0 +CFLAGS += -O3 -DNDEBUG +CFLAGS+=-std=c++11 -g -DVERBOSE +MOSEKPLATFORM=osx64x86 +MOSEKVERSION=7 +# msse4.2 is necessary for me to get embree to compile correctly +AFLAGS=-m64 -msse4.2 + + +all: obj subdivgui + +.PHONY: subdivgui + +$(info Hello, $(LOGNAME)) +ifeq ($(LOGNAME),ajx) + LIB+=/opt/local/lib/gcc47/libstdc++.dylib + LIBIGL=/usr/local/igl/libigl/ + DEFAULT_PREFIX=/opt/local + ANTTWEAKBAR_LIB=-L$(LIBIGL)/external/AntTweakBar/lib -lAntTweakBar_clang -framework AppKit +else + IGL_NO_MOSEK=1 + LIBIGL=/Users/yotam/Work/ext/libigl + DEFAULT_PREFIX=/usr/local +## I don't have Alec's patched GLUT with GLUT_ACTIVE_COMMAND + CFLAGS += -DGLUT_ACTIVE_COMMAND=8 + ANTTWEAKBAR_LIB=-L$(LIBIGL)/external/AntTweakBar/lib -lAntTweakBar -framework AppKit +endif +EIGEN3_INC=-I$(DEFAULT_PREFIX)/include/eigen3 -I$(DEFAULT_PREFIX)/include/eigen3/unsupported +LIBIGL_INC=-I$(LIBIGL)/include +# LIBIGL_LIB=-L$(LIBIGL)/lib -ligl -liglembree -liglcgal -ligltetgen -liglbbw -liglmosek +LIBIGL_LIB=-L$(LIBIGL)/lib +EMBREE_INC=-I$(LIBIGL)/external/embree/embree/ -I$(LIBIGL)/external/embree/ +EMBREE_LIB=-L$(LIBIGL)/external/embree/build -lembree -lsys + +ANTTWEAKBAR_INC=-I$(LIBIGL)/external/AntTweakBar/include + +TETGEN=$(LIBIGL)/external/tetgen +TETGEN_LIB=-L$(TETGEN) -ltet +TETGEN_INC=-I$(TETGEN) + +# YIMAGE Library +YIMG=$(LIBIGL)/external/yimg/ +YIMG_LIB=-L$(YIMG) -lyimg -lz -L/usr/X11/lib -lpng -bind_at_load +YIMG_INC=-I/usr/X11/include -I$(YIMG) + +CARBON_LIB=-framework Carbon + +# Use free glut for mouse scrolling +#FREE_GLUT=/opt/local/ +#FREE_GLUT_INC=-I$(FREE_GLUT)/include +#FREE_GLUT_LIB=-L$(FREE_GLUT)/lib -lglut +GLUT_LIB=-framework GLUT -framework OpenGL +GLUT_INC= + +ifdef IGL_NO_MOSEK +CFLAGS+=-DIGL_NO_MOSEK +else +# Adjust your mosek paths etc. accordingly +ifndef MOSEKPLATFORM + MOSEKPLATFORM=osx64x86 +endif +ifndef MOSEKVERSION + MOSEKVERSION=7 +endif +IGLMOSEK=../mosek/ +IGLMOSEK_INC=-I$(IGLMOSEK)/ +MOSEK=/usr/local/mosek +MOSEK_INC=-I$(MOSEK)/$(MOSEKVERSION)/tools/platform/$(MOSEKPLATFORM)/h +MOSEK_LIB=-L$(MOSEK)/$(MOSEKVERSION)/tools/platform/$(MOSEKPLATFORM)/bin -lmosek64 +endif + +CGAL=$(DEFAULT_PREFIX) +CGAL_LIB=-L$(CGAL)/lib -lCGAL -lCGAL_Core -lgmp -lmpfr -lboost_thread-mt -lboost_system-mt +CGAL_INC=-I$(CGAL)/include -I/usr/include/ +# This is absolutely necessary for Exact Construction +CGAL_FLAGS=-frounding-math -fsignaling-nans +CFLAGS+=$(CGAL_FLAGS) + +SUBDIV=. +SUBDIV_INC=-I$(SUBDIV) +SUBDIV_LIB=-L$(SUBDIV) -lsubdivision_skinning -losdCPU + +INC+=$(LIBIGL_INC) $(ANTTWEAKBAR_INC) $(EIGEN3_INC) $(MATLAB_INC) $(GLUT_INC) ${CGAL_INC} ${TETGEN_INC} $(MOSEK_INC) $(EMBREE_INC) $(SUBDIV_IN) $(YIMG_INC) +LIB+=$(OPENGL_LIB) $(GLUT_LIB) $(ANTTWEAKBAR_LIB) $(LIBIGL_LIB) $(MATLAB_LIB) ${CGAL_LIB} $(CARBON_LIB) $(TETGEN_LIB) $(MOSEK_LIB) $(EMBREE_LIB) $(SUBDIV_LIB) $(YIMG_LIB) + +CPP_FILES=$(wildcard ./*.cpp) +#OBJ_FILES=$(addprefix obj/,$(notdir $(CPP_FILES:.cpp=.o))) +OBJ_FILES=obj/subdivgui.o obj/clean.o obj/robust_bbw.o obj/subdiv_weights.o obj/Mesh.o + + +subdivgui: obj $(OBJ_FILES) + $(GG) $(OPENMP) $(AFLAGS) $(CFLAGS) -o subdivgui $(LIB) $(OBJ_FILES) + +obj: + mkdir -p obj + +obj/%.o: %.cpp + $(GG) $(OPENMP) $(AFLAGS) $(CFLAGS) -c $< -o $@ $(INC) + +obj/%.o: %.cpp %.h + $(GG) $(OPENMP) $(AFLAGS) $(CFLAGS) -c $< -o $@ $(INC) + +clean: + rm -f $(OBJ_FILES) + rm -f subdivgui diff --git a/subdivgui/Mesh.cpp b/subdivgui/Mesh.cpp new file mode 100644 index 0000000..f7654f7 --- /dev/null +++ b/subdivgui/Mesh.cpp @@ -0,0 +1,110 @@ +#include "Mesh.h" +#include +#include +#include +#include + +void Mesh::draw_and_cache() +{ + using namespace Eigen; + using namespace igl; + if(wireframe) + { + glPushAttrib(GL_POLYGON_BIT); + glPushAttrib(GL_ENABLE_BIT); + glPushAttrib(GL_LIGHTING_BIT); + glPushAttrib(GL_LINE_BIT); + glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); + glEnable(GL_COLOR_MATERIAL); + glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); + glColor3f(0,0,0); + if(!glIsList(dl) || stale) + { + if(glIsList(dl)) + { + glDeleteLists(dl,1); + } + dl = glGenLists(1); + glNewList(dl,GL_COMPILE_AND_EXECUTE); + draw_mesh(U,Q,N); + glEndList(); + }else + { + glCallList(dl); + } + glPopAttrib(); + glPopAttrib(); + glPopAttrib(); + glPopAttrib(); + } else + { + + if(!glIsBuffer(ibo)) + { + T.resize(Q.rows()*2,3); + Matrix TCT(T.rows(),3); + for(int q=0;q10000) + for(int f=0;f(); + UCT.row((f+1)*3+c) = U.row(T(f+1,c)).cast(); + } + Matrix v1 = UCT.row(f*3+1)-UCT.row(f*3+0); + Matrix v2 = UCT.row(f*3+2)-UCT.row(f*3+0); + Matrix n = v1.cross(v2); + for(int c = 0;c<3;c++) + { + for(int d = 0;d<3;d++) + { + const float nd = n(d); + NCT(f*3+c,d) = nd; + NCT((f+1)*3+c,d) = nd; + } + } + } + + glEnableClientState(GL_VERTEX_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER,vbo); + glBufferData(GL_ARRAY_BUFFER,sizeof(float)*UCT.size(),UCT.data(),GL_DYNAMIC_DRAW); + glVertexPointer(3,GL_FLOAT,0,0); + glEnableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER,nbo); + glBufferData(GL_ARRAY_BUFFER,sizeof(float)*NCT.size(),NCT.data(),GL_DYNAMIC_DRAW); + glNormalPointer(GL_FLOAT,0,0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ibo); + glDrawElements(GL_TRIANGLES,T.size(),GL_UNSIGNED_INT,0); + glBindBuffer(GL_ARRAY_BUFFER,0); + } + + stale = false; +} diff --git a/subdivgui/Mesh.h b/subdivgui/Mesh.h new file mode 100644 index 0000000..68645b4 --- /dev/null +++ b/subdivgui/Mesh.h @@ -0,0 +1,25 @@ +#include +#include +class Mesh +{ + public: + // Rest and deformed positions + Eigen::Matrix V,U,N; + Eigen::Matrix UCT,NCT; + // Quad and triangle face lists + Eigen::Matrix Q; + Eigen::Matrix F; + Eigen::Matrix T,T1; + // Display list, vertex/normal/index buffer + GLuint dl=0,vbo=0,nbo=0,ibo=0; + // Display list, vbo, and nbo are stale + bool wireframe = false; + bool stale = true; + bool must_update = true; + bool force_visible; + Mesh(bool _force_visible):force_visible(_force_visible){}; + void draw_and_cache(); + public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; + diff --git a/subdivgui/README.md b/subdivgui/README.md new file mode 100644 index 0000000..d32481e --- /dev/null +++ b/subdivgui/README.md @@ -0,0 +1,49 @@ +Posing tool for "Skinning Cubic Bezier Splines and Catmull-Clark Subdivision +Surfaces" + +# Compiling +This code depends on: + + - libigl + - eigen + - stdlib + - cgal + - tetgen + - mosek (optional) + - libsubdivision_skinning + - OpenSubDiv + +To compile libigl and its dependencies see the [libigl +instructions](https://github.com/libigl/libigl). Remember that libigl is a +_header only_ library. No compilation is necessary. CGAL and tetgen must be +compiled. Academics can install Mosek for free, but this is optional. Just +defined the flag `-DIGL_NO_MOSEK` to disable Mosek support when building this +project. + +# Running +Prepare a coarse subdivision surface cage as a .obj file `cage.obj` and a skeleton using +the [.tgf file +format](http://igl.ethz.ch/projects/libigl/file-formats/tgf.html) of libigl +`skeleton.tgf`. +You could use the `libigl/examples/skeleton-builder/` example to build your +skeleton with a GUI. + +Run this program for the first time to compute bounded biharmonic weights: + + ./subdivgui cage.obj skeleton.tgf weights.dmat + +This will attempt to _clean_ the model by meshing self-intersections, and +filling holes. Then it will compute a tetrahedral mesh of the surface's solid +volume. Finally it will compute bounded biharmonic weights for each skeleton +bone. When finished the weights will be saved to the provided path +`weights.dmat` in the [.dmat file +format](http://igl.ethz.ch/projects/libigl/file-formats/dmat.html) of libigl. + +The GUI will now start and you may follow the standard output instructions to +interact and change visualization settings. + +Subsequent runs of + + ./subdivgui cage.obj skeleton.tgf weights.dmat + +will load weights from `weights.dmat` rather than recompute them. diff --git a/subdivgui/clean.cpp b/subdivgui/clean.cpp new file mode 100644 index 0000000..2df107a --- /dev/null +++ b/subdivgui/clean.cpp @@ -0,0 +1,124 @@ +#include "clean.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool clean( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + Eigen::MatrixXd & CV, + Eigen::MatrixXi & CF, + Eigen::VectorXi & IM) +{ + using namespace igl; + using namespace Eigen; + using namespace std; + writeOBJ("VF.obj",V,F); + const auto & validate_IM = []( + const Eigen::MatrixXd & V, + const Eigen::MatrixXd & CV, + const Eigen::VectorXi & IM) + { + for(int i = 0;i1e-6) + { + cout<thresh).cast().sum(); + MatrixXi CT(count,TT.cols()); + int c = 0; + for(int t = 0;tthresh) + { + CT.row(c++) = TT.row(t); + } + } + assert(c==count); + boundary_facets(CT,CF); + writeMESH("CVCTCF.mesh",TV,CT,CF); + cout<<"remove_unreferenced"< +bool clean( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + Eigen::MatrixXd & CV, + Eigen::MatrixXi & CF, + Eigen::VectorXi & IM); + diff --git a/subdivgui/refine.cpp b/subdivgui/refine.cpp new file mode 100644 index 0000000..d4139fc --- /dev/null +++ b/subdivgui/refine.cpp @@ -0,0 +1,149 @@ +#!/bin/bash +/*/../bin/ls > /dev/null +# BEGIN BASH SCRIPT +export PS4="" +set -o xtrace +TEMP="$0.cpp" +#clang++ -O3 -std=c++11 -o .main $TEMP \ +#g++ -O3 -std=c++11 -o .main $TEMP \ +# -fopenmp \ +printf "//" | cat - $0 >$TEMP +clang++ -g -O3 -DNDEBUG -std=c++11 -o .main $TEMP \ + -I. libosdCPU.a libsubdivision_skinning.a \ + -I/opt/local/include/eigen3/ -I/usr/local/igl/libigl/include \ + -I/usr/local/igl/libigl/external/AntTweakBar/include \ + -I/Applications/MATLAB_R2014a.app/extern/include/ \ + -I/usr/local/igl/libigl/external/AntTweakBar/src \ + -I/usr/local/igl/libigl/external/glfw/include \ + -L/usr/local/igl/libigl/external/glfw/lib -lglfw3 -framework QuartzCore -framework IOKit \ + -I/usr/local/igl/libigl/external/Singular_Value_Decomposition/ \ + -I/opt/local/include/ -I/usr/include/ \ + -L/opt/local/lib -lCGAL -lCGAL_Core -lgmp -lmpfr -lboost_thread-mt -lboost_system-mt \ + -L/Applications/MATLAB_R2014a.app/extern/lib/ \ + -framework OpenGL \ + -framework GLUT \ + -framework AppKit \ + -L/opt/local/lib -lboost_thread-mt -lboost_system-mt \ + -L/Applications/MATLAB_R2014a.app/bin/maci64/ -lmx -lmat -lmex -leng \ + -L/usr/local/igl/libigl/external/AntTweakBar/lib -lAntTweakBar \ + -L/opt/local/lib -lboost_program_options-mt && ./.main "$@" +#-DIGL_STATIC_LIBRARY -L/usr/local/igl/libigl/lib -liglviewer -ligl -liglmatlab -liglsvd3x3 -msse4.2 -fopenmp \ +#rm -f .main +rm -f $TEMP +# END BASH SCRIPT +exit +*/ + +// This is a tiny program to read in a coarse mesh and write out a refined +// mesh. + +#include +#include +#include +#include +#include +#include +#include +#include + + +extern "C" +{ +#include "subdivision_skinning_wrapper.h" +} + +int main(int argc, char * argv[]) +{ + using namespace std; + using namespace Eigen; + using namespace igl; + MatrixXd CV,V; + MatrixXi CQ,Q; + + int level = 3; + + string filename = "torus.obj", output_filename = ""; + switch(argc) + { + case 4: + output_filename = argv[3]; + case 3: + level = atoi(argv[2]); + case 2: + // Read and prepare mesh + filename = argv[1]; + break; + default: + cerr<<"Usage:"< > vCQ; + vector > vCV; + if(!readOBJ(filename,vCV,vCQ)) + { + return EXIT_FAILURE; + } + list_to_matrix(vCV,CV); + if(!list_to_matrix(vCQ,4,-1,CQ)) + { + cerr<<"Error: "< CVT = + CV.cast().transpose(); + const subdivision_evaluator_real_t * vs = CVT.data(); + const int num_vs = CV.rows(); + const MatrixXi CQT = CQ.cast().transpose(); + const int * faces = CQT.data(); + const int num_faces = CQ.rows(); + eval = new_subdivision_evaluator( + num_vs, (subdivision_evaluator_real_t*)vs, num_faces, (int*)faces,level); + + ///////////////////////////////////////////////////////////////////////// + // Ask subdiv lib for refined mesh + ///////////////////////////////////////////////////////////////////////// + const int num_refined_faces = num_refined_quad_faces_of_subdivision_evaluator( eval ); + int* refined_faces = new int[ num_refined_faces*4 ]; + get_refined_quad_faces_of_subdivision_evaluator( eval, refined_faces ); + const auto & QT = Map(refined_faces,4,num_refined_faces); + Q = QT.transpose(); + delete[] refined_faces; + + const int num_refined_vs = num_refined_vertices_of_subdivision_evaluator( eval ); + subdivision_evaluator_real_t* refined_vs = new subdivision_evaluator_real_t[ num_refined_vs*3 ]; + get_refined_vertices_of_subdivision_evaluator( eval, refined_vs ); + const auto & VT = Map > + (refined_vs,3,num_refined_vs); + V = VT.cast().transpose(); + delete[] refined_vs; + + if(!writeOBJ(output_filename,V,Q)) + { + cerr<<"Failed to write to "< +#include +#include +#include +#include +#include +#include + +bool robust_bbw( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + Eigen::MatrixXd & W) +{ + using namespace igl; + using namespace Eigen; + using namespace std; + // clean mesh + MatrixXd CV; + MatrixXi CF; + VectorXi IM; + if(!clean(V,F,CV,CF,IM)) + { + return false; + } + MatrixXd TV; + MatrixXi TT; + // compute tet-mesh + { + MatrixXi _1; +#ifdef VERBOSE + cerr<<"mesh_with_skeleton"<thresh).cast().sum(); + TT.resize(count,oldTT.cols()); + int c = 0; + for(int t = 0;tthresh) + { + TT.row(c++) = oldTT.row(t); + } + } + } + + // compute weights + VectorXi b; + MatrixXd bc; + if(!boundary_conditions(TV,TT,C,{},BE,{},b,bc)) + { + cout< +bool robust_bbw( + const Eigen::MatrixXd & V, + const Eigen::MatrixXi & F, + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + Eigen::MatrixXd & W); +#endif diff --git a/subdivgui/subdiv_weights.cpp b/subdivgui/subdiv_weights.cpp new file mode 100644 index 0000000..84094b2 --- /dev/null +++ b/subdivgui/subdiv_weights.cpp @@ -0,0 +1,129 @@ +#include "subdiv_weights.h" +#include "robust_bbw.h" +#include +#include + +extern "C" +{ +#include "subdivision_skinning_wrapper.h" +} + +bool subdiv_weights( + const Eigen::MatrixXd & CV, + const Eigen::MatrixXi & CQ, + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const int computation_level, + const int evaluation_level, + const WeightsType type, + Eigen::MatrixXd & W) +{ + using namespace std; + using namespace Eigen; + using namespace igl; + + typedef subdivision_evaluator_real_t Scalar; + typedef Eigen::Matrix MatrixXS; + + void* computation_eval; + void* evaluation_eval; + + // 1. Create evaluator from control mesh with level L1 appropriate for + // computing weights. + + { + const MatrixXS CVT = CV.cast().transpose(); + const Scalar * vs = CVT.data(); + const int num_vs = CV.rows(); + const MatrixXi CQT = CQ.cast().transpose(); + const int * faces = CQT.data(); + const int num_faces = CQ.rows(); + computation_eval = new_subdivision_evaluator( + num_vs, (Scalar*)vs, num_faces, (int*)faces, computation_level); + } + + // 2. Compute refined mesh R1 (V,Q) + MatrixXd V; + MatrixXi Q,F; + int * refined_faces; + int num_refined_faces; + Scalar * refined_vs; + int num_refined_vs; + { + num_refined_faces = + num_refined_quad_faces_of_subdivision_evaluator( computation_eval ); + refined_faces = new int[ num_refined_faces*4 ]; + get_refined_quad_faces_of_subdivision_evaluator( + computation_eval, refined_faces ); + const auto & QT = Map(refined_faces,4,num_refined_faces); + Q = QT.transpose(); + polygon_mesh_to_triangle_mesh(Q,F); + num_refined_vs = + num_refined_vertices_of_subdivision_evaluator( computation_eval ); + refined_vs = new Scalar[ num_refined_vs*3 ]; + get_refined_vertices_of_subdivision_evaluator(computation_eval,refined_vs); + const auto & VT = Map + (refined_vs,3,num_refined_vs); + V = VT.cast().transpose(); + } + + // 3. Compute weights W1 on R1 (V,F) + MatrixXd W1; + switch(type) + { + default: + assert(false && "Unknown weighting type."); + case WEIGHTS_TYPE_BBW: + if(!robust_bbw(V,F,C,BE,W1)) + { + return false; + } + break; + case WEIGHTS_TYPE_BONE_HEAT: + if(!bone_heat(V,F,C,{},BE,{},W1)) + { + return false; + } + break; + } + + // Let L2 be the level appropriate for our skinning engine. + if(computation_level == evaluation_level) + { + W = W1; + return true; + } + // 4. Create evaluator from R1 with L2-L1. + { + assert(num_refined_vs == W1.rows()); + evaluation_eval = new_subdivision_evaluator( + num_refined_vs, (Scalar*)refined_vs, + num_refined_faces, (int*)refined_faces, + evaluation_level-computation_level); + } + + // 5. Evaluate R1 using the weights as the control mesh positions to get weights W2 for L2. + { + const int num_weights = W1.cols(); + const int num_refined_vs = + num_refined_vertices_of_subdivision_evaluator( evaluation_eval ); + Scalar* refined_W = new Scalar[ num_refined_vs*num_weights ]; + const MatrixXS W1T = W1.cast().transpose(); + const Scalar * coarse_W = W1T.data(); + get_refined_vertices_of_subdivision_evaluator_with_control_vertices( + evaluation_eval, + num_weights, + coarse_W, + refined_W); + const auto & WT = Map(refined_W,num_weights,num_refined_vs); + W = WT.cast().transpose(); + delete[] refined_W; + } + + + delete[] refined_faces; + delete[] refined_vs; + delete_subdivision_evaluator( computation_eval ); + delete_subdivision_evaluator( evaluation_eval ); + return true; +} diff --git a/subdivgui/subdiv_weights.h b/subdivgui/subdiv_weights.h new file mode 100644 index 0000000..f9a423e --- /dev/null +++ b/subdivgui/subdiv_weights.h @@ -0,0 +1,17 @@ +#include +enum WeightsType +{ + WEIGHTS_TYPE_BBW = 0, + WEIGHTS_TYPE_BONE_HEAT = 1, + NUM_WEIGHTS_TYPES = 2 +}; +bool subdiv_weights( + const Eigen::MatrixXd & CV, + const Eigen::MatrixXi & CQ, + const Eigen::MatrixXd & C, + const Eigen::MatrixXi & BE, + const int computation_level, + const int evaluation_level, + const WeightsType type, + Eigen::MatrixXd & W); + diff --git a/subdivgui/subdivgui.cpp b/subdivgui/subdivgui.cpp new file mode 100644 index 0000000..15fd649 --- /dev/null +++ b/subdivgui/subdivgui.cpp @@ -0,0 +1,1139 @@ +#include "clean.h" +#include "subdiv_weights.h" +#include "Mesh.h" +extern "C" +{ +#include "subdivision_skinning_wrapper.h" +} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __APPLE__ +#include +#include +#else +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define VERBOSE +#define DEBUG_WEIGHTS + +enum SkelStyleType +{ + SKEL_STYLE_TYPE_3D = 0, + SKEL_STYLE_TYPE_VECTOR_GRAPHICS = 1, + NUM_SKEL_STYLE_TYPE = 2 +}skel_style; + +double fps = 0.; +bool force_anim = false; +bool floor_visible = true; +bool render_to_png_on_next = false; +bool render_to_png_on_anim = false; + +Eigen::MatrixXd M; +Mesh c(false),r(true),l(false); // coarse, refined, lbs + +Eigen::Vector3d Vmid; +double bbd = 1.0; +igl::Camera camera; + +Eigen::MatrixXd C; +Eigen::MatrixXi BE; +Eigen::VectorXi P,RP; + +struct State +{ + igl::MouseController mouse; + Eigen::MatrixXf colors; +} s; + +bool wireframe = false; + +// See README for descriptions +enum RotationType +{ + ROTATION_TYPE_IGL_TRACKBALL = 0, + ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP = 1, + NUM_ROTATION_TYPES = 2, +} rotation_type = ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP; + +std::stack undo_stack; +std::stack redo_stack; + +bool is_rotating = false; +bool centroid_is_visible = false; +int down_x,down_y; +igl::Camera down_camera; +std::string output_prefix; + +struct CameraAnimation +{ + bool is_animating = false; + double DURATION = 0.5; + double start_time = 0; + Eigen::Quaterniond from_quat,to_quat; +} canim; + +typedef std::vector< +Eigen::Quaterniond, + Eigen::aligned_allocator > RotationList; + +struct PoseAnimation +{ + bool is_animating = false; + double DURATION = 2; + double start_time = 0; + RotationList pose; + int count = 0; +} panim; + +int width,height; +Eigen::Vector4f light_pos(-0.1,-0.1,0.9,0); + +#define REBAR_NAME "temp.rbr" +igl::ReTwBar rebar; + +// Subdiv library pointers +void* engine; +void* eval; + +bool show_cage_on_drag = true; + +void push_undo() +{ + undo_stack.push(s); + // Clear + redo_stack = std::stack(); +} + +// No-op setter, does nothing +void TW_CALL no_op(const void * /*value*/, void * /*clientData*/) +{ +} + +void TW_CALL set_rotation_type(const void * value, void * clientData) +{ + using namespace Eigen; + using namespace std; + using namespace igl; + const RotationType old_rotation_type = rotation_type; + rotation_type = *(const RotationType *)(value); + if(rotation_type == ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP && + old_rotation_type != ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP) + { + push_undo(); + canim.from_quat = camera.m_rotation_conj; + snap_to_fixed_up(canim.from_quat,canim.to_quat); + // start animation + canim.start_time = get_seconds(); + canim.is_animating = true; + } +} +void TW_CALL get_rotation_type(void * value, void *clientData) +{ + RotationType * rt = (RotationType *)(value); + *rt = rotation_type; +} + +void reshape(int width, int height) +{ + ::width = width; + ::height = height; + glViewport(0,0,width,height); + // Send the new window size to AntTweakBar + TwWindowSize(width, height); + camera.m_aspect = (double)width/(double)height; + s.mouse.reshape(width,height); +} + +void push_scene() +{ + using namespace igl; + using namespace std; + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluPerspective(camera.m_angle,camera.m_aspect,camera.m_near,camera.m_far); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + gluLookAt( + camera.eye()(0), camera.eye()(1), camera.eye()(2), + camera.at()(0), camera.at()(1), camera.at()(2), + camera.up()(0), camera.up()(1), camera.up()(2)); +} + +void push_object() +{ + using namespace igl; + glPushMatrix(); + glScaled(2./bbd,2./bbd,2./bbd); + glTranslated(-Vmid(0),-Vmid(1),-Vmid(2)); +} + +void pop_object() +{ + glPopMatrix(); +} + +void pop_scene() +{ + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +// Set up double-sided lights +void lights() +{ + using namespace std; + using namespace Eigen; + glEnable(GL_LIGHTING); + glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + float WHITE[4] = {0.8,0.8,0.8,1.}; + float GREY[4] = {0.4,0.4,0.4,1.}; + float BLACK[4] = {0.,0.,0.,1.}; + Vector4f pos = light_pos; + glLightfv(GL_LIGHT0,GL_AMBIENT,GREY); + glLightfv(GL_LIGHT0,GL_DIFFUSE,WHITE); + glLightfv(GL_LIGHT0,GL_SPECULAR,BLACK); + glLightfv(GL_LIGHT0,GL_POSITION,pos.data()); + pos(0) *= -1; + pos(1) *= -1; + pos(2) *= -1; + glLightfv(GL_LIGHT1,GL_AMBIENT,GREY); + glLightfv(GL_LIGHT1,GL_DIFFUSE,WHITE); + glLightfv(GL_LIGHT1,GL_SPECULAR,BLACK); + glLightfv(GL_LIGHT1,GL_POSITION,pos.data()); +} + +template +void update_refined( + const Eigen::Matrix & TT, + const Eigen::PlainObjectBase & Q, + subdivision_evaluator_real_t * vs_modified, + Eigen::PlainObjectBase & U, + Eigen::PlainObjectBase & UN) +{ + using namespace Eigen; + using namespace igl; + + // Retrieve refined mesh + const int num_refined_vs = num_refined_vertices_of_subdivision_evaluator( eval ); + subdivision_evaluator_real_t* refined_vs = new subdivision_evaluator_real_t[ num_refined_vs*3 ]; + get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 3, vs_modified, refined_vs ); + const auto & UT = Map > + (refined_vs,3,num_refined_vs); + U = UT.cast().transpose(); + // Clean up + delete[] refined_vs; +} + +void display() +{ + using namespace igl; + using namespace std; + using namespace Eigen; + //const float back[4] = {0.75, 0.75, 0.75,0}; + const float back[4] = {1.,1.,1.,0}; + glClearColor(back[0],back[1],back[2],0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if(canim.is_animating) + { + double t = (get_seconds() - canim.start_time)/canim.DURATION; + if(t > 1) + { + t = 1; + canim.is_animating = false; + } + Quaterniond q = canim.from_quat.slerp(t,canim.to_quat).normalized(); + camera.orbit(q.conjugate()); + } + + RotationList dQ; + if(panim.is_animating) + { + double t = + render_to_png_on_anim ? + double(panim.count)/(30.*panim.DURATION) : + (get_seconds() - panim.start_time)/panim.DURATION; + if(t > 1) + { + t = 1; + panim.is_animating = false; + } + const auto & ease = [](const double t) + { + return 3.*t*t-2.*t*t*t; + }; + double f = (t<0.5?ease(2.*t):ease(2.-2.*t)); + dQ.resize(panim.pose.size()); + for(int e = 0;e<(int)panim.pose.size();e++) + { + dQ[e] = panim.pose[e].slerp(f,Quaterniond::Identity()).normalized(); + } + panim.count++; + }else + { + dQ = s.mouse.rotations(); + } + RotationList vQ; + vector vT; + forward_kinematics(C,BE,P,dQ,vQ,vT); + + // Vertical stack, row-major --> horizontal stack of transposes, col-major + Matrix TT(4,4*BE.rows()); + // draw_skeleton needs a different stack + MatrixXd T(BE.rows()*(3+1),3); + for(int w = 0;w(); + T.block(w*(3+1),0,3+1,3) = a.matrix().transpose().block(0,0,3+1,3); + } + ///////////////////////////////////////////////////////////////////////// + // Tell subdiv lib about transformations and get modified cage, refined + ///////////////////////////////////////////////////////////////////////// + if(c.stale || r.stale) + { + const subdivision_evaluator_real_t* transforms = TT.data(); + subdivision_evaluator_real_t* vs_modified = new subdivision_evaluator_real_t[ c.V.size() ]; + // Retrieve control mesh + compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( engine, (subdivision_evaluator_real_t*)transforms, vs_modified ); + const auto & CUT = Map > + (vs_modified,3,c.V.rows()); + c.U = CUT.cast().transpose(); + if((!s.mouse.is_widget_down() && r.must_update && show_cage_on_drag) || + r.force_visible) + { + if(r.stale) + { + update_refined(TT,r.Q,vs_modified,r.U,r.N); + } + r.must_update = false; + } + delete[] vs_modified; + } + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_NORMALIZE); + lights(); + push_scene(); + + double y_boost = 0; + // Draw a nice floor + if(floor_visible) + { + if(c.U.rows() > 0) + { + y_boost = 2.*(c.U.col(1).minCoeff()-c.V.col(1).minCoeff())/bbd; + } + if(panim.is_animating && render_to_png_on_anim) + { + glTranslated(0,-y_boost,0); + } + glEnable(GL_DEPTH_TEST); + glPushMatrix(); + double floor_offset = -2./bbd*(c.V.col(1).maxCoeff()-Vmid(1)); + floor_offset += y_boost; + glTranslated(0,floor_offset,0); + const float GREY[4] = {0.5,0.5,0.6,1.0}; + const float DARK_GREY[4] = {0.2,0.2,0.3,1.0}; + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + static GLuint floor_dl = 0; + if(!glIsList(floor_dl)) + { + floor_dl = glGenLists(1); + glNewList(floor_dl,GL_COMPILE_AND_EXECUTE); + draw_floor(GREY,DARK_GREY); + glEndList(); + }else + { + glCallList(floor_dl); + } + glDisable(GL_CULL_FACE); + glPopMatrix(); + } + + push_object(); + + const auto & draw_skeleton = [](const MatrixXd & T) + { + switch(skel_style) + { + default: + case SKEL_STYLE_TYPE_3D: + { + draw_skeleton_3d(C,BE,T,s.colors); + break; + } + case SKEL_STYLE_TYPE_VECTOR_GRAPHICS: + draw_skeleton_vector_graphics(C,BE,T); + break; + } + }; + // Set material properties + glDisable(GL_COLOR_MATERIAL); + glMaterialfv(GL_FRONT, GL_AMBIENT,GOLD_AMBIENT); + glMaterialfv(GL_FRONT, GL_DIFFUSE,GOLD_DIFFUSE); + glMaterialfv(GL_FRONT, GL_SPECULAR,GOLD_SPECULAR); + glMaterialf (GL_FRONT, GL_SHININESS, 128); + glMaterialfv(GL_BACK, GL_AMBIENT,SILVER_AMBIENT); + glMaterialfv(GL_BACK, GL_DIFFUSE,FAST_GREEN_DIFFUSE); + glMaterialfv(GL_BACK, GL_SPECULAR,SILVER_SPECULAR); + glMaterialf (GL_BACK, GL_SHININESS, 128); + if(wireframe) + { + glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); + } + glLineWidth(1.0); + + if((r.must_update && show_cage_on_drag) || c.force_visible) + { + if(c.stale) + { + c.N.conservativeResize(c.Q.rows(),c.N.cols()); + } + c.draw_and_cache(); + } + + if((!r.must_update && show_cage_on_drag) || r.force_visible) + { + r.draw_and_cache(); + } + + if(l.force_visible) + { + // Set material properties + glDisable(GL_COLOR_MATERIAL); + glMaterialfv(GL_FRONT, GL_AMBIENT,SILVER_AMBIENT); + glMaterialfv(GL_FRONT, GL_DIFFUSE,FAST_RED_DIFFUSE); + glMaterialfv(GL_FRONT, GL_SPECULAR,SILVER_SPECULAR); + glMaterialf (GL_FRONT, GL_SHININESS, 128); + glMaterialfv(GL_BACK, GL_AMBIENT,SILVER_AMBIENT); + glMaterialfv(GL_BACK, GL_DIFFUSE,SILVER_DIFFUSE); + glMaterialfv(GL_BACK, GL_SPECULAR,SILVER_SPECULAR); + glMaterialf (GL_BACK, GL_SHININESS, 128); + if(l.stale) + { + l.U = M*T; + } + l.draw_and_cache(); + } + + glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); + glDisable(GL_DEPTH_TEST); + draw_skeleton(T); + + if(centroid_is_visible && c.U.rows() > 0) + { + Vector3d cen; + centroid(c.U,c.F,cen); + glEnable(GL_DEPTH_TEST); + glPushMatrix(); + glTranslated(cen(0),cen(1),cen(2)); + glScaled(bbd/2.,bbd/2.,bbd/2.); + glScaled(0.1,0.1,0.1); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(0,-100000); + draw_beach_ball(); + glDisable(GL_POLYGON_OFFSET_FILL); + glPopMatrix(); + } + + // Mouse is always on top + glDisable(GL_DEPTH_TEST); + if(!panim.is_animating) + { + s.mouse.draw(); + } + pop_object(); + pop_scene(); + + report_gl_error(); + + bool was_rendering_to_png = false; + if(render_to_png_on_next || (render_to_png_on_anim && panim.is_animating)) + { + was_rendering_to_png = true; + string prefix = STR(getenv("HOME")<<"/Desktop/"<< "subdivgui-"); + string filename; + next_filename(prefix,4,".png",filename); + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT,viewport); + render_to_png_async(filename.c_str(),viewport[2],viewport[3],true,false); + if(render_to_png_on_next) + { + glClearColor(1,1,1,1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glFlush(); + usleep(1000*1); + render_to_png_on_next = false; + } + } + + TwDraw(); + glutSwapBuffers(); + + if(canim.is_animating || + panim.is_animating || + force_anim || + was_rendering_to_png) + { + r.must_update = true; + c.stale = l.stale = r.stale = true; + glutPostRedisplay(); + } + { + static int count = 0; + static double start = get_seconds(); + if(++count > 10) + { + double now = get_seconds(); + fps = double(count)/(now-start); + count = 0; + start = now; + } + } +} + +void mouse_wheel(int wheel, int direction, int mouse_x, int mouse_y) +{ + using namespace std; + using namespace igl; + using namespace Eigen; + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT,viewport); + if(wheel == 0 && TwMouseMotion(mouse_x, viewport[3] - mouse_y)) + { + static double mouse_scroll_y = 0; + const double delta_y = 0.125*direction; + mouse_scroll_y += delta_y; + TwMouseWheel(mouse_scroll_y); + return; + } + push_undo(); + + if(wheel==0) + { + // factor of zoom change + double s = (1.-0.01*direction); + //// FOV zoom: just widen angle. This is hardly ever appropriate. + //camera.m_angle *= s; + //camera.m_angle = min(max(camera.m_angle,1),89); + camera.push_away(s); + }else + { + // Dolly zoom: + camera.dolly_zoom((double)direction*1.0); + } + glutPostRedisplay(); +} + +void mouse(int glutButton, int glutState, int mouse_x, int mouse_y) +{ + using namespace std; + using namespace Eigen; + using namespace igl; + bool tw_using = TwEventMouseButtonGLUT(glutButton,glutState,mouse_x,mouse_y); + const int mod = (glutButton <=2 ? glutGetModifiers() : 0); + const bool option_down = mod & GLUT_ACTIVE_ALT; + switch(glutButton) + { + case GLUT_RIGHT_BUTTON: + case GLUT_LEFT_BUTTON: + { + push_scene(); + push_object(); + switch(glutState) + { + case 1: + { + // up + const bool mouse_was_selecting = s.mouse.is_selecting(); + is_rotating = false; + s.mouse.up(mouse_x,mouse_y); + glutSetCursor(GLUT_CURSOR_INHERIT); + if(mouse_was_selecting) + { + s.mouse.set_selection_from_last_drag(C,BE,P,RP); + MouseController::VectorXb S; + MouseController::propogate_to_descendants_if( + s.mouse.selection(),P,S); + MouseController::color_if(S,MAYA_SEA_GREEN,MAYA_VIOLET,s.colors); + } + break; + } + case 0: + if(!tw_using) + { + down_x = mouse_x; + down_y = mouse_y; + if(option_down || glutButton==GLUT_RIGHT_BUTTON) + { + glutSetCursor(GLUT_CURSOR_CYCLE); + // collect information for trackball + is_rotating = true; + down_camera = camera; + }else + { + push_undo(); + s.mouse.down(mouse_x,mouse_y); + if(s.mouse.is_widget_down()) + { + r.must_update = true; + c.stale = l.stale = r.stale = true; + } + } + } + break; + } + pop_object(); + pop_scene(); + break; + } + // Scroll down + case 3: + { + mouse_wheel(0,-1,mouse_x,mouse_y); + break; + } + // Scroll up + case 4: + { + mouse_wheel(0,1,mouse_x,mouse_y); + break; + } + // Scroll left + case 5: + { + mouse_wheel(1,-1,mouse_x,mouse_y); + break; + } + // Scroll right + case 6: + { + mouse_wheel(1,1,mouse_x,mouse_y); + break; + } + } + glutPostRedisplay(); +} + +void mouse_drag(int mouse_x, int mouse_y) +{ + using namespace igl; + using namespace std; + using namespace Eigen; + + push_scene(); + push_object(); + if(is_rotating) + { + glutSetCursor(GLUT_CURSOR_CYCLE); + Quaterniond q; + switch(rotation_type) + { + case ROTATION_TYPE_IGL_TRACKBALL: + { + // Rotate according to trackball + igl::trackball( + width, + height, + 2.0, + down_camera.m_rotation_conj, + down_x, + down_y, + mouse_x, + mouse_y, + q); + break; + } + case ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP: + { + // Rotate according to two axis valuator with fixed up vector + two_axis_valuator_fixed_up( + width, height, + 2.0, + down_camera.m_rotation_conj, + down_x, down_y, mouse_x, mouse_y, + q); + break; + } + default: + break; + } + camera.orbit(q.conjugate()); + }else if(s.mouse.drag(mouse_x,mouse_y)) + { + if(s.mouse.is_widget_down()) + { + c.stale = l.stale = r.stale = true; + } + } + pop_object(); + pop_scene(); + glutPostRedisplay(); +} + +void init_relative() +{ + using namespace Eigen; + using namespace igl; + using namespace std; + const auto Vmax = c.V.colwise().maxCoeff(); + const auto Vmin = c.V.colwise().minCoeff(); + Vmid = 0.5*(Vmax + Vmin); + bbd = (Vmax-Vmin).norm(); + camera.push_away(2); +} + +void undo() +{ + using namespace std; + if(!undo_stack.empty()) + { + redo_stack.push(s); + s = undo_stack.top(); + undo_stack.pop(); + s.mouse.reshape(width,height); + } +} + +void redo() +{ + using namespace std; + if(!redo_stack.empty()) + { + undo_stack.push(s); + s = redo_stack.top(); + redo_stack.pop(); + s.mouse.reshape(width,height); + } +} + +bool save() +{ + using namespace std; + using namespace igl; + using namespace Eigen; + string output_filename; + next_filename(output_prefix,4,".dmat",output_filename); + MatrixXd T; + forward_kinematics(C,BE,P,s.mouse.rotations(),T); + if(writeDMAT(output_filename,T)) + { + cout<eval_level) + { + cerr<<"comp_level ("< > vCQ,vCF; + vector > vCV; + if(!readOBJ(filename,vCV,vCQ)) + { + return EXIT_FAILURE; + } + list_to_matrix(vCV,c.V); + polygon_mesh_to_triangle_mesh(vCQ,c.F); + if(!list_to_matrix(vCQ,4,-1,c.Q)) + { + cerr<<"Error: "< CVT = + c.V.cast().transpose(); + const subdivision_evaluator_real_t * vs = CVT.data(); + const int num_vs = c.V.rows(); + const MatrixXi CQT = c.Q.cast().transpose(); + const int * faces = CQT.data(); + const int num_faces = c.Q.rows(); + eval = new_subdivision_evaluator( + num_vs, (subdivision_evaluator_real_t*)vs, num_faces, (int*)faces, eval_level); + + ///////////////////////////////////////////////////////////////////////// + // Ask subdiv lib for refined mesh + ///////////////////////////////////////////////////////////////////////// + const int num_refined_faces = num_refined_quad_faces_of_subdivision_evaluator( eval ); + int* refined_faces = new int[ num_refined_faces*4 ]; + get_refined_quad_faces_of_subdivision_evaluator( eval, refined_faces ); + const auto & QT = Map(refined_faces,4,num_refined_faces); + r.Q = QT.transpose(); + polygon_mesh_to_triangle_mesh(r.Q,r.F); + delete[] refined_faces; + + const int num_refined_vs = num_refined_vertices_of_subdivision_evaluator( eval ); + subdivision_evaluator_real_t* refined_vs = new subdivision_evaluator_real_t[ num_refined_vs*3 ]; + get_refined_vertices_of_subdivision_evaluator( eval, refined_vs ); + const auto & VT = Map > + (refined_vs,3,num_refined_vs); + r.V = VT.cast().transpose(); + delete[] refined_vs; + + + ///////////////////////////////////////////////////////////////////////// + // Load skeleton and load/compute weights + ///////////////////////////////////////////////////////////////////////// + MatrixXd W; + // Read in skeleton and precompute hierarchy + readTGF(skel_filename,C,BE); + // initialize mouse interface + s.mouse.set_size(BE.rows()); + // Rigid parts (not used) + colon(0,BE.rows()-1,RP); + assert(RP.size() == BE.rows()); + // Bone parents + bone_parents(BE,P); + if(weights_filename.size() == 0 || !file_exists(weights_filename.c_str())) + { + cout< 0) + { + if(writeDMAT(weights_filename,W)) + { + cout< WT = + W.cast().transpose(); + const subdivision_evaluator_real_t * weights = WT.data(); + engine = new_subdivision_skinning_engine( eval, W.cols(), weights ); + + + // Handle degenerate quads before draw (these only occur in coarse) + for(int i = 0;i=8 "); + glutInitWindowSize(glutGet(GLUT_SCREEN_WIDTH)/2.0,glutGet(GLUT_SCREEN_HEIGHT)/2.0); + glutCreateWindow("skeleton-poser"); + glutDisplayFunc(display); + glutReshapeFunc(reshape); + glutKeyboardFunc(key); + glutMouseFunc(mouse); + glutMotionFunc(mouse_drag); + glutPassiveMotionFunc((GLUTmousemotionfun)TwEventMouseMotionGLUT); + glutMainLoop(); + + // Clean up + delete_subdivision_skinning_engine( engine ); + delete_subdivision_evaluator( eval ); + return EXIT_SUCCESS; +} diff --git a/subdivgui/subdivision_skinning_wrapper.cpp b/subdivgui/subdivision_skinning_wrapper.cpp new file mode 100644 index 0000000..0d0b229 --- /dev/null +++ b/subdivgui/subdivision_skinning_wrapper.cpp @@ -0,0 +1,203 @@ +#include "subdivision_skinning.h" +#include "subdivision_matrices_osd_eigen.h" + +#include // std::copy + +// Static definitions. +namespace +{ + const int kDefaultLevel = 4; + + struct subdivision_evaluator_t + { + subdivision_skinning::vertices_t vertices; + subdivision_skinning::faces_t faces; + subdivision_matrix::SparseMatrix_t M_matrices; + std::vector< subdivision_matrix::index_t > quad_faces; + }; + + struct subdivision_skinning_engine_t + { + subdivision_matrix::SparseMatrix_t M_matrices; + int num_transforms; + subdivision_skinning::engine_t* engine; + + subdivision_skinning_engine_t() : engine( 0 ) {} + ~subdivision_skinning_engine_t() + { + if( engine ) subdivision_skinning::delete_engine_t( engine ); + engine = 0; + } + }; +} + + +extern "C" { + +#include "subdivision_skinning_wrapper.h" + +void* new_subdivision_evaluator( int num_vertices, const subdivision_evaluator_real_t* vertices, int num_faces, const int* faces, int level ) +{ + assert( num_vertices > 0 ); + assert( num_faces > 0 ); + assert( vertices ); + assert( faces ); + assert( level > 0 ); + + // Create the result object. + subdivision_evaluator_t* result = new subdivision_evaluator_t; + + // Copy the vertices. + result->vertices.resize( num_vertices*3 ); + std::copy( vertices, vertices + num_vertices*3, result->vertices.begin() ); + + // Copy the faces. + result->faces.first.reserve( num_faces ); + result->faces.second.reserve( num_faces*4 ); + for( int fi = 0; fi < num_faces; ++fi ) + { + // Number of vertices in the face: + result->faces.first.push_back( 0 ); + for( int vi = 0; vi < 4; ++vi ) + { + const int vertex_index = faces[ 4*fi + vi ]; + if( -1 != vertex_index ) + { + result->faces.second.push_back( vertex_index ); + result->faces.first.back() += 1; + } + } + assert( result->faces.first.back() == 3 || result->faces.first.back() == 4 ); + } + + // Create M_matrices and quads. + subdivision_matrix::compute_subdivision_coefficients_for_mesh( + num_vertices, + result->faces, + level, + result->M_matrices, + &result->quad_faces + ); + + return result; +} +void delete_subdivision_evaluator( void* subdivision_evaluator ) +{ + delete static_cast< subdivision_evaluator_t* >( subdivision_evaluator ); +} + +int num_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator_in ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + + assert( subdivision_evaluator->quad_faces.size() % 4 == 0 ); + return subdivision_evaluator->quad_faces.size() / 4; +} +void get_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator_in, int* refined_quad_faces_out ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + + assert( refined_quad_faces_out ); + std::copy( subdivision_evaluator->quad_faces.begin(), subdivision_evaluator->quad_faces.end(), refined_quad_faces_out ); +} + +int num_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator_in ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + + return subdivision_evaluator->M_matrices.rows(); +} +void get_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator_in, subdivision_evaluator_real_t* refined_vertices_out ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + assert( refined_vertices_out ); + + assert( subdivision_evaluator->vertices.size() == subdivision_evaluator->M_matrices.cols()*3 ); + const Eigen::Map< const Eigen::Matrix< subdivision_skinning::vertices_t::value_type, Eigen::Dynamic, 3, Eigen::RowMajor > > vs( &subdivision_evaluator->vertices[0], subdivision_evaluator->M_matrices.cols(), 3 ); + Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, 3, Eigen::RowMajor > > out( refined_vertices_out, subdivision_evaluator->M_matrices.rows(), 3 ); + out = ( subdivision_evaluator->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); +} +void get_refined_vertices_of_subdivision_evaluator_with_control_vertices( const void* subdivision_evaluator_in, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ) +{ + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + assert( subdivision_evaluator ); + assert( vertex_dimension > 0 ); + assert( control_vertices ); + assert( refined_vertices_out ); + + const Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > vs( control_vertices, subdivision_evaluator->M_matrices.cols(), vertex_dimension ); + Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > out( refined_vertices_out, subdivision_evaluator->M_matrices.rows(), vertex_dimension ); + out = ( subdivision_evaluator->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); +} + +void* new_subdivision_skinning_engine( const void* subdivision_evaluator_in, int num_transforms, const subdivision_evaluator_real_t* weights_in ) +{ + assert( num_transforms > 0 ); + assert( weights_in ); + assert( subdivision_evaluator_in ); + const subdivision_evaluator_t* subdivision_evaluator = static_cast< const subdivision_evaluator_t* >( subdivision_evaluator_in ); + + subdivision_skinning_engine_t* result = new subdivision_skinning_engine_t; + result->num_transforms = num_transforms; + result->M_matrices = subdivision_evaluator->M_matrices; + const subdivision_matrix::MatrixXX_t weights = Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( weights_in, subdivision_evaluator->M_matrices.rows(), num_transforms ).cast< subdivision_matrix::real_t >(); + result->engine = subdivision_skinning::new_engine_for_control_mesh( subdivision_evaluator->vertices, subdivision_evaluator->faces, subdivision_evaluator->M_matrices, weights ); + + return result; +} +void delete_subdivision_skinning_engine( void* subdivision_skinning_engine ) +{ + delete static_cast< subdivision_skinning_engine_t* >( subdivision_skinning_engine ); +} + +void compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( const void* subdivision_skinning_engine_in, const subdivision_evaluator_real_t* transforms_in, subdivision_evaluator_real_t* control_mesh_vertices_out ) +{ + const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine_in ); + assert( engine ); + assert( engine->engine ); + assert( transforms_in ); + assert( control_mesh_vertices_out ); + + // Copy the transforms. + std::vector< subdivision_skinning::transform_t > transforms( engine->num_transforms ); + for( int i = 0; i < engine->num_transforms; ++i ) + { + transforms.at(i) = Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, 4, 4, Eigen::RowMajor > >( transforms_in + 16*i, 4, 4 ); + } + + // Solve for the new control mesh vertices. + subdivision_skinning::vertices_t control_mesh_vertices; + update_control_vertices_given_handle_transforms( engine->engine, transforms, control_mesh_vertices ); + + // Copy to the output. + assert( control_mesh_vertices.size() == engine->M_matrices.cols()*3 ); + // Copy (and convert floating point formats if needed). + Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, 3, Eigen::RowMajor > >( control_mesh_vertices_out, engine->M_matrices.cols(), 3 ) = + Eigen::Map< Eigen::Matrix< subdivision_skinning::vertices_t::value_type, Eigen::Dynamic, 3, Eigen::RowMajor > >( &control_mesh_vertices[0], engine->M_matrices.cols(), 3 ); +} + +int num_refined_vertices_of_subdivision_skinning_engine( const void* subdivision_skinning_engine ) +{ + const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine ); + assert( engine ); + + return engine->M_matrices.rows(); +} +void get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( const void* subdivision_skinning_engine, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ) +{ + const subdivision_skinning_engine_t* engine = static_cast< const subdivision_skinning_engine_t* >( subdivision_skinning_engine ); + assert( engine ); + assert( vertex_dimension > 0 ); + assert( control_vertices ); + assert( refined_vertices_out ); + + const Eigen::Map< const Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > vs( control_vertices, engine->M_matrices.cols(), vertex_dimension ); + Eigen::Map< Eigen::Matrix< subdivision_evaluator_real_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > out( refined_vertices_out, engine->M_matrices.rows(), vertex_dimension ); + out = ( engine->M_matrices * vs.cast< subdivision_matrix::real_t >() ).cast< subdivision_evaluator_real_t >(); +} + +} diff --git a/subdivgui/subdivision_skinning_wrapper.h b/subdivgui/subdivision_skinning_wrapper.h new file mode 100644 index 0000000..a328059 --- /dev/null +++ b/subdivgui/subdivision_skinning_wrapper.h @@ -0,0 +1,102 @@ +typedef float subdivision_evaluator_real_t; + +/* +Given a control mesh with 'num_vertices' 3D vertices, +3*num_vertices values 'vertices' arranged: + x0 y0 z0 x1 y1 z1 ... +'num_faces' triangle or quad faces, +4*num_faces indices into the num_vertices arranged: + face0_vertex0 face0_vertex1 face0_vertex2 face0_vertex3 face1_vertex0 face1_vertex1 face1_vertex2 face1_vertex3 ... +and a positive integer level representing the number of subdivision steps to perform (e.g. 4), +creates and returns an opaque subdivision_evaluator*. +When finished, the memory used by the subdivision_evaluator* must be freed using delete_subdivision_evaluator(). +NOTE: For a triangle face, set the face's last vertex to -1. +*/ +void* new_subdivision_evaluator( int num_vertices, const subdivision_evaluator_real_t* vertices, int num_faces, const int* faces, int level ); +// Frees the memory associated with the subdivision_evaluator created by new_subdivision_evaluator(). +void delete_subdivision_evaluator( void* subdivision_evaluator ); + +// Returns the number of quad faces generated by the subdivision_evaluator. +int num_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator ); +/* +Given a 'subdivision_evaluator', +upon return, places 'num_refined_quad_faces_of_subdivision_evaluator()' quad faces in 'refined_quad_faces_out'. +The quads are arranged: + face0_vertex0 face0_vertex1 face0_vertex2 face0_vertex3 face1_vertex0 face1_vertex1 face1_vertex2 face1_vertex3 ... +NOTE: The 'refined_quad_faces_out' parameter must point to (at least) 4*num_refined_quad_faces_of_subdivision_evaluator() integers of memory. +NOTE: All faces are quads, so there will be no -1 indices. + This is in contrast to new_subdivision_evaluator(), which can take triangles or quads. +*/ +void get_refined_quad_faces_of_subdivision_evaluator( const void* subdivision_evaluator, int* refined_quad_faces_out ); + +// Returns the number of refined vertices generated by the subdivision_evaluator. +int num_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator ); +/* +Given a 'subdivision_evaluator', +upon return, places 'num_refined_vertices_of_subdivision_evaluator()' 3D vertex values in 'refined_vertices_out'. +The vertices are arranged: + x0 y0 z0 x1 y1 z1 ... +NOTE: The 'refined_vertices_out' parameter must point to (at least) 3*num_refined_vertices_of_subdivision_evaluator() subdivision_evaluator_real_t of memory. +*/ +void get_refined_vertices_of_subdivision_evaluator( const void* subdivision_evaluator, subdivision_evaluator_real_t* refined_vertices_out ); +/* +Given a 'subdivision_evaluator' and +a 'vertex_dimension'-dimensional control mesh 'control_vertices' of the same quantity +and arrangement as was passed to new_subdivision_evaluator(): + (in 3D) x0 y0 z0 x1 y1 z1 ... + (in general) vertex0_coordinate0 vertex0_coordinate1 ... vertex0_coordinate_vertex_dimension-1 vertex1_coordinate0 vertex1_coordinate1 ... +Upon return, places 'num_refined_vertices_of_subdivision_evaluator()' 'vertex_dimension'-dimensional +vertex values in 'refined_vertices_out'. The vertices are arranged: + (in 3D) x0 y0 z0 x1 y1 z1 ... + (in general) vertex0_coordinate0 vertex0_coordinate1 ... vertex0_coordinate_vertex_dimension-1 vertex1_coordinate0 vertex1_coordinate1 ... +NOTE: The 'refined_vertices_out' parameter must point to (at least) 'vertex_dimension*num_refined_vertices_of_subdivision_evaluator()' subdivision_evaluator_real_t of memory. +NOTE: 'vertex_dimension' must be positive (greater than zero). +*/ +void get_refined_vertices_of_subdivision_evaluator_with_control_vertices( const void* subdivision_evaluator, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ); + +/* +Given a subdivision_evaluator* and num_refined_vertices_of_subdivision_evaluator()-by-'num_transforms' +'weights', which are values arranged: + vertex0_transform0 vertex0_transform1 vertex0_transform2 ... vertex1_transform0 vertex1_transform1 vertex1_transform2 ... +creates and returns an opaque subdivision_skinning_engine*. +When finished, the memory must be freed by delete_subdivision_skinning_engine(). +NOTE: The subdivision_skinning_engine does *not* depend on the 'subdivision_evaluator'. + You may call delete_subdivision_evaluator() to free the subdivision_evaluator + once you have created the subdivision_skinning_engine(). +*/ +void* new_subdivision_skinning_engine( const void* subdivision_evaluator, int num_transforms, const subdivision_evaluator_real_t* weights ); +// Frees the memory associated with the subdivision_skinning_engine created by new_subdivision_skinning_engine(). +void delete_subdivision_skinning_engine( void* subdivision_skinning_engine ); + +/* +Given a subdivision_skinning_engine* and +a set of 'transforms', +returns in 'control_mesh_vertices_out' 3D control mesh vertices +that provide the best fitting refined subdivision surface to the linear blend +skin deformation specified by the weights and 'transforms'. +The parameter 'transforms' contains 4x4 matrices stacked vertically into a very tall matrix. +The matrix is provided in row-major order, and the number of transforms +is the parameter 'num_transforms' passed to new_subdivision_skinning_engine(). +*/ +void compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( const void* subdivision_skinning_engine, const subdivision_evaluator_real_t* transforms, subdivision_evaluator_real_t* control_mesh_vertices_out ); + +// Given a 'subdivision_skinning_engine', +// returns the number of refined vertices generated by the subdivision_skinning_engine. +// NOTE: The returned value will be the same as would be returned by +// 'num_refined_vertices_of_subdivision_evaluator()' with the 'subdivision_evaluator' +// used to create the 'subdivision_skinning_engine'. +int num_refined_vertices_of_subdivision_skinning_engine( const void* subdivision_skinning_engine ); +/* +Given a 'subdivision_skinning_engine' and +a 'vertex_dimension'-dimensional control mesh 'control_vertices' of the same quantity +and arrangement as was passed to new_subdivision_evaluator(): + (in 3D) x0 y0 z0 x1 y1 z1 ... + (in general) vertex0_coordinate0 vertex0_coordinate1 ... vertex0_coordinate_vertex_dimension-1 vertex1_coordinate0 vertex1_coordinate1 ... +Upon return, places 'num_refined_vertices_of_subdivision_skinning_engine()' 'vertex_dimension'-dimensional +vertex values in 'refined_vertices_out'. The vertices are arranged: + (in 3D) x0 y0 z0 x1 y1 z1 ... + (in general) vertex0_coordinate0 vertex0_coordinate1 ... vertex0_coordinate_vertex_dimension-1 vertex1_coordinate0 vertex1_coordinate1 ... +NOTE: The 'refined_vertices_out' parameter must point to (at least) 'vertex_dimension*num_refined_vertices_of_subdivision_skinning_engine()' subdivision_evaluator_real_t of memory. +NOTE: 'vertex_dimension' must be positive (greater than zero). +*/ +void get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( const void* subdivision_skinning_engine, int vertex_dimension, const subdivision_evaluator_real_t* control_vertices, subdivision_evaluator_real_t* refined_vertices_out ); diff --git a/subdivgui/subdivision_skinning_wrapper_test.cpp b/subdivgui/subdivision_skinning_wrapper_test.cpp new file mode 100644 index 0000000..36647ba --- /dev/null +++ b/subdivgui/subdivision_skinning_wrapper_test.cpp @@ -0,0 +1,264 @@ +// c++ -g subdivision_skinning_wrapper_test.cpp -o subdivision_skinning_wrapper_test ../../build/lib/libsubdivision_skinning.a ../../build/lib/libosdCPU.a +// c++ -g subdivision_skinning_wrapper_test.cpp -o subdivision_skinning_wrapper_test ../../build/Debug/lib/libsubdivision_skinning.a ../../build/Debug/lib/libosdCPU.a + +#include +#include +#include +#include +#include // atoi +#include +#include + +#include // std::fill + +extern "C" +{ +#include "subdivision_skinning_wrapper.h" +} + +namespace +{ + +subdivision_evaluator_real_t TORUS_VS[] = { 1.25052, 0.517982, 0.353553, 0.597239, 0.247384, 0.353553, 0.597239, 0.247384, -0.353553, 1.25052, 0.517982, -0.353553, 0.517982, 1.25052, 0.353553, 0.247384, 0.597239, 0.353553, 0.247384, 0.597239, -0.353553, 0.517982, 1.25052, -0.353553, -0.517982, 1.25052, 0.353553, -0.247384, 0.597239, 0.353553, -0.247384, 0.597239, -0.353553, -0.517982, 1.25052, -0.353553, -1.25052, 0.517982, 0.353553, -0.597239, 0.247384, 0.353553, -0.597239, 0.247384, -0.353553, -1.25052, 0.517982, -0.353553, -1.25052, -0.517982, 0.353553, -0.597239, -0.247384, 0.353553, -0.597239, -0.247384, -0.353553, -1.25052, -0.517982, -0.353553, -0.517982, -1.25052, 0.353553, -0.247384, -0.597239, 0.353553, -0.247384, -0.597239, -0.353553, -0.517982, -1.25052, -0.353553, 0.517982, -1.25052, 0.353553, 0.247384, -0.597239, 0.353553, 0.247384, -0.597239, -0.353553, 0.517982, -1.25052, -0.353553, 1.25052, -0.517982, 0.353553, 0.597239, -0.247384, 0.353553, 0.597239, -0.247384, -0.353553, 1.25052, -0.517982, -0.353553 }; +// #define TORUS_FACES { {4, 5, 1, 0}, {5, 6, 2, 1}, {6, 7, 3, 2}, {7, 4, 0, 3}, {8, 9, 5, 4}, {9, 10, 6, 5}, {10, 11, 7, 6}, {11, 8, 4, 7}, {12, 13, 9, 8}, {13, 14, 10, 9}, {14, 15, 11, 10}, {15, 12, 8, 11}, {16, 17, 13, 12}, {17, 18, 14, 13}, {18, 19, 15, 14}, {19, 16, 12, 15}, {20, 21, 17, 16}, {21, 22, 18, 17}, {22, 23, 19, 18}, {23, 20, 16, 19}, {24, 25, 21, 20}, {25, 26, 22, 21}, {26, 27, 23, 22}, {27, 24, 20, 23}, {28, 29, 25, 24}, {29, 30, 26, 25}, {30, 31, 27, 26}, {31, 28, 24, 27}, {0, 1, 29, 28}, {1, 2, 30, 29}, {2, 3, 31, 30}, {3, 0, 28, 31} } +int TORUS_FACES[] = { 4, 5, 1, 0, 5, 6, 2, 1, 6, 7, 3, 2, 7, 4, 0, 3, 8, 9, 5, 4, 9, 10, 6, 5, 10, 11, 7, 6, 11, 8, 4, 7, 12, 13, 9, 8, 13, 14, 10, 9, 14, 15, 11, 10, 15, 12, 8, 11, 16, 17, 13, 12, 17, 18, 14, 13, 18, 19, 15, 14, 19, 16, 12, 15, 20, 21, 17, 16, 21, 22, 18, 17, 22, 23, 19, 18, 23, 20, 16, 19, 24, 25, 21, 20, 25, 26, 22, 21, 26, 27, 23, 22, 27, 24, 20, 23, 28, 29, 25, 24, 29, 30, 26, 25, 30, 31, 27, 26, 31, 28, 24, 27, 0, 1, 29, 28, 1, 2, 30, 29, 2, 3, 31, 30, 3, 0, 28, 31 }; + +void print_OBJ( std::ostream& out, int num_vs, subdivision_evaluator_real_t* vs, int num_faces, int* quad_faces, const std::string& header_message = "" ) +{ + // Save an optional header message. + out << "# OBJ: " << header_message << '\n'; + + // Save vertices. + for( int i = 0; i < num_vs; ++i ) + { + out << "v " << vs[3*i+0] << ' ' << vs[3*i+1] << ' ' << vs[3*i+2] << '\n'; + } + + out << '\n'; + + for( int i = 0; i < num_faces; ++i ) + { + // Vertices are 1-indexed. + out << "f " << (1+quad_faces[ 4*i + 0 ]) << ' ' << (1+quad_faces[ 4*i + 1 ]) << ' ' << (1+quad_faces[ 4*i + 2 ]); + if( -1 != quad_faces[ 4*i + 3 ] ) out << ' ' << (1+quad_faces[ 4*i + 3 ]) << '\n'; + else out << '\n'; + } +} +void save_OBJ( const std::string& out_path, int num_vs, subdivision_evaluator_real_t* vs, int num_faces, int* quad_faces, const std::string& header_message = "" ) +{ + std::ofstream out( out_path ); + print_OBJ( out, num_vs, vs, num_faces, quad_faces, header_message ); + std::cout << "Saved a mesh to: " << out_path << '\n'; +} + +bool load_OBJ( std::istream& in, int* num_vs_out, subdivision_evaluator_real_t** vs_out, int* num_faces_out, int** quad_faces_out ) +{ + std::string line; + std::istringstream linestr; + + std::vector< subdivision_evaluator_real_t > vs; + std::vector< int > faces; + + int line_number = 0; + std::string type; + while( !( in >> std::ws ).eof() ) + { + line_number += 1; + + std::getline( in, line ); + if( line.empty() ) continue; + + linestr.clear(); + linestr.str( line ); + linestr >> type; + + if( type == std::string("v") ) + { + subdivision_evaluator_real_t x(-31337), y(-31337), z(-31337); + linestr >> x >> y >> z; + vs.push_back( x ); + vs.push_back( y ); + vs.push_back( z ); + if( !linestr ) + { + std::cerr << "Invalid vertex encountered on line " << line_number << ".\n"; + return false; + } + } + else if( type == std::string("f") ) + { + int num_face_vertices = 0; + while( !( linestr >> std::ws ).eof() ) + { + std::string vi; + linestr >> vi; + num_face_vertices += 1; + + // Now look for a slash, in case there's a bundle. + int slash = vi.find( "/" ); + if( std::string::npos == slash ) + { + faces.push_back( atoi( vi.c_str() ) ); + } + else + { + faces.push_back( atoi( vi.substr( 0, slash ).c_str() ) ); + } + // OBJ are 1-indexed, and a negative number indexes from the end. + // Nothing should equal 0. + if( faces.back() == 0 ) + { + std::cerr << "Invalid face vertex index of 0 encountered on line " << line_number << ".\n"; + return false; + } + } + + if( !( num_face_vertices == 3 || num_face_vertices == 4 ) ) + { + std::cerr << "Invalid face without three or four vertices encountered on line " << line_number << ".\n"; + return false; + } + // A triangle has a -1 as the fourth vertex index. + // UPDATE: push_back a 0, because + if( 3 == num_face_vertices ) faces.push_back( 0 ); + } + } + + assert( vs.size() % 3 == 0 ); + *num_vs_out = vs.size()/3; + *vs_out = new subdivision_evaluator_real_t[ vs.size() ]; + std::copy( vs.begin(), vs.end(), *vs_out ); + + assert( faces.size() % 4 == 0 ); + *num_faces_out = faces.size()/4; + *quad_faces_out = new int[ faces.size() ]; + for( int i = 0; i < faces.size(); ++i ) + { + (*quad_faces_out)[i] = + faces[i] >= 0 + ? faces[i] - 1 + // Negative indices add to the back. + : vs.size() + faces[i] + ; + } + + return true; +} +bool load_OBJ( const std::string& in_path, int* num_vs_out, subdivision_evaluator_real_t** vs_out, int* num_faces_out, int** quad_faces_out ) +{ + std::ifstream in( in_path ); + return load_OBJ( in, num_vs_out, vs_out, num_faces_out, quad_faces_out ); +} + +} + + +int main( int argc, const char* argv[] ) +{ + subdivision_evaluator_real_t* vs = &TORUS_VS[0]; + int* faces = &TORUS_FACES[0]; + int num_vs = sizeof( TORUS_VS )/sizeof( subdivision_evaluator_real_t )/3; + int num_faces = sizeof( TORUS_FACES )/sizeof( int )/4; + + if( argc == 2 ) + { + const bool success = load_OBJ( argv[1], &num_vs, &vs, &num_faces, &faces ); + if( !success ) return -1; + } + + std::cout << "Number of vertices: " << num_vs << '\n'; + std::cout << "Number of faces: " << num_faces << '\n'; + + save_OBJ( "original.obj", num_vs, vs, num_faces, faces ); + + void* eval = new_subdivision_evaluator( num_vs, vs, num_faces, faces, 4 ); + + const int num_refined_faces = num_refined_quad_faces_of_subdivision_evaluator( eval ); + int* refined_faces = new int[ num_refined_faces*4 ]; + get_refined_quad_faces_of_subdivision_evaluator( eval, refined_faces ); + + const int num_refined_vs = num_refined_vertices_of_subdivision_evaluator( eval ); + subdivision_evaluator_real_t* refined_vs = new subdivision_evaluator_real_t[ num_refined_vs*3 ]; + get_refined_vertices_of_subdivision_evaluator( eval, refined_vs ); + + save_OBJ( "original-refined.obj", num_refined_vs, refined_vs, num_refined_faces, refined_faces ); + + // Test the non-3 dimensional path by duplicating submitting 6-dimensional + // vertices xyzxyz: + { + std::vector< subdivision_evaluator_real_t > control_vs6( num_vs*6 ); + for( int i = 0; i < num_vs; ++i ) + for( int c = 0; c < 3; ++c ) + { + control_vs6[ 6*i + 3 + c ] = control_vs6[ 6*i + c ] = vs[ 3*i + c ]; + } + std::vector< subdivision_evaluator_real_t > refined_vs6( num_refined_vs*6 ); + get_refined_vertices_of_subdivision_evaluator_with_control_vertices( eval, 6, &control_vs6[0], &refined_vs6[0] ); + // Verify the output: + subdivision_evaluator_real_t total_diff = 0.; + for( int i = 0; i < num_refined_vs; ++i ) + for( int c = 0; c < 3; ++c ) + { + total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs6[ 6*i + 3 + c ] ); + total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs[ 3*i + c ] ); + } + std::cout << "Total difference of 6-dimensional versus 3-dimensional (should be 0): " << total_diff << '\n'; + } + + subdivision_evaluator_real_t* weights = new subdivision_evaluator_real_t[ num_refined_vs ]; + std::fill( weights, weights + num_refined_vs, 1. ); + void* engine = new_subdivision_skinning_engine( eval, 1, weights ); + + subdivision_evaluator_real_t* vs_modified = new subdivision_evaluator_real_t[ num_vs*3 ]; + /// This produces identical output as input: + // const subdivision_evaluator_real_t transforms[] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 }; + /// This produces output whose coordinates are scaled by 2: + const subdivision_evaluator_real_t transforms[] = { 2,0,0,0, 0,2,0,0, 0,0,2,0, 0,0,0,2 }; + compute_control_mesh_vertices_given_transforms_for_subdivision_skinning_engine( engine, (subdivision_evaluator_real_t*)transforms, vs_modified ); + save_OBJ( "modified.obj", num_vs, vs_modified, num_faces, faces ); + + const int num_refined_vs2 = num_refined_vertices_of_subdivision_skinning_engine( engine ); + std::cout << "num_refined_vs eval: " << num_refined_vs << '\n'; + std::cout << "num_refined_vs engine: " << num_refined_vs2 << '\n'; + + get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 3, vs_modified, refined_vs ); + save_OBJ( "modified-refined.obj", num_refined_vs, refined_vs, num_refined_faces, refined_faces ); + + // Test the non-3 dimensional path by duplicating submitting 6-dimensional + // vertices xyzxyz: + { + std::vector< subdivision_evaluator_real_t > control_vs6( num_vs*6 ); + for( int i = 0; i < num_vs; ++i ) + for( int c = 0; c < 3; ++c ) + { + control_vs6[ 6*i + 3 + c ] = control_vs6[ 6*i + c ] = vs_modified[ 3*i + c ]; + } + std::vector< subdivision_evaluator_real_t > refined_vs6( num_refined_vs*6 ); + get_refined_vertices_of_subdivision_skinning_engine_with_control_vertices( engine, 6, &control_vs6[0], &refined_vs6[0] ); + // Verify the output: + subdivision_evaluator_real_t total_diff = 0.; + for( int i = 0; i < num_refined_vs; ++i ) + for( int c = 0; c < 3; ++c ) + { + total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs6[ 6*i + 3 + c ] ); + total_diff += fabs( refined_vs6[ 6*i + c ] - refined_vs[ 3*i + c ] ); + } + std::cout << "Total difference of 6-dimensional versus 3-dimensional (should be 0): " << total_diff << '\n'; + } + + delete_subdivision_skinning_engine( engine ); + delete_subdivision_evaluator( eval ); + + delete [] vs_modified; + delete [] refined_faces; + delete [] refined_vs; + delete [] weights; + + if( argc == 2 ) + { + delete[] vs; + delete[] faces; + } + + return 0; +} diff --git a/subdivgui/torus-weights.dmat b/subdivgui/torus-weights.dmat new file mode 100644 index 0000000..83177da --- /dev/null +++ b/subdivgui/torus-weights.dmat @@ -0,0 +1,6145 @@ +3 2048 +0.015133502893149853 +0.010123889893293381 +0.04667104035615921 +0.055925481021404266 +0.068578071892261505 +0.023803740739822388 +0.0067197130993008614 +0.0025531980209052563 +0.0005114417290315032 +0.083386391401290894 +0.035530652850866318 +0.013178594410419464 +0.0043263225816190243 +0.0018387807067483664 +0.0006119568133726716 +0.00012640187924262136 +0.097239851951599121 +0.047335430979728699 +0.020199459046125412 +0.10515975952148438 +0.053864415735006332 +0.024153944104909897 +0.0092480648308992386 +0.0074113733135163784 +0.0025777928531169891 +0.0012733344919979572 +0.00035598408430814743 +0.0033858420792967081 +0.0018256900366395712 +0.0013325412292033434 +0.00062251795316115022 +0.00014861712406855077 +7.0341673563234508e-05 +2.2089388949098065e-05 +2.2089388949098065e-05 +1.24389334814623e-05 +0.10594052076339722 +0.054015573114156723 +0.024264043197035789 +0.10214966535568237 +0.050938498228788376 +0.022602127864956856 +0.0088332286104559898 +0.009398595429956913 +0.094822190701961517 +0.045714862644672394 +0.019850442185997963 +0.083460442721843719 +0.037358369678258896 +0.015301105566322803 +0.0060330652631819248 +0.0078416699543595314 +0.0031726444140076637 +0.003399119945243001 +0.0034980606287717819 +0.0025012055411934853 +0.0018550233216956258 +0.0021988279186189175 +0.0021417660173028708 +0.0020074453204870224 +0.069504700601100922 +0.027007846161723137 +0.009614194743335247 +0.056333191692829132 +0.017927203327417374 +0.0048177307471632957 +0.0017707889201119542 +0.0036904572043567896 +0.046238094568252563 +0.011772106401622295 +0.0018108868971467018 +0.040425505489110947 +0.0085899988189339638 +0.0003639824572019279 +0.00013969211431685835 +0.00065333320526406169 +0.00025364221073687077 +0.00067904405295848846 +0.0014924393035471439 +6.8779379944317043e-05 +6.2312305090017617e-05 +0.00022130682191345841 +0.00056182616390287876 +0.0011650328524410725 +0.039047356694936752 +0.0078094718046486378 +1.2284504009585362e-06 +0.041201382875442505 +0.0082402769476175308 +7.2695530661803787e-07 +3.63476851816813e-06 +6.1422401813615579e-06 +7.2695343078521546e-06 +1.2284476724744309e-05 +7.2695347625995055e-06 +1.2284476724744309e-05 +0.40523016452789307 +0.39934235811233521 +0.57431298494338989 +0.57346248626708984 +0.57073938846588135 +0.41062578558921814 +0.26528865098953247 +0.25354275107383728 +0.24187153577804565 +0.56662285327911377 +0.41476759314537048 +0.27575773000717163 +0.16317635774612427 +0.14895834028720856 +0.13472330570220947 +0.12195476889610291 +0.56182485818862915 +0.41728919744491577 +0.28387719392776489 +0.55728989839553833 +0.41821926832199097 +0.2888527512550354 +0.18266557157039642 +0.17530147731304169 +0.55352568626403809 +0.41742819547653198 +0.29003378748893738 +0.55060327053070068 +0.41462841629981995 +0.28691339492797852 +0.18064051866531372 +0.18424098193645477 +0.54890871047973633 +0.41020715236663818 +0.27983731031417847 +0.54914367198944092 +0.40522670745849609 +0.27000406384468079 +0.16029945015907288 +0.17246425151824951 +0.55136984586715698 +0.40032240748405457 +0.25855305790901184 +0.55500960350036621 +0.39570248126983643 +0.24656495451927185 +0.13058854639530182 +0.14558042585849762 +0.55958932638168335 +0.39197248220443726 +0.23587208986282349 +0.5647398829460144 +0.39013510942459106 +0.22905859351158142 +0.10981877148151398 +0.11778660118579865 +0.56964570283889771 +0.39077353477478027 +0.22769403457641602 +0.57304501533508301 +0.39405140280723572 +0.23233343660831451 +0.11238889396190643 +0.10801457613706589 +0.94489485025405884 +0.95089691877365112 +0.98936879634857178 +0.98581874370574951 +0.97880291938781738 +0.93408578634262085 +0.85072463750839233 +0.86340010166168213 +0.87088894844055176 +0.96777623891830444 +0.919048011302948 +0.83468085527420044 +0.71376919746398926 +0.72586923837661743 +0.73574274778366089 +0.74159961938858032 +0.95469188690185547 +0.90247505903244019 +0.81809127330780029 +0.94400101900100708 +0.88917505741119385 +0.80478167533874512 +0.69141727685928345 +0.70150649547576904 +0.93810427188873291 +0.88168531656265259 +0.79674112796783447 +0.93735206127166748 +0.88027262687683105 +0.79412174224853516 +0.68155944347381592 +0.68472898006439209 +0.94160234928131104 +0.88475871086120605 +0.79691636562347412 +0.95022118091583252 +0.89452004432678223 +0.80495846271514893 +0.68676972389221191 +0.68213087320327759 +0.96156573295593262 +0.90788644552230835 +0.81708234548568726 +0.97298383712768555 +0.92213982343673706 +0.83112305402755737 +0.70528608560562134 +0.69494843482971191 +0.98232680559158325 +0.93497908115386963 +0.84525978565216064 +0.98794877529144287 +0.94451969861984253 +0.85801607370376587 +0.72790753841400146 +0.71660035848617554 +0.98998343944549561 +0.95010405778884888 +0.8676796555519104 +0.99034428596496582 +0.95230108499526978 +0.87230199575424194 +0.74230331182479858 +0.73737150430679321 +0.98978841304779053 +0.99264109134674072 +0.96596550941467285 +0.96139669418334961 +0.95481759309768677 +0.98494434356689453 +0.99497824907302856 +0.9980425238609314 +0.99955254793167114 +0.94746679067611694 +0.9789544939994812 +0.99016332626342773 +0.98741596937179565 +0.99398064613342285 +0.9977080225944519 +0.99942326545715332 +0.94059020280838013 +0.97283148765563965 +0.98435240983963013 +0.9354407787322998 +0.96775609254837036 +0.9792514443397522 +0.97200047969818115 +0.97909414768218994 +0.93289035558700562 +0.96471327543258667 +0.976021409034729 +0.93342965841293335 +0.9644923210144043 +0.97527867555618286 +0.96702426671981812 +0.96783745288848877 +0.93729603290557861 +0.96739757061004639 +0.97727680206298828 +0.94447338581085205 +0.97324836254119873 +0.9819062352180481 +0.97538810968399048 +0.96963292360305786 +0.95347285270690918 +0.98067319393157959 +0.98805785179138184 +0.96133220195770264 +0.9871099591255188 +0.99362218379974365 +0.99047845602035522 +0.98306280374526978 +0.96655130386352539 +0.99132239818572998 +0.99746984243392944 +0.96909219026565552 +0.99339973926544189 +0.99945133924484253 +0.99918311834335327 +0.99612939357757568 +0.9693872332572937 +0.99385625123977661 +0.99996531009674072 +0.96833932399749756 +0.99363023042678833 +0.99995863437652588 +0.99991953372955322 +0.99995732307434082 +0.74315786361694336 +0.74744880199432373 +0.64222943782806396 +0.63948148488998413 +0.63619703054428101 +0.73805087804794312 +0.82802480459213257 +0.83453613519668579 +0.84012448787689209 +0.63289910554885864 +0.7327420711517334 +0.82118487358093262 +0.89394170045852661 +0.90153336524963379 +0.90862482786178589 +0.91434162855148315 +0.63028466701507568 +0.72822940349578857 +0.81515657901763916 +0.62922453880310059 +0.72589385509490967 +0.81162548065185547 +0.8825458288192749 +0.88708227872848511 +0.62993746995925903 +0.72610193490982056 +0.81111443042755127 +0.63199061155319214 +0.72820574045181274 +0.81298345327377319 +0.88234525918960571 +0.8810310959815979 +0.6349407434463501 +0.73182493448257446 +0.81707155704498291 +0.63833504915237427 +0.73684704303741455 +0.82369625568389893 +0.89401805400848389 +0.88656139373779297 +0.64160311222076416 +0.74250298738479614 +0.83183342218399048 +0.64405733346939087 +0.74736714363098145 +0.83911728858947754 +0.91179448366165161 +0.9034113883972168 +0.64542257785797119 +0.7506643533706665 +0.84418559074401855 +0.6458364725112915 +0.75226867198944092 +0.84668028354644775 +0.92033565044403076 +0.91753911972045898 +0.64540308713912964 +0.75217932462692261 +0.84659665822982788 +0.64419370889663696 +0.75052005052566528 +0.84428369998931885 +0.91824966669082642 +0.92035526037216187 +0.30927857756614685 +0.30818697810173035 +0.20747382938861847 +0.20976966619491577 +0.21406683325767517 +0.31172549724578857 +0.417716383934021 +0.41711965203285217 +0.41724205017089844 +0.21972322463989258 +0.31506502628326416 +0.41886928677558899 +0.52648448944091797 +0.5275537371635437 +0.52888226509094238 +0.53026574850082397 +0.22566118836402893 +0.31852409243583679 +0.42028400301933289 +0.23036742210388184 +0.32101967930793762 +0.42153486609458923 +0.52608466148376465 +0.52590751647949219 +0.23306205868721008 +0.32231718301773071 +0.42259150743484497 +0.23369862139225006 +0.32303044199943542 +0.42381852865219116 +0.52884793281555176 +0.52709770202636719 +0.23245105147361755 +0.32328197360038757 +0.42512363195419312 +0.22971366345882416 +0.32270258665084839 +0.42595717310905457 +0.53306007385253906 +0.53101563453674316 +0.22589190304279327 +0.32113519310951233 +0.42597335577011108 +0.22140225768089294 +0.31863489747047424 +0.42502987384796143 +0.53514450788497925 +0.5345461368560791 +0.21673908829689026 +0.31555506587028503 +0.4233163595199585 +0.21247440576553345 +0.31254729628562927 +0.42135432362556458 +0.53401947021484375 +0.53485763072967529 +0.20916728675365448 +0.31010231375694275 +0.41952982544898987 +0.20736396312713623 +0.30854982137680054 +0.41809338331222534 +0.53159964084625244 +0.53287893533706665 +0.024655768647789955 +0.022065259516239166 +0.0046740635298192501 +0.0062362141907215118 +0.0089996485039591789 +0.029172785580158234 +0.068392336368560791 +0.062590867280960083 +0.059261992573738098 +0.012335603125393391 +0.03475315123796463 +0.075845956802368164 +0.13805806636810303 +0.13056677579879761 +0.12484358251094818 +0.1216149777173996 +0.016049753874540329 +0.040902946144342422 +0.084056258201599121 +0.020382221788167953 +0.047497469931840897 +0.092052794992923737 +0.15342403948307037 +0.14618796110153198 +0.024558417499065399 +0.053248092532157898 +0.098163388669490814 +0.02678905613720417 +0.055702243000268936 +0.1000140979886055 +0.15938813984394073 +0.15829449892044067 +0.026084203273057938 +0.053893931210041046 +0.097124792635440826 +0.022253260016441345 +0.048343721777200699 +0.090909063816070557 +0.15154910087585449 +0.15668357908725739 +0.016416117548942566 +0.040623661130666733 +0.083019040524959564 +0.011003151535987854 +0.033357284963130951 +0.075345434248447418 +0.13860474526882172 +0.14518123865127563 +0.0072471913881599903 +0.027881529182195663 +0.068893678486347198 +0.0051835179328918457 +0.024246705695986748 +0.063784003257751465 +0.12716147303581238 +0.13249167799949646 +0.0044523198157548904 +0.022261599078774452 +0.06024516373872757 +0.004298690240830183 +0.021493451669812202 +0.058614443987607956 +0.12112457305192947 +0.12313301116228104 +0.00048608914948999882 +0.00016814572154544294 +3.7639674701495096e-05 +0.0011133537627756596 +0.0036219146568328142 +0.0020299246534705162 +0.0008166654733940959 +0.00016413519915658981 +0.0019827447831630707 +0.00283798947930336 +0.0077050924301147461 +0.0055088363587856293 +0.0035308159422129393 +0.0040209521539509296 +0.011114285327494144 +0.0098312990739941597 +0.0041225766763091087 +0.0035043163225054741 +0.0091191818937659264 +0.010993135161697865 +0.0023191117215901613 +0.0012042183661833405 +0.0031570566352456808 +0.0060315313749015331 +0.00047064479440450668 +0.0001031544161378406 +0.00024596479488536716 +0.0012207983527332544 +6.1422406361089088e-06 +3.6347694276628317e-06 +7.269562729561585e-07 +1.2284507420190494e-06 +0.030904743820428848 +0.012133371084928513 +0.019048787653446198 +0.0064151789993047714 +0.041761666536331177 +0.029322858899831772 +0.012768389657139778 +0.0043603847734630108 +0.0096521321684122086 +0.003721810644492507 +0.0012772006448358297 +0.00135098269674927 +0.055520318448543549 +0.041757911443710327 +0.021916905418038368 +0.068915300071239471 +0.051594004034996033 +0.031447678804397583 +0.016845552250742912 +0.02278677374124527 +0.012538217008113861 +0.0078152278438210487 +0.0028924962971359491 +0.0059095257893204689 +0.0022872376721352339 +0.008601129986345768 +0.0042731557041406631 +0.0019323949236422777 +0.0030990885570645332 +0.0017795120365917683 +0.00071105093229562044 +0.00085997418500483036 +0.00030898631666786969 +0.0011002445826306939 +0.00022283743601292372 +0.00080528459511697292 +0.0001588442682987079 +0.00022554102179128677 +4.1186627640854567e-05 +4.1186627640854567e-05 +0.076390206813812256 +0.054540373384952545 +0.036734841763973236 +0.076750904321670532 +0.052683711051940918 +0.036823589354753494 +0.024560019373893738 +0.023525062948465347 +0.01542601827532053 +0.015274198725819588 +0.073165535926818848 +0.048656642436981201 +0.034430168569087982 +0.066776454448699951 +0.04198986291885376 +0.030463989824056625 +0.0214067492634058 +0.017844721674919128 +0.012700803577899933 +0.014415952377021313 +0.0091447578743100166 +0.0084151504561305046 +0.0053342031314969063 +0.0070639271289110184 +0.0048174732364714146 +0.0033216630108654499 +0.002912384457886219 +0.0024516182020306587 +0.0034446930512785912 +0.0025082258507609367 +0.009473457932472229 +0.0054828459396958351 +0.0056124003604054451 +0.0034986380487680435 +0.0024862000718712807 +0.0023643565364181995 +0.056700333952903748 +0.032228369265794754 +0.024027612060308456 +0.044266294687986374 +0.022104794159531593 +0.016025200486183167 +0.012473096139729023 +0.0069779055193066597 +0.0060706660151481628 +0.0097719067707657814 +0.033016007393598557 +0.014480995945632458 +0.0091625256463885307 +0.024960685521364212 +0.009806465357542038 +0.004709719680249691 +0.0031049617100507021 +0.00090679724235087633 +0.0011129705235362053 +0.0029885682743042707 +0.0026179463602602482 +0.0011241843458265066 +0.0010418086312711239 +0.00033343571703881025 +0.00038316153222694993 +0.0004270598292350769 +0.00014035930507816374 +0.00021596177248284221 +0.0010280265705659986 +0.00055991113185882568 +0.0048687029629945755 +0.0037282691337168217 +0.0022416776046156883 +0.002005694666877389 +0.001210853923112154 +0.0019955611787736416 +0.020581081509590149 +0.0079739280045032501 +0.0025012895930558443 +0.019523678347468376 +0.0079478472471237183 +0.0019526750547811389 +9.2205278633628041e-05 +8.1475309343659319e-07 +3.0711219096701825e-06 +0.00022710685152560472 +0.020600691437721252 +0.0088959075510501862 +0.0020602508448064327 +0.023923920467495918 +0.0030575336422771215 +0.00012844322191085666 +0.00027220178162679076 +1.8173853959524422e-06 +4.0737554627412464e-06 +3.4514428989496082e-05 +5.6338894864893518e-06 +5.0783804908860475e-05 +1.1350260137987789e-05 +1.706048715277575e-05 +8.1475081969983876e-06 +7.9964875112636946e-06 +4.0971324779093266e-05 +9.0621797426138073e-05 +9.5204704848583788e-06 +2.9291437385836616e-05 +1.3512924851966091e-05 +6.3048384618014097e-05 +0.48829841613769531 +0.40230017900466919 +0.40803709626197815 +0.32629817724227905 +0.48972496390342712 +0.41290107369422913 +0.33522078394889832 +0.25949084758758545 +0.27076727151870728 +0.20260824263095856 +0.2476133406162262 +0.1890043318271637 +0.48993855714797974 +0.41622915863990784 +0.3428080677986145 +0.48902362585067749 +0.41795137524604797 +0.34833574295043945 +0.28016084432601929 +0.28680762648582458 +0.22562777996063232 +0.21531456708908081 +0.15616221725940704 +0.16966730356216431 +0.11923602223396301 +0.17974540591239929 +0.13260370492935181 +0.090743646025657654 +0.10225266963243484 +0.075790420174598694 +0.12806300818920135 +0.14175024628639221 +0.090447671711444855 +0.10429596155881882 +0.061904661357402802 +0.050795838236808777 +0.48738372325897217 +0.41805684566497803 +0.35148081183433533 +0.48519524931907654 +0.41629737615585327 +0.35183694958686829 +0.28994926810264587 +0.28904294967651367 +0.23363102972507477 +0.23201955854892731 +0.48240727186203003 +0.41255408525466919 +0.34891456365585327 +0.47943302989006042 +0.40772044658660889 +0.34306424856185913 +0.28379490971565247 +0.27519047260284424 +0.22235965728759766 +0.2302727997303009 +0.18304941058158875 +0.17708767950534821 +0.1379537433385849 +0.1668437272310257 +0.13000160455703735 +0.098959118127822876 +0.089676707983016968 +0.10445607453584671 +0.18413855135440826 +0.14048191905021667 +0.14172400534152985 +0.1062820628285408 +0.47714987397193909 +0.40275204181671143 +0.33547711372375488 +0.47590628266334534 +0.39796385169029236 +0.32705804705619812 +0.26441323757171631 +0.25255861878395081 +0.19724725186824799 +0.21091051399707794 +0.47552180290222168 +0.39366352558135986 +0.31842553615570068 +0.4759974479675293 +0.39075452089309692 +0.31082653999328613 +0.24089509248733521 +0.23181888461112976 +0.17042133212089539 +0.18299463391304016 +0.13797599077224731 +0.12374857068061829 +0.088612712919712067 +0.11303316801786423 +0.076786130666732788 +0.050825707614421844 +0.042721103876829147 +0.062609776854515076 +0.1531166285276413 +0.11790478229522705 +0.10313782095909119 +0.076595678925514221 +0.47751569747924805 +0.39013463258743286 +0.30613651871681213 +0.47996106743812561 +0.39207231998443604 +0.3055342435836792 +0.22766050696372986 +0.22922855615615845 +0.16070385277271271 +0.16243980824947357 +0.48291990160942078 +0.39653381705284119 +0.30950188636779785 +0.48589363694190979 +0.31716609001159668 +0.23666301369667053 +0.17596548795700073 +0.16560837626457214 +0.10942850261926651 +0.11664719879627228 +0.071358680725097656 +0.079102300107479095 +0.043492436408996582 +0.039739243686199188 +0.10814529657363892 +0.069659367203712463 +0.068003371357917786 +0.039238519966602325 +0.97007870674133301 +0.94853287935256958 +0.94005501270294189 +0.90956830978393555 +0.96109670400619507 +0.92705929279327393 +0.89741373062133789 +0.8575969934463501 +0.84301018714904785 +0.79366177320480347 +0.86790657043457031 +0.80569130182266235 +0.94781041145324707 +0.91065293550491333 +0.88137590885162354 +0.93260222673416138 +0.89511537551879883 +0.86430621147155762 +0.82621526718139648 +0.8107873797416687 +0.76382619142532349 +0.77884972095489502 +0.71998554468154907 +0.70751219987869263 +0.64213001728057861 +0.69604414701461792 +0.63341259956359863 +0.56424999237060547 +0.55946546792984009 +0.568825364112854 +0.73928529024124146 +0.73119616508483887 +0.65713858604431152 +0.65050792694091797 +0.57230508327484131 +0.57415181398391724 +0.92032122611999512 +0.88468724489212036 +0.85063701868057251 +0.91355162858963013 +0.88020271062850952 +0.84268784523010254 +0.80009335279464722 +0.79474431276321411 +0.74402743577957153 +0.75168353319168091 +0.91261255741119385 +0.88181722164154053 +0.84066480398178101 +0.91730880737304688 +0.88901889324188232 +0.84445887804031372 +0.79485267400741577 +0.80029189586639404 +0.74263811111450195 +0.74097573757171631 +0.68269693851470947 +0.68135714530944824 +0.61754459142684937 +0.68392151594161987 +0.61701464653015137 +0.54955869913101196 +0.54874122142791748 +0.55195486545562744 +0.6876407265663147 +0.62596023082733154 +0.62063270807266235 +0.55530709028244019 +0.92693030834197998 +0.90092253684997559 +0.85364478826522827 +0.93972885608673096 +0.91507214307785034 +0.86676275730133057 +0.8106454610824585 +0.82399845123291016 +0.75956940650939941 +0.74911540746688843 +0.95291799306869507 +0.92885398864746094 +0.88131844997406006 +0.96416383981704712 +0.94027948379516602 +0.89520144462585449 +0.83827167749404907 +0.85190278291702271 +0.78558319807052612 +0.77222263813018799 +0.69993370771408081 +0.71088260412216187 +0.6322396993637085 +0.72231638431549072 +0.64052969217300415 +0.55720502138137817 +0.56211632490158081 +0.55304920673370361 +0.69050288200378418 +0.6195647120475769 +0.62496614456176758 +0.55004394054412842 +0.97158539295196533 +0.94777089357376099 +0.90668541193008423 +0.97502821683883667 +0.95159047842025757 +0.91454368829727173 +0.86335629224777222 +0.8707427978515625 +0.80884474515914917 +0.79844564199447632 +0.97606444358825684 +0.95211148262023926 +0.91804879903793335 +0.97484081983566284 +0.9165690541267395 +0.87235242128372192 +0.81288856267929077 +0.81405532360076904 +0.74057197570800781 +0.74262571334838867 +0.6607363224029541 +0.66081142425537109 +0.57394462823867798 +0.5716126561164856 +0.73303806781768799 +0.64910471439361572 +0.65654385089874268 +0.56730228662490845 +0.97883152961730957 +0.99151647090911865 +0.98756253719329834 +0.99571084976196289 +0.97295904159545898 +0.9820399284362793 +0.99190664291381836 +0.99671697616577148 +0.99280190467834473 +0.99529200792312622 +0.99897956848144531 +0.99822705984115601 +0.9661407470703125 +0.97583580017089844 +0.98669856786727905 +0.95954716205596924 +0.97008895874023438 +0.98093569278717041 +0.98727571964263916 +0.98160654306411743 +0.98357433080673218 +0.99013900756835938 +0.99110442399978638 +0.98328810930252075 +0.98046988248825073 +0.97520720958709717 +0.96982795000076294 +0.96121299266815186 +0.94876909255981445 +0.97382491827011108 +0.99876558780670166 +0.99614739418029785 +0.9943002462387085 +0.98912417888641357 +0.98277819156646729 +0.98799300193786621 +0.9543304443359375 +0.965931236743927 +0.97597658634185791 +0.95142996311187744 +0.96420061588287354 +0.97286772727966309 +0.97736406326293945 +0.97530066967010498 +0.97430205345153809 +0.97783964872360229 +0.9515727162361145 +0.96556538343429565 +0.97234272956848145 +0.9550432562828064 +0.96996605396270752 +0.97469627857208252 +0.97594201564788818 +0.97926938533782959 +0.97547644376754761 +0.97345435619354248 +0.96698600053787231 +0.96791797876358032 +0.95523959398269653 +0.9721342921257019 +0.95869255065917969 +0.93889141082763672 +0.94540524482727051 +0.93706321716308594 +0.96952641010284424 +0.96097421646118164 +0.95598548650741577 +0.94043153524398804 +0.96168428659439087 +0.97692406177520752 +0.97978311777114868 +0.97001463174819946 +0.98417508602142334 +0.98637759685516357 +0.9849236011505127 +0.99104529619216919 +0.98664295673370361 +0.98023527860641479 +0.97722983360290527 +0.98948860168457031 +0.99217361211776733 +0.9819607138633728 +0.99262231588363647 +0.99602800607681274 +0.9957699179649353 +0.99870312213897705 +0.9970395565032959 +0.99265670776367188 +0.98693913221359253 +0.99357640743255615 +0.98471242189407349 +0.99803286790847778 +0.99210357666015625 +0.97801762819290161 +0.98570585250854492 +0.96743130683898926 +0.97912156581878662 +0.96587854623794556 +0.97535699605941772 +0.95571845769882202 +0.98427397012710571 +0.99377191066741943 +0.99796146154403687 +0.98468190431594849 +0.99376982450485229 +0.99844050407409668 +0.99983251094818115 +0.99996787309646606 +0.99996072053909302 +0.99936115741729736 +0.9841422438621521 +0.99329984188079834 +0.99837756156921387 +0.98256903886795044 +0.997772216796875 +0.99984961748123169 +0.99957275390625 +0.99994778633117676 +0.99994808435440063 +0.99977624416351318 +0.99750721454620361 +0.99679237604141235 +0.99010097980499268 +0.99025356769561768 +0.99976342916488647 +0.99626344442367554 +0.99746239185333252 +0.98929500579833984 +0.69254499673843384 +0.74544382095336914 +0.74066799879074097 +0.79069626331329346 +0.68832230567932129 +0.73538321256637573 +0.784809410572052 +0.83135873079299927 +0.82460862398147583 +0.86712372303009033 +0.83748292922973633 +0.87405365705490112 +0.68397772312164307 +0.73029994964599609 +0.77865636348724365 +0.68038523197174072 +0.7267032265663147 +0.77331918478012085 +0.81796389818191528 +0.81297361850738525 +0.85324364900588989 +0.85979181528091431 +0.89774537086486816 +0.89032107591629028 +0.92328363656997681 +0.88442426919937134 +0.91631650924682617 +0.94389092922210693 +0.93772125244140625 +0.95116126537322998 +0.91170972585678101 +0.90519636869430542 +0.9378669261932373 +0.93088692426681519 +0.95828104019165039 +0.96400988101959229 +0.67868435382843018 +0.72572040557861328 +0.77036893367767334 +0.67916721105575562 +0.72695732116699219 +0.77024859189987183 +0.81103253364562988 +0.81179141998291016 +0.8482062816619873 +0.84917920827865601 +0.68127918243408203 +0.72983288764953613 +0.77227294445037842 +0.68458014726638794 +0.73416769504547119 +0.77616035938262939 +0.81473028659820557 +0.82004690170288086 +0.85404342412948608 +0.84984004497528076 +0.88134652376174927 +0.88406938314437866 +0.91028124094009399 +0.88986349105834961 +0.9143904447555542 +0.9349479079246521 +0.9404718279838562 +0.9327431321144104 +0.88142281770706177 +0.91140609979629517 +0.90934157371520996 +0.93380999565124512 +0.68874484300613403 +0.73968470096588135 +0.78203356266021729 +0.69311261177062988 +0.74512296915054321 +0.78900080919265747 +0.82772362232208252 +0.83572983741760254 +0.87022793292999268 +0.86122703552246094 +0.69668763875961304 +0.74921941757202148 +0.79515671730041504 +0.69898056983947754 +0.75168591737747192 +0.79941797256469727 +0.84195071458816528 +0.84577703475952148 +0.88391125202178955 +0.87830942869186401 +0.90791326761245728 +0.91501599550247192 +0.93949449062347412 +0.91932523250579834 +0.94508159160614014 +0.96427416801452637 +0.96815896034240723 +0.95773011445999146 +0.89865678548812866 +0.92184245586395264 +0.93121582269668579 +0.94893050193786621 +0.70000916719436646 +0.75242805480957031 +0.80152297019958496 +0.69981735944747925 +0.75153827667236328 +0.80145162343978882 +0.84693890810012817 +0.84569692611694336 +0.88657695055007935 +0.88664847612380981 +0.69847500324249268 +0.74914884567260742 +0.79942440986633301 +0.6960374116897583 +0.79572427272796631 +0.84240871667861938 +0.87991005182266235 +0.8841935396194458 +0.91952735185623169 +0.91652137041091919 +0.94633674621582031 +0.94312489032745361 +0.96737241744995117 +0.96897470951080322 +0.92065185308456421 +0.94779819250106812 +0.94795185327529907 +0.9694640040397644 +0.25820696353912354 +0.30853444337844849 +0.31036153435707092 +0.36243328452110291 +0.26157429814338684 +0.31331261992454529 +0.36395972967147827 +0.41733831167221069 +0.4182334840297699 +0.47243461012840271 +0.41708076000213623 +0.47278648614883423 +0.26604557037353516 +0.31684726476669312 +0.36620005965232849 +0.27066594362258911 +0.31995999813079834 +0.36860650777816772 +0.41957053542137146 +0.42095652222633362 +0.47292736172676086 +0.47249117493629456 +0.52697396278381348 +0.52611804008483887 +0.58017754554748535 +0.52588540315628052 +0.57857602834701538 +0.63145208358764648 +0.62950593233108521 +0.63451707363128662 +0.52957999706268311 +0.5281984806060791 +0.58468729257583618 +0.5823744535446167 +0.63787358999252319 +0.64095520973205566 +0.27409639954566956 +0.32177981734275818 +0.37040865421295166 +0.27588796615600586 +0.32270842790603638 +0.37150111794471741 +0.42206472158432007 +0.42316091060638428 +0.47473981976509094 +0.47366973757743835 +0.27648156881332397 +0.32323700189590454 +0.37244337797164917 +0.27605515718460083 +0.32311922311782837 +0.37326148152351379 +0.4244956374168396 +0.42563366889953613 +0.47799834609031677 +0.47625398635864258 +0.52788674831390381 +0.52991336584091187 +0.5809701681137085 +0.53208702802658081 +0.5835411548614502 +0.63338184356689453 +0.63661074638366699 +0.63082361221313477 +0.52649295330047607 +0.57814717292785645 +0.57903897762298584 +0.62938642501831055 +0.27452385425567627 +0.32203894853591919 +0.37344777584075928 +0.27194792032241821 +0.31999826431274414 +0.37272119522094727 +0.42607632279396057 +0.42563045024871826 +0.48015916347503662 +0.47942864894866943 +0.26853311061859131 +0.31712669134140015 +0.37102726101875305 +0.2647060751914978 +0.31400144100189209 +0.36864039301872253 +0.42423677444458008 +0.42233383655548096 +0.47893741726875305 +0.47996243834495544 +0.53497689962387085 +0.5350908637046814 +0.59002625942230225 +0.53448659181594849 +0.5905337929725647 +0.6448674201965332 +0.64573991298675537 +0.642974853515625 +0.5338934063911438 +0.586234450340271 +0.58855420351028442 +0.64002782106399536 +0.26111429929733276 +0.31123381853103638 +0.36616379022598267 +0.25828754901885986 +0.30919390916824341 +0.36404016613960266 +0.42040923237800598 +0.41874736547470093 +0.47599983215332031 +0.47750943899154663 +0.25663748383522034 +0.30820313096046448 +0.36255151033401489 +0.25651434063911438 +0.36194515228271484 +0.41758555173873901 +0.47353118658065796 +0.47462582588195801 +0.53224641084671021 +0.53093922138214111 +0.58834546804428101 +0.58673542737960815 +0.64330768585205078 +0.64489096403121948 +0.53347676992416382 +0.5903174877166748 +0.58955186605453491 +0.6457211971282959 +0.013306679204106331 +0.023065753281116486 +0.026727410033345222 +0.040883742272853851 +0.016949694603681564 +0.031883995980024338 +0.046157479286193848 +0.065233804285526276 +0.071963898837566376 +0.096365906298160553 +0.060566075146198273 +0.090377382934093475 +0.021362723782658577 +0.037764664739370346 +0.052812390029430389 +0.026231225579977036 +0.044152412563562393 +0.060149699449539185 +0.079917170107364655 +0.088141903281211853 +0.11270741373300552 +0.10415934026241302 +0.13413681089878082 +0.14213895797729492 +0.17682003974914551 +0.15001349151134491 +0.18400387465953827 +0.22275160253047943 +0.22826285660266876 +0.21676525473594666 +0.12287205457687378 +0.1274387538433075 +0.16497811675071716 +0.17009904980659485 +0.21170821785926819 +0.20833146572113037 +0.031720824539661407 +0.050631552934646606 +0.067634135484695435 +0.036811940371990204 +0.055040512233972549 +0.073740646243095398 +0.095492199063301086 +0.09976959228515625 +0.12639006972312927 +0.12067542970180511 +0.039299789816141129 +0.055298358201980591 +0.075954370200634003 +0.03811810165643692 +0.051554031670093536 +0.073525048792362213 +0.099073603749275208 +0.094344384968280792 +0.12480651587247849 +0.12783937156200409 +0.15940162539482117 +0.15842512249946594 +0.19467104971408844 +0.15433456003665924 +0.19261281192302704 +0.23328571021556854 +0.23124393820762634 +0.23364050686359406 +0.15624317526817322 +0.1900656670331955 +0.19385802745819092 +0.23196910321712494 +0.033339120447635651 +0.044590950012207031 +0.067467041313648224 +0.026345092803239822 +0.036769792437553406 +0.059449188411235809 +0.087055183947086334 +0.079037025570869446 +0.1115306168794632 +0.11886975169181824 +0.019828289747238159 +0.030392436310648918 +0.051794786006212234 +0.015155134722590446 +0.02583085373044014 +0.045702375471591949 +0.071959309279918671 +0.066163569688796997 +0.097731441259384155 +0.10421386361122131 +0.14185503125190735 +0.13547031581401825 +0.17788016796112061 +0.12970875203609467 +0.17241425812244415 +0.2190566211938858 +0.21452119946479797 +0.2237045019865036 +0.14845529198646545 +0.18860957026481628 +0.18346899747848511 +0.2279122918844223 +0.012366226874291897 +0.023074984550476074 +0.041245713829994202 +0.011130799539387226 +0.021752454340457916 +0.03843596950173378 +0.061797145754098892 +0.059170205146074295 +0.088280424475669861 +0.092282339930534363 +0.010746725834906101 +0.02156943641602993 +0.037221737205982208 +0.011228388175368309 +0.037853512912988663 +0.05862823873758316 +0.086959533393383026 +0.086354434490203857 +0.12183139473199844 +0.12104246765375137 +0.16182702779769897 +0.16214689612388611 +0.20716269314289093 +0.20804350078105927 +0.12493962049484253 +0.16752447187900543 +0.16377219557762146 +0.21066701412200928 +1.7060483514796942e-05 +5.6337619753321633e-05 +5.1569306378951296e-05 +0.00032773989369161427 +0.00020790300914086401 +0.00029607006581500173 +0.0007534385658800602 +0.0010658662067726254 +8.7080756202340126e-05 +0.0004103379906155169 +0.0009763265261426568 +0.00067047472111880779 +0.0016345372423529625 +0.0013594340998679399 +0.0015338005032390356 +0.0024281525984406471 +0.003325027646496892 +0.0020606638863682747 +0.0027899532578885555 +0.0045276088640093803 +0.0067344466224312782 +0.0065673971548676491 +0.0094463508576154709 +0.014130396768450737 +0.018123690038919449 +0.010635358281433582 +0.00040893431287258863 +0.0013645788421854377 +0.0024157501757144928 +0.0043504550121724606 +0.0075070685707032681 +0.0052656810730695724 +0.0019389945082366467 +0.0019301363499835134 +0.0020640376023948193 +0.0022821871098130941 +0.0032072223257273436 +0.0038037372287362814 +0.0059737232513725758 +0.0047095436602830887 +0.0022001797333359718 +0.0025594411417841911 +0.0020972592756152153 +0.0026704289484769106 +0.004141039215028286 +0.0039241430349647999 +0.0068114269524812698 +0.006785999983549118 +0.010626517236232758 +0.011251019313931465 +0.017587929964065552 +0.010297050699591637 +0.017229970544576645 +0.026815479621291161 +0.024571403861045837 +0.026028759777545929 +0.0088252751156687737 +0.012653146870434284 +0.01579553447663784 +0.02260168269276619 +0.0015262407250702381 +0.0022883543279021978 +0.00082552095409482718 +0.0014895831700414419 +0.0029427534900605679 +0.0017130477353930473 +0.0037882255855947733 +0.0057116718962788582 +0.00036380323581397533 +0.00075364368967711926 +0.00012419161794241518 +0.00029707021894864738 +0.00079071888467296958 +0.00024209149705711752 +0.00076942320447415113 +0.0019824046175926924 +0.0044784676283597946 +0.0020701999310404062 +0.005805023480206728 +0.00061175297014415264 +0.0029912334866821766 +0.0089158406481146812 +0.0060016140341758728 +0.013504713773727417 +0.0076378895901143551 +0.014406494796276093 +0.0099428780376911163 +0.019433563575148582 +2.767466867226176e-05 +7.3403520218562335e-05 +8.1475081969983876e-06 +9.5204704848583788e-06 +3.1836905691307038e-05 +4.0737559174885973e-06 +3.0711221370438579e-06 +0.00015839737898204476 +8.9376462710788473e-06 +5.6338903959840536e-06 +1.6530178982065991e-05 +1.2323876944719814e-05 +8.4072860772721469e-05 +1.8173864191339817e-06 +8.1475377555761952e-07 +4.161659671808593e-05 +0.0010748544009402394 +0.0013400557218119502 +0.0043791406787931919 +0.0043504908680915833 +6.270085577853024e-05 +0.0015322179533541203 +0.0011133870575577021 +0.0046985414810478687 +0.026868920773267746 +0.035909593105316162 +0.0092298546805977821 +0.0043493947014212608 +0.048339173197746277 +0.017005749046802521 +0.0055262870155274868 +0.0023479815572500229 +0.00067699531791731715 +0.062610544264316559 +0.02692362479865551 +0.07374003529548645 +0.034910824149847031 +0.014318119734525681 +0.010261562652885914 +0.0032972129993140697 +0.0014032534090802073 +0.0050550410524010658 +0.002152508357539773 +0.0013228424359112978 +0.00046838208800181746 +0.00043219231883995235 +0.00011608095519477502 +9.5219642389565706e-05 +2.7210680855205283e-05 +0.077261820435523987 +0.037259247153997421 +0.075253471732139587 +0.035767383873462677 +0.014977328479290009 +0.015584236010909081 +0.070376664400100708 +0.032699767500162125 +0.062254488468170166 +0.027610663324594498 +0.011419865302741528 +0.013678303919732571 +0.0051265261135995388 +0.0054822466336190701 +0.0043653007596731186 +0.0022935718297958374 +0.0025098021142184734 +0.0024869914632290602 +0.0056406166404485703 +0.0024604543577879667 +0.050556696951389313 +0.020053049549460411 +0.038271822035312653 +0.012282284907996655 +0.0043652569875121117 +0.0079308617860078812 +0.028558926656842232 +0.006645232904702425 +0.022281371057033539 +0.003335301298648119 +0.00055986980441957712 +0.001913504907861352 +0.00065577711211517453 +0.0015631458954885602 +0.00020207287161611021 +0.00012287290883250535 +0.00035680961445905268 +0.00083976093446835876 +0.0029918898362666368 +0.0016153876204043627 +0.019711341708898544 +0.0020822321530431509 +0.019869621843099594 +0.0019871655385941267 +2.0368790956126759e-06 +5.9800866438308731e-05 +0.021893318742513657 +0.0023557627573609352 +6.9507434091065079e-05 +1.721258377074264e-05 +6.3143193074211013e-06 +1.0675825251382776e-05 +8.9622581072035246e-06 +3.2030307920649648e-05 +2.9068345611449331e-05 +0.48720365762710571 +0.48914864659309387 +0.33085599541664124 +0.32168793678283691 +0.48999801278114319 +0.33925169706344604 +0.20916500687599182 +0.19582706689834595 +0.18232287466526031 +0.4895969033241272 +0.34584957361221313 +0.48826909065246582 +0.35022628307342529 +0.2294093519449234 +0.22086581587791443 +0.12636122107505798 +0.11171770840883255 +0.13747382164001465 +0.097147159278392792 +0.084373928606510162 +0.48636123538017273 +0.35203808546066284 +0.48387932777404785 +0.35081616044044495 +0.23258495330810547 +0.23343469202518463 +0.48088857531547546 +0.34628093242645264 +0.47815024852752686 +0.3394133448600769 +0.21701338887214661 +0.22682191431522369 +0.13449807465076447 +0.14036393165588379 +0.12446896731853485 +0.14183099567890167 +0.4764094352722168 +0.33133292198181152 +0.47561785578727722 +0.32272979617118835 +0.19009305536746979 +0.20425410568714142 +0.47564071416854858 +0.31437960267066956 +0.47661486268043518 +0.30800068378448486 +0.16567455232143402 +0.176316037774086 +0.082237035036087036 +0.095663033425807953 +0.072510190308094025 +0.11067306995391846 +0.47864830493927002 +0.30529427528381348 +0.48140206933021545 +0.30691668391227722 +0.16230139136314392 +0.16076645255088806 +0.48444142937660217 +0.31301119923591614 +0.17027856409549713 +0.074731111526489258 +0.069083325564861298 +0.068176239728927612 +0.97298192977905273 +0.96612024307250977 +0.90405595302581787 +0.91379225254058838 +0.95499682426452637 +0.88980084657669067 +0.7864755392074585 +0.80015242099761963 +0.81002205610275269 +0.9401434063911438 +0.87269306182861328 +0.92579275369644165 +0.85676950216293335 +0.75720149278640747 +0.77117103338241577 +0.63771349191665649 +0.64646166563034058 +0.62942802906036377 +0.6541174054145813 +0.65942037105560303 +0.91622745990753174 +0.84593456983566284 +0.91233319044113159 +0.84092265367507935 +0.74191856384277344 +0.7472873330116272 +0.91430056095123291 +0.84186136722564697 +0.92154818773269653 +0.8484044075012207 +0.74526852369308472 +0.7412114143371582 +0.61692726612091064 +0.6188015341758728 +0.61787199974060059 +0.62302374839782715 +0.93310654163360596 +0.85986810922622681 +0.9464486837387085 +0.87401688098907471 +0.76573222875595093 +0.75395649671554565 +0.95890134572982788 +0.88845193386077881 +0.96847021579742432 +0.90135115385055542 +0.7921520471572876 +0.77888977527618408 +0.6363033652305603 +0.62843018770217896 +0.64482724666595459 +0.62197023630142212 +0.97370588779449463 +0.9111132025718689 +0.97574895620346069 +0.9168858528137207 +0.81226885318756104 +0.8041234016418457 +0.9758150577545166 +0.91796547174453735 +0.81424760818481445 +0.66126549243927002 +0.65917783975601196 +0.65309822559356689 +0.9810415506362915 +0.97608786821365356 +0.99400544166564941 +0.99698054790496826 +0.96959376335144043 +0.9894568920135498 +0.99303197860717773 +0.99699753522872925 +0.99905931949615479 +0.96274381875991821 +0.98380160331726074 +0.95669472217559814 +0.9782707691192627 +0.98046314716339111 +0.9868929386138916 +0.97516131401062012 +0.98528140783309937 +0.96494150161743164 +0.99209731817245483 +0.99583220481872559 +0.95254528522491455 +0.97414505481719971 +0.95107543468475342 +0.97223639488220215 +0.97351104021072388 +0.9757654070854187 +0.95290178060531616 +0.97316884994506836 +0.95797735452651978 +0.97690701484680176 +0.97752213478088379 +0.97411495447158813 +0.95647037029266357 +0.95505857467651367 +0.96184796094894409 +0.95797300338745117 +0.96581363677978516 +0.9830361008644104 +0.97393691539764404 +0.98951965570449829 +0.9898267388343811 +0.98336082696914673 +0.97990161180496216 +0.99434220790863037 +0.98341542482376099 +0.99723362922668457 +0.99848490953445435 +0.9950789213180542 +0.98873263597488403 +0.98020100593566895 +0.99466657638549805 +0.97048217058181763 +0.98465621471405029 +0.99832576513290405 +0.98447060585021973 +0.99841982126235962 +0.9999614953994751 +0.99980688095092773 +0.98355567455291748 +0.99819982051849365 +0.99984371662139893 +0.99730831384658813 +0.99751609563827515 +0.99712008237838745 +0.69442051649093628 +0.69048690795898438 +0.78782308101654053 +0.79335469007492065 +0.68612730503082275 +0.78172910213470459 +0.86346620321273804 +0.8706810474395752 +0.87715792655944824 +0.68201631307601929 +0.77578765153884888 +0.67922711372375488 +0.77144753932952881 +0.85079467296600342 +0.85631352663040161 +0.91963177919387817 +0.92709439992904663 +0.91351550817489624 +0.93452340364456177 +0.94077980518341064 +0.6786874532699585 +0.76999795436859131 +0.68005424737930298 +0.77103555202484131 +0.8487275242805481 +0.84833657741546631 +0.68280130624771118 +0.77397614717483521 +0.68657487630844116 +0.77884107828140259 +0.85723716020584106 +0.85159486532211304 +0.91192877292633057 +0.90942591428756714 +0.91768777370452881 +0.91000825166702271 +0.69096565246582031 +0.7854994535446167 +0.69506156444549561 +0.79229927062988281 +0.87455451488494873 +0.86567139625549316 +0.69799327850341797 +0.79754048585891724 +0.69965171813964844 +0.80075627565383911 +0.88566190004348755 +0.88144439458847046 +0.94263571500778198 +0.9356803297996521 +0.94681000709533691 +0.92647749185562134 +0.70006144046783447 +0.80174553394317627 +0.69928568601608276 +0.80066871643066406 +0.88564324378967285 +0.88693273067474365 +0.69739061594009399 +0.79776179790496826 +0.88226884603500366 +0.94495850801467896 +0.9473157525062561 +0.94814568758010864 +0.25711563229560852 +0.25971692800521851 +0.36308702826499939 +0.36203908920288086 +0.26370760798454285 +0.36501103639602661 +0.4724108874797821 +0.47256046533584595 +0.47311067581176758 +0.26842081546783447 +0.36743062734603882 +0.27261358499526978 +0.36963129043579102 +0.47326484322547913 +0.47266647219657898 +0.57926630973815918 +0.58123773336410522 +0.57817894220352173 +0.58354008197784424 +0.58576822280883789 +0.27516952157020569 +0.37100863456726074 +0.27630695700645447 +0.37195611000061035 +0.47543418407440186 +0.47415655851364136 +0.27640116214752197 +0.37289971113204956 +0.27543294429779053 +0.37346532940864563 +0.47878673672676086 +0.4771314263343811 +0.58220797777175903 +0.57989227771759033 +0.58490484952926636 +0.57844549417495728 +0.27335357666015625 +0.37320202589035034 +0.27033254504203796 +0.37199854850769043 +0.4801909327507019 +0.47989565134048462 +0.26663064956665039 +0.36988276243209839 +0.26284030079841614 +0.36737558245658875 +0.47824716567993164 +0.47952684760093689 +0.59038549661636353 +0.58942580223083496 +0.59050118923187256 +0.5874706506729126 +0.25957959890365601 +0.36504027247428894 +0.25728970766067505 +0.36319887638092041 +0.47528234124183655 +0.47675126791000366 +0.25636699795722961 +0.36212483048439026 +0.47403919696807861 +0.5875929594039917 +0.58899694681167603 +0.5899965763092041 +0.012022273615002632 +0.01498226635158062 +0.043294068425893784 +0.039034292101860046 +0.019109625369310379 +0.049366183578968048 +0.10008487850427628 +0.093098171055316925 +0.088299266993999481 +0.023729689419269562 +0.056429196149110794 +0.0288880355656147 +0.063906975090503693 +0.11684734374284744 +0.10842246562242508 +0.18045313656330109 +0.17330262064933777 +0.18727408349514008 +0.16729545593261719 +0.1632331907749176 +0.034453991800546646 +0.071016803383827209 +0.038519073277711868 +0.075491286814212799 +0.12777362763881683 +0.12394017726182938 +0.039163120090961456 +0.075256682932376862 +0.036173753440380096 +0.070886231958866119 +0.12210247665643692 +0.12678457796573639 +0.19394291937351227 +0.19468130171298981 +0.19079643487930298 +0.19228988885879517 +0.029950721189379692 +0.063557885587215424 +0.022858774289488792 +0.05543135479092598 +0.10778040438890457 +0.11528643220663071 +0.017258718609809875 +0.04854872077703476 +0.013522611930966377 +0.043264962732791901 +0.094865292310714722 +0.10085593909025192 +0.17510353028774261 +0.18068753182888031 +0.16986903548240662 +0.18613837659358978 +0.011598210781812668 +0.039638187736272812 +0.010876227170228958 +0.037632625550031662 +0.08701864629983902 +0.090061187744140625 +0.010833658277988434 +0.037272341549396515 +0.086322993040084839 +0.161688432097435 +0.16253188252449036 +0.16546429693698883 +0.00010301536531187594 +3.0446892196778208e-05 +0.00038935017073526978 +0.0015202309004962444 +0.00069631880614906549 +0.00020667295029852539 +0.0010081962682306767 +0.001681107678450644 +0.0040116049349308014 +0.0026684883050620556 +0.0080389734357595444 +0.005511898547410965 +0.010977452620863914 +0.0033013341017067432 +0.0017449188744649291 +0.0021245020907372236 +0.0024211725685745478 +0.0064570726826786995 +0.0053773978725075722 +0.0026561429258435965 +0.0025614493060857058 +0.0064233429729938507 +0.0069182151928544044 +0.017697442322969437 +0.016940830275416374 +0.016146119683980942 +0.014312740415334702 +0.001911085331812501 +0.001083789044059813 +0.0028117797337472439 +0.0047940779477357864 +0.00049333990318700671 +0.00015902721497695893 +0.00038809614488855004 +0.0013012392446398735 +0.0042307008989155293 +0.0077103618532419205 +0.0020904596894979477 +0.012256835587322712 +2.7725738618755713e-05 +6.3143197621684521e-06 +2.0368797777337022e-06 +4.2623494664439932e-05 +8.6491791080334224e-06 +2.2475196601590142e-05 +0.0011378158815205097 +0.0010878263274207711 +0.0012340223183855414 +0.010850145481526852 +0.0022048312239348888 +0.00081319909077137709 +0.0031959451735019684 +0.0076179457828402519 +0.025897277519106865 +0.09803493320941925 +0.073100760579109192 +0.060295898467302322 +0.013740242458879948 +0.042928833514451981 +0.1250988245010376 +0.31683215498924255 +0.29926878213882446 +0.28477036952972412 +0.28533279895782471 +0.021782159805297852 +0.060271255671977997 +0.14989390969276428 +0.0325213223695755 +0.078994832932949066 +0.17362278699874878 +0.34619683027267456 +0.33206984400749207 +0.57663643360137939 +0.58408772945404053 +0.59132617712020874 +0.57041734457015991 +0.7638934850692749 +0.78184866905212402 +0.80220133066177368 +0.82242381572723389 +0.60243523120880127 +0.62702643871307373 +0.84442532062530518 +0.87455219030380249 +0.045002151280641556 +0.098184607923030853 +0.19640383124351501 +0.056535873562097549 +0.11494040489196777 +0.21727105975151062 +0.38314738869667053 +0.36186182498931885 +0.063602052628993988 +0.12384467571973801 +0.22929263114929199 +0.06184861809015274 +0.11696245521306992 +0.21957086026668549 +0.40713554620742798 +0.40378117561340332 +0.61892116069793701 +0.58847510814666748 +0.57107925415039062 +0.64519703388214111 +0.82737481594085693 +0.79409956932067871 +0.76544588804244995 +0.75489544868469238 +0.050633646547794342 +0.093501552939414978 +0.18675483763217926 +0.033025398850440979 +0.059812508523464203 +0.14104041457176208 +0.3618151843547821 +0.38984689116477966 +0.014547517523169518 +0.025897625833749771 +0.096151411533355713 +0.0031790481880307198 +0.0054109543561935425 +0.06933942437171936 +0.31674620509147644 +0.33366513252258301 +0.68153679370880127 +0.67283231019973755 +0.6613774299621582 +0.68607127666473389 +0.93241286277770996 +0.91760599613189697 +0.8903767466545105 +0.85909247398376465 +0.00019021646585315466 +0.00014899366942699999 +0.062027681618928909 +0.00014244935300666839 +5.0679947889875621e-05 +0.059810981154441833 +0.29898834228515625 +0.30980557203292847 +0.66001683473587036 +0.68162912130355835 +0.90819960832595825 +0.92981189489364624 +0.0002405467675998807 +0.0002404639235464856 +6.679337820969522e-05 +6.7207452957518399e-05 +4.3764775909949094e-05 +0.00016735131794121116 +0.00060320424381643534 +0.00054859643569216132 +0.00046688600559718907 +5.4626962082693353e-05 +0.00025886428193189204 +0.0011274173157289624 +0.0035806854721158743 +0.001743150525726378 +0.00094800168881192803 +0.00053944980027154088 +0.00015096765127964318 +0.00067007687175646424 +0.0024471532087773085 +0.00037697251536883414 +0.0014729846734553576 +0.0047175409272313118 +0.012379924766719341 +0.0069911666214466095 +0.00077428924851119518 +0.0026861981023102999 +0.007812021765857935 +0.0013820274034515023 +0.004274941049516201 +0.011322351172566414 +0.026096979156136513 +0.019228968769311905 +0.0019819883164018393 +0.0056925313547253609 +0.014103353954851627 +0.0020986646413803101 +0.005880377721041441 +0.014272919856011868 +0.030770659446716309 +0.030871845781803131 +0.0016655247891321778 +0.0047035887837409973 +0.011585254222154617 +0.0010250131599605083 +0.0029509777668863535 +0.0074308658950030804 +0.016589326784014702 +0.025366678833961487 +0.00044078062637709081 +0.0012981008039787412 +0.0032989270985126495 +9.7685384389478713e-05 +0.00030726389377377927 +0.00077726278686895967 +0.0016818279400467873 +0.0073779518716037273 +1.2624898772628512e-05 +5.3821728215552866e-05 +0.00011806917609646916 +3.4536220482550561e-05 +0.00012617712491191924 +0.00023791387502569705 +0.00024443562142550945 +0.00017262969049625099 +6.0529409893206321e-06 +6.240085895115044e-06 +6.5965787143795751e-06 +8.8622282419237308e-06 +7.2445473051629961e-05 +2.0332121493993327e-05 +6.4691839725128375e-06 +6.1358095990726724e-06 +5.9563171816989779e-06 +0.00031679609674029052 +8.0140918726101518e-05 +1.2211583452881314e-05 +5.2477821554930415e-06 +8.6949266915326007e-06 +1.3544612556870561e-05 +1.2846231584262569e-05 +0.0007456786697730422 +0.0001880715717561543 +2.7116604542243294e-05 +0.0012471723603084683 +0.00031824523466639221 +5.3436298912856728e-05 +6.0218677390366793e-05 +1.6124811736517586e-05 +0.0016331198858097196 +0.00041791016701608896 +8.6911335529293865e-05 +0.001639125868678093 +0.00040744178113527596 +0.00011677103611873463 +0.00034048233646899462 +0.00015979299496393651 +0.0012918335851281881 +0.00029790401458740234 +0.00013240751286502928 +0.00090892281150445342 +0.00019104941748082638 +0.00012337569205556065 +0.00058003788581117988 +0.00053437997121363878 +0.00062297045951709151 +0.00012544267519842833 +9.2262162070255727e-05 +0.00038145107100717723 +7.646039011888206e-05 +5.468513336381875e-05 +0.00027200885233469307 +0.0004542413807939738 +0.00017918446974363178 +3.609703344409354e-05 +2.2808328139944933e-05 +5.8335946960141882e-05 +1.2965072528459132e-05 +5.340963980415836e-06 +2.2808317226008512e-05 +0.00011325850937282667 +1.6740519640734419e-05 +6.2953868109616451e-06 +2.1161938548175385e-06 +7.9030596680240706e-06 +5.9373032854637131e-06 +4.0911222640716005e-06 +6.5631752477202099e-06 +1.5728292055428028e-06 +0.01012277789413929 +0.0072704367339611053 +0.033927410840988159 +0.038490027189254761 +0.045078027993440628 +0.01486713532358408 +0.003750665346160531 +0.0015220077475532889 +0.00030487889307551086 +0.052338216453790665 +0.020327106118202209 +0.006579776294529438 +0.0018004276789724827 +0.00085909001063555479 +0.00031344703165814281 +6.5075830207206309e-05 +0.059006586670875549 +0.025538766756653786 +0.0095672477036714554 +0.063908331096172333 +0.029750654473900795 +0.012239974923431873 +0.0043472419492900372 +0.0030525554902851582 +0.066234089434146881 +0.032167855650186539 +0.013963902369141579 +0.065540015697479248 +0.031951993703842163 +0.013944023288786411 +0.0053008468821644783 +0.0052781524136662483 +0.061638321727514267 +0.02892833948135376 +0.012122001498937607 +0.054597306996583939 +0.023585807532072067 +0.0091761648654937744 +0.003278347896412015 +0.004437791183590889 +0.045870747417211533 +0.017182851210236549 +0.0058773485943675041 +0.038297869265079498 +0.011747440323233604 +0.003088897792622447 +0.0012280441587790847 +0.0021719392389059067 +0.0332990363240242 +0.0082312272861599922 +0.0012325737625360489 +0.030875694006681442 +0.0065095266327261925 +0.00028855484561063349 +0.00014923200069461018 +0.00053284625755622983 +0.030597984790802002 +0.0061321621760725975 +2.6542305931798182e-05 +0.031604744493961334 +0.0063234623521566391 +5.7609431678429246e-06 +7.8621878856210969e-06 +2.8000351449009031e-05 +0.25679844617843628 +0.25251859426498413 +0.35774612426757812 +0.36049038171768188 +0.36377471685409546 +0.26190364360809326 +0.17190335690975189 +0.16538909077644348 +0.15981729328632355 +0.36707821488380432 +0.26722213625907898 +0.17876096069812775 +0.1059940978884697 +0.098381303250789642 +0.091271623969078064 +0.085569262504577637 +0.36970207095146179 +0.27175167202949524 +0.18481074273586273 +0.37076964974403381 +0.27409976720809937 +0.18834961950778961 +0.11735579371452332 +0.11285452544689178 +0.37005984783172607 +0.27389630675315857 +0.18885330855846405 +0.36800742149353027 +0.27179363369941711 +0.18697646260261536 +0.11745519936084747 +0.11881140619516373 +0.36505597829818726 +0.26817435026168823 +0.18288707733154297 +0.36165827512741089 +0.26315158605575562 +0.17626672983169556 +0.10579684376716614 +0.1132318377494812 +0.35838547348976135 +0.25749468803405762 +0.16813807189464569 +0.35592737793922424 +0.25262957811355591 +0.16086500883102417 +0.088117145001888275 +0.096446357667446136 +0.35456010699272156 +0.24933180212974548 +0.15580639243125916 +0.35414597392082214 +0.24772664904594421 +0.15331609547138214 +0.079654261469841003 +0.082422904670238495 +0.35457935929298401 +0.24781274795532227 +0.15339407324790955 +0.35578638315200806 +0.24946205317974091 +0.1556863933801651 +0.081704035401344299 +0.079631403088569641 +0.69063413143157959 +0.69170331954956055 +0.79242157936096191 +0.79015088081359863 +0.78587871789932251 +0.68821364641189575 +0.58223897218704224 +0.58281910419464111 +0.5826835036277771 +0.7802436351776123 +0.68489938974380493 +0.58110338449478149 +0.47349491715431213 +0.47241699695587158 +0.47108244895935059 +0.46969515085220337 +0.77432131767272949 +0.68146014213562012 +0.57970333099365234 +0.76962494850158691 +0.67897564172744751 +0.57846081256866455 +0.47391039133071899 +0.47408133745193481 +0.76693522930145264 +0.67768126726150513 +0.57740622758865356 +0.76630055904388428 +0.6769677996635437 +0.57617813348770142 +0.47114858031272888 +0.47289931774139404 +0.76754403114318848 +0.67671054601669312 +0.574867844581604 +0.77026379108428955 +0.6772695779800415 +0.57401895523071289 +0.46692454814910889 +0.46897751092910767 +0.77405333518981934 +0.67880314588546753 +0.57397913932800293 +0.77850204706192017 +0.68126797676086426 +0.57490032911300659 +0.46481731534004211 +0.4654262363910675 +0.7831273078918457 +0.68432015180587769 +0.57659858465194702 +0.78737473487854004 +0.68731480836868286 +0.57855439186096191 +0.46593481302261353 +0.46509814262390137 +0.790688157081604 +0.68976163864135742 +0.58038073778152466 +0.79250919818878174 +0.69132441282272339 +0.58182322978973389 +0.46835890412330627 +0.46707707643508911 +0.97521680593490601 +0.97729778289794922 +0.99378567934036255 +0.99345576763153076 +0.99017775058746338 +0.97047644853591919 +0.93148535490036011 +0.937358558177948 +0.94051355123519897 +0.9835515022277832 +0.96349316835403442 +0.92356562614440918 +0.86180609464645386 +0.86938291788101196 +0.87510770559310913 +0.87828373908996582 +0.97466540336608887 +0.95528209209442139 +0.91471183300018311 +0.96609699726104736 +0.94745707511901855 +0.90647661685943604 +0.84627264738082886 +0.85355192422866821 +0.95992457866668701 +0.94171285629272461 +0.9006233811378479 +0.95772743225097656 +0.93982541561126709 +0.89912539720535278 +0.84043902158737183 +0.84145891666412354 +0.96007925271987915 +0.94236671924591064 +0.9022672176361084 +0.9665483832359314 +0.94870537519454956 +0.90864431858062744 +0.84835135936737061 +0.84319281578063965 +0.97551774978637695 +0.95722317695617676 +0.91663473844528198 +0.98418545722961426 +0.96531522274017334 +0.92439979314804077 +0.86128491163253784 +0.85472136735916138 +0.99044215679168701 +0.97141188383102417 +0.93090420961380005 +0.99287146329879761 +0.9749792218208313 +0.93591731786727905 +0.87267082929611206 +0.86737412214279175 +0.99246454238891602 +0.97645115852355957 +0.93928700685501099 +0.99262034893035889 +0.97722995281219482 +0.94093960523605347 +0.87870889902114868 +0.87667572498321533 +0.93881171941757202 +0.95197218656539917 +0.96655380725860596 +0.92323309183120728 +0.9759676456451416 +0.98477375507354736 +0.98920929431915283 +0.99114346504211426 +0.90528023242950439 +0.88888019323348999 +0.95227789878845215 +0.96382403373718262 +0.87912964820861816 +0.88229483366012573 +0.94330060482025146 +0.9445807933807373 +0.89801061153411865 +0.91927993297576904 +0.95851588249206543 +0.94865584373474121 +0.94206452369689941 +0.96528404951095581 +0.98294007778167725 +0.97069257497787476 +0.98507422208786011 +0.99478644132614136 +0.9961249828338623 +0.99250471591949463 +0.9926680326461792 +0.98186188936233521 +0.99235093593597412 +0.99451214075088501 +0.006124905776232481 +0.0054511744529008865 +0.017849573865532875 +0.029224488884210587 +0.014727689325809479 +0.034441091120243073 +0.050531450659036636 +0.084676653146743774 +0.1119256317615509 +0.17781247198581696 +0.064557187259197235 +0.15433177351951599 +0.025248236954212189 +0.051494292914867401 +0.073854774236679077 +0.037223756313323975 +0.06939350813627243 +0.096102148294448853 +0.13770477473735809 +0.16181644797325134 +0.22682397067546844 +0.20373369753360748 +0.30841708183288574 +0.32466575503349304 +0.4488111138343811 +0.33919626474380493 +0.45291340351104736 +0.58029836416244507 +0.57328259944915771 +0.69052088260650635 +0.58782386779785156 +0.70707869529724121 +0.28241920471191406 +0.29088670015335083 +0.4417077898979187 +0.44343101978302002 +0.59579610824584961 +0.72398138046264648 +0.61244481801986694 +0.74424397945404053 +0.051600746810436249 +0.088712699711322784 +0.11842751502990723 +0.067351400852203369 +0.10704801231622696 +0.14027699828147888 +0.18519218266010284 +0.20713703334331512 +0.2693403959274292 +0.2483045756816864 +0.081473596394062042 +0.12086991965770721 +0.15938873589038849 +0.089562393724918365 +0.12287279963493347 +0.16942830383777618 +0.22519366443157196 +0.22795577347278595 +0.30641713738441467 +0.29103979468345642 +0.37154689431190491 +0.39458504319190979 +0.48511788249015808 +0.4086574912071228 +0.51130807399749756 +0.60314315557479858 +0.63365674018859863 +0.71654391288757324 +0.57706964015960693 +0.68474316596984863 +0.35358190536499023 +0.457000732421875 +0.46532315015792847 +0.56927233934402466 +0.67048507928848267 +0.6761479377746582 +0.085810065269470215 +0.1069074273109436 +0.15998847782611847 +0.069485262036323547 +0.077538639307022095 +0.13011418282985687 +0.20541238784790039 +0.1648726612329483 +0.27085515856742859 +0.30039229989051819 +0.044932987540960312 +0.042029920965433121 +0.088302232325077057 +0.019631881266832352 +0.013122384436428547 +0.0468892902135849 +0.11741450428962708 +0.079407565295696259 +0.18722851574420929 +0.2286653071641922 +0.37655514478683472 +0.34704560041427612 +0.51752567291259766 +0.32309243083000183 +0.50769126415252686 +0.67761707305908203 +0.68441402912139893 +0.82743167877197266 +0.66735988855361938 +0.804770827293396 +0.4004528820514679 +0.52650302648544312 +0.52594822645187378 +0.65421360731124878 +0.77835279703140259 +0.74992012977600098 +0.0041942880488932133 +0.0015057018026709557 +0.02205176092684269 +0.00017832100274972618 +8.3197337517049164e-05 +0.015599644742906094 +0.064146347343921661 +0.061182774603366852 +0.15493053197860718 +0.1624966561794281 +8.7867229012772441e-05 +0.00058127881493419409 +0.014976511709392071 +0.0013119862414896488 +0.017272535711526871 +0.059114594012498856 +0.14505571126937866 +0.14949971437454224 +0.30574744939804077 +0.2915196418762207 +0.47893974184989929 +0.45477408170700073 +0.64370787143707275 +0.77573680877685547 +0.673481285572052 +0.81288248300552368 +0.31289452314376831 +0.50147527456283569 +0.49569201469421387 +0.68548434972763062 +0.83665597438812256 +0.83992135524749756 +0.00013432864216156304 +0.00026453929604031146 +0.00019823464390356094 +0.00038044241955503821 +9.1013920609839261e-05 +0.00017764508083928376 +0.00032175530213862658 +0.00054824393009766936 +0.00077556591713801026 +0.0010606766445562243 +0.00054217322031036019 +0.0007395892171189189 +0.00013123116514179856 +0.00042000843677669764 +0.00055257632629945874 +0.00034758701804094017 +0.0010180692188441753 +0.0013070586137473583 +0.0016781493322923779 +0.0034538202453404665 +0.0042789825238287449 +0.0020984369330108166 +0.0024906329344958067 +0.0050639742985367775 +0.0069895060732960701 +0.0094129275530576706 +0.012265095487236977 +0.017472647130489349 +0.026765987277030945 +0.010487742722034454 +0.00073638797039166093 +0.0012562647461891174 +0.0016185487620532513 +0.0036093373782932758 +0.0051732235588133335 +0.0017284800997003913 +0.00079824094427749515 +0.0020304766949266195 +0.0026848646812140942 +0.0015208295080810785 +0.0034358017146587372 +0.0046550449915230274 +0.0061872843652963638 +0.0095407208427786827 +0.012541779316961765 +0.0078546740114688873 +0.0025324674788862467 +0.0050712917000055313 +0.0070560392923653126 +0.003478311700746417 +0.0060063344426453114 +0.0090818209573626518 +0.012921650893986225 +0.014632197096943855 +0.021214304491877556 +0.017520468682050705 +0.02275068499147892 +0.028919987380504608 +0.038589559495449066 +0.031604688614606857 +0.044519815593957901 +0.060899410396814346 +0.064099788665771484 +0.051055446267127991 +0.015711992979049683 +0.020024929195642471 +0.029505215585231781 +0.038712076842784882 +0.0036315650213509798 +0.0054132617078721523 +0.0092818969860672951 +0.0028951170388609171 +0.0038499606307595968 +0.0074729616753757 +0.013199332170188427 +0.0096044940873980522 +0.01742248423397541 +0.021290242671966553 +0.0018015453824773431 +0.0020769282709807158 +0.0047388914972543716 +0.00078492052853107452 +0.00068478315370157361 +0.0020971575286239386 +0.0052628517150878906 +0.0017375715542584658 +0.0050202463753521442 +0.01129248458892107 +0.02127908356487751 +0.011791062541306019 +0.02370557002723217 +0.0038436539471149445 +0.010508292354643345 +0.02339646965265274 +0.0074759172275662422 +0.042436908930540085 +0.028610927984118462 +0.043896127492189407 +0.036182254552841187 +0.057232052087783813 +0.00018080623704008758 +0.00011409330181777477 +0.00049882667372003198 +2.829588447639253e-05 +7.499953790102154e-05 +8.5110223153606057e-05 +0.00028257168014533818 +0.00014832630404271185 +0.00014860638475511223 +0.0011643404141068459 +7.0012763899285346e-05 +0.0001878375478554517 +0.0001873654400696978 +0.00013412159751169384 +0.00035999412648379803 +0.00035478331847116351 +0.00053531327284872532 +0.00026215851539745927 +0.00017377721087541431 +0.00037089607212692499 +0.00019958565826527774 +0.00059388269437476993 +0.00032591712079010904 +0.00013861076149623841 +0.00056673318613320589 +0.0023474788758903742 +0.00018740951782092452 +0.00097708823159337044 +7.3731766860873904e-06 +6.2796789279673249e-06 +9.4427841759170406e-06 +5.455744485516334e-06 +4.0222384996013716e-05 +4.2603860492818058e-05 +1.0004458999901544e-05 +5.9548146964516491e-06 +8.3358090705587529e-06 +6.9560787778755184e-06 +6.3552743085892871e-06 +8.6473573901457712e-06 +0.00017064277199096978 +0.00012970845273230225 +3.182054206263274e-05 +0.00040187552804127336 +0.00025199545780196786 +7.3430644988548011e-05 +1.8378015738562681e-05 +3.8708865758962929e-05 +1.8293365428689867e-05 +7.8440098150167614e-06 +6.3616248553444166e-06 +7.714984349149745e-06 +1.6737300029490143e-05 +3.2838848710525781e-05 +5.0414921133778989e-05 +8.9350891357753426e-05 +0.00024500046856701374 +4.1272662201663479e-05 +1.4323344657896087e-05 +1.1378865565347951e-05 +3.0005376174813136e-05 +1.8644801457412541e-05 +5.4833108151797205e-05 +7.3617615271359682e-05 +0.00067516113631427288 +0.00037686785799451172 +0.00012735892960336059 +0.00088496797252446413 +0.00043141920468769968 +0.00017190507787745446 +6.9952562625985593e-05 +0.00010296627442585304 +0.00010288772318745032 +4.7411493142135441e-05 +0.00087899278150871396 +0.00035869702696800232 +0.00017114421643782407 +0.00067500438308343291 +0.00023778177273925394 +0.00012791187327820808 +0.0001270200009457767 +0.00013162796676624566 +0.00027877039974555373 +0.00019099337805528194 +0.00023839612549636513 +0.00044587059528566897 +0.00069442967651411891 +0.00058582937344908714 +0.0010513984598219395 +0.0017126991879194975 +0.002130456268787384 +0.0010494112502783537 +0.00010146862041437998 +0.00015051872469484806 +0.00035210189525969326 +0.00055177247850224376 +0.0004608446906786412 +0.00015462905867025256 +8.3804094174411148e-05 +0.0003118389577139169 +0.00010041244968306273 +5.5133237765403464e-05 +0.00010960299550788477 +7.3305440309923142e-05 +0.00022818101570010185 +0.00029403128428384662 +0.00019079651974607259 +5.4663061746396124e-05 +3.2928106520557776e-05 +8.9787172328215092e-05 +2.1838850443600677e-05 +1.4856579582556151e-05 +3.7489873648155481e-05 +1.1729141988325864e-05 +5.6694796512601897e-05 +0.00013621688412968069 +0.0003629909479059279 +0.00018689727585297078 +0.00054172088857740164 +5.6694771046750247e-05 +0.00022909019025973976 +0.0007143083494156599 +0.00022753722441848367 +0.0013497876934707165 +0.00053138280054554343 +0.0011315356241539121 +0.00089262780966237187 +0.0019294191151857376 +3.014120738953352e-05 +8.2068618212360889e-06 +5.2256286835472565e-06 +1.0570328413450625e-05 +5.96181234868709e-06 +3.5869302337232511e-06 +2.5134356747003039e-06 +3.0188768960215384e-06 +1.5544147800028441e-06 +1.172916563518811e-05 +7.1676504376227967e-06 +6.0780112107750028e-06 +4.736849859909853e-06 +6.853051672806032e-06 +5.6453013712598477e-06 +5.1359347708057612e-06 +8.0607542258803733e-06 +4.5249535105540417e-06 +3.4765769214573083e-06 +9.9729513749480247e-06 +1.49766447066213e-05 +2.9396018362604082e-05 +5.2008377679158002e-05 +1.9650522517622449e-05 +6.2255403463495895e-06 +4.8239900934277102e-05 +4.490102128329454e-06 +3.2342228223569691e-05 +0.021062202751636505 +0.0083865812048316002 +0.012331981211900711 +0.0041249752976000309 +0.026906434446573257 +0.017581194639205933 +0.007601443212479353 +0.0025355876423418522 +0.0051158564165234566 +0.0019561115186661482 +0.00076130998786538839 +0.00076709286076948047 +0.033468704670667648 +0.023010943084955215 +0.011751458048820496 +0.039583172649145126 +0.027816649526357651 +0.015868436545133591 +0.0080832857638597488 +0.010972523130476475 +0.0056302649900317192 +0.0036500908900052309 +0.0012741454411298037 +0.0024044071324169636 +0.00077469355892390013 +0.00371134327724576 +0.0015267991693690419 +0.00051516591338440776 +0.0009943441255018115 +0.00016455925651825964 +0.00015825389709789306 +0.0005429581506177783 +8.5795159975532442e-05 +0.0002883013803511858 +2.5523542717564851e-05 +7.530315087933559e-06 +0.044309303164482117 +0.031235991045832634 +0.019353756681084633 +0.046821683645248413 +0.032441455870866776 +0.021479597315192223 +0.013270226307213306 +0.01422162726521492 +0.0088277561590075493 +0.007530679926276207 +0.04641004279255867 +0.030760582536458969 +0.021388957276940346 +0.042871534824371338 +0.026516376063227654 +0.018961058929562569 +0.013215886428952217 +0.010747164487838745 +0.007563476450741291 +0.0088402898982167244 +0.0054370532743632793 +0.0049432036466896534 +0.0030067681800574064 +0.0038582826964557171 +0.0024466696195304394 +0.0014897871296852827 +0.001084975665435195 +0.0017001400701701641 +0.0048921965062618256 +0.0023619693238288164 +0.002988726831972599 +0.0014711139956489205 +0.03651069849729538 +0.020390134304761887 +0.014811381697654724 +0.028742603957653046 +0.014217452146112919 +0.0099686942994594574 +0.0075169196352362633 +0.0043653682805597782 +0.0036860262043774128 +0.0056689046323299408 +0.022092780098319054 +0.0097572263330221176 +0.0058750705793499947 +0.017770847305655479 +0.007153855636715889 +0.0032088952139019966 +0.0020454586483538151 +0.0006477650604210794 +0.00083098362665623426 +0.0020021474920213223 +0.0016728349728509784 +0.00084542774129658937 +0.0007075062021613121 +0.00029816076857969165 +0.00032122537959367037 +0.00027277588378638029 +0.00010594761988613755 +0.00049993948778137565 +0.0027116716373711824 +0.0017698887968435884 +0.0011969620827585459 +0.00075710960663855076 +0.015670489519834518 +0.0062012821435928345 +0.0018848632462322712 +0.015304228290915489 +0.0062052085995674133 +0.0015501471934840083 +9.9846212833654135e-05 +1.3546190530178137e-05 +2.9706849090871401e-05 +0.00021265874966047704 +0.015803419053554535 +0.0066306339576840401 +0.0015844000736251473 +0.017327141016721725 +0.0021365846041589975 +7.7950520790182054e-05 +0.00015461182920262218 +7.0724718170822598e-06 +1.5375151633634232e-05 +1.9687175154103898e-05 +8.1368125393055379e-06 +2.0870797015959397e-05 +6.5558751884964295e-06 +1.113298912969185e-05 +6.5898922912310809e-05 +9.7911979537457228e-05 +2.3114382202038541e-05 +3.0537536076735705e-05 +0.30742216110229492 +0.2545170783996582 +0.25928613543510437 +0.20924535393714905 +0.31164330244064331 +0.26457449793815613 +0.21513168513774872 +0.16856585443019867 +0.17532670497894287 +0.1327945739030838 +0.16244792938232422 +0.12585577368736267 +0.31599453091621399 +0.26967254281044006 +0.2212984710931778 +0.31959915161132812 +0.27328541874885559 +0.22665655612945557 +0.1819932609796524 +0.18700048327445984 +0.14671109616756439 +0.14014701545238495 +0.10218131542205811 +0.10961879044771194 +0.076618917286396027 +0.11550047993659973 +0.083525031805038452 +0.055819801986217499 +0.061751715838909149 +0.048708673566579819 +0.088188029825687408 +0.09470706433057785 +0.062021441757678986 +0.069022715091705322 +0.041615430265665054 +0.035870973020792007 +0.32130950689315796 +0.27427619695663452 +0.22961995005607605 +0.32083055377006531 +0.27304160594940186 +0.22974218428134918 +0.18893992900848389 +0.18817153573036194 +0.15171469748020172 +0.15076838433742523 +0.31871965527534485 +0.27016651630401611 +0.22771675884723663 +0.31541824340820312 +0.2658313512802124 +0.22382909059524536 +0.18522825837135315 +0.17991319298744202 +0.14585310220718384 +0.15006013214588165 +0.11847015470266342 +0.11572344601154327 +0.089251786470413208 +0.10993731766939163 +0.08512599766254425 +0.063985854387283325 +0.058505967259407043 +0.066292271018028259 +0.11844970285892487 +0.088329799473285675 +0.09027426689863205 +0.065420940518379211 +0.31125175952911377 +0.26031330227851868 +0.21795685589313507 +0.30688148736953735 +0.25487405061721802 +0.21099144220352173 +0.17224320769309998 +0.16424685716629028 +0.12970094382762909 +0.1386803537607193 +0.30330455303192139 +0.25077691674232483 +0.20483788847923279 +0.30101051926612854 +0.24830982089042664 +0.20057877898216248 +0.15803676843643188 +0.15421807765960693 +0.11606947332620621 +0.12164627015590668 +0.091970548033714294 +0.084922492504119873 +0.060320168733596802 +0.080654710531234741 +0.054841235280036926 +0.035476602613925934 +0.03176533430814743 +0.041762642562389374 +0.10117749124765396 +0.077730923891067505 +0.068470820784568787 +0.050267267972230911 +0.29998147487640381 +0.24756628274917603 +0.19847401976585388 +0.30017182230949402 +0.24845001101493835 +0.19854065775871277 +0.15305615961551666 +0.15428555011749268 +0.11341147869825363 +0.11334533244371414 +0.30150839686393738 +0.2508259117603302 +0.20055285096168518 +0.30393701791763306 +0.20423170924186707 +0.15754687786102295 +0.1200161799788475 +0.11576826870441437 +0.080446429550647736 +0.083409793674945831 +0.053610574454069138 +0.056773900985717773 +0.032543838024139404 +0.030994631350040436 +0.079340249300003052 +0.052183996886014938 +0.052033640444278717 +0.030522456392645836 +0.74170327186584473 +0.69136625528335571 +0.68956422805786133 +0.63749039173126221 +0.73836380243301392 +0.68663966655731201 +0.63598614931106567 +0.58260852098464966 +0.58173054456710815 +0.5275300145149231 +0.58285081386566162 +0.52716743946075439 +0.73391801118850708 +0.68312788009643555 +0.63376784324645996 +0.7293170690536499 +0.68003106117248535 +0.63137924671173096 +0.58041000366210938 +0.57903605699539185 +0.5270611047744751 +0.52748572826385498 +0.47300076484680176 +0.47386619448661804 +0.41980201005935669 +0.47410714626312256 +0.42141219973564148 +0.36852988600730896 +0.37048518657684326 +0.36545664072036743 +0.47038266062736511 +0.4717690646648407 +0.41528365015983582 +0.4175989031791687 +0.36209762096405029 +0.35901820659637451 +0.72589778900146484 +0.67821770906448364 +0.62958705425262451 +0.72411024570465088 +0.67729002237319946 +0.62849700450897217 +0.57793259620666504 +0.57683646678924561 +0.52525734901428223 +0.52632570266723633 +0.72351729869842529 +0.67675966024398804 +0.62755393981933594 +0.7239384651184082 +0.67686545848846436 +0.62673020362854004 +0.57549929618835449 +0.5743517279624939 +0.52199357748031616 +0.52374225854873657 +0.47211018204689026 +0.47008198499679565 +0.41902706027030945 +0.46790263056755066 +0.41645371913909912 +0.36661580204963684 +0.36338463425636292 +0.3691743016242981 +0.47350358963012695 +0.42184746265411377 +0.42095816135406494 +0.37060973048210144 +0.72544944286346436 +0.67791718244552612 +0.62652552127838135 +0.72799026966094971 +0.67992180585861206 +0.62722253799438477 +0.57388848066329956 +0.57431012392044067 +0.51980352401733398 +0.52055144309997559 +0.73136532306671143 +0.68276095390319824 +0.62888723611831665 +0.73515892028808594 +0.68586498498916626 +0.6312527060508728 +0.57568478584289551 +0.57757675647735596 +0.52099978923797607 +0.51998454332351685 +0.46498966217041016 +0.46486741304397583 +0.40994825959205627 +0.4654679000377655 +0.40943711996078491 +0.35511595010757446 +0.35424250364303589 +0.35701146721839905 +0.46608513593673706 +0.41375464200973511 +0.41142696142196655 +0.35996314883232117 +0.73873496055603027 +0.68862771987915039 +0.63371962308883667 +0.74156498908996582 +0.6906745433807373 +0.63584476709365845 +0.57949966192245483 +0.5811658501625061 +0.52393603324890137 +0.52242445945739746 +0.74322855472564697 +0.69167840480804443 +0.63734132051467896 +0.74337095022201538 +0.63796031475067139 +0.58233505487442017 +0.52641457319259644 +0.5253143310546875 +0.467710942029953 +0.46902042627334595 +0.41162633895874023 +0.41323557496070862 +0.35667017102241516 +0.35509079694747925 +0.46647816896438599 +0.40965288877487183 +0.41041970252990723 +0.35426133871078491 +0.98649036884307861 +0.9766157865524292 +0.97315305471420288 +0.95903843641281128 +0.98249948024749756 +0.96723920106887817 +0.95363098382949829 +0.93471980094909668 +0.92773938179016113 +0.90356189012527466 +0.93931782245635986 +0.90958058834075928 +0.9758833646774292 +0.95944023132324219 +0.9461357593536377 +0.96765941381454468 +0.95122039318084717 +0.93760800361633301 +0.9191584587097168 +0.91041970252990723 +0.88666820526123047 +0.89553755521774292 +0.86578249931335449 +0.85766041278839111 +0.8231239914894104 +0.84968715906143188 +0.81591796875 +0.77722388505935669 +0.77172529697418213 +0.78319180011749268 +0.87705790996551514 +0.87252026796340942 +0.83495837450027466 +0.82985347509384155 +0.78822511434555054 +0.79157626628875732 +0.95976436138153076 +0.9442138671875 +0.92956459522247314 +0.95397853851318359 +0.94017601013183594 +0.92372769117355347 +0.90312892198562622 +0.89920657873153687 +0.87300026416778564 +0.87858283519744873 +0.95192676782608032 +0.94058191776275635 +0.9220244288444519 +0.95423460006713867 +0.94510090351104736 +0.92490208148956299 +0.90020418167114258 +0.90513920783996582 +0.87488865852355957 +0.87172985076904297 +0.84039163589477539 +0.84142971038818359 +0.80528509616851807 +0.84555703401565552 +0.80735331773757935 +0.76671266555786133 +0.76874446868896484 +0.76635801792144775 +0.84347456693649292 +0.80985164642333984 +0.80607759952545166 +0.76802617311477661 +0.96052825450897217 +0.95285463333129883 +0.9313279390335083 +0.96921765804290771 +0.96148520708084106 +0.93965518474578857 +0.91255289316177368 +0.92066037654876709 +0.8882831335067749 +0.88090270757675171 +0.97749626636505127 +0.96864622831344604 +0.94762265682220459 +0.98351514339447021 +0.97354507446289062 +0.95393931865692139 +0.92782390117645264 +0.93361163139343262 +0.90211355686187744 +0.89562678337097168 +0.85804253816604614 +0.86440867185592651 +0.82202589511871338 +0.87014138698577881 +0.82745552062988281 +0.78082716464996338 +0.78533285856246948 +0.77622091770172119 +0.85144847631454468 +0.81135135889053345 +0.81647086143493652 +0.77205044031143188 +0.98638308048248291 +0.97588944435119629 +0.9582829475402832 +0.98683321475982666 +0.97683954238891602 +0.96078139543533325 +0.93781214952468872 +0.94033259153366089 +0.91143077611923218 +0.9075055718421936 +0.98722255229949951 +0.97744256258010864 +0.96201235055923462 +0.98775696754455566 +0.96176451444625854 +0.94102704524993896 +0.91290122270584106 +0.91338187456130981 +0.87798130512237549 +0.87882184982299805 +0.83804374933242798 +0.83775818347930908 +0.79272115230560303 +0.79182016849517822 +0.87487733364105225 +0.83232355117797852 +0.83607667684555054 +0.78918313980102539 +0.85807645320892334 +0.83280551433563232 +0.91099584102630615 +0.81248676776885986 +0.8921886682510376 +0.94545423984527588 +0.93156462907791138 +0.96782815456390381 +0.95884543657302856 +0.97537112236022949 +0.79188323020935059 +0.87305343151092529 +0.77241337299346924 +0.85273116827011108 +0.91430801153182983 +0.89664065837860107 +0.94160735607147217 +0.95633840560913086 +0.9810338020324707 +0.97006708383560181 +0.98430907726287842 +0.95773029327392578 +0.97411513328552246 +0.97922980785369873 +0.97018033266067505 +0.98730820417404175 +0.9903721809387207 +0.98742097616195679 +0.99479973316192627 +0.99157965183258057 +0.99221032857894897 +0.99396419525146484 +0.75761282444000244 +0.83451670408248901 +0.75706517696380615 +0.82441037893295288 +0.88278204202651978 +0.87870615720748901 +0.91915309429168701 +0.92784643173217773 +0.77835243940353394 +0.83111810684204102 +0.81100213527679443 +0.85383838415145874 +0.88902121782302856 +0.90838837623596191 +0.92886626720428467 +0.91951066255569458 +0.94297808408737183 +0.945281982421875 +0.95544564723968506 +0.95315593481063843 +0.95863986015319824 +0.95836162567138672 +0.96282613277435303 +0.95823031663894653 +0.94778770208358765 +0.96431481838226318 +0.95748162269592285 +0.96261250972747803 +0.84335809946060181 +0.88226115703582764 +0.87471848726272583 +0.91114640235900879 +0.93055027723312378 +0.95368731021881104 +0.95939654111862183 +0.94313132762908936 +0.90502119064331055 +0.94032341241836548 +0.92708516120910645 +0.9656299352645874 +0.97602325677871704 +0.99160563945770264 +0.98950892686843872 +0.97593176364898682 +0.97694778442382812 +0.98826152086257935 +0.98584556579589844 +0.9952617883682251 +0.99312728643417358 +0.98770362138748169 +0.99222373962402344 +0.98006439208984375 +0.96445494890213013 +0.96609950065612793 +0.97603780031204224 +0.97089564800262451 +0.93338847160339355 +0.978912353515625 +0.9214823842048645 +0.97663611173629761 +0.99500960111618042 +0.98815453052520752 +0.993446946144104 +0.99540179967880249 +0.89190810918807983 +0.95988667011260986 +0.9344865083694458 +0.97444379329681396 +0.98176813125610352 +0.98804390430450439 +0.99320733547210693 +0.99173301458358765 +0.99410301446914673 +0.99503183364868164 +0.993233323097229 +0.99225962162017822 +0.99567997455596924 +0.9957001805305481 +0.99460440874099731 +0.99281013011932373 +0.003127706702798605 +0.010069572366774082 +0.039167258888483047 +0.021620471030473709 +0.019865250214934349 +0.062399726361036301 +0.19120326638221741 +0.1650317907333374 +0.14718291163444519 +0.030994966626167297 +0.085040837526321411 +0.044052902609109879 +0.10718297213315964 +0.23768916726112366 +0.21555636823177338 +0.45094302296638489 +0.446359783411026 +0.45487996935844421 +0.68294632434844971 +0.69864171743392944 +0.71560198068618774 +0.44141650199890137 +0.44569641351699829 +0.7332007884979248 +0.75809472799301147 +0.059491999447345734 +0.12955310940742493 +0.07480369508266449 +0.15031644701957703 +0.28003782033920288 +0.25880876183509827 +0.08663514256477356 +0.16619299352169037 +0.089529350399971008 +0.16779376566410065 +0.30702248215675354 +0.30046126246452332 +0.49850049614906311 +0.47333419322967529 +0.52136689424514771 +0.73403310775756836 +0.69944870471954346 +0.67442327737808228 +0.46018019318580627 +0.67152702808380127 +0.078947670757770538 +0.14707459509372711 +0.057965997606515884 +0.11016935110092163 +0.25066307187080383 +0.28788405656814575 +0.031712763011455536 +0.066534854471683502 +0.010016873478889465 +0.031387574970722198 +0.17177748680114746 +0.20685577392578125 +0.51247048377990723 +0.52222782373428345 +0.50381660461425781 +0.83534884452819824 +0.81697177886962891 +0.79163056612014771 +0.527701735496521 +0.7645714282989502 +0.0012271085288375616 +0.017312303185462952 +0.00011091176565969363 +0.015344230458140373 +0.15288758277893066 +0.1575072705745697 +0.00039346588891930878 +0.015338579192757607 +0.14635853469371796 +0.46662652492523193 +0.48939961194992065 +0.79479217529296875 +0.82762986421585083 +0.49924218654632568 +0.84055507183074951 +0.00014760620251763612 +0.00011017064389307052 +0.00033795059425756335 +0.00040315045043826103 +9.2740199761465192e-05 +0.00037793707451783121 +0.0014524813741445541 +0.00084804015932604671 +0.00066034123301506042 +0.00021375680807977915 +0.00085813092300668359 +0.00053999177180230618 +0.0019118172349408269 +0.0058766622096300125 +0.0030300887301564217 +0.0093535715714097023 +0.0050992951728403568 +0.015797680243849754 +0.0024667237885296345 +0.0010119040962308645 +0.0011244737543165684 +0.0036060058046132326 +0.0019894475117325783 +0.0058117881417274475 +0.015045914798974991 +0.010110538452863693 +0.0030590384267270565 +0.0082064876332879066 +0.0036994372494518757 +0.0095007270574569702 +0.021850325167179108 +0.019673807546496391 +0.04216143861413002 +0.034229155629873276 +0.045239709317684174 +0.024682288989424706 +0.003341267816722393 +0.0085608130320906639 +0.0023596857208758593 +0.0061538252048194408 +0.014535174705088139 +0.019744236022233963 +0.0012626420939341187 +0.0033470403868705034 +0.00041032681474462152 +0.0011081253178417683 +0.0026303415652364492 +0.0080142337828874588 +0.016837187111377716 +0.030393805354833603 +0.0054385112598538399 +0.040780007839202881 +6.4186599047388881e-05 +0.00018568182713352144 +4.0961971535580233e-05 +0.0001135315396822989 +0.00017115205992013216 +0.00040838224231265485 +0.00010466154344612733 +0.00027879272238351405 +0.00039306565304286778 +0.00033345355768688023 +0.00016124799731187522 +0.00075663242023438215 +7.1496083364763763e-06 +1.5679015632485971e-05 +6.3547486206516623e-06 +5.741307631978998e-06 +8.9158551418222487e-05 +1.7971011402551085e-05 +6.774074790882878e-06 +7.7828854045947082e-06 +8.9423601821181364e-06 +0.00027733010938391089 +5.061857882537879e-05 +0.00053693423978984356 +9.9322249297983944e-05 +2.9949624149594456e-05 +1.1304300642223097e-05 +2.7200405384064652e-05 +1.5170480764936656e-05 +9.0235989773645997e-05 +2.4482391381752677e-05 +3.2535877835471183e-05 +0.00079751823795959353 +0.00015305007400456816 +0.0009184720111079514 +0.00017943330749403685 +0.00014246991486288607 +7.146289135562256e-05 +0.00079001008998602629 +0.00015223721857182682 +0.00055745593272149563 +0.00010336767445551232 +0.00029997708043083549 +0.00023943466658238322 +0.00088930106721818447 +0.00050321855815127492 +0.0011442881077528 +0.00023617129772901535 +0.00038052198942750692 +6.8099754571449012e-05 +0.00025014672428369522 +4.3783158616861328e-05 +0.00018202584760729223 +0.00026780771440826356 +0.00013645457511302084 +2.3106458684196696e-05 +5.346062607713975e-05 +8.7168446043506265e-06 +2.8510268748505041e-05 +9.3518450739793479e-05 +0.00037477700971066952 +0.00071823387406766415 +0.00011634847760433331 +0.0010390219977125525 +1.6840560419950634e-05 +3.7324748518585693e-06 +8.3421446106513031e-06 +4.1385396798432339e-06 +2.7493729248817544e-06 +3.645778861027793e-06 +6.7651758399733808e-06 +5.2747927838936448e-06 +6.4418504734931048e-06 +2.2803656975156628e-05 +8.1328416854375973e-06 +1.4406447917281184e-05 +0.018844079226255417 +0.023807615041732788 +0.0057281083427369595 +0.0028938620816916227 +0.030184756964445114 +0.0096431644633412361 +0.0027444544248282909 +0.0012940085725858808 +0.00038431162829510868 +0.036640696227550507 +0.013851489871740341 +0.04217856377363205 +0.01772746816277504 +0.0066133150830864906 +0.0046272757463157177 +0.0011289477115496993 +0.0004868677060585469 +0.0019454166758805513 +0.00016070650599431247 +4.5279251935426146e-05 +0.045886602252721786 +0.02064017578959465 +0.047025755047798157 +0.021764898672699928 +0.0090371659025549889 +0.0082972105592489243 +0.045012608170509338 +0.020428696647286415 +0.040024872869253159 +0.017062976956367493 +0.0066514355130493641 +0.0083210775628685951 +0.0027701454237103462 +0.0030988564249128103 +0.0020940236281603575 +0.0027264186646789312 +0.032644715160131454 +0.012386534363031387 +0.02512006089091301 +0.0077381199225783348 +0.0027825052384287119 +0.0046642972156405449 +0.019647471606731415 +0.004368883091956377 +0.016449615359306335 +0.0023844428360462189 +0.00045020857942290604 +0.0013499683700501919 +0.00049773190403357148 +0.00094307423569262028 +0.00018546072533354163 +0.0014717178419232368 +0.01532488688826561 +0.001632612431421876 +0.015499933622777462 +0.0015599243342876434 +1.5540945241809823e-05 +8.3952130808029324e-05 +0.016363039612770081 +0.0017383586382493377 +4.1147428419208154e-05 +9.5099321697489358e-06 +1.3691577805730049e-05 +4.749219078803435e-05 +0.3055497407913208 +0.30947861075401306 +0.21211643517017365 +0.20659244060516357 +0.31384050846099854 +0.21821704506874084 +0.13646194338798523 +0.12923061847686768 +0.1227557361125946 +0.31796190142631531 +0.22417771816253662 +0.32076281309127808 +0.22853672504425049 +0.14916048943996429 +0.14363500475883484 +0.080246441066265106 +0.072818882763385773 +0.086278289556503296 +0.065375290811061859 +0.059106029570102692 +0.32130885124206543 +0.22999288141727448 +0.31994429230690002 +0.22895462810993195 +0.15118065476417542 +0.15159851312637329 +0.31719744205474854 +0.22601334750652313 +0.3134227991104126 +0.22114866971969604 +0.14266328513622284 +0.14830149710178375 +0.087587170302867889 +0.090140558779239655 +0.081847503781318665 +0.089666225016117096 +0.30902978777885437 +0.21449172496795654 +0.30493152141571045 +0.20769412815570831 +0.1253872811794281 +0.13424576818943024 +0.30199828743934631 +0.2024553120136261 +0.3003389835357666 +0.19924086332321167 +0.11432739347219467 +0.11852471530437469 +0.05723736435174942 +0.064070895314216614 +0.05315052717924118 +0.073148094117641449 +0.29992881417274475 +0.19825002551078796 +0.30070140957832336 +0.19931767880916595 +0.11433476209640503 +0.11306086182594299 +0.30258843302726746 +0.20220470428466797 +0.11767453700304031 +0.054962828755378723 +0.052654918283224106 +0.051844183355569839 +0.74278151988983154 +0.74020719528198242 +0.63684743642807007 +0.63787460327148438 +0.73624390363693237 +0.63494616746902466 +0.52755975723266602 +0.52739852666854858 +0.52683877944946289 +0.73155337572097778 +0.63254696130752563 +0.72737616300582886 +0.63036066293716431 +0.52672797441482544 +0.5273166298866272 +0.42071759700775146 +0.41873824596405029 +0.42181307077407837 +0.41643154621124268 +0.414202481508255 +0.72482740879058838 +0.62898898124694824 +0.72369176149368286 +0.62804180383682251 +0.5245627760887146 +0.52584028244018555 +0.72359645366668701 +0.62709599733352661 +0.72455310821533203 +0.62651926279067993 +0.52120041847229004 +0.52286332845687866 +0.41778841614723206 +0.42010512948036194 +0.41508764028549194 +0.42155072093009949 +0.72660332918167114 +0.62675720453262329 +0.72958570718765259 +0.62792980670928955 +0.51976323127746582 +0.52007603645324707 +0.73324960470199585 +0.63001984357833862 +0.73701393604278564 +0.6325109601020813 +0.52168750762939453 +0.52041447162628174 +0.40958678722381592 +0.41055160760879517 +0.40946918725967407 +0.41251459717750549 +0.74026948213577271 +0.63484281301498413 +0.74256879091262817 +0.63668948411941528 +0.52465558052062988 +0.52318310737609863 +0.74350792169570923 +0.63777369260787964 +0.52590352296829224 +0.41237837076187134 +0.41097497940063477 +0.40997433662414551 +0.98747038841247559 +0.98482924699783325 +0.95663285255432129 +0.960773766040802 +0.97951340675354004 +0.9501071572303772 +0.89975720643997192 +0.90686511993408203 +0.91162139177322388 +0.97185122966766357 +0.9419061541557312 +0.96354979276657104 +0.93343091011047363 +0.88242572546005249 +0.89110660552978516 +0.81947952508926392 +0.82664930820465088 +0.81264132261276245 +0.83265161514282227 +0.83668887615203857 +0.95650619268417358 +0.92624980211257935 +0.95238441228866577 +0.92223870754241943 +0.87171292304992676 +0.87536555528640747 +0.95255911350250244 +0.9229552149772644 +0.95690655708312988 +0.92773616313934326 +0.87763714790344238 +0.87285381555557251 +0.80601978302001953 +0.80526554584503174 +0.80916935205459595 +0.80763500928878784 +0.96474897861480713 +0.93539518117904663 +0.97358375787734985 +0.9438251256942749 +0.8920472264289856 +0.88450980186462402 +0.98084378242492676 +0.9510074257850647 +0.98539865016937256 +0.95637780427932739 +0.90496128797531128 +0.89899218082427979 +0.82478362321853638 +0.81923675537109375 +0.82998692989349365 +0.81381374597549438 +0.98676306009292603 +0.95972669124603271 +0.98688817024230957 +0.96151930093765259 +0.91268479824066162 +0.9096832275390625 +0.98759323358535767 +0.96213573217391968 +0.9134710431098938 +0.83819890022277832 +0.83732527494430542 +0.83438146114349365 +0.90132009983062744 +0.92184245586395264 +0.88297533988952637 +0.96275174617767334 +0.97191798686981201 +0.97853785753250122 +0.86283481121063232 +0.84315454959869385 +0.93434232473373413 +0.94911479949951172 +0.97937023639678955 +0.98851710557937622 +0.96895849704742432 +0.99362963438034058 +0.99522286653518677 +0.82790595293045044 +0.8251183032989502 +0.91795158386230469 +0.92261737585067749 +0.84112107753753662 +0.86798137426376343 +0.93564069271087646 +0.92331922054290771 +0.95644932985305786 +0.95574945211410522 +0.96189689636230469 +0.96041285991668701 +0.89665669202804565 +0.925709068775177 +0.96773850917816162 +0.95112204551696777 +0.95397013425827026 +0.97428381443023682 +0.99371135234832764 +0.98338556289672852 +0.98993861675262451 +0.98112112283706665 +0.99513870477676392 +0.97092187404632568 +0.97965127229690552 +0.97000271081924438 +0.99090147018432617 +0.99513036012649536 +0.94760793447494507 +0.98496818542480469 +0.99455070495605469 +0.99401307106018066 +0.99534451961517334 +0.97401636838912964 +0.98767125606536865 +0.95251578092575073 +0.94087857007980347 +0.92380404472351074 +0.9502989649772644 +0.89524537324905396 +0.92434602975845337 +0.93919265270233154 +0.90287333726882935 +0.92154049873352051 +0.86172252893447876 +0.67884159088134766 +0.69889235496520996 +0.71461766958236694 +0.71454083919525146 +0.88097798824310303 +0.89239335060119629 +0.82990670204162598 +0.86231893301010132 +0.86714076995849609 +0.80222326517105103 +0.64455521106719971 +0.66051876544952393 +0.42078575491905212 +0.4146389365196228 +0.40831783413887024 +0.42619684338569641 +0.23428082466125488 +0.21681882441043854 +0.19717618823051453 +0.17742761969566345 +0.39749443531036377 +0.37295150756835938 +0.15555261075496674 +0.12543538212776184 +0.84905731678009033 +0.84779989719390869 +0.77933216094970703 +0.84131455421447754 +0.83412110805511475 +0.76012688875198364 +0.60801935195922852 +0.62873965501785278 +0.8415757417678833 +0.83044052124023438 +0.75085699558258057 +0.85469090938568115 +0.84567916393280029 +0.76512807607650757 +0.58683145046234131 +0.58837723731994629 +0.37790614366531372 +0.40812575817108154 +0.42542266845703125 +0.35230171680450439 +0.17077019810676575 +0.20370155572891235 +0.23241235315799713 +0.24309709668159485 +0.87986177206039429 +0.87949061393737793 +0.8036310076713562 +0.91064143180847168 +0.9222603440284729 +0.85414189100265503 +0.63641399145126343 +0.60646259784698486 +0.93921446800231934 +0.96233022212982178 +0.90203773975372314 +0.95639550685882568 +0.98599910736083984 +0.93029659986495972 +0.68311417102813721 +0.66568160057067871 +0.31820958852767944 +0.32648861408233643 +0.33713012933731079 +0.31385999917984009 +0.067524872720241547 +0.082172729074954987 +0.10906142741441727 +0.13974249362945557 +0.96076244115829468 +0.99204152822494507 +0.93797111511230469 +0.95865625143051147 +0.99170905351638794 +0.94018828868865967 +0.70100796222686768 +0.69018828868865967 +0.33997589349746704 +0.31835862994194031 +0.091793090105056763 +0.070175819098949432 +0.59452933073043823 +0.60041719675064087 +0.42562013864517212 +0.42647027969360352 +0.42921686172485352 +0.58920687437057495 +0.73410815000534058 +0.74590867757797241 +0.75766158103942871 +0.4333224892616272 +0.58497351408004761 +0.72311484813690186 +0.83324301242828369 +0.84929847717285156 +0.86432874202728271 +0.8775058388710022 +0.43802419304847717 +0.58204066753387451 +0.71367561817169189 +0.44233313202857971 +0.58030778169631958 +0.70642971992492676 +0.80495446920394897 +0.817707359790802 +0.44569993019104004 +0.57988560199737549 +0.70215415954589844 +0.44801467657089233 +0.58109664916992188 +0.70176422595977783 +0.79326248168945312 +0.79653012752532959 +0.44910925626754761 +0.58410030603408813 +0.70605933666229248 +0.44875770807266235 +0.58889293670654297 +0.71572303771972656 +0.8089299201965332 +0.79666393995285034 +0.44696462154388428 +0.59497398138046265 +0.72986167669296265 +0.44396543502807617 +0.60134661197662354 +0.74600416421890259 +0.85282218456268311 +0.82905280590057373 +0.43996992707252502 +0.60672950744628906 +0.76082909107208252 +0.43516248464584351 +0.6095576286315918 +0.77016419172286987 +0.88849937915802002 +0.87483543157577515 +0.43034166097640991 +0.60917270183563232 +0.77218782901763916 +0.42692047357559204 +0.60582238435745239 +0.76742857694625854 +0.8873666524887085 +0.89181286096572876 +0.055099107325077057 +0.049096837639808655 +0.010624615475535393 +0.014172346331179142 +0.021124724298715591 +0.065893940627574921 +0.14926896989345551 +0.13659378886222839 +0.1291051059961319 +0.03190704807639122 +0.080871835350990295 +0.16530689597129822 +0.28622561693191528 +0.2741222083568573 +0.26424366235733032 +0.25838750600814819 +0.044562429189682007 +0.097336843609809875 +0.18188157677650452 +0.054751798510551453 +0.11050670593976974 +0.19516485929489136 +0.30852246284484863 +0.2984774112701416 +0.060262620449066162 +0.11789672821760178 +0.20317195355892181 +0.061008915305137634 +0.11931990832090378 +0.20576149225234985 +0.31810009479522705 +0.31511127948760986 +0.057105880230665207 +0.11494346708059311 +0.20295122265815735 +0.048869885504245758 +0.10528887808322906 +0.19491812586784363 +0.31265023350715637 +0.31733468174934387 +0.037811335176229477 +0.091988116502761841 +0.18282538652420044 +0.026634678244590759 +0.077783718705177307 +0.16882225871086121 +0.29444190859794617 +0.30459734797477722 +0.017493965104222298 +0.06498485803604126 +0.15471738576889038 +0.011992864310741425 +0.055467326194047928 +0.14197850227355957 +0.27206957340240479 +0.28328639268875122 +0.0099997706711292267 +0.049889635294675827 +0.13231821358203888 +0.0096478089690208435 +0.047692969441413879 +0.12769389152526855 +0.25769016146659851 +0.26262694597244263 +8.8843124103732407e-05 +8.8447413872927427e-05 +0.00010705757449613884 +0.00011325976811349392 +0.00010436150478199124 +0.0001884212251752615 +0.0012711102608591318 +0.00043543509673327208 +0.00014255350106395781 +0.00019494839943945408 +0.00071838195435702801 +0.0032569558825343847 +0.010783601552248001 +0.0051603042520582676 +0.0019786017946898937 +0.00051165930926799774 +0.00040320566040463746 +0.001629729988053441 +0.006080330815166235 +0.00065091811120510101 +0.0024932743981480598 +0.0085085742175579071 +0.023652257397770882 +0.01785331591963768 +0.00087556720245629549 +0.0031189320143312216 +0.01001468114554882 +0.00103033147752285 +0.0035557295195758343 +0.01077729370445013 +0.027674930170178413 +0.026884408667683601 +0.0010656605008989573 +0.0036740973591804504 +0.01060125045478344 +0.00092927570221945643 +0.0031658732332289219 +0.0089175812900066376 +0.021333625540137291 +0.025929361581802368 +0.00065644440473988652 +0.0021440254058688879 +0.0060648047365248203 +0.00036998032010160387 +0.0011426531709730625 +0.0032889298163354397 +0.0082935681566596031 +0.014765292406082153 +0.00014963683497626334 +0.00044645724119618535 +0.0012976126745343208 +3.2107058359542862e-05 +9.0741254098247737e-05 +0.00026015631738118827 +0.00066769588738679886 +0.0033378449734300375 +1.4818086128798313e-05 +1.1673658264044207e-05 +8.1743328337324783e-06 +5.5931050155777484e-05 +4.6288154408102855e-05 +3.559068136382848e-05 +7.2646835178602487e-05 +1.4740799997525755e-05 +4.364742198958993e-05 +3.2617204851703718e-05 +2.4470038624713197e-05 +2.8117654437664896e-05 +2.8243655833648518e-05 +4.550921221380122e-05 +7.1902068157214671e-05 +7.4768053309526294e-05 +5.8274374168831855e-05 +2.2660669856122695e-05 +3.5748693335335702e-05 +5.417604188551195e-05 +6.4203770307358354e-05 +8.5310457507148385e-05 +0.00010350167576689273 +8.9096822193823755e-05 +1.3222830602899194e-05 +1.8874272427638061e-05 +3.2629839552100748e-05 +5.8257983255316503e-06 +6.3566330936737359e-06 +2.4843655410222709e-05 +9.8379532573744655e-05 +6.3144834712147713e-05 +2.61089667219494e-06 +1.739346657814167e-06 +3.2233703677775338e-05 +1.9651181446533883e-06 +6.3885562440191279e-07 +4.0052240365184844e-05 +0.00019952212460339069 +0.0001575155183672905 +3.231336677345098e-06 +6.6210418481205124e-07 +4.1365496144862846e-05 +6.7083083195029758e-06 +1.4065359437154257e-06 +3.705367271322757e-05 +0.0001850718108471483 +0.00020677817519754171 +1.1462076145107858e-05 +2.4401588234468363e-06 +2.8547818146762438e-05 +1.5325973436119966e-05 +3.301543983980082e-06 +1.7829845091910101e-05 +8.8436507212463766e-05 +0.00014229361840989441 +1.7305404981016181e-05 +3.931852916139178e-06 +8.0818736023502424e-06 +1.7577849575900473e-05 +4.6748377826588694e-06 +3.6862113574898103e-06 +1.0085982466989662e-05 +3.8019174098735675e-05 +1.7564276276971214e-05 +7.8742586993030272e-06 +9.3175667643663473e-06 +1.9929144400521182e-05 +1.787388464435935e-05 +2.9943032131996006e-05 +4.6335713705047965e-05 +1.3378542462305631e-05 +8.7301668827421963e-05 +0.00010970189032377675 +0.00010456530435476452 +7.9480174463242292e-05 +5.4360130889108405e-05 +6.0851260059280321e-05 +4.4659471313934773e-05 +6.1271130107343197e-05 +7.4477247835602611e-05 +3.3045747841242701e-05 +3.5606393794296309e-05 +2.7377993319532834e-05 +2.0634837710531428e-05 +2.9240760341053829e-05 +3.5262393794255331e-05 +3.9056125388015062e-05 +1.74719825736247e-05 +1.5815101505722851e-05 +1.2667949704336934e-05 +7.668198122701142e-06 +4.7176927182590589e-06 +4.2760866563185118e-06 +4.961295871908078e-06 +1.1231586540816352e-05 +2.7332375793776009e-06 +1.5931724419715465e-06 +2.3318093553825747e-06 +8.354140845767688e-07 +1.7592318499737303e-06 +3.3471758342784597e-06 +3.4697748105827486e-06 +2.9400603125395719e-06 +4.8926717681752052e-06 +7.4615895755414385e-06 +8.5505025708698668e-06 +2.2572588932234794e-05 +2.7873995350091718e-05 +2.3886370399850421e-05 +1.5420131603605114e-05 +6.8584336077037733e-06 +5.4796160839032382e-05 +6.1689148424193263e-05 +4.7513582103420049e-05 +9.5737821538932621e-05 +9.7118710982613266e-05 +6.9805188104510307e-05 +3.8152440538397059e-05 +2.7635644073598087e-05 +0.00013364896585699171 +0.00012482231250032783 +8.5014864453114569e-05 +0.00015085795894265175 +0.00013790755474474281 +9.1276975581422448e-05 +4.573043406708166e-05 +4.4228116166777909e-05 +0.00014456336793955415 +0.00013610150199383497 +8.9526991359889507e-05 +0.00012683392560575157 +0.00012575072469189763 +8.3501501649152488e-05 +4.1486709960736334e-05 +4.3927291699219495e-05 +0.00012739372323267162 +0.0006369667244143784 +0.0015402426943182945 +0.00030804879497736692 +0.00082258740440011024 +0.00035073497565463185 +0.00012230357970111072 +5.0572143663885072e-05 +0.00022442433692049235 +0.0041129360906779766 +0.0017536734230816364 +0.000588388298638165 +0.00013580739323515445 +5.0289785576751456e-05 +4.8690086259739473e-05 +0.00010127771383849904 +0.0092848679050803185 +0.0038149801548570395 +0.0012319802772253752 +0.013520850799977779 +0.0050455136224627495 +0.0014706178335472941 +0.0003033083921764046 +0.00026013143360614777 +0.015517104417085648 +0.0050390707328915596 +0.0012133019044995308 +0.015483599156141281 +0.0044723856262862682 +0.00086049584206193686 +0.00017290476534981281 +0.00024668429978191853 +0.013836544938385487 +0.0037393218372017145 +0.00060793408192694187 +0.011198399588465691 +0.0029508722946047783 +0.00044662103755399585 +9.9523735116235912e-05 +0.00012362778943497688 +0.0080661792308092117 +0.0021531879901885986 +0.00034618412610143423 +0.0048114648088812828 +0.0013275780947878957 +0.00025487295351922512 +0.00011038748198188841 +9.7437310614623129e-05 +0.0023106671869754791 +0.00070660782512277365 +0.00020214680989738554 +0.0019450237741693854 +0.00077409687219187617 +0.00029867439297959208 +0.00016776658594608307 +0.00013425186625681818 +0.0030831459444016218 +0.0012872375082224607 +0.00046787236351519823 +0.0030810171738266945 +0.0012765937717631459 +0.00044590479228645563 +0.00016655150102451444 +0.00019123975653201342 +0.060702193528413773 +0.047859650105237961 +0.033408548682928085 +0.0756535604596138 +0.020410465076565742 +0.013196388259530067 +0.0099740736186504364 +0.0086924303323030472 +0.092737041413784027 +0.10828185826539993 +0.040017005056142807 +0.03066713735461235 +0.11733958870172501 +0.11368421465158463 +0.045585185289382935 +0.045587953180074692 +0.097866855561733246 +0.077215738594532013 +0.032364919781684875 +0.040351025760173798 +0.055616430938243866 +0.033511802554130554 +0.013902929611504078 +0.023275963962078094 +0.014455188065767288 +0.0051103862933814526 +0.0036290523130446672 +0.0062744528986513615 +0.0073258024640381336 +0.018134437501430511 +0.00764832878485322 +0.0054866019636392593 +0.96297037601470947 +0.98241543769836426 +0.96310162544250488 +0.96436029672622681 +0.94351065158843994 +0.93623608350753784 +0.93670016527175903 +0.91096293926239014 +0.87842220067977905 +0.81846576929092407 +0.9341655969619751 +0.84431725740432739 +0.91923147439956665 +0.90674775838851929 +0.9042283296585083 +0.89386093616485596 +0.87901246547698975 +0.87245017290115356 +0.84544974565505981 +0.81539678573608398 +0.76063781976699829 +0.78845113515853882 +0.6886904239654541 +0.66942471265792847 +0.54890161752700806 +0.6522025465965271 +0.54281347990036011 +0.41776922345161438 +0.42361834645271301 +0.30769956111907959 +0.41146507859230042 +0.29206123948097229 +0.71727180480957031 +0.70801305770874023 +0.55806940793991089 +0.55576366186141968 +0.4040449857711792 +0.27579304575920105 +0.38751399517059326 +0.25571480393409729 +0.8720090389251709 +0.85674697160720825 +0.84483760595321655 +0.85589772462844849 +0.84026819467544556 +0.8228994607925415 +0.79024779796600342 +0.76933783292770386 +0.71523362398147583 +0.73642128705978394 +0.84536087512969971 +0.83047342300415039 +0.80618107318878174 +0.84366118907928467 +0.83513736724853516 +0.80010765790939331 +0.75339949131011963 +0.75419950485229492 +0.68088197708129883 +0.69454425573348999 +0.61930835247039795 +0.59699976444244385 +0.5095478892326355 +0.5842786431312561 +0.48387446999549866 +0.39353513717651367 +0.36343091726303101 +0.28100442886352539 +0.4194856584072113 +0.31274861097335815 +0.63694471120834351 +0.53751641511917114 +0.52906447649002075 +0.42722901701927185 +0.3270287811756134 +0.32148778438568115 +0.85748958587646484 +0.86086416244506836 +0.81598383188247681 +0.88624840974807739 +0.90035653114318848 +0.85386049747467041 +0.78211444616317749 +0.82814937829971313 +0.72307419776916504 +0.68983572721481323 +0.92205095291137695 +0.9434891939163208 +0.90253520011901855 +0.95540744066238403 +0.97707110643386841 +0.94840097427368164 +0.87948060035705566 +0.91968560218811035 +0.81165850162506104 +0.76834613084793091 +0.62082690000534058 +0.65183019638061523 +0.48143252730369568 +0.67657411098480225 +0.49192556738853455 +0.32195588946342468 +0.31544563174247742 +0.17235232889652252 +0.33161202073097229 +0.19466929137706757 +0.59467834234237671 +0.4697687029838562 +0.47181007266044617 +0.34378072619438171 +0.22043636441230774 +0.24808424711227417 +0.97522461414337158 +0.99052035808563232 +0.97544693946838379 +0.98029804229736328 +0.99196898937225342 +0.98244768381118774 +0.93576145172119141 +0.9388163685798645 +0.8450663685798645 +0.83727622032165527 +0.97931146621704102 +0.99052280187606812 +0.9829632043838501 +0.97476410865783691 +0.97966986894607544 +0.94075697660446167 +0.85467207431793213 +0.8504984974861145 +0.6942484974861145 +0.70844578742980957 +0.52105456590652466 +0.54517519474029541 +0.3562808632850647 +0.22424611449241638 +0.32651051878929138 +0.18710950016975403 +0.68706446886062622 +0.49843409657478333 +0.50429844856262207 +0.31448638439178467 +0.16333049535751343 +0.16001562774181366 +0.51156723499298096 +0.59743523597717285 +0.59176468849182129 +0.67332136631011963 +0.51018410921096802 +0.5869213342666626 +0.66445738077163696 +0.73996090888977051 +0.72845721244812012 +0.79633110761642456 +0.75184446573257446 +0.81025600433349609 +0.50993025302886963 +0.58335083723068237 +0.65663927793502808 +0.51062881946563721 +0.58103054761886597 +0.65035718679428101 +0.71816098690032959 +0.70973855257034302 +0.77009326219558716 +0.78258699178695679 +0.84134715795516968 +0.82526868581771851 +0.87377440929412842 +0.81084167957305908 +0.85513120889663696 +0.89178371429443359 +0.87098133563995361 +0.91372185945510864 +0.87120062112808228 +0.85699355602264404 +0.90793383121490479 +0.89209467172622681 +0.9329221248626709 +0.94747567176818848 +0.5118180513381958 +0.57991272211074829 +0.64583438634872437 +0.51328390836715698 +0.58026683330535889 +0.64350801706314087 +0.70386344194412231 +0.70141637325286865 +0.75382721424102783 +0.76012575626373291 +0.51506024599075317 +0.58237463235855103 +0.64402937889099121 +0.51708865165710449 +0.586273193359375 +0.64785397052764893 +0.70328336954116821 +0.71017730236053467 +0.75642603635787964 +0.75220674276351929 +0.79419994354248047 +0.79399240016937256 +0.82345670461654663 +0.80155164003372192 +0.82547855377197266 +0.84014153480529785 +0.84622353315353394 +0.84448850154876709 +0.80014938116073608 +0.83949315547943115 +0.82877075672149658 +0.85500586032867432 +0.5192185640335083 +0.5918346643447876 +0.65524095296859741 +0.52119863033294678 +0.59818613529205322 +0.66546905040740967 +0.72238749265670776 +0.73783695697784424 +0.78533023595809937 +0.76779919862747192 +0.52267670631408691 +0.60425961017608643 +0.67683559656143188 +0.52321767807006836 +0.60856068134307861 +0.68707633018493652 +0.7538420557975769 +0.76644355058670044 +0.82455843687057495 +0.80571287870407104 +0.84074491262435913 +0.86446046829223633 +0.88768172264099121 +0.88312321901321411 +0.91270560026168823 +0.92577779293060303 +0.94980299472808838 +0.89495331048965454 +0.81827247142791748 +0.83819901943206787 +0.86067992448806763 +0.86617231369018555 +0.52230352163314819 +0.60975134372711182 +0.69336473941802979 +0.52001070976257324 +0.60785263776779175 +0.69438064098358154 +0.7720569372177124 +0.77062314748764038 +0.83914750814437866 +0.83639585971832275 +0.5170101523399353 +0.60327839851379395 +0.6903107762336731 +0.51397222280502319 +0.68247389793395996 +0.76298224925994873 +0.82349920272827148 +0.8341294527053833 +0.89039772748947144 +0.88298195600509644 +0.92844170331954956 +0.9203038215637207 +0.95618164539337158 +0.96012216806411743 +0.89128804206848145 +0.92799311876296997 +0.9318091869354248 +0.95978438854217529 +0.029913948848843575 +0.051460810005664825 +0.0599355548620224 +0.090426243841648102 +0.038863137364387512 +0.072898104786872864 +0.10257620364427567 +0.14239703118801117 +0.15698157250881195 +0.20633129775524139 +0.13208714127540588 +0.19430013000965118 +0.052018985152244568 +0.089217349886894226 +0.11859224736690521 +0.066995903849601746 +0.10463257133960724 +0.1356203705072403 +0.17376638948917389 +0.18917390704154968 +0.23615553975105286 +0.22114245593547821 +0.28000813722610474 +0.29248014092445374 +0.3578532338142395 +0.30392298102378845 +0.36653700470924377 +0.43566066026687622 +0.44028955698013306 +0.43113335967063904 +0.26070046424865723 +0.26879248023033142 +0.34283140301704407 +0.34947344660758972 +0.42764008045196533 +0.42577457427978516 +0.079003609716892242 +0.11493588238954544 +0.14923560619354248 +0.085563473403453827 +0.1193658709526062 +0.15714028477668762 +0.19983674585819244 +0.2051527351140976 +0.25586965680122375 +0.24826896190643311 +0.086508505046367645 +0.11782404780387878 +0.1591639518737793 +0.082016244530677795 +0.11074335128068924 +0.15541322529315948 +0.20502027869224548 +0.1995764821767807 +0.25708311796188354 +0.25883325934410095 +0.31706467270851135 +0.31819695234298706 +0.38176092505455017 +0.31549263000488281 +0.38193401694297791 +0.44872862100601196 +0.44912838935852051 +0.44699570536613464 +0.31225785613059998 +0.3738892674446106 +0.3790152370929718 +0.4441412091255188 +0.072608873248100281 +0.098922863602638245 +0.14627143740653992 +0.059959307312965393 +0.0848274827003479 +0.13318203389644623 +0.18924489617347717 +0.17592827975749969 +0.24020236730575562 +0.25059053301811218 +0.046891242265701294 +0.071091361343860626 +0.11864857375621796 +0.035746384412050247 +0.059698686003684998 +0.10478372871875763 +0.16169081628322601 +0.14808546006679535 +0.21436014771461487 +0.22764120995998383 +0.29970329999923706 +0.28893047571182251 +0.36721855401992798 +0.27762696146965027 +0.35924124717712402 +0.44208070635795593 +0.43765619397163391 +0.44560110569000244 +0.30896574258804321 +0.37930378317832947 +0.37414112687110901 +0.44802659749984741 +0.028384454548358917 +0.052220925688743591 +0.093309372663497925 +0.0249611996114254 +0.048403598368167877 +0.085452750325202942 +0.13664121925830841 +0.12925420701503754 +0.19115373492240906 +0.20154261589050293 +0.02392839640378952 +0.047882471233606339 +0.081946469843387604 +0.025152355432510376 +0.083425335586071014 +0.127642422914505 +0.18710340559482574 +0.18594013154506683 +0.25942456722259521 +0.25736427307128906 +0.33924871683120728 +0.33915919065475464 +0.42600333690643311 +0.42836776375770569 +0.26695576310157776 +0.3508470356464386 +0.34345164895057678 +0.43266543745994568 +0.0001062732408172451 +9.6947485872078687e-05 +0.00010553436004556715 +0.00016416516155004501 +0.00013455490989144892 +0.00037890375824645162 +0.00049194484017789364 +0.00074746244354173541 +0.0020823006052523851 +0.00275190151296556 +0.00025910616386681795 +0.0010058486368507147 +0.00039058085530996323 +0.0011532070348039269 +0.0015499681467190385 +0.00086960254702717066 +0.0020943018607795238 +0.0031958767212927341 +0.0046409936621785164 +0.0074208853766322136 +0.010795380920171738 +0.0062109613791108131 +0.0076213255524635315 +0.014307480305433273 +0.018755441531538963 +0.021081456914544106 +0.028645284473896027 +0.038271799683570862 +0.050236556679010391 +0.026010563597083092 +0.0010762127349153161 +0.0033096808474510908 +0.0056139994412660599 +0.010587513446807861 +0.017196374014019966 +0.011999480426311493 +0.0013601930113509297 +0.0028327750042080879 +0.0046695908531546593 +0.0017483015544712543 +0.0033578742295503616 +0.0056526586413383484 +0.0093657290562987328 +0.010477758012712002 +0.016870196908712387 +0.014629652723670006 +0.002017253777012229 +0.0036739769857376814 +0.0062682460993528366 +0.0020851795561611652 +0.0035175690427422523 +0.0063426941633224487 +0.010842164047062397 +0.0099834296852350235 +0.016960045322775841 +0.017705356702208519 +0.027577048167586327 +0.027138784527778625 +0.041753590106964111 +0.024007393047213554 +0.038860779255628586 +0.059618793427944183 +0.053509727120399475 +0.061236709356307983 +0.02558135986328125 +0.036663822829723358 +0.041025739163160324 +0.05809740349650383 +0.0018050122307613492 +0.0026857710909098387 +0.0054055117070674896 +0.0012427420588210225 +0.0016073986189439893 +0.0036536082625389099 +0.0075594554655253887 +0.0045893783681094646 +0.0096709281206130981 +0.014095734804868698 +0.00067741359816864133 +0.00075420172652229667 +0.0019512936705723405 +0.00026842652005143464 +0.00022383281611837447 +0.0007630717009305954 +0.002184620127081871 +0.0006490703672170639 +0.0021294227335602045 +0.0053411563858389854 +0.011388028040528297 +0.0055781076662242413 +0.014580097049474716 +0.0016689753392711282 +0.0075752390548586845 +0.02170959860086441 +0.014188233762979507 +0.032068744301795959 +0.018166707828640938 +0.032351575791835785 +0.023446045815944672 +0.043524399399757385 +5.553553273784928e-05 +2.6811798306880519e-05 +0.00015364217688329518 +1.394152786815539e-05 +2.495604167052079e-05 +9.3170647232909687e-06 +6.7610759288072586e-05 +1.8587472368380986e-05 +9.5480481832055375e-06 +0.00042620152817107737 +5.4291940614348277e-05 +6.9506466388702393e-05 +3.8020727515686303e-05 +0.00010380568710388616 +9.1268200776539743e-05 +7.248049951158464e-05 +0.00027258871705271304 +4.5099044655216858e-05 +3.648198617156595e-05 +0.00020408829732332379 +0.0024847211316227913 +0.0031866927165538073 +0.0098924813792109489 +0.0097353272140026093 +0.00017071490583475679 +0.0036385974381119013 +0.0025145495310425758 +0.010674496181309223 +3.2810065022204071e-05 +3.9124999602790922e-05 +4.5877739466959611e-05 +5.8382494898978621e-05 +3.4353535738773644e-05 +4.2235111322952434e-05 +5.8946712670149282e-05 +7.5473784818314016e-05 +6.4615342125762254e-05 +8.1611309724394232e-05 +6.9222391175571829e-05 +9.0556852228473872e-05 +2.7678579499479383e-05 +2.7483802114147693e-05 +4.5153628889238462e-05 +1.5576490113744512e-05 +1.1353935406077653e-05 +2.4198085156967863e-05 +4.2781699448823929e-05 +2.5917985112755559e-05 +4.5251457777339965e-05 +6.1098544392734766e-05 +7.3266513936687261e-05 +6.0152960941195488e-05 +9.7488220490049571e-05 +7.5210111390333623e-05 +0.00015842467837501317 +0.00028925671358592808 +0.00052701844833791256 +0.00013005769869778305 +0.00010222879063803703 +9.662552474765107e-05 +0.00011170261859660968 +9.0420275228098035e-05 +0.00010353658581152558 +0.00011920783435925841 +6.1649843701161444e-06 +3.3344183520966908e-06 +1.1037328476959374e-05 +2.2182305201567942e-06 +1.0234739420411643e-06 +9.1019383035018109e-06 +2.7563381081563421e-05 +3.7011141102993861e-05 +7.9062323493417352e-05 +5.2412193326745182e-05 +1.1668344086501747e-06 +5.3517220521825948e-07 +1.0295788342773449e-05 +1.6274316294584423e-06 +9.6933183613145957e-07 +1.0514933819649741e-05 +4.1467017581453547e-05 +3.9857710362412035e-05 +0.00010339338768972084 +9.9822813353966922e-05 +0.00018322437244933099 +0.00020713217963930219 +0.0004670562339015305 +0.00019918351608794183 +0.00048353036982007325 +0.00106619275175035 +0.0010221998672932386 +0.00096461130306124687 +0.0001275242684641853 +0.00026409752899780869 +0.00038416113238781691 +0.00076905189780518413 +3.4026877528958721e-06 +1.9160377178195631e-06 +9.6476114777033217e-06 +5.8417040236236062e-06 +2.9212201297923457e-06 +7.8210068750195205e-06 +3.3201125916093588e-05 +2.3341477572103031e-05 +7.1184156695380807e-05 +9.2552487330976874e-05 +7.8400189522653818e-06 +3.6240980989532545e-06 +5.4012512009649072e-06 +8.944664841692429e-06 +4.2677766032284126e-06 +3.2999341783579439e-06 +1.2560714822029695e-05 +4.9411091822548769e-06 +1.9310629795654677e-05 +4.42780255980324e-05 +0.0001161253749160096 +6.1607599491253495e-05 +0.00018532927788328379 +2.0051787942065857e-05 +7.7147815318312496e-05 +0.00024925015168264508 +7.5726908107753843e-05 +0.00050724059110507369 +0.00016569213767070323 +0.00042663756175898015 +0.0003135042788926512 +0.0008022399852052331 +9.3541711976286024e-06 +5.6960079746204428e-06 +2.9741008802375291e-06 +1.0795373782457318e-05 +1.1752559657907113e-05 +7.7364747994579375e-06 +4.9380682867194992e-06 +1.7445590856368653e-05 +1.1553074727999046e-05 +6.2454219005303457e-06 +1.6523408703505993e-05 +2.5181132514262572e-05 +2.2755461031920277e-05 +2.5583323804312386e-05 +4.402858394314535e-05 +4.4366934162098914e-05 +7.3811483161989599e-05 +3.8211484934436157e-05 +2.6246623747283593e-05 +6.8875786382704973e-05 +5.2760602557100356e-05 +0.00010116674093296751 +8.3676241047214717e-05 +3.0689257982885465e-05 +7.9266137618105859e-06 +1.7831869627116248e-05 +1.4548651051882189e-05 +1.3547400158131495e-05 +8.9739536633715034e-05 +9.9336532002780586e-05 +7.4254247010685503e-05 +7.6400785474106669e-05 +6.1907056078780442e-05 +4.774965054821223e-05 +5.4124724556459114e-05 +5.322000288288109e-05 +3.5931476304540411e-05 +3.5387478419579566e-05 +6.8470864789560437e-05 +4.6146924432832748e-05 +3.64418447134085e-05 +2.4826529624988325e-05 +3.2073679903987795e-05 +1.7029889932018705e-05 +8.9771410785033368e-06 +1.4227743122319225e-05 +1.9467373931547627e-05 +7.448073574778391e-06 +1.1535853445820976e-05 +2.3053618861013092e-05 +2.5283110517193563e-05 +1.5787236407049932e-05 +2.0408304408192635e-05 +7.4591785050870385e-06 +1.1784220987465233e-05 +1.8055128748528659e-05 +8.9007353381020948e-06 +2.6302499463781714e-05 +3.7415476981550455e-05 +3.2552339689573273e-05 +2.9093869670759887e-05 +2.6699441150412895e-05 +2.8757567633874714e-05 +2.6597333999234252e-05 +5.7929523791244719e-06 +2.4514654342056019e-06 +4.2696569835243281e-06 +1.8027990336122457e-06 +1.5575238876408548e-06 +1.8532210788180237e-06 +2.7159960609424161e-06 +2.6875334242504323e-06 +2.7778010007750709e-06 +4.5642200348083861e-06 +1.0812152595462976e-06 +3.3450144201196963e-06 +2.6032801088149427e-06 +6.2808203438180499e-06 +1.5255676771630533e-05 +8.2723581726895645e-06 +5.0538083087303676e-06 +1.458033420931315e-05 +8.1334028436685912e-06 +3.7247355066938326e-06 +2.9921807254140731e-06 +4.6621048568340484e-06 +2.8080003176000901e-06 +1.0348022442485671e-05 +5.0997077778447419e-06 +2.3405555111821741e-06 +4.6748059503443073e-06 +2.067679588435567e-06 +3.525677584548248e-06 +5.4031957006372977e-06 +2.8679660317720845e-06 +3.7963941394991707e-06 +2.6685069315135479e-05 +4.384299973025918e-05 +2.6740659450297244e-05 +6.1836610257159919e-05 +7.9938901762943715e-05 +5.6274551752721891e-05 +3.5265245969640091e-05 +5.9428020904306322e-05 +3.7327030440792441e-05 +1.9912426068913192e-05 +0.0001015372690744698 +0.00011236704449402168 +8.5526553448289633e-05 +0.00013498334737960249 +0.00013362301979213953 +0.00010691610077628866 +7.8411860158666968e-05 +8.9380962890572846e-05 +6.2868799432180822e-05 +5.299900658428669e-05 +3.3440916013205424e-05 +4.1753675759537145e-05 +2.5512434149277397e-05 +4.5559208956547081e-05 +2.9053433536319062e-05 +1.654015613894444e-05 +1.7643907995079644e-05 +1.3640673387271818e-05 +2.1405696315923706e-05 +1.0866440788959153e-05 +1.8870361600420438e-05 +9.0610083134379238e-06 +0.00015076555428095162 +0.000138469273224473 +0.00011662952601909637 +0.00014753948198631406 +0.00013159753871150315 +0.00011513535719132051 +9.1169953520875424e-05 +8.6815140093676746e-05 +6.4162319176830351e-05 +6.6195440012961626e-05 +0.00013402561307884753 +0.00011847940186271444 +0.00010718433622969314 +0.00011468904267530888 +9.4624425400979817e-05 +7.9438163083977997e-05 +5.4280946642393246e-05 +5.9877267631236464e-05 +4.2619929445208982e-05 +4.0355986129725352e-05 +2.8210459277033806e-05 +2.9040063964203e-05 +2.2119420464150608e-05 +1.8282877135789022e-05 +4.5075292291585356e-05 +2.961392056022305e-05 +2.8501430278993212e-05 +1.7440286683267914e-05 +0.00020293616398703307 +0.00031848362414166331 +0.00011953237117268145 +7.7809607319068164e-05 +0.00055079581215977669 +0.00087683688616380095 +0.00021152934641577303 +4.6441808080999181e-05 +0.00029675368568859994 +7.2182207077275962e-05 +0.0001160983883892186 +4.2069554183399305e-05 +0.0027539781294763088 +0.0027951020747423172 +0.0010518633062019944 +0.0061093294061720371 +0.0046271649189293385 +0.0022423528134822845 +0.00092439970467239618 +0.0014383222442120314 +0.00062439549947157502 +0.00030308938585221767 +8.0700687249191105e-05 +0.00020061570103280246 +5.5980224715312943e-05 +0.00029936042847111821 +7.8164717706386e-05 +2.4539453079341911e-05 +1.1847007044707425e-05 +4.2987201595678926e-05 +7.0068643253762275e-05 +4.0858358261175454e-05 +6.3478655647486448e-05 +4.7418398025911301e-05 +6.6684478952083737e-05 +9.2267131549306214e-05 +0.008514823392033577 +0.0051546185277402401 +0.0028013139963150024 +0.0092094847932457924 +0.0047834622673690319 +0.0025317228864878416 +0.0013789250515401363 +0.0010238061659038067 +0.00060966878663748503 +0.00074181827949360013 +0.0087733874097466469 +0.0041197137907147408 +0.0020212144590914249 +0.0076472824439406395 +0.0033450834453105927 +0.0015728207072243094 +0.00072224671021103859 +0.00051643361803144217 +0.00030481783323921263 +0.00043085205834358931 +0.00020677360589616001 +0.00014516155351884663 +4.3837902921950445e-05 +0.00010838724847417325 +3.3830532629508525e-05 +1.6404344478360144e-06 +1.1550574527063873e-05 +1.519164129604178e-06 +0.00028230613679625094 +8.2715865573845804e-05 +6.4366424339823425e-05 +4.7065955186553765e-06 +0.0061325943097472191 +0.0025543526280671358 +0.0012049698270857334 +0.0044372016564011574 +0.0017450418090447783 +0.00089567073155194521 +0.00039202749030664563 +0.00030262221116572618 +0.00018626177916303277 +0.00022756076941732317 +0.0026754387654364109 +0.00096127425786107779 +0.00058251287555322051 +0.0013297481928020716 +0.00062405620701611042 +0.00035836698953062296 +0.00021676995675079525 +0.00022483707289211452 +0.00015506810450460762 +0.00015928791253827512 +0.00010246912279399112 +0.00012103439075872302 +9.3946000561118126e-05 +0.00014988193288445473 +0.00013025864609517157 +0.00011617668496910483 +0.00014594571257475764 +7.4541327194310725e-05 +9.6164629212580621e-05 +3.9023245335556567e-05 +6.0139245761092752e-05 +3.7230514863040298e-05 +0.0012506854254752398 +0.0010355503764003515 +0.00047138443915173411 +0.0020360106136649847 +0.0014079783577471972 +0.00078260013833642006 +0.00039067937177605927 +0.00049727392615750432 +0.00028882763581350446 +0.00021209304395597428 +0.0020306888036429882 +0.00098796328529715538 +0.00076593249104917049 +0.0010146794375032187 +0.00038193745422177017 +0.00034465736825950444 +0.00013926015526521951 +0.00026371111744083464 +0.00018729829753283411 +0.00013565830886363983 +0.00012930083903484046 +9.4903807621449232e-05 +0.00011615934636211023 +0.0001363737101200968 +0.00018314085900783539 +0.00015193993749562651 +0.00015114090638235211 +0.00014989431656431407 +0.1419064998626709 +0.16713814437389374 +0.088952615857124329 +0.18718546628952026 +0.10760337859392166 +0.054249659180641174 +0.067681930959224701 +0.03110591322183609 +0.041067503392696381 +0.02421853318810463 +0.20714041590690613 +0.12627615034580231 +0.22595207393169403 +0.145909383893013 +0.084158211946487427 +0.10093120485544205 +0.055067680776119232 +0.041600864380598068 +0.016176262870430946 +0.025405313819646835 +0.0089564602822065353 +0.035702262073755264 +0.016438500955700874 +0.0066397995688021183 +0.011695949360728264 +0.0020564680453389883 +0.009218861348927021 +0.011214454658329487 +0.0027845038566738367 +0.0040698661468923092 +0.00028265916625969112 +0.00077012152178213 +0.24044814705848694 +0.1635531485080719 +0.24087083339691162 +0.17330749332904816 +0.11401074379682541 +0.11749016493558884 +0.074873179197311401 +0.067444100975990295 +0.21944737434387207 +0.16632239520549774 +0.18690061569213867 +0.14349116384983063 +0.10683777183294296 +0.087687499821186066 +0.064322270452976227 +0.073703326284885406 +0.046395458281040192 +0.043467059731483459 +0.026966404169797897 +0.036547001451253891 +0.024130124598741531 +0.014822861179709435 +0.012602455914020538 +0.015740947797894478 +0.043387014418840408 +0.023032080382108688 +0.026722928509116173 +0.014785819686949253 +0.15511569380760193 +0.11545043438673019 +0.12445593625307083 +0.087364010512828827 +0.066506929695606232 +0.04459960013628006 +0.036815248429775238 +0.051156956702470779 +0.094614997506141663 +0.058922965079545975 +0.072790667414665222 +0.034072950482368469 +0.023186003789305687 +0.0081523219123482704 +0.0097216246649622917 +0.022085828706622124 +0.018573785200715065 +0.0096682151779532433 +0.0083493869751691818 +0.0041264607571065426 +0.0038815177977085114 +0.0033804990816861391 +0.0017746237572282553 +0.0064309104345738888 +0.027907125651836395 +0.019493952393531799 +0.014019357040524483 +0.0096708247438073158 +0.066583774983882904 +0.021014265716075897 +0.07850944995880127 +0.023354338482022285 +0.0049585043452680111 +0.011841405183076859 +0.0065499721094965935 +0.0044397939927875996 +0.10808297991752625 +0.040107719600200653 +0.065496966242790222 +0.025543838739395142 +0.018147809430956841 +0.011954288929700851 +0.0067918663844466209 +0.0082253850996494293 +0.00482206791639328 +0.0036280341446399689 +0.0023875092156231403 +0.0033898639958351851 +0.0042573818936944008 +0.0027676376048475504 +0.0042821681126952171 +0.0024913651868700981 +0.97000336647033691 +0.95402085781097412 +0.95160281658172607 +0.97403007745742798 +0.93179559707641602 +0.92059457302093506 +0.80327039957046509 +0.83262026309967041 +0.85214012861251831 +0.90639448165893555 +0.88803553581237793 +0.88220703601837158 +0.85790622234344482 +0.74799269437789917 +0.77418214082717896 +0.54575973749160767 +0.55223697423934937 +0.54006499052047729 +0.31490117311477661 +0.30003538727760315 +0.28392964601516724 +0.55815136432647705 +0.55418753623962402 +0.26670396327972412 +0.24187803268432617 +0.86324620246887207 +0.83318758010864258 +0.84994286298751831 +0.81391614675521851 +0.70498490333557129 +0.72560691833496094 +0.84298819303512573 +0.80110716819763184 +0.84821617603302002 +0.80459553003311157 +0.68155759572982788 +0.68586045503616333 +0.49637299776077271 +0.5211835503578186 +0.47426781058311462 +0.26367330551147461 +0.29804152250289917 +0.32308971881866455 +0.5341792106628418 +0.32601249217987061 +0.87049561738967896 +0.83287239074707031 +0.90376216173171997 +0.87754833698272705 +0.7449716329574585 +0.7041851282119751 +0.93972831964492798 +0.92681986093521118 +0.96770173311233521 +0.96527713537216187 +0.82766264677047729 +0.79123061895370483 +0.48687368631362915 +0.47620904445648193 +0.49598133563995361 +0.16452831029891968 +0.18267141282558441 +0.20752967894077301 +0.46930637955665588 +0.23381322622299194 +0.9790615439414978 +0.98060548305511475 +0.98001939058303833 +0.98266857862472534 +0.8471103310585022 +0.84243291616439819 +0.97771322727203369 +0.98230564594268799 +0.85357195138931274 +0.53335624933242798 +0.51059406995773315 +0.20519720017910004 +0.17236119508743286 +0.50072580575942993 +0.1594158411026001 +0.51264876127243042 +0.51074123382568359 +0.66880601644515991 +0.67790895700454712 +0.50990927219390869 +0.6603703498840332 +0.78938251733779907 +0.80332493782043457 +0.81701672077178955 +0.51018929481506348 +0.65329235792160034 +0.51119095087051392 +0.64786189794540405 +0.76471400260925293 +0.77610409259796143 +0.8642851710319519 +0.88318294286727905 +0.84672856330871582 +0.90038609504699707 +0.91461420059204102 +0.51251429319381714 +0.6443558931350708 +0.51413118839263916 +0.6433720588684082 +0.75236904621124268 +0.75645476579666138 +0.51605236530303955 +0.64551252126693726 +0.51815032958984375 +0.65108591318130493 +0.76113635301589966 +0.75350427627563477 +0.82334047555923462 +0.82540696859359741 +0.83029139041900635 +0.83348667621612549 +0.52024930715560913 +0.66010618209838867 +0.52202242612838745 +0.67111629247665405 +0.79537177085876465 +0.77600163221359253 +0.52309662103652954 +0.682273268699646 +0.52297484874725342 +0.69089114665985107 +0.8316950798034668 +0.81566977500915527 +0.90092581510543823 +0.87394309043884277 +0.92205131053924561 +0.84854692220687866 +0.52128744125366211 +0.69451999664306641 +0.51855695247650146 +0.69296979904174805 +0.83752745389938354 +0.83882510662078857 +0.51545393466949463 +0.68671000003814697 +0.82932835817337036 +0.92493546009063721 +0.9307553768157959 +0.93106710910797119 +0.027010895311832428 +0.033863898366689682 +0.095937788486480713 +0.086201980710029602 +0.044914036989212036 +0.11018115282058716 +0.21351775527000427 +0.19983980059623718 +0.18996906280517578 +0.059579208493232727 +0.1272563636302948 +0.073670297861099243 +0.14313116669654846 +0.24276857078075409 +0.22881762683391571 +0.36225935816764832 +0.35352316498756409 +0.37048178911209106 +0.34585824608802795 +0.34054705500602722 +0.082974955439567566 +0.15391238033771515 +0.086748279631137848 +0.15889787673950195 +0.25793886184692383 +0.25264120101928711 +0.084909401834011078 +0.15798646211624146 +0.077894322574138641 +0.15149223804473877 +0.25443145632743835 +0.25854921340942383 +0.38218346238136292 +0.38069522380828857 +0.38098374009132385 +0.37674015760421753 +0.066512890160083771 +0.14006371796131134 +0.053301114588975906 +0.12593933939933777 +0.23408575356006622 +0.24577575922012329 +0.040962181985378265 +0.11152496933937073 +0.031476318836212158 +0.098640128970146179 +0.20781946182250977 +0.22101669013500214 +0.36332190036773682 +0.3708515465259552 +0.35505637526512146 +0.37699070572853088 +0.02627725712954998 +0.088883057236671448 +0.024242755025625229 +0.083110034465789795 +0.18772834539413452 +0.1958729475736618 +0.024178223684430122 +0.082029208540916443 +0.18574611842632294 +0.33871176838874817 +0.34081399440765381 +0.34688740968704224 +0.00011443332914495841 +0.00010456666495883837 +0.00026648462517186999 +0.00012556638102978468 +0.00022147920390125364 +0.00089996610768139362 +0.0042235422879457474 +0.0017085127765312791 +0.00055638293270021677 +0.00061543146148324013 +0.0023469417355954647 +0.0011266659712418914 +0.004001762717962265 +0.012923505157232285 +0.008479720912873745 +0.023709699511528015 +0.014231662265956402 +0.033113040030002594 +0.0077419676817953587 +0.004122583195567131 +0.0015680997166782618 +0.0052147586829960346 +0.0018987143412232399 +0.0059986892156302929 +0.017451677471399307 +0.01593736931681633 +0.0020855364855378866 +0.0064025186002254486 +0.0019977989140897989 +0.006029962096363306 +0.015826422721147537 +0.017563918605446815 +0.040759500116109848 +0.041842512786388397 +0.036057956516742706 +0.039300449192523956 +0.0015416997484862804 +0.0045773810707032681 +0.0009430198697373271 +0.0027422329876571894 +0.0073906918987631798 +0.011974923312664032 +0.00045089275226928294 +0.001288877334445715 +0.00013498438056558371 +0.00038196422974579036 +0.001064843381755054 +0.0035711301025003195 +0.010769587010145187 +0.018855882808566093 +0.0051479456014931202 +0.028046105057001114 +1.8910937797045335e-05 +4.1596300434321165e-05 +2.9458218705258332e-05 +2.0295025024097413e-05 +2.2945690943743102e-05 +0.00010920152999460697 +8.1416488683316857e-05 +6.1882383306510746e-05 +0.00011523705325089395 +0.0026821356732398272 +0.0024702560622245073 +0.0028323973529040813 +2.9748654924333096e-05 +3.4450658858986571e-05 +6.0423790273489431e-05 +5.2893774409312755e-05 +3.2201805879594758e-05 +5.3880325140198693e-05 +7.1918104367796332e-05 +8.8412714831065387e-05 +8.6278261733241379e-05 +2.1798572561237961e-05 +3.4636337659321725e-05 +1.0027056305261794e-05 +1.5708528735558502e-05 +4.4809461542172357e-05 +5.1445407734718174e-05 +0.00012179365148767829 +8.6649932200089097e-05 +0.00020623981254175305 +0.00010127819405170158 +0.00011417248606448993 +3.6691008062916808e-06 +9.1724696176243015e-06 +1.4911998960087658e-06 +9.8137170425616205e-06 +9.1764959506690502e-05 +6.4887266489677131e-05 +1.2204671975268866e-06 +1.0534184184507467e-05 +2.3630609575775452e-06 +1.0224066500086337e-05 +9.9600714747793972e-05 +0.0001035831737681292 +0.00048403002438135445 +0.00043347722385078669 +0.00046468895743601024 +0.00032555294455960393 +4.608255039784126e-06 +8.8313354353886098e-06 +6.9649781835323665e-06 +6.6623888415051624e-06 +5.8111472753807902e-05 +8.2872400525957346e-05 +8.4916418927605264e-06 +4.2197225411655381e-06 +9.2239024525042623e-06 +2.8240169740456622e-06 +1.0667537026165519e-05 +3.0929273634683341e-05 +0.0001269348431378603 +0.00024872209178283811 +3.957715307478793e-05 +0.00037452607648447156 +9.7423635452287272e-06 +4.396237272885628e-06 +1.2920089830004144e-05 +1.3640866200148594e-05 +2.202731411671266e-05 +6.4165205913013779e-06 +2.0959696485078894e-05 +3.3433589123887941e-05 +5.6631048209965229e-05 +7.8693417890463024e-05 +2.9376331440289505e-05 +1.0094765457324684e-05 +0.00010290280624758452 +7.5855525210499763e-05 +6.5501968492753804e-05 +8.6286498117260635e-05 +4.8550406063441187e-05 +4.2803731048479676e-05 +2.9351662305998616e-05 +4.1029688873095438e-05 +5.0607799494173378e-05 +2.5846598873613402e-05 +2.2394904590328224e-05 +1.0256934729113709e-05 +8.0325471571995877e-06 +7.2486432145524304e-06 +1.6959593267529272e-05 +1.6101881556096487e-05 +2.4131490135914646e-05 +8.0273257481167093e-06 +2.8293212380958721e-05 +2.9282462492119521e-05 +3.1406664220412495e-06 +2.3921804768178845e-06 +1.2820745496355812e-06 +2.1058858692413196e-06 +3.0628407330368645e-06 +3.1760996535012964e-06 +2.3606530703546014e-06 +4.3304289647494443e-06 +1.4002148418512661e-05 +1.5414092558785342e-05 +1.2857331057603005e-05 +5.2520640565489884e-06 +3.5896855479222722e-06 +2.6142333808820695e-06 +7.4784870776056778e-06 +3.740514785022242e-06 +4.3054817069787532e-05 +4.0833623643266037e-05 +8.1755686551332474e-05 +7.1645008574705571e-05 +4.5841981773264706e-05 +2.8376414775266312e-05 +0.00011975521920248866 +9.7442483820486814e-05 +0.0001457954931538552 +0.00011347068357281387 +6.539632158819586e-05 +5.8705485571408644e-05 +2.7663032597047277e-05 +2.2586567865801044e-05 +2.969870729430113e-05 +1.4813690540904645e-05 +0.0001509835128672421 +0.00011698646994773299 +0.00014152348740026355 +0.00011167004413437098 +6.2107050325721502e-05 +6.5654610807541758e-05 +0.00012507205246947706 +0.00010154642222914845 +5.7319666666444391e-05 +2.8603708415175788e-05 +2.8097349058953114e-05 +2.9110891773598269e-05 +0.00050733989337459207 +0.00018843315774574876 +7.3140625318046659e-05 +0.00019194050400983542 +0.0013769890647381544 +0.00052657164633274078 +0.00015794328646734357 +3.6619767342926934e-05 +7.9345154517795891e-05 +0.0044190092012286186 +0.0016646938165649772 +0.007562185637652874 +0.0026621294673532248 +0.00072698498843237758 +0.00047083522076718509 +6.7323140683583915e-05 +4.8042973503470421e-05 +8.4597966633737087e-05 +5.2949348173569888e-05 +7.7849188528489321e-05 +0.0090397540479898453 +0.0027333602774888277 +0.0090965256094932556 +0.0022698559332638979 +0.00051341229118406773 +0.0006942584877833724 +0.0082777468487620354 +0.0017881013918668032 +0.0069196717813611031 +0.0013776756823062897 +0.00026034249458462 +0.00036148750223219395 +3.7226640415610746e-05 +5.3079449571669102e-05 +3.423444286454469e-05 +7.5108626333530992e-05 +0.0053003397770226002 +0.0010469019180163741 +0.0035574708599597216 +0.0007434748113155365 +0.0001723362656775862 +0.00020376857719384134 +0.0018974244594573975 +0.00044380401959642768 +0.001078728586435318 +0.00035722079337574542 +0.00017351929272990674 +0.00015192809223663062 +0.00011286017252132297 +7.5658310379367322e-05 +0.00014399929204955697 +4.7792866098461673e-05 +0.0016387391369789839 +0.00063509761821478605 +0.0022356202825903893 +0.00084813189459964633 +0.00029659835854545236 +0.0002555942046456039 +0.0015730854356661439 +0.00059190118918195367 +0.00020599027629941702 +0.00011270138929830864 +0.00014276104047894478 +0.00015426050231326371 +0.098576940596103668 +0.078127078711986542 +0.11663533747196198 +0.035728007555007935 +0.027385711669921875 +0.021255519241094589 +0.13615706562995911 +0.15516431629657745 +0.061646081507205963 +0.048216678202152252 +0.012590794824063778 +0.0059709879569709301 +0.020064093172550201 +0.0030690524727106094 +0.0030321788508445024 +0.16996961832046509 +0.17246048152446747 +0.075591340661048889 +0.072005279362201691 +0.15622274577617645 +0.12945719063282013 +0.057935953140258789 +0.069762520492076874 +0.025853240862488747 +0.027309626340866089 +0.0219570342451334 +0.025274388492107391 +0.10143224149942398 +0.073207132518291473 +0.029449708759784698 +0.044083859771490097 +0.045536555349826813 +0.025557177141308784 +0.0059005911462008953 +0.015313250012695789 +0.0058306967839598656 +0.011168601922690868 +0.0027708364650607109 +0.016821267083287239 +0.020321046933531761 +0.02999095618724823 +0.0090964799746870995 +0.0048269988037645817 +0.052383452653884888 +0.015009399503469467 +0.0043115150183439255 +0.0048991371877491474 +0.0034215417690575123 diff --git a/subdivgui/torus.obj b/subdivgui/torus.obj new file mode 100644 index 0000000..3366646 --- /dev/null +++ b/subdivgui/torus.obj @@ -0,0 +1,66 @@ +# OBJ: +v 1.25052 0.517982 0.353553 +v 0.597239 0.247384 0.353553 +v 0.597239 0.247384 -0.353553 +v 1.25052 0.517982 -0.353553 +v 0.517982 1.25052 0.353553 +v 0.247384 0.597239 0.353553 +v 0.247384 0.597239 -0.353553 +v 0.517982 1.25052 -0.353553 +v -0.517982 1.25052 0.353553 +v -0.247384 0.597239 0.353553 +v -0.247384 0.597239 -0.353553 +v -0.517982 1.25052 -0.353553 +v -1.25052 0.517982 0.353553 +v -0.597239 0.247384 0.353553 +v -0.597239 0.247384 -0.353553 +v -1.25052 0.517982 -0.353553 +v -1.25052 -0.517982 0.353553 +v -0.597239 -0.247384 0.353553 +v -0.597239 -0.247384 -0.353553 +v -1.25052 -0.517982 -0.353553 +v -0.517982 -1.25052 0.353553 +v -0.247384 -0.597239 0.353553 +v -0.247384 -0.597239 -0.353553 +v -0.517982 -1.25052 -0.353553 +v 0.517982 -1.25052 0.353553 +v 0.247384 -0.597239 0.353553 +v 0.247384 -0.597239 -0.353553 +v 0.517982 -1.25052 -0.353553 +v 1.25052 -0.517982 0.353553 +v 0.597239 -0.247384 0.353553 +v 0.597239 -0.247384 -0.353553 +v 1.25052 -0.517982 -0.353553 + +f 5 6 2 1 +f 6 7 3 2 +f 7 8 4 3 +f 8 5 1 4 +f 9 10 6 5 +f 10 11 7 6 +f 11 12 8 7 +f 12 9 5 8 +f 13 14 10 9 +f 14 15 11 10 +f 15 16 12 11 +f 16 13 9 12 +f 17 18 14 13 +f 18 19 15 14 +f 19 20 16 15 +f 20 17 13 16 +f 21 22 18 17 +f 22 23 19 18 +f 23 24 20 19 +f 24 21 17 20 +f 25 26 22 21 +f 26 27 23 22 +f 27 28 24 23 +f 28 25 21 24 +f 29 30 26 25 +f 30 31 27 26 +f 31 32 28 27 +f 32 29 25 28 +f 1 2 30 29 +f 2 3 31 30 +f 3 4 32 31 +f 4 1 29 32 diff --git a/subdivgui/torus.tgf b/subdivgui/torus.tgf new file mode 100644 index 0000000..1925d90 --- /dev/null +++ b/subdivgui/torus.tgf @@ -0,0 +1,10 @@ + 1 -0.97850775718688965 -0.44190680980682373 0 + 2 -0.97500056090718179 0.49802185198690269 0 + 3 0.92239266633987427 -0.45593556761741638 0 + 4 0.92239260537065337 0.57518016513747017 2.3841862018575111e-07 + 5 0.35773405348396409 1.0556660886616451 1.1920922876756005e-07 +# + 1 2 + 3 4 + 4 5 +#